summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2009-08-07 17:50:24 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2009-08-07 17:50:24 +1000
commit9fdbbf93bb3bb2b62335e780bb94871fc39dc5d8 (patch)
tree4196a9136c92beb75a3cb893efdd6df375ebf6e1
parent790fbe2a7af552d7300ca69a4d6da0b38e6bfd09 (diff)
parentaac53f92ef0abde7a33a5cda1c6af2ea61f58ad5 (diff)
Merge branch 'quilt/usb'
Conflicts: drivers/usb/gadget/m66592-udc.c drivers/usb/host/r8a66597-hcd.c
-rw-r--r--Documentation/usb/usbmon.txt2
-rw-r--r--drivers/usb/Kconfig3
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/class/usbtmc.c56
-rw-r--r--drivers/usb/core/devio.c166
-rw-r--r--drivers/usb/core/driver.c41
-rw-r--r--drivers/usb/core/generic.c4
-rw-r--r--drivers/usb/core/hub.c112
-rw-r--r--drivers/usb/core/message.c32
-rw-r--r--drivers/usb/core/usb.h7
-rw-r--r--drivers/usb/gadget/Kconfig4
-rw-r--r--drivers/usb/gadget/amd5536udc.c56
-rw-r--r--drivers/usb/gadget/audio.c24
-rw-r--r--drivers/usb/gadget/dummy_hcd.c5
-rw-r--r--drivers/usb/gadget/f_audio.c97
-rw-r--r--drivers/usb/gadget/gmidi.c8
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c49
-rw-r--r--drivers/usb/gadget/pxa25x_udc.h1
-rw-r--r--drivers/usb/gadget/s3c-hsotg.c6
-rw-r--r--drivers/usb/host/Kconfig18
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-atmel.c230
-rw-r--r--drivers/usb/host/ehci-au1xxx.c29
-rw-r--r--drivers/usb/host/ehci-dbg.c46
-rw-r--r--drivers/usb/host/ehci-hcd.c37
-rw-r--r--drivers/usb/host/ehci-hub.c62
-rw-r--r--drivers/usb/host/ehci-mem.c26
-rw-r--r--drivers/usb/host/ehci-pci.c3
-rw-r--r--drivers/usb/host/ehci-q.c50
-rw-r--r--drivers/usb/host/ehci-sched.c66
-rw-r--r--drivers/usb/host/ehci-w90x900.c181
-rw-r--r--drivers/usb/host/ehci.h13
-rw-r--r--drivers/usb/host/isp1362-hcd.c2905
-rw-r--r--drivers/usb/host/isp1362.h1079
-rw-r--r--drivers/usb/host/isp1760-hcd.c4
-rw-r--r--drivers/usb/host/isp1760-hcd.h2
-rw-r--r--drivers/usb/host/isp1760-if.c21
-rw-r--r--drivers/usb/host/ohci-at91.c2
-rw-r--r--drivers/usb/host/ohci-au1xxx.c27
-rw-r--r--drivers/usb/host/ohci-hcd.c1
-rw-r--r--drivers/usb/host/ohci-pxa27x.c14
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c1
-rw-r--r--drivers/usb/host/uhci-q.c1
-rw-r--r--drivers/usb/host/whci/asl.c12
-rw-r--r--drivers/usb/host/whci/hcd.c8
-rw-r--r--drivers/usb/host/whci/pzl.c12
-rw-r--r--drivers/usb/host/whci/qset.c4
-rw-r--r--drivers/usb/host/whci/whci-hc.h1
-rw-r--r--drivers/usb/misc/idmouse.c21
-rw-r--r--drivers/usb/misc/ldusb.c6
-rw-r--r--drivers/usb/misc/legousbtower.c6
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c53
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.h2
-rw-r--r--drivers/usb/misc/usbsevseg.c69
-rw-r--r--drivers/usb/mon/Kconfig4
-rw-r--r--drivers/usb/mon/Makefile2
-rw-r--r--drivers/usb/mon/mon_bin.c12
-rw-r--r--drivers/usb/mon/mon_dma.c95
-rw-r--r--drivers/usb/mon/mon_main.c1
-rw-r--r--drivers/usb/mon/mon_text.c14
-rw-r--r--drivers/usb/mon/usb_mon.h14
-rw-r--r--drivers/usb/serial/moto_modem.c2
-rw-r--r--drivers/usb/serial/option.c134
-rw-r--r--drivers/usb/serial/usb-serial.c16
-rw-r--r--drivers/usb/storage/datafab.c4
-rw-r--r--drivers/usb/storage/jumpshot.c2
-rw-r--r--include/linux/usb.h25
-rw-r--r--include/linux/usb/audio.h287
-rw-r--r--include/linux/usb/ch9.h8
-rw-r--r--include/linux/usb/ehci_def.h13
-rw-r--r--include/linux/usb/isp1362.h46
-rw-r--r--include/linux/usb/isp1760.h18
-rw-r--r--include/linux/usbdevice_fs.h2
73 files changed, 5645 insertions, 741 deletions
diff --git a/Documentation/usb/usbmon.txt b/Documentation/usb/usbmon.txt
index 6c3c625b7f30..4efd8470921c 100644
--- a/Documentation/usb/usbmon.txt
+++ b/Documentation/usb/usbmon.txt
@@ -305,7 +305,7 @@ Before the call, hdr, data, and alloc should be filled. Upon return, the area
pointed by hdr contains the next event structure, and the data buffer contains
the data, if any. The event is removed from the kernel buffer.
-The MON_IOCX_GET copies 48 bytes, MON_IOCX_GETX copies 64 bytes.
+The MON_IOCX_GET copies 48 bytes to hdr area, MON_IOCX_GETX copies 64 bytes.
MON_IOCX_MFETCH, defined as _IOWR(MON_IOC_MAGIC, 7, struct mon_mfetch_arg)
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index dcd49f1e96d0..ebd7237230e3 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -39,6 +39,7 @@ config USB_ARCH_HAS_OHCI
default y if ARCH_AT91
default y if ARCH_PNX4008 && I2C
default y if MFD_TC6393XB
+ default y if ARCH_W90X900
# PPC:
default y if STB03xxx
default y if PPC_MPC52xx
@@ -58,6 +59,8 @@ config USB_ARCH_HAS_EHCI
default y if PPC_83xx
default y if SOC_AU1200
default y if ARCH_IXP4XX
+ default y if ARCH_W90X900
+ default y if ARCH_AT91SAM9G45
default PCI
# ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 19cb7d5480d7..dbe4fb694ad2 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_FHCI_HCD) += host/
obj-$(CONFIG_USB_XHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/
+obj-$(CONFIG_USB_ISP1362_HCD) += host/
obj-$(CONFIG_USB_U132_HCD) += host/
obj-$(CONFIG_USB_R8A66597_HCD) += host/
obj-$(CONFIG_USB_HWA_HCD) += host/
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index b09a527f7341..40ef4da786d8 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -86,6 +86,8 @@ struct usbtmc_device_data {
bool TermCharEnabled;
bool auto_abort;
+ bool zombie; /* fd of disconnected device */
+
struct usbtmc_dev_capabilities capabilities;
struct kref kref;
struct mutex io_mutex; /* only one i/o function running at a time */
@@ -367,13 +369,13 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
{
struct usbtmc_device_data *data;
struct device *dev;
- unsigned long int n_characters;
+ u32 n_characters;
u8 *buffer;
int actual;
- int done;
- int remaining;
+ size_t done;
+ size_t remaining;
int retval;
- int this_part;
+ size_t this_part;
/* Get pointer to private data structure */
data = filp->private_data;
@@ -384,6 +386,10 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
return -ENOMEM;
mutex_lock(&data->io_mutex);
+ if (data->zombie) {
+ retval = -ENODEV;
+ goto exit;
+ }
remaining = count;
done = 0;
@@ -455,6 +461,18 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
(buffer[6] << 16) +
(buffer[7] << 24);
+ /* Ensure the instrument doesn't lie about it */
+ if(n_characters > actual - 12) {
+ dev_err(dev, "Device lies about message size: %u > %d\n", n_characters, actual - 12);
+ n_characters = actual - 12;
+ }
+
+ /* Ensure the instrument doesn't send more back than requested */
+ if(n_characters > this_part) {
+ dev_err(dev, "Device returns more than requested: %zu > %zu\n", done + n_characters, done + this_part);
+ n_characters = this_part;
+ }
+
/* Copy buffer to user space */
if (copy_to_user(buf + done, &buffer[12], n_characters)) {
/* There must have been an addressing problem */
@@ -465,6 +483,8 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
done += n_characters;
if (n_characters < USBTMC_SIZE_IOBUFFER)
remaining = 0;
+ else
+ remaining -= n_characters;
}
/* Update file position value */
@@ -496,6 +516,10 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
return -ENOMEM;
mutex_lock(&data->io_mutex);
+ if (data->zombie) {
+ retval = -ENODEV;
+ goto exit;
+ }
remaining = count;
done = 0;
@@ -925,6 +949,10 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
data = file->private_data;
mutex_lock(&data->io_mutex);
+ if (data->zombie) {
+ retval = -ENODEV;
+ goto skip_io_on_zombie;
+ }
switch (cmd) {
case USBTMC_IOCTL_CLEAR_OUT_HALT:
@@ -952,6 +980,7 @@ static long usbtmc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
+skip_io_on_zombie:
mutex_unlock(&data->io_mutex);
return retval;
}
@@ -995,6 +1024,7 @@ static int usbtmc_probe(struct usb_interface *intf,
usb_set_intfdata(intf, data);
kref_init(&data->kref);
mutex_init(&data->io_mutex);
+ data->zombie = 0;
/* Initialize USBTMC bTag and other fields */
data->bTag = 1;
@@ -1065,14 +1095,30 @@ static void usbtmc_disconnect(struct usb_interface *intf)
usb_deregister_dev(intf, &usbtmc_class);
sysfs_remove_group(&intf->dev.kobj, &capability_attr_grp);
sysfs_remove_group(&intf->dev.kobj, &data_attr_grp);
+ mutex_lock(&data->io_mutex);
+ data->zombie = 1;
+ mutex_unlock(&data->io_mutex);
kref_put(&data->kref, usbtmc_delete);
}
+static int usbtmc_suspend (struct usb_interface *intf, pm_message_t message)
+{
+ /* this driver does not have pending URBs */
+ return 0;
+}
+
+static int usbtmc_resume (struct usb_interface *intf)
+{
+ return 0;
+}
+
static struct usb_driver usbtmc_driver = {
.name = "usbtmc",
.id_table = usbtmc_devices,
.probe = usbtmc_probe,
- .disconnect = usbtmc_disconnect
+ .disconnect = usbtmc_disconnect,
+ .suspend = usbtmc_suspend,
+ .resume = usbtmc_resume,
};
static int __init usbtmc_init(void)
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 4247eccf858c..a1add776e89a 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -52,6 +52,7 @@
#include "hcd.h" /* for usbcore internals */
#include "usb.h"
+#include "hub.h"
#define USB_MAXBUS 64
#define USB_DEVICE_MAX USB_MAXBUS * 128
@@ -99,11 +100,15 @@ MODULE_PARM_DESC(usbfs_snoop, "true to log all usbfs traffic");
dev_info(dev , format , ## arg); \
} while (0)
-#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
+enum snoop_when {
+ SUBMIT, COMPLETE
+};
+#define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0)
#define MAX_USBFS_BUFFER_SIZE 16384
+
static int connected(struct dev_state *ps)
{
return (!list_empty(&ps->list) &&
@@ -300,24 +305,42 @@ static struct async *async_getpending(struct dev_state *ps,
return NULL;
}
-static void snoop_urb(struct urb *urb, void __user *userurb)
+static void snoop_urb(struct usb_device *udev,
+ void __user *userurb, int pipe, unsigned length,
+ int timeout_or_status, enum snoop_when when)
{
- unsigned j;
- unsigned char *data = urb->transfer_buffer;
+ static const char *types[] = {"isoc", "int", "ctrl", "bulk"};
+ static const char *dirs[] = {"out", "in"};
+ int ep;
+ const char *t, *d;
if (!usbfs_snoop)
return;
- dev_info(&urb->dev->dev, "direction=%s\n",
- usb_urb_dir_in(urb) ? "IN" : "OUT");
- dev_info(&urb->dev->dev, "userurb=%p\n", userurb);
- dev_info(&urb->dev->dev, "transfer_buffer_length=%u\n",
- urb->transfer_buffer_length);
- dev_info(&urb->dev->dev, "actual_length=%u\n", urb->actual_length);
- dev_info(&urb->dev->dev, "data: ");
- for (j = 0; j < urb->transfer_buffer_length; ++j)
- printk("%02x ", data[j]);
- printk("\n");
+ ep = usb_pipeendpoint(pipe);
+ t = types[usb_pipetype(pipe)];
+ d = dirs[!!usb_pipein(pipe)];
+
+ if (userurb) { /* Async */
+ if (when == SUBMIT)
+ dev_info(&udev->dev, "userurb %p, ep%d %s-%s, "
+ "length %u\n",
+ userurb, ep, t, d, length);
+ else
+ dev_info(&udev->dev, "userurb %p, ep%d %s-%s, "
+ "actual_length %u status %d\n",
+ userurb, ep, t, d, length,
+ timeout_or_status);
+ } else {
+ if (when == SUBMIT)
+ dev_info(&udev->dev, "ep%d %s-%s, length %u, "
+ "timeout %d\n",
+ ep, t, d, length, timeout_or_status);
+ else
+ dev_info(&udev->dev, "ep%d %s-%s, actual_length %u, "
+ "status %d\n",
+ ep, t, d, length, timeout_or_status);
+ }
}
static void async_completed(struct urb *urb)
@@ -346,7 +369,8 @@ static void async_completed(struct urb *urb)
secid = as->secid;
}
snoop(&urb->dev->dev, "urb complete\n");
- snoop_urb(urb, as->userurb);
+ snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
+ as->status, COMPLETE);
spin_unlock(&ps->lock);
if (signr)
@@ -655,6 +679,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
struct async *as;
usb_lock_device(dev);
+ usb_hub_release_all_ports(dev, ps);
/* Protect against simultaneous open */
mutex_lock(&usbfs_mutex);
@@ -688,7 +713,7 @@ static int proc_control(struct dev_state *ps, void __user *arg)
unsigned int tmo;
unsigned char *tbuf;
unsigned wLength;
- int i, j, ret;
+ int i, pipe, ret;
if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
return -EFAULT;
@@ -708,24 +733,17 @@ static int proc_control(struct dev_state *ps, void __user *arg)
free_page((unsigned long)tbuf);
return -EINVAL;
}
- snoop(&dev->dev, "control read: bRequest=%02x "
- "bRrequestType=%02x wValue=%04x "
- "wIndex=%04x wLength=%04x\n",
- ctrl.bRequest, ctrl.bRequestType, ctrl.wValue,
- ctrl.wIndex, ctrl.wLength);
+ pipe = usb_rcvctrlpipe(dev, 0);
+ snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT);
usb_unlock_device(dev);
- i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest,
+ i = usb_control_msg(dev, pipe, ctrl.bRequest,
ctrl.bRequestType, ctrl.wValue, ctrl.wIndex,
tbuf, ctrl.wLength, tmo);
usb_lock_device(dev);
+ snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE);
+
if ((i > 0) && ctrl.wLength) {
- if (usbfs_snoop) {
- dev_info(&dev->dev, "control read: data ");
- for (j = 0; j < i; ++j)
- printk("%02x ", (u8)(tbuf)[j]);
- printk("\n");
- }
if (copy_to_user(ctrl.data, tbuf, i)) {
free_page((unsigned long)tbuf);
return -EFAULT;
@@ -738,22 +756,15 @@ static int proc_control(struct dev_state *ps, void __user *arg)
return -EFAULT;
}
}
- snoop(&dev->dev, "control write: bRequest=%02x "
- "bRrequestType=%02x wValue=%04x "
- "wIndex=%04x wLength=%04x\n",
- ctrl.bRequest, ctrl.bRequestType, ctrl.wValue,
- ctrl.wIndex, ctrl.wLength);
- if (usbfs_snoop) {
- dev_info(&dev->dev, "control write: data: ");
- for (j = 0; j < ctrl.wLength; ++j)
- printk("%02x ", (unsigned char)(tbuf)[j]);
- printk("\n");
- }
+ pipe = usb_sndctrlpipe(dev, 0);
+ snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT);
+
usb_unlock_device(dev);
i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest,
ctrl.bRequestType, ctrl.wValue, ctrl.wIndex,
tbuf, ctrl.wLength, tmo);
usb_lock_device(dev);
+ snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE);
}
free_page((unsigned long)tbuf);
if (i < 0 && i != -EPIPE) {
@@ -772,7 +783,7 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
unsigned int tmo, len1, pipe;
int len2;
unsigned char *tbuf;
- int i, j, ret;
+ int i, ret;
if (copy_from_user(&bulk, arg, sizeof(bulk)))
return -EFAULT;
@@ -799,18 +810,14 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
kfree(tbuf);
return -EINVAL;
}
- snoop(&dev->dev, "bulk read: len=0x%02x timeout=%04d\n",
- bulk.len, bulk.timeout);
+ snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT);
+
usb_unlock_device(dev);
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
usb_lock_device(dev);
+ snoop_urb(dev, NULL, pipe, len2, i, COMPLETE);
+
if (!i && len2) {
- if (usbfs_snoop) {
- dev_info(&dev->dev, "bulk read: data ");
- for (j = 0; j < len2; ++j)
- printk("%02x ", (u8)(tbuf)[j]);
- printk("\n");
- }
if (copy_to_user(bulk.data, tbuf, len2)) {
kfree(tbuf);
return -EFAULT;
@@ -823,17 +830,12 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
return -EFAULT;
}
}
- snoop(&dev->dev, "bulk write: len=0x%02x timeout=%04d\n",
- bulk.len, bulk.timeout);
- if (usbfs_snoop) {
- dev_info(&dev->dev, "bulk write: data: ");
- for (j = 0; j < len1; ++j)
- printk("%02x ", (unsigned char)(tbuf)[j]);
- printk("\n");
- }
+ snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT);
+
usb_unlock_device(dev);
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
usb_lock_device(dev);
+ snoop_urb(dev, NULL, pipe, len2, i, COMPLETE);
}
kfree(tbuf);
if (i < 0)
@@ -1051,13 +1053,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
is_in = 0;
uurb->endpoint &= ~USB_DIR_IN;
}
- snoop(&ps->dev->dev, "control urb: bRequest=%02x "
- "bRrequestType=%02x wValue=%04x "
- "wIndex=%04x wLength=%04x\n",
- dr->bRequest, dr->bRequestType,
- __le16_to_cpup(&dr->wValue),
- __le16_to_cpup(&dr->wIndex),
- __le16_to_cpup(&dr->wLength));
break;
case USBDEVFS_URB_TYPE_BULK:
@@ -1070,7 +1065,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
uurb->number_of_packets = 0;
if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
return -EINVAL;
- snoop(&ps->dev->dev, "bulk urb\n");
break;
case USBDEVFS_URB_TYPE_ISO:
@@ -1102,7 +1096,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
return -EINVAL;
}
uurb->buffer_length = totlen;
- snoop(&ps->dev->dev, "iso urb\n");
break;
case USBDEVFS_URB_TYPE_INTERRUPT:
@@ -1111,7 +1104,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
return -EINVAL;
if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
return -EINVAL;
- snoop(&ps->dev->dev, "interrupt urb\n");
break;
default:
@@ -1198,11 +1190,14 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
return -EFAULT;
}
}
- snoop_urb(as->urb, as->userurb);
+ snoop_urb(ps->dev, as->userurb, as->urb->pipe,
+ as->urb->transfer_buffer_length, 0, SUBMIT);
async_newpending(as);
if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) {
dev_printk(KERN_DEBUG, &ps->dev->dev,
"usbfs: usb_submit_urb returned %d\n", ret);
+ snoop_urb(ps->dev, as->userurb, as->urb->pipe,
+ 0, ret, COMPLETE);
async_removepending(as);
free_async(as);
return ret;
@@ -1548,6 +1543,29 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
}
#endif
+static int proc_claim_port(struct dev_state *ps, void __user *arg)
+{
+ unsigned portnum;
+ int rc;
+
+ if (get_user(portnum, (unsigned __user *) arg))
+ return -EFAULT;
+ rc = usb_hub_claim_port(ps->dev, portnum, ps);
+ if (rc == 0)
+ snoop(&ps->dev->dev, "port %d claimed by process %d: %s\n",
+ portnum, task_pid_nr(current), current->comm);
+ return rc;
+}
+
+static int proc_release_port(struct dev_state *ps, void __user *arg)
+{
+ unsigned portnum;
+
+ if (get_user(portnum, (unsigned __user *) arg))
+ return -EFAULT;
+ return usb_hub_release_port(ps->dev, portnum, ps);
+}
+
/*
* NOTE: All requests here that have interface numbers as parameters
* are assuming that somehow the configuration has been prevented from
@@ -1645,7 +1663,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file,
break;
case USBDEVFS_REAPURBNDELAY32:
- snoop(&dev->dev, "%s: REAPURBDELAY32\n", __func__);
+ snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
ret = proc_reapurbnonblock_compat(ps, p);
break;
@@ -1666,7 +1684,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file,
break;
case USBDEVFS_REAPURBNDELAY:
- snoop(&dev->dev, "%s: REAPURBDELAY\n", __func__);
+ snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
ret = proc_reapurbnonblock(ps, p);
break;
@@ -1689,6 +1707,16 @@ static int usbdev_ioctl(struct inode *inode, struct file *file,
snoop(&dev->dev, "%s: IOCTL\n", __func__);
ret = proc_ioctl_default(ps, p);
break;
+
+ case USBDEVFS_CLAIM_PORT:
+ snoop(&dev->dev, "%s: CLAIM_PORT\n", __func__);
+ ret = proc_claim_port(ps, p);
+ break;
+
+ case USBDEVFS_RELEASE_PORT:
+ snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__);
+ ret = proc_release_port(ps, p);
+ break;
}
usb_unlock_device(dev);
if (ret >= 0)
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 69e5773abfce..1c976c141f33 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -207,6 +207,9 @@ static int usb_probe_interface(struct device *dev)
intf->needs_binding = 0;
+ if (usb_device_is_owned(udev))
+ return -ENODEV;
+
if (udev->authorized == 0) {
dev_err(&intf->dev, "Device is not authorized for usage\n");
return -ENODEV;
@@ -232,7 +235,7 @@ static int usb_probe_interface(struct device *dev)
/* The interface should always appear to be in use
* unless the driver suports autosuspend.
*/
- intf->pm_usage_cnt = !(driver->supports_autosuspend);
+ atomic_set(&intf->pm_usage_cnt, !driver->supports_autosuspend);
/* Carry out a deferred switch to altsetting 0 */
if (intf->needs_altsetting0) {
@@ -344,7 +347,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
usb_pm_lock(udev);
iface->condition = USB_INTERFACE_BOUND;
mark_active(iface);
- iface->pm_usage_cnt = !(driver->supports_autosuspend);
+ atomic_set(&iface->pm_usage_cnt, !driver->supports_autosuspend);
usb_pm_unlock(udev);
/* if interface was already added, bind now; else let
@@ -1065,7 +1068,7 @@ static int autosuspend_check(struct usb_device *udev, int reschedule)
intf = udev->actconfig->interface[i];
if (!is_active(intf))
continue;
- if (intf->pm_usage_cnt > 0)
+ if (atomic_read(&intf->pm_usage_cnt) > 0)
return -EBUSY;
if (intf->needs_remote_wakeup &&
!udev->do_remote_wakeup) {
@@ -1461,17 +1464,19 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
status = -ENODEV;
else {
udev->auto_pm = 1;
- intf->pm_usage_cnt += inc_usage_cnt;
+ atomic_add(inc_usage_cnt, &intf->pm_usage_cnt);
udev->last_busy = jiffies;
- if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) {
+ if (inc_usage_cnt >= 0 &&
+ atomic_read(&intf->pm_usage_cnt) > 0) {
if (udev->state == USB_STATE_SUSPENDED)
status = usb_resume_both(udev,
PMSG_AUTO_RESUME);
if (status != 0)
- intf->pm_usage_cnt -= inc_usage_cnt;
+ atomic_sub(inc_usage_cnt, &intf->pm_usage_cnt);
else
udev->last_busy = jiffies;
- } else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) {
+ } else if (inc_usage_cnt <= 0 &&
+ atomic_read(&intf->pm_usage_cnt) <= 0) {
status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
}
}
@@ -1516,7 +1521,7 @@ void usb_autopm_put_interface(struct usb_interface *intf)
status = usb_autopm_do_interface(intf, -1);
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
- __func__, status, intf->pm_usage_cnt);
+ __func__, status, atomic_read(&intf->pm_usage_cnt));
}
EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
@@ -1544,10 +1549,10 @@ void usb_autopm_put_interface_async(struct usb_interface *intf)
status = -ENODEV;
} else {
udev->last_busy = jiffies;
- --intf->pm_usage_cnt;
+ atomic_dec(&intf->pm_usage_cnt);
if (udev->autosuspend_disabled || udev->autosuspend_delay < 0)
status = -EPERM;
- else if (intf->pm_usage_cnt <= 0 &&
+ else if (atomic_read(&intf->pm_usage_cnt) <= 0 &&
!timer_pending(&udev->autosuspend.timer)) {
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
round_jiffies_up_relative(
@@ -1555,7 +1560,7 @@ void usb_autopm_put_interface_async(struct usb_interface *intf)
}
}
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
- __func__, status, intf->pm_usage_cnt);
+ __func__, status, atomic_read(&intf->pm_usage_cnt));
}
EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async);
@@ -1599,7 +1604,7 @@ int usb_autopm_get_interface(struct usb_interface *intf)
status = usb_autopm_do_interface(intf, 1);
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
- __func__, status, intf->pm_usage_cnt);
+ __func__, status, atomic_read(&intf->pm_usage_cnt));
return status;
}
EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
@@ -1627,10 +1632,14 @@ int usb_autopm_get_interface_async(struct usb_interface *intf)
status = -ENODEV;
else if (udev->autoresume_disabled)
status = -EPERM;
- else if (++intf->pm_usage_cnt > 0 && udev->state == USB_STATE_SUSPENDED)
- queue_work(ksuspend_usb_wq, &udev->autoresume);
+ else {
+ atomic_inc(&intf->pm_usage_cnt);
+ if (atomic_read(&intf->pm_usage_cnt) > 0 &&
+ udev->state == USB_STATE_SUSPENDED)
+ queue_work(ksuspend_usb_wq, &udev->autoresume);
+ }
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
- __func__, status, intf->pm_usage_cnt);
+ __func__, status, atomic_read(&intf->pm_usage_cnt));
return status;
}
EXPORT_SYMBOL_GPL(usb_autopm_get_interface_async);
@@ -1652,7 +1661,7 @@ int usb_autopm_set_interface(struct usb_interface *intf)
status = usb_autopm_do_interface(intf, 0);
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
- __func__, status, intf->pm_usage_cnt);
+ __func__, status, atomic_read(&intf->pm_usage_cnt));
return status;
}
EXPORT_SYMBOL_GPL(usb_autopm_set_interface);
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index 30ecac3af15a..05e6d313961e 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -158,7 +158,9 @@ static int generic_probe(struct usb_device *udev)
/* Choose and set the configuration. This registers the interfaces
* with the driver core and lets interface drivers bind to them.
*/
- if (udev->authorized == 0)
+ if (usb_device_is_owned(udev))
+ ; /* Don't configure if the device is owned */
+ else if (udev->authorized == 0)
dev_err(&udev->dev, "Device is not authorized for usage\n");
else {
c = usb_choose_configuration(udev);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 71f86c60d83c..a880516020f3 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -78,6 +78,7 @@ struct usb_hub {
u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds;
struct delayed_work init_work;
+ void **port_owners;
};
@@ -162,8 +163,10 @@ static inline char *portspeed(int portstatus)
}
/* Note that hdev or one of its children must be locked! */
-static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev)
+static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
{
+ if (!hdev || !hdev->actconfig)
+ return NULL;
return usb_get_intfdata(hdev->actconfig->interface[0]);
}
@@ -372,7 +375,7 @@ static void kick_khubd(struct usb_hub *hub)
unsigned long flags;
/* Suppress autosuspend until khubd runs */
- to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
+ atomic_set(&to_usb_interface(hub->intfdev)->pm_usage_cnt, 1);
spin_lock_irqsave(&hub_event_lock, flags);
if (!hub->disconnected && list_empty(&hub->event_list)) {
@@ -384,8 +387,10 @@ static void kick_khubd(struct usb_hub *hub)
void usb_kick_khubd(struct usb_device *hdev)
{
- /* FIXME: What if hdev isn't bound to the hub driver? */
- kick_khubd(hdev_to_hub(hdev));
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ if (hub)
+ kick_khubd(hub);
}
@@ -677,7 +682,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
msecs_to_jiffies(delay));
/* Suppress autosuspend until init is done */
- to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
+ atomic_set(&to_usb_interface(hub->intfdev)->
+ pm_usage_cnt, 1);
return; /* Continues at init2: below */
} else {
hub_power_on(hub, true);
@@ -860,19 +866,17 @@ static int hub_configure(struct usb_hub *hub,
u16 wHubCharacteristics;
unsigned int pipe;
int maxp, ret;
- char *message;
+ char *message = "out of memory";
hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,
&hub->buffer_dma);
if (!hub->buffer) {
- message = "can't allocate hub irq buffer";
ret = -ENOMEM;
goto fail;
}
hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
if (!hub->status) {
- message = "can't kmalloc hub status buffer";
ret = -ENOMEM;
goto fail;
}
@@ -880,7 +884,6 @@ static int hub_configure(struct usb_hub *hub,
hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
if (!hub->descriptor) {
- message = "can't kmalloc hub descriptor";
ret = -ENOMEM;
goto fail;
}
@@ -904,6 +907,12 @@ static int hub_configure(struct usb_hub *hub,
dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
(hdev->maxchild == 1) ? "" : "s");
+ hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL);
+ if (!hub->port_owners) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
@@ -1082,7 +1091,6 @@ static int hub_configure(struct usb_hub *hub,
hub->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!hub->urb) {
- message = "couldn't allocate interrupt urb";
ret = -ENOMEM;
goto fail;
}
@@ -1131,11 +1139,13 @@ static void hub_disconnect(struct usb_interface *intf)
hub_quiesce(hub, HUB_DISCONNECT);
usb_set_intfdata (intf, NULL);
+ hub->hdev->maxchild = 0;
if (hub->hdev->speed == USB_SPEED_HIGH)
highspeed_hubs--;
usb_free_urb(hub->urb);
+ kfree(hub->port_owners);
kfree(hub->descriptor);
kfree(hub->status);
usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer,
@@ -1250,6 +1260,79 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
}
}
+/*
+ * Allow user programs to claim ports on a hub. When a device is attached
+ * to one of these "claimed" ports, the program will "own" the device.
+ */
+static int find_port_owner(struct usb_device *hdev, unsigned port1,
+ void ***ppowner)
+{
+ if (hdev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+ if (port1 == 0 || port1 > hdev->maxchild)
+ return -EINVAL;
+
+ /* This assumes that devices not managed by the hub driver
+ * will always have maxchild equal to 0.
+ */
+ *ppowner = &(hdev_to_hub(hdev)->port_owners[port1 - 1]);
+ return 0;
+}
+
+/* In the following three functions, the caller must hold hdev's lock */
+int usb_hub_claim_port(struct usb_device *hdev, unsigned port1, void *owner)
+{
+ int rc;
+ void **powner;
+
+ rc = find_port_owner(hdev, port1, &powner);
+ if (rc)
+ return rc;
+ if (*powner)
+ return -EBUSY;
+ *powner = owner;
+ return rc;
+}
+
+int usb_hub_release_port(struct usb_device *hdev, unsigned port1, void *owner)
+{
+ int rc;
+ void **powner;
+
+ rc = find_port_owner(hdev, port1, &powner);
+ if (rc)
+ return rc;
+ if (*powner != owner)
+ return -ENOENT;
+ *powner = NULL;
+ return rc;
+}
+
+void usb_hub_release_all_ports(struct usb_device *hdev, void *owner)
+{
+ int n;
+ void **powner;
+
+ n = find_port_owner(hdev, 1, &powner);
+ if (n == 0) {
+ for (; n < hdev->maxchild; (++n, ++powner)) {
+ if (*powner == owner)
+ *powner = NULL;
+ }
+ }
+}
+
+/* The caller must hold udev's lock */
+bool usb_device_is_owned(struct usb_device *udev)
+{
+ struct usb_hub *hub;
+
+ if (udev->state == USB_STATE_NOTATTACHED || !udev->parent)
+ return false;
+ hub = hdev_to_hub(udev->parent);
+ return !!hub->port_owners[udev->portnum - 1];
+}
+
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{
@@ -2849,14 +2932,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
/* For a suspended device, treat this as a
* remote wakeup event.
*/
- if (udev->do_remote_wakeup)
- status = remote_wakeup(udev);
-
- /* Otherwise leave it be; devices can't tell the
- * difference between suspended and disabled.
- */
- else
- status = 0;
+ status = remote_wakeup(udev);
#endif
} else {
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 9720e699f472..da718e84d58d 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -459,35 +459,23 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
io->urbs[i]->context = io;
/*
- * Some systems need to revert to PIO when DMA is
- * temporarily unavailable. For their sakes, both
- * transfer_buffer and transfer_dma are set when
- * possible. However this can only work on systems
- * without:
+ * Some systems need to revert to PIO when DMA is temporarily
+ * unavailable. For their sakes, both transfer_buffer and
+ * transfer_dma are set when possible.
*
- * - HIGHMEM, since DMA buffers located in high memory
- * are not directly addressable by the CPU for PIO;
- *
- * - IOMMU, since dma_map_sg() is allowed to use an
- * IOMMU to make virtually discontiguous buffers be
- * "dma-contiguous" so that PIO and DMA need diferent
- * numbers of URBs.
- *
- * So when HIGHMEM or IOMMU are in use, transfer_buffer
- * is NULL to prevent stale pointers and to help spot
- * bugs.
+ * Note that if IOMMU coalescing occurred, we cannot
+ * trust sg_page anymore, so check if S/G list shrunk.
*/
+ if (io->nents == io->entries && !PageHighMem(sg_page(sg)))
+ io->urbs[i]->transfer_buffer = sg_virt(sg);
+ else
+ io->urbs[i]->transfer_buffer = NULL;
+
if (dma) {
io->urbs[i]->transfer_dma = sg_dma_address(sg);
len = sg_dma_len(sg);
-#if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU)
- io->urbs[i]->transfer_buffer = NULL;
-#else
- io->urbs[i]->transfer_buffer = sg_virt(sg);
-#endif
} else {
/* hc may use _only_ transfer_buffer */
- io->urbs[i]->transfer_buffer = sg_virt(sg);
len = sg->length;
}
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index c0e0ae2bb8e7..9a8b15e6377a 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -37,6 +37,13 @@ extern int usb_match_device(struct usb_device *dev,
extern void usb_forced_unbind_intf(struct usb_interface *intf);
extern void usb_rebind_intf(struct usb_interface *intf);
+extern int usb_hub_claim_port(struct usb_device *hdev, unsigned port,
+ void *owner);
+extern int usb_hub_release_port(struct usb_device *hdev, unsigned port,
+ void *owner);
+extern void usb_hub_release_all_ports(struct usb_device *hdev, void *owner);
+extern bool usb_device_is_owned(struct usb_device *udev);
+
extern int usb_hub_init(void);
extern void usb_hub_cleanup(void);
extern int usb_major_init(void);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index b7f10bc25c2c..26cfd0c028c0 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -124,7 +124,7 @@ choice
config USB_GADGET_AT91
boolean "Atmel AT91 USB Device Port"
- depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9
+ depends on ARCH_AT91 && !ARCH_AT91SAM9RL && !ARCH_AT91CAP9 && !ARCH_AT91SAM9G45
select USB_GADGET_SELECTED
help
Many Atmel AT91 processors (such as the AT91RM2000) have a
@@ -143,7 +143,7 @@ config USB_AT91
config USB_GADGET_ATMEL_USBA
boolean "Atmel USBA"
select USB_GADGET_DUALSPEED
- depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL
+ depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
USBA is the integrated high-speed USB Device controller on
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index 77352ccc245e..d5b65962dd36 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/amd5536udc.c
@@ -2378,40 +2378,34 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix)
if (!ep->cancel_transfer && !list_empty(&ep->queue)) {
req = list_entry(ep->queue.next,
struct udc_request, queue);
- if (req) {
- /*
- * length bytes transfered
- * check dma done of last desc. in PPBDU mode
- */
- if (use_dma_ppb_du) {
- td = udc_get_last_dma_desc(req);
- if (td) {
- dma_done =
- AMD_GETBITS(td->status,
- UDC_DMA_IN_STS_BS);
- /* don't care DMA done */
- req->req.actual =
- req->req.length;
- }
- } else {
- /* assume all bytes transferred */
+ /*
+ * length bytes transfered
+ * check dma done of last desc. in PPBDU mode
+ */
+ if (use_dma_ppb_du) {
+ td = udc_get_last_dma_desc(req);
+ if (td) {
+ dma_done =
+ AMD_GETBITS(td->status,
+ UDC_DMA_IN_STS_BS);
+ /* don't care DMA done */
req->req.actual = req->req.length;
}
+ } else {
+ /* assume all bytes transferred */
+ req->req.actual = req->req.length;
+ }
- if (req->req.actual == req->req.length) {
- /* complete req */
- complete_req(ep, req, 0);
- req->dma_going = 0;
- /* further request available ? */
- if (list_empty(&ep->queue)) {
- /* disable interrupt */
- tmp = readl(
- &dev->regs->ep_irqmsk);
- tmp |= AMD_BIT(ep->num);
- writel(tmp,
- &dev->regs->ep_irqmsk);
- }
-
+ if (req->req.actual == req->req.length) {
+ /* complete req */
+ complete_req(ep, req, 0);
+ req->dma_going = 0;
+ /* further request available ? */
+ if (list_empty(&ep->queue)) {
+ /* disable interrupt */
+ tmp = readl(&dev->regs->ep_irqmsk);
+ tmp |= AMD_BIT(ep->num);
+ writel(tmp, &dev->regs->ep_irqmsk);
}
}
}
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c
index 9f80f4e970bd..a3a0f4a27ef0 100644
--- a/drivers/usb/gadget/audio.c
+++ b/drivers/usb/gadget/audio.c
@@ -106,20 +106,20 @@ static int audio_set_endpoint_req(struct usb_configuration *c,
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
- case SET_CUR:
+ case UAC_SET_CUR:
value = 0;
break;
- case SET_MIN:
+ case UAC_SET_MIN:
break;
- case SET_MAX:
+ case UAC_SET_MAX:
break;
- case SET_RES:
+ case UAC_SET_RES:
break;
- case SET_MEM:
+ case UAC_SET_MEM:
break;
default:
@@ -142,13 +142,13 @@ static int audio_get_endpoint_req(struct usb_configuration *c,
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
- case GET_CUR:
- case GET_MIN:
- case GET_MAX:
- case GET_RES:
+ case UAC_GET_CUR:
+ case UAC_GET_MIN:
+ case UAC_GET_MAX:
+ case UAC_GET_RES:
value = 3;
break;
- case GET_MEM:
+ case UAC_GET_MEM:
break;
default:
break;
@@ -171,11 +171,11 @@ audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl)
* Audio class messages; interface activation uses set_alt().
*/
switch (ctrl->bRequestType) {
- case USB_AUDIO_SET_ENDPOINT:
+ case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
value = audio_set_endpoint_req(c, ctrl);
break;
- case USB_AUDIO_GET_ENDPOINT:
+ case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
value = audio_get_endpoint_req(c, ctrl);
break;
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index a56b24d305f8..5e0966485188 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -1306,11 +1306,6 @@ restart:
setup = *(struct usb_ctrlrequest*) urb->setup_packet;
w_index = le16_to_cpu(setup.wIndex);
w_value = le16_to_cpu(setup.wValue);
- if (le16_to_cpu(setup.wLength) !=
- urb->transfer_buffer_length) {
- status = -EOVERFLOW;
- goto return_urb;
- }
/* paranoia, in case of stale queued data */
list_for_each_entry (req, &ep->queue, queue) {
diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c
index 66527ba2d2ea..98e9bb977291 100644
--- a/drivers/usb/gadget/f_audio.c
+++ b/drivers/usb/gadget/f_audio.c
@@ -28,6 +28,9 @@ static int audio_buf_size = 48000;
module_param(audio_buf_size, int, S_IRUGO);
MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
+static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
+static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
+
/*
* DESCRIPTORS ... most are static, but strings and full
* configuration descriptors are built on demand.
@@ -50,16 +53,16 @@ static struct usb_interface_descriptor ac_interface_desc __initdata = {
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
};
-DECLARE_USB_AC_HEADER_DESCRIPTOR(2);
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
-#define USB_DT_AC_HEADER_LENGH USB_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
+#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
/* B.3.2 Class-Specific AC Interface Descriptor */
-static struct usb_ac_header_descriptor_2 ac_header_desc = {
- .bLength = USB_DT_AC_HEADER_LENGH,
+static struct uac_ac_header_descriptor_2 ac_header_desc = {
+ .bLength = UAC_DT_AC_HEADER_LENGTH,
.bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = HEADER,
+ .bDescriptorSubtype = UAC_HEADER,
.bcdADC = __constant_cpu_to_le16(0x0100),
- .wTotalLength = __constant_cpu_to_le16(USB_DT_AC_HEADER_LENGH),
+ .wTotalLength = __constant_cpu_to_le16(UAC_DT_AC_HEADER_LENGTH),
.bInCollection = F_AUDIO_NUM_INTERFACES,
.baInterfaceNr = {
[0] = F_AUDIO_AC_INTERFACE,
@@ -68,33 +71,33 @@ static struct usb_ac_header_descriptor_2 ac_header_desc = {
};
#define INPUT_TERMINAL_ID 1
-static struct usb_input_terminal_descriptor input_terminal_desc = {
- .bLength = USB_DT_AC_INPUT_TERMINAL_SIZE,
+static struct uac_input_terminal_descriptor input_terminal_desc = {
+ .bLength = UAC_DT_INPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = INPUT_TERMINAL,
+ .bDescriptorSubtype = UAC_INPUT_TERMINAL,
.bTerminalID = INPUT_TERMINAL_ID,
- .wTerminalType = USB_AC_TERMINAL_STREAMING,
+ .wTerminalType = UAC_TERMINAL_STREAMING,
.bAssocTerminal = 0,
.wChannelConfig = 0x3,
};
-DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(0);
+DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
#define FEATURE_UNIT_ID 2
-static struct usb_ac_feature_unit_descriptor_0 feature_unit_desc = {
- .bLength = USB_DT_AC_FEATURE_UNIT_SIZE(0),
+static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
+ .bLength = UAC_DT_FEATURE_UNIT_SIZE(0),
.bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = FEATURE_UNIT,
+ .bDescriptorSubtype = UAC_FEATURE_UNIT,
.bUnitID = FEATURE_UNIT_ID,
.bSourceID = INPUT_TERMINAL_ID,
.bControlSize = 2,
- .bmaControls[0] = (FU_MUTE | FU_VOLUME),
+ .bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME),
};
static struct usb_audio_control mute_control = {
.list = LIST_HEAD_INIT(mute_control.list),
.name = "Mute Control",
- .type = MUTE_CONTROL,
+ .type = UAC_MUTE_CONTROL,
/* Todo: add real Mute control code */
.set = generic_set_cmd,
.get = generic_get_cmd,
@@ -103,7 +106,7 @@ static struct usb_audio_control mute_control = {
static struct usb_audio_control volume_control = {
.list = LIST_HEAD_INIT(volume_control.list),
.name = "Volume Control",
- .type = VOLUME_CONTROL,
+ .type = UAC_VOLUME_CONTROL,
/* Todo: add real Volume control code */
.set = generic_set_cmd,
.get = generic_get_cmd,
@@ -113,17 +116,17 @@ static struct usb_audio_control_selector feature_unit = {
.list = LIST_HEAD_INIT(feature_unit.list),
.id = FEATURE_UNIT_ID,
.name = "Mute & Volume Control",
- .type = FEATURE_UNIT,
+ .type = UAC_FEATURE_UNIT,
.desc = (struct usb_descriptor_header *)&feature_unit_desc,
};
#define OUTPUT_TERMINAL_ID 3
-static struct usb_output_terminal_descriptor output_terminal_desc = {
- .bLength = USB_DT_AC_OUTPUT_TERMINAL_SIZE,
+static struct uac_output_terminal_descriptor output_terminal_desc = {
+ .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = OUTPUT_TERMINAL,
+ .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
.bTerminalID = OUTPUT_TERMINAL_ID,
- .wTerminalType = USB_AC_OUTPUT_TERMINAL_SPEAKER,
+ .wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER,
.bAssocTerminal = FEATURE_UNIT_ID,
.bSourceID = FEATURE_UNIT_ID,
};
@@ -148,22 +151,22 @@ static struct usb_interface_descriptor as_interface_alt_1_desc = {
};
/* B.4.2 Class-Specific AS Interface Descriptor */
-static struct usb_as_header_descriptor as_header_desc = {
- .bLength = USB_DT_AS_HEADER_SIZE,
+static struct uac_as_header_descriptor as_header_desc = {
+ .bLength = UAC_DT_AS_HEADER_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = AS_GENERAL,
+ .bDescriptorSubtype = UAC_AS_GENERAL,
.bTerminalLink = INPUT_TERMINAL_ID,
.bDelay = 1,
- .wFormatTag = USB_AS_AUDIO_FORMAT_TYPE_I_PCM,
+ .wFormatTag = UAC_FORMAT_TYPE_I_PCM,
};
-DECLARE_USB_AS_FORMAT_TYPE_I_DISCRETE_DESC(1);
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
-static struct usb_as_formate_type_i_discrete_descriptor_1 as_type_i_desc = {
- .bLength = USB_AS_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
+ .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = FORMAT_TYPE,
- .bFormatType = USB_AS_FORMAT_TYPE_I,
+ .bDescriptorSubtype = UAC_FORMAT_TYPE,
+ .bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
.bSamFreqType = 1,
@@ -174,17 +177,17 @@ static struct usb_endpoint_descriptor as_out_ep_desc __initdata = {
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_AS_ENDPOINT_ADAPTIVE
+ .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE
| USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE),
.bInterval = 4,
};
/* Class-specific AS ISO OUT Endpoint Descriptor */
-static struct usb_as_iso_endpoint_descriptor as_iso_out_desc __initdata = {
- .bLength = USB_AS_ISO_ENDPOINT_DESC_SIZE,
+static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = {
+ .bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
.bDescriptorType = USB_DT_CS_ENDPOINT,
- .bDescriptorSubtype = EP_GENERAL,
+ .bDescriptorSubtype = UAC_EP_GENERAL,
.bmAttributes = 1,
.bLockDelayUnits = 1,
.wLockDelay = __constant_cpu_to_le16(1),
@@ -456,11 +459,11 @@ f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
* Audio class messages; interface activation uses set_alt().
*/
switch (ctrl->bRequestType) {
- case USB_AUDIO_SET_INTF:
+ case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
value = audio_set_intf_req(f, ctrl);
break;
- case USB_AUDIO_GET_INTF:
+ case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
value = audio_get_intf_req(f, ctrl);
break;
@@ -632,6 +635,18 @@ f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
/*-------------------------------------------------------------------------*/
+static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
+{
+ con->data[cmd] = value;
+
+ return 0;
+}
+
+static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
+{
+ return con->data[cmd];
+}
+
/* Todo: add more control selecotor dynamically */
int __init control_selector_init(struct f_audio *audio)
{
@@ -642,10 +657,10 @@ int __init control_selector_init(struct f_audio *audio)
list_add(&mute_control.list, &feature_unit.control);
list_add(&volume_control.list, &feature_unit.control);
- volume_control.data[_CUR] = 0xffc0;
- volume_control.data[_MIN] = 0xe3a0;
- volume_control.data[_MAX] = 0xfff0;
- volume_control.data[_RES] = 0x0030;
+ volume_control.data[UAC__CUR] = 0xffc0;
+ volume_control.data[UAC__MIN] = 0xe3a0;
+ volume_control.data[UAC__MAX] = 0xfff0;
+ volume_control.data[UAC__RES] = 0x0030;
return 0;
}
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index b9312dc6e041..d0b1e836f0e0 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -191,7 +191,7 @@ module_param(qlen, uint, S_IRUGO);
#define GMIDI_MS_INTERFACE 1
#define GMIDI_NUM_INTERFACES 2
-DECLARE_USB_AC_HEADER_DESCRIPTOR(1);
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1);
@@ -237,12 +237,12 @@ static const struct usb_interface_descriptor ac_interface_desc = {
};
/* B.3.2 Class-Specific AC Interface Descriptor */
-static const struct usb_ac_header_descriptor_1 ac_header_desc = {
- .bLength = USB_DT_AC_HEADER_SIZE(1),
+static const struct uac_ac_header_descriptor_1 ac_header_desc = {
+ .bLength = UAC_DT_AC_HEADER_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MS_HEADER,
.bcdADC = cpu_to_le16(0x0100),
- .wTotalLength = cpu_to_le16(USB_DT_AC_HEADER_SIZE(1)),
+ .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)),
.bInCollection = 1,
.baInterfaceNr = {
[0] = GMIDI_MS_INTERFACE,
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index ed21e263f832..e6fedbd5a654 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -56,6 +56,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
/*
* This driver is PXA25x only. Grab the right register definitions.
@@ -1008,15 +1009,27 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active)
return 0;
}
+/* boards may consume current from VBUS, up to 100-500mA based on config.
+ * the 500uA suspend ceiling means that exclusively vbus-powered PXA designs
+ * violate USB specs.
+ */
+static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
+{
+ struct pxa25x_udc *udc;
+
+ udc = container_of(_gadget, struct pxa25x_udc, gadget);
+
+ if (udc->transceiver)
+ return otg_set_power(udc->transceiver, mA);
+ return -EOPNOTSUPP;
+}
+
static const struct usb_gadget_ops pxa25x_udc_ops = {
.get_frame = pxa25x_udc_get_frame,
.wakeup = pxa25x_udc_wakeup,
.vbus_session = pxa25x_udc_vbus_session,
.pullup = pxa25x_udc_pullup,
-
- // .vbus_draw ... boards may consume current from VBUS, up to
- // 100-500mA based on config. the 500uA suspend ceiling means
- // that exclusively vbus-powered PXA designs violate USB specs.
+ .vbus_draw = pxa25x_udc_vbus_draw,
};
/*-------------------------------------------------------------------------*/
@@ -1303,9 +1316,23 @@ fail:
* for set_configuration as well as eventual disconnect.
*/
DMSG("registered gadget driver '%s'\n", driver->driver.name);
+
+ /* connect to bus through transceiver */
+ if (dev->transceiver) {
+ retval = otg_set_peripheral(dev->transceiver, &dev->gadget);
+ if (retval) {
+ DMSG("can't bind to transceiver\n");
+ if (driver->unbind)
+ driver->unbind(&dev->gadget);
+ goto bind_fail;
+ }
+ }
+
pullup(dev);
dump_state(dev);
return 0;
+bind_fail:
+ return retval;
}
EXPORT_SYMBOL(usb_gadget_register_driver);
@@ -1351,6 +1378,9 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
stop_activity(dev, driver);
local_irq_enable();
+ if (dev->transceiver)
+ (void) otg_set_peripheral(dev->transceiver, NULL);
+
driver->unbind(&dev->gadget);
dev->gadget.dev.driver = NULL;
dev->driver = NULL;
@@ -2162,6 +2192,8 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
dev->dev = &pdev->dev;
dev->mach = pdev->dev.platform_data;
+ dev->transceiver = otg_get_transceiver();
+
if (gpio_is_valid(dev->mach->gpio_vbus)) {
if ((retval = gpio_request(dev->mach->gpio_vbus,
"pxa25x_udc GPIO VBUS"))) {
@@ -2264,6 +2296,10 @@ lubbock_fail0:
if (gpio_is_valid(dev->mach->gpio_vbus))
gpio_free(dev->mach->gpio_vbus);
err_gpio_vbus:
+ if (dev->transceiver) {
+ otg_put_transceiver(dev->transceiver);
+ dev->transceiver = NULL;
+ }
clk_put(dev->clk);
err_clk:
return retval;
@@ -2305,6 +2341,11 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev)
clk_put(dev->clk);
+ if (dev->transceiver) {
+ otg_put_transceiver(dev->transceiver);
+ dev->transceiver = NULL;
+ }
+
platform_set_drvdata(pdev, NULL);
the_controller = NULL;
return 0;
diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h
index 1d51aa21e6eb..f572c5617462 100644
--- a/drivers/usb/gadget/pxa25x_udc.h
+++ b/drivers/usb/gadget/pxa25x_udc.h
@@ -128,6 +128,7 @@ struct pxa25x_udc {
struct device *dev;
struct clk *clk;
struct pxa2xx_udc_mach_info *mach;
+ struct otg_transceiver *transceiver;
u64 dma_mask;
struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS];
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index 50c71aae2cc2..4b5dbd0127f5 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -2392,7 +2392,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
grstctl = readl(hsotg->regs + S3C_GRSTCTL);
} while (!(grstctl & S3C_GRSTCTL_CSftRst) && timeout-- > 0);
- if (!grstctl & S3C_GRSTCTL_CSftRst) {
+ if (!(grstctl & S3C_GRSTCTL_CSftRst)) {
dev_err(hsotg->dev, "Failed to get CSftRst asserted\n");
return -EINVAL;
}
@@ -2514,8 +2514,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
* DMA mode we may need this. */
writel(S3C_DOEPMSK_SetupMsk | S3C_DOEPMSK_AHBErrMsk |
S3C_DOEPMSK_EPDisbldMsk |
- using_dma(hsotg) ? (S3C_DIEPMSK_XferComplMsk |
- S3C_DIEPMSK_TimeOUTMsk) : 0,
+ (using_dma(hsotg) ? (S3C_DIEPMSK_XferComplMsk |
+ S3C_DIEPMSK_TimeOUTMsk) : 0),
hsotg->regs + S3C_DOEPMSK);
writel(0, hsotg->regs + S3C_DAINTMSK);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index f21ca7d27a43..9b43b226817f 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -113,6 +113,12 @@ config USB_EHCI_HCD_PPC_OF
Enables support for the USB controller present on the PowerPC
OpenFirmware platform bus.
+config USB_W90X900_EHCI
+ bool "W90X900(W90P910) EHCI support"
+ depends on USB_EHCI_HCD && ARCH_W90X900
+ ---help---
+ Enables support for the W90X900 USB controller
+
config USB_OXU210HP_HCD
tristate "OXU210HP HCD support"
depends on USB
@@ -153,6 +159,18 @@ config USB_ISP1760_HCD
To compile this driver as a module, choose M here: the
module will be called isp1760.
+config USB_ISP1362_HCD
+ tristate "ISP1362 HCD support"
+ depends on USB
+ default N
+ ---help---
+ Supports the Philips ISP1362 chip as a host controller
+
+ This driver does not support isochronous transfers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called isp1362-hcd.
+
config USB_OHCI_HCD
tristate "OHCI HCD support"
depends on USB && USB_ARCH_HAS_OHCI
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 289d748bb414..f58b2494c44a 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_PCI) += pci-quirks.o
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
+obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_FHCI_HCD) += fhci.o
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
new file mode 100644
index 000000000000..87c1b7c34c0e
--- /dev/null
+++ b/drivers/usb/host/ehci-atmel.c
@@ -0,0 +1,230 @@
+/*
+ * Driver for EHCI UHP on Atmel chips
+ *
+ * Copyright (C) 2009 Atmel Corporation,
+ * Nicolas Ferre <nicolas.ferre@atmel.com>
+ *
+ * Based on various ehci-*.c drivers
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+/* interface and function clocks */
+static struct clk *iclk, *fclk;
+static int clocked;
+
+/*-------------------------------------------------------------------------*/
+
+static void atmel_start_clock(void)
+{
+ clk_enable(iclk);
+ clk_enable(fclk);
+ clocked = 1;
+}
+
+static void atmel_stop_clock(void)
+{
+ clk_disable(fclk);
+ clk_disable(iclk);
+ clocked = 0;
+}
+
+static void atmel_start_ehci(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "start\n");
+ atmel_start_clock();
+}
+
+static void atmel_stop_ehci(struct platform_device *pdev)
+{
+ dev_dbg(&pdev->dev, "stop\n");
+ atmel_stop_clock();
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int ehci_atmel_setup(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int retval = 0;
+
+ /* registers start at offset 0x0 */
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs +
+ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+ retval = ehci_halt(ehci);
+ if (retval)
+ return retval;
+
+ /* data structure init */
+ retval = ehci_init(hcd);
+ if (retval)
+ return retval;
+
+ ehci->sbrn = 0x20;
+
+ ehci_reset(ehci);
+ ehci_port_power(ehci, 0);
+
+ return retval;
+}
+
+static const struct hc_driver ehci_atmel_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Atmel EHCI UHP HS",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /* generic hardware linkage */
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ /* basic lifecycle operations */
+ .reset = ehci_atmel_setup,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /* managing i/o requests and associated device resources */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /* scheduling support */
+ .get_frame_number = ehci_get_frame,
+
+ /* root hub support */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+};
+
+static int __init ehci_atmel_drv_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ const struct hc_driver *driver = &ehci_atmel_hc_driver;
+ struct resource *res;
+ int irq;
+ int retval;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ pr_debug("Initializing Atmel-SoC USB Host Controller\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev,
+ "Found HC with no IRQ. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ retval = -ENODEV;
+ goto fail_create_hcd;
+ }
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto fail_create_hcd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev,
+ "Found HC with no register addr. Check %s setup!\n",
+ dev_name(&pdev->dev));
+ retval = -ENODEV;
+ goto fail_request_resource;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ driver->description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ retval = -EBUSY;
+ goto fail_request_resource;
+ }
+
+ hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ if (hcd->regs == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ retval = -EFAULT;
+ goto fail_ioremap;
+ }
+
+ iclk = clk_get(&pdev->dev, "ehci_clk");
+ if (IS_ERR(iclk)) {
+ dev_err(&pdev->dev, "Error getting interface clock\n");
+ retval = -ENOENT;
+ goto fail_get_iclk;
+ }
+ fclk = clk_get(&pdev->dev, "uhpck");
+ if (IS_ERR(fclk)) {
+ dev_err(&pdev->dev, "Error getting function clock\n");
+ retval = -ENOENT;
+ goto fail_get_fclk;
+ }
+
+ atmel_start_ehci(pdev);
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval)
+ goto fail_add_hcd;
+
+ return retval;
+
+fail_add_hcd:
+ atmel_stop_ehci(pdev);
+ clk_put(fclk);
+fail_get_fclk:
+ clk_put(iclk);
+fail_get_iclk:
+ iounmap(hcd->regs);
+fail_ioremap:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+fail_request_resource:
+ usb_put_hcd(hcd);
+fail_create_hcd:
+ dev_err(&pdev->dev, "init %s fail, %d\n",
+ dev_name(&pdev->dev), retval);
+
+ return retval;
+}
+
+static int __exit ehci_atmel_drv_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ ehci_shutdown(hcd);
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ atmel_stop_ehci(pdev);
+ clk_put(fclk);
+ clk_put(iclk);
+ fclk = iclk = NULL;
+
+ return 0;
+}
+
+static struct platform_driver ehci_atmel_driver = {
+ .probe = ehci_atmel_drv_probe,
+ .remove = __exit_p(ehci_atmel_drv_remove),
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver.name = "atmel-ehci",
+};
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
index 59d208d94d4e..ed77be76d6bb 100644
--- a/drivers/usb/host/ehci-au1xxx.c
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -199,10 +199,9 @@ static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int ehci_hcd_au1xxx_drv_suspend(struct platform_device *pdev,
- pm_message_t message)
+static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
unsigned long flags;
int rc;
@@ -229,12 +228,6 @@ static int ehci_hcd_au1xxx_drv_suspend(struct platform_device *pdev,
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
- /* make sure snapshot being resumed re-enumerates everything */
- if (message.event == PM_EVENT_PRETHAW) {
- ehci_halt(ehci);
- ehci_reset(ehci);
- }
-
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
au1xxx_stop_ehc();
@@ -248,10 +241,9 @@ bail:
return rc;
}
-
-static int ehci_hcd_au1xxx_drv_resume(struct platform_device *pdev)
+static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
au1xxx_start_ehc();
@@ -305,20 +297,25 @@ static int ehci_hcd_au1xxx_drv_resume(struct platform_device *pdev)
return 0;
}
+static struct dev_pm_ops au1xxx_ehci_pmops = {
+ .suspend = ehci_hcd_au1xxx_drv_suspend,
+ .resume = ehci_hcd_au1xxx_drv_resume,
+};
+
+#define AU1XXX_EHCI_PMOPS &au1xxx_ehci_pmops
+
#else
-#define ehci_hcd_au1xxx_drv_suspend NULL
-#define ehci_hcd_au1xxx_drv_resume NULL
+#define AU1XXX_EHCI_PMOPS NULL
#endif
static struct platform_driver ehci_hcd_au1xxx_driver = {
.probe = ehci_hcd_au1xxx_drv_probe,
.remove = ehci_hcd_au1xxx_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
- .suspend = ehci_hcd_au1xxx_drv_suspend,
- .resume = ehci_hcd_au1xxx_drv_resume,
.driver = {
.name = "au1xxx-ehci",
.owner = THIS_MODULE,
+ .pm = AU1XXX_EHCI_PMOPS,
}
};
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 7f4ace73d44a..874d2000bf92 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -134,10 +134,11 @@ dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
static void __maybe_unused
dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{
+ struct ehci_qh_hw *hw = qh->hw;
+
ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
- qh, qh->hw_next, qh->hw_info1, qh->hw_info2,
- qh->hw_current);
- dbg_qtd ("overlay", ehci, (struct ehci_qtd *) &qh->hw_qtd_next);
+ qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current);
+ dbg_qtd("overlay", ehci, (struct ehci_qtd *) &hw->hw_qtd_next);
}
static void __maybe_unused
@@ -400,31 +401,32 @@ static void qh_lines (
char *next = *nextp;
char mark;
__le32 list_end = EHCI_LIST_END(ehci);
+ struct ehci_qh_hw *hw = qh->hw;
- if (qh->hw_qtd_next == list_end) /* NEC does this */
+ if (hw->hw_qtd_next == list_end) /* NEC does this */
mark = '@';
else
- mark = token_mark(ehci, qh->hw_token);
+ mark = token_mark(ehci, hw->hw_token);
if (mark == '/') { /* qh_alt_next controls qh advance? */
- if ((qh->hw_alt_next & QTD_MASK(ehci))
- == ehci->async->hw_alt_next)
+ if ((hw->hw_alt_next & QTD_MASK(ehci))
+ == ehci->async->hw->hw_alt_next)
mark = '#'; /* blocked */
- else if (qh->hw_alt_next == list_end)
+ else if (hw->hw_alt_next == list_end)
mark = '.'; /* use hw_qtd_next */
/* else alt_next points to some other qtd */
}
- scratch = hc32_to_cpup(ehci, &qh->hw_info1);
- hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &qh->hw_current) : 0;
+ scratch = hc32_to_cpup(ehci, &hw->hw_info1);
+ hw_curr = (mark == '*') ? hc32_to_cpup(ehci, &hw->hw_current) : 0;
temp = scnprintf (next, size,
"qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
qh, scratch & 0x007f,
speed_char (scratch),
(scratch >> 8) & 0x000f,
- scratch, hc32_to_cpup(ehci, &qh->hw_info2),
- hc32_to_cpup(ehci, &qh->hw_token), mark,
- (cpu_to_hc32(ehci, QTD_TOGGLE) & qh->hw_token)
+ scratch, hc32_to_cpup(ehci, &hw->hw_info2),
+ hc32_to_cpup(ehci, &hw->hw_token), mark,
+ (cpu_to_hc32(ehci, QTD_TOGGLE) & hw->hw_token)
? "data1" : "data0",
- (hc32_to_cpup(ehci, &qh->hw_alt_next) >> 1) & 0x0f);
+ (hc32_to_cpup(ehci, &hw->hw_alt_next) >> 1) & 0x0f);
size -= temp;
next += temp;
@@ -435,10 +437,10 @@ static void qh_lines (
mark = ' ';
if (hw_curr == td->qtd_dma)
mark = '*';
- else if (qh->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma))
+ else if (hw->hw_qtd_next == cpu_to_hc32(ehci, td->qtd_dma))
mark = '+';
else if (QTD_LENGTH (scratch)) {
- if (td->hw_alt_next == ehci->async->hw_alt_next)
+ if (td->hw_alt_next == ehci->async->hw->hw_alt_next)
mark = '#';
else if (td->hw_alt_next != list_end)
mark = '/';
@@ -550,12 +552,15 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
next += temp;
do {
+ struct ehci_qh_hw *hw;
+
switch (hc32_to_cpu(ehci, tag)) {
case Q_TYPE_QH:
+ hw = p.qh->hw;
temp = scnprintf (next, size, " qh%d-%04x/%p",
p.qh->period,
hc32_to_cpup(ehci,
- &p.qh->hw_info2)
+ &hw->hw_info2)
/* uframe masks */
& (QH_CMASK | QH_SMASK),
p.qh);
@@ -576,7 +581,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
/* show more info the first time around */
if (temp == seen_count) {
u32 scratch = hc32_to_cpup(ehci,
- &p.qh->hw_info1);
+ &hw->hw_info1);
struct ehci_qtd *qtd;
char *type = "";
@@ -609,7 +614,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
} else
temp = 0;
if (p.qh) {
- tag = Q_NEXT_TYPE(ehci, p.qh->hw_next);
+ tag = Q_NEXT_TYPE(ehci, hw->hw_next);
p = p.qh->qh_next;
}
break;
@@ -879,8 +884,7 @@ static int debug_close(struct inode *inode, struct file *file)
struct debug_buffer *buf = file->private_data;
if (buf) {
- if (buf->output_buf)
- vfree(buf->output_buf);
+ vfree(buf->output_buf);
kfree(buf);
}
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 11c627ce6022..6887aac5e73d 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -30,7 +30,6 @@
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
-#include <linux/reboot.h>
#include <linux/usb.h>
#include <linux/moduleparam.h>
#include <linux/dma-mapping.h>
@@ -127,6 +126,8 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
switch (action) {
case TIMER_IO_WATCHDOG:
+ if (!ehci->need_io_watchdog)
+ return;
t = EHCI_IO_JIFFIES;
break;
case TIMER_ASYNC_OFF:
@@ -247,6 +248,12 @@ static int ehci_reset (struct ehci_hcd *ehci)
retval = handshake (ehci, &ehci->regs->command,
CMD_RESET, 0, 250 * 1000);
+ if (ehci->has_hostpc) {
+ ehci_writel(ehci, USBMODE_EX_HC | USBMODE_EX_VBPS,
+ (u32 __iomem *)(((u8 *)ehci->regs) + USBMODE_EX));
+ ehci_writel(ehci, TXFIFO_DEFAULT,
+ (u32 __iomem *)(((u8 *)ehci->regs) + TXFILLTUNING));
+ }
if (retval)
return retval;
@@ -505,9 +512,14 @@ static int ehci_init(struct usb_hcd *hcd)
u32 temp;
int retval;
u32 hcc_params;
+ struct ehci_qh_hw *hw;
spin_lock_init(&ehci->lock);
+ /*
+ * keep io watchdog by default, those good HCDs could turn off it later
+ */
+ ehci->need_io_watchdog = 1;
init_timer(&ehci->watchdog);
ehci->watchdog.function = ehci_watchdog;
ehci->watchdog.data = (unsigned long) ehci;
@@ -544,12 +556,13 @@ static int ehci_init(struct usb_hcd *hcd)
* from automatically advancing to the next td after short reads.
*/
ehci->async->qh_next.qh = NULL;
- ehci->async->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
- ehci->async->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
- ehci->async->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
- ehci->async->hw_qtd_next = EHCI_LIST_END(ehci);
+ hw = ehci->async->hw;
+ hw->hw_next = QH_NEXT(ehci, ehci->async->qh_dma);
+ hw->hw_info1 = cpu_to_hc32(ehci, QH_HEAD);
+ hw->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
+ hw->hw_qtd_next = EHCI_LIST_END(ehci);
ehci->async->qh_state = QH_STATE_LINKED;
- ehci->async->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma);
+ hw->hw_alt_next = QTD_NEXT(ehci, ehci->async->dummy->qtd_dma);
/* clear interrupt enables, set irq latency */
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
@@ -979,7 +992,7 @@ rescan:
/* endpoints can be iso streams. for now, we don't
* accelerate iso completions ... so spin a while.
*/
- if (qh->hw_info1 == 0) {
+ if (qh->hw->hw_info1 == 0) {
ehci_vdbg (ehci, "iso delay\n");
goto idle_timeout;
}
@@ -1117,6 +1130,16 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ixp4xx_ehci_driver
#endif
+#ifdef CONFIG_USB_W90X900_EHCI
+#include "ehci-w90x900.c"
+#define PLATFORM_DRIVER ehci_hcd_w90x900_driver
+#endif
+
+#ifdef CONFIG_ARCH_AT91
+#include "ehci-atmel.c"
+#define PLATFORM_DRIVER ehci_atmel_driver
+#endif
+
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER)
#error "missing bus glue for ehci-hcd"
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index f46ad27c9a90..6fef1ee7d101 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -111,6 +111,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int port;
int mask;
+ u32 __iomem *hostpc_reg = NULL;
ehci_dbg(ehci, "suspend root hub\n");
@@ -142,6 +143,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
u32 t2 = t1;
+ if (ehci->has_hostpc)
+ hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
+ + HOSTPC0 + 4 * (port & 0xff));
/* keep track of which ports we suspend */
if (t1 & PORT_OWNER)
set_bit(port, &ehci->owned_ports);
@@ -151,15 +155,37 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
}
/* enable remote wakeup on all ports */
- if (hcd->self.root_hub->do_remote_wakeup)
- t2 |= PORT_WAKE_BITS;
- else
+ if (hcd->self.root_hub->do_remote_wakeup) {
+ /* only enable appropriate wake bits, otherwise the
+ * hardware can not go phy low power mode. If a race
+ * condition happens here(connection change during bits
+ * set), the port change detection will finally fix it.
+ */
+ if (t1 & PORT_CONNECT) {
+ t2 |= PORT_WKOC_E | PORT_WKDISC_E;
+ t2 &= ~PORT_WKCONN_E;
+ } else {
+ t2 |= PORT_WKOC_E | PORT_WKCONN_E;
+ t2 &= ~PORT_WKDISC_E;
+ }
+ } else
t2 &= ~PORT_WAKE_BITS;
if (t1 != t2) {
ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
port + 1, t1, t2);
ehci_writel(ehci, t2, reg);
+ if (hostpc_reg) {
+ u32 t3;
+
+ msleep(5);/* 5ms for HCD enter low pwr mode */
+ t3 = ehci_readl(ehci, hostpc_reg);
+ ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
+ t3 = ehci_readl(ehci, hostpc_reg);
+ ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
+ port, (t3 & HOSTPC_PHCD) ?
+ "succeeded" : "failed");
+ }
}
}
@@ -563,7 +589,8 @@ static int ehci_hub_control (
int ports = HCS_N_PORTS (ehci->hcs_params);
u32 __iomem *status_reg = &ehci->regs->port_status[
(wIndex & 0xff) - 1];
- u32 temp, status;
+ u32 __iomem *hostpc_reg = NULL;
+ u32 temp, temp1, status;
unsigned long flags;
int retval = 0;
unsigned selector;
@@ -575,6 +602,9 @@ static int ehci_hub_control (
* power, "this is the one", etc. EHCI spec supports this.
*/
+ if (ehci->has_hostpc)
+ hostpc_reg = (u32 __iomem *)((u8 *)ehci->regs
+ + HOSTPC0 + 4 * ((wIndex & 0xff) - 1));
spin_lock_irqsave (&ehci->lock, flags);
switch (typeReq) {
case ClearHubFeature:
@@ -773,7 +803,11 @@ static int ehci_hub_control (
if (temp & PORT_CONNECT) {
status |= 1 << USB_PORT_FEAT_CONNECTION;
// status may be from integrated TT
- status |= ehci_port_speed(ehci, temp);
+ if (ehci->has_hostpc) {
+ temp1 = ehci_readl(ehci, hostpc_reg);
+ status |= ehci_port_speed(ehci, temp1);
+ } else
+ status |= ehci_port_speed(ehci, temp);
}
if (temp & PORT_PE)
status |= 1 << USB_PORT_FEAT_ENABLE;
@@ -832,6 +866,24 @@ static int ehci_hub_control (
|| (temp & PORT_RESET) != 0)
goto error;
ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+ /* After above check the port must be connected.
+ * Set appropriate bit thus could put phy into low power
+ * mode if we have hostpc feature
+ */
+ if (hostpc_reg) {
+ temp &= ~PORT_WKCONN_E;
+ temp |= (PORT_WKDISC_E | PORT_WKOC_E);
+ ehci_writel(ehci, temp | PORT_SUSPEND,
+ status_reg);
+ msleep(5);/* 5ms for HCD enter low pwr mode */
+ temp1 = ehci_readl(ehci, hostpc_reg);
+ ehci_writel(ehci, temp1 | HOSTPC_PHCD,
+ hostpc_reg);
+ temp1 = ehci_readl(ehci, hostpc_reg);
+ ehci_dbg(ehci, "Port%d phy low pwr mode %s\n",
+ wIndex, (temp1 & HOSTPC_PHCD) ?
+ "succeeded" : "failed");
+ }
set_bit(wIndex, &ehci->suspended_ports);
break;
case USB_PORT_FEAT_POWER:
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index 10d52919abbb..aeda96e0af67 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -75,7 +75,8 @@ static void qh_destroy(struct ehci_qh *qh)
}
if (qh->dummy)
ehci_qtd_free (ehci, qh->dummy);
- dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
+ dma_pool_free(ehci->qh_pool, qh->hw, qh->qh_dma);
+ kfree(qh);
}
static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
@@ -83,12 +84,14 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
struct ehci_qh *qh;
dma_addr_t dma;
- qh = (struct ehci_qh *)
- dma_pool_alloc (ehci->qh_pool, flags, &dma);
+ qh = kzalloc(sizeof *qh, GFP_ATOMIC);
if (!qh)
- return qh;
-
- memset (qh, 0, sizeof *qh);
+ goto done;
+ qh->hw = (struct ehci_qh_hw *)
+ dma_pool_alloc(ehci->qh_pool, flags, &dma);
+ if (!qh->hw)
+ goto fail;
+ memset(qh->hw, 0, sizeof *qh->hw);
qh->refcount = 1;
qh->ehci = ehci;
qh->qh_dma = dma;
@@ -99,10 +102,15 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags)
qh->dummy = ehci_qtd_alloc (ehci, flags);
if (qh->dummy == NULL) {
ehci_dbg (ehci, "no dummy td\n");
- dma_pool_free (ehci->qh_pool, qh, qh->qh_dma);
- qh = NULL;
+ goto fail1;
}
+done:
return qh;
+fail1:
+ dma_pool_free(ehci->qh_pool, qh->hw, qh->qh_dma);
+fail:
+ kfree(qh);
+ return NULL;
}
/* to share a qh (cpu threads, or hc) */
@@ -180,7 +188,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
/* QHs for control/bulk/intr transfers */
ehci->qh_pool = dma_pool_create ("ehci_qh",
ehci_to_hcd(ehci)->self.controller,
- sizeof (struct ehci_qh),
+ sizeof(struct ehci_qh_hw),
32 /* byte alignment (for hw parts) */,
4096 /* can't cross 4K */);
if (!ehci->qh_pool) {
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index b5b83c43898a..a88ad517ec5c 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -129,6 +129,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
return retval;
switch (pdev->vendor) {
+ case PCI_VENDOR_ID_INTEL:
+ ehci->need_io_watchdog = 0;
+ break;
case PCI_VENDOR_ID_TDI:
if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) {
hcd->has_tt = 1;
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 7673554fa64d..377ed530b920 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -87,31 +87,33 @@ qtd_fill(struct ehci_hcd *ehci, struct ehci_qtd *qtd, dma_addr_t buf,
static inline void
qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
{
+ struct ehci_qh_hw *hw = qh->hw;
+
/* writes to an active overlay are unsafe */
BUG_ON(qh->qh_state != QH_STATE_IDLE);
- qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
- qh->hw_alt_next = EHCI_LIST_END(ehci);
+ hw->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma);
+ hw->hw_alt_next = EHCI_LIST_END(ehci);
/* Except for control endpoints, we make hardware maintain data
* toggle (like OHCI) ... here (re)initialize the toggle in the QH,
* and set the pseudo-toggle in udev. Only usb_clear_halt() will
* ever clear it.
*/
- if (!(qh->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
+ if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) {
unsigned is_out, epnum;
is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8));
- epnum = (hc32_to_cpup(ehci, &qh->hw_info1) >> 8) & 0x0f;
+ epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f;
if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
- qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
+ hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
usb_settoggle (qh->dev, epnum, is_out, 1);
}
}
/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
wmb ();
- qh->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
+ hw->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
}
/* if it weren't for a common silicon quirk (writing the dummy into the qh
@@ -129,7 +131,7 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh)
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_current)
+ if (cpu_to_hc32(ehci, qtd->qtd_dma) == qh->hw->hw_current)
qtd = NULL;
}
@@ -260,7 +262,7 @@ __acquires(ehci->lock)
struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
/* S-mask in a QH means it's an interrupt urb */
- if ((qh->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) {
+ if ((qh->hw->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) {
/* ... update hc-wide periodic stats (for usbfs) */
ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
@@ -315,6 +317,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
unsigned count = 0;
u8 state;
__le32 halt = HALT_BIT(ehci);
+ struct ehci_qh_hw *hw = qh->hw;
if (unlikely (list_empty (&qh->qtd_list)))
return count;
@@ -392,7 +395,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
qtd->hw_token = cpu_to_hc32(ehci,
token);
wmb();
- qh->hw_token = cpu_to_hc32(ehci, token);
+ hw->hw_token = cpu_to_hc32(ehci,
+ token);
goto retry_xacterr;
}
stopped = 1;
@@ -435,8 +439,8 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* qh unlinked; token in overlay may be most current */
if (state == QH_STATE_IDLE
&& cpu_to_hc32(ehci, qtd->qtd_dma)
- == qh->hw_current) {
- token = hc32_to_cpu(ehci, qh->hw_token);
+ == hw->hw_current) {
+ token = hc32_to_cpu(ehci, hw->hw_token);
/* An unlink may leave an incomplete
* async transaction in the TT buffer.
@@ -449,9 +453,9 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
* patch the qh later and so that completions can't
* activate it while we "know" it's stopped.
*/
- if ((halt & qh->hw_token) == 0) {
+ if ((halt & hw->hw_token) == 0) {
halt:
- qh->hw_token |= halt;
+ hw->hw_token |= halt;
wmb ();
}
}
@@ -510,7 +514,7 @@ halt:
* it after fault cleanup, or recovering from silicon wrongly
* overlaying the dummy qtd (which reduces DMA chatter).
*/
- if (stopped != 0 || qh->hw_qtd_next == EHCI_LIST_END(ehci)) {
+ if (stopped != 0 || hw->hw_qtd_next == EHCI_LIST_END(ehci)) {
switch (state) {
case QH_STATE_IDLE:
qh_refresh(ehci, qh);
@@ -528,7 +532,7 @@ halt:
* except maybe high bandwidth ...
*/
if ((cpu_to_hc32(ehci, QH_SMASK)
- & qh->hw_info2) != 0) {
+ & hw->hw_info2) != 0) {
intr_deschedule (ehci, qh);
(void) qh_schedule (ehci, qh);
} else
@@ -649,7 +653,7 @@ qh_urb_transaction (
* (this will usually be overridden later.)
*/
if (is_input)
- qtd->hw_alt_next = ehci->async->hw_alt_next;
+ qtd->hw_alt_next = ehci->async->hw->hw_alt_next;
/* qh makes control packets use qtd toggle; maybe switch it */
if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
@@ -744,6 +748,7 @@ qh_make (
int is_input, type;
int maxp = 0;
struct usb_tt *tt = urb->dev->tt;
+ struct ehci_qh_hw *hw;
if (!qh)
return qh;
@@ -890,8 +895,9 @@ done:
/* init as live, toggle clear, advance to dummy */
qh->qh_state = QH_STATE_IDLE;
- qh->hw_info1 = cpu_to_hc32(ehci, info1);
- qh->hw_info2 = cpu_to_hc32(ehci, info2);
+ hw = qh->hw;
+ hw->hw_info1 = cpu_to_hc32(ehci, info1);
+ hw->hw_info2 = cpu_to_hc32(ehci, info2);
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
qh_refresh (ehci, qh);
return qh;
@@ -933,11 +939,11 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
/* splice right after start */
qh->qh_next = head->qh_next;
- qh->hw_next = head->hw_next;
+ qh->hw->hw_next = head->hw->hw_next;
wmb ();
head->qh_next.qh = qh;
- head->hw_next = dma;
+ head->hw->hw_next = dma;
qh_get(qh);
qh->xacterrs = 0;
@@ -984,7 +990,7 @@ static struct ehci_qh *qh_append_tds (
/* usb_reset_device() briefly reverts to address 0 */
if (usb_pipedevice (urb->pipe) == 0)
- qh->hw_info1 &= ~qh_addr_mask;
+ qh->hw->hw_info1 &= ~qh_addr_mask;
}
/* just one way to queue requests: swap with the dummy qtd.
@@ -1169,7 +1175,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
while (prev->qh_next.qh != qh)
prev = prev->qh_next.qh;
- prev->hw_next = qh->hw_next;
+ prev->hw->hw_next = qh->hw->hw_next;
prev->qh_next = qh->qh_next;
wmb ();
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index edd61ee90323..327437af2122 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -60,6 +60,20 @@ periodic_next_shadow(struct ehci_hcd *ehci, union ehci_shadow *periodic,
}
}
+static __hc32 *
+shadow_next_periodic(struct ehci_hcd *ehci, union ehci_shadow *periodic,
+ __hc32 tag)
+{
+ switch (hc32_to_cpu(ehci, tag)) {
+ /* our ehci_shadow.qh is actually software part */
+ case Q_TYPE_QH:
+ return &periodic->qh->hw->hw_next;
+ /* others are hw parts */
+ default:
+ return periodic->hw_next;
+ }
+}
+
/* caller must hold ehci->lock */
static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
{
@@ -71,7 +85,8 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
while (here.ptr && here.ptr != ptr) {
prev_p = periodic_next_shadow(ehci, prev_p,
Q_NEXT_TYPE(ehci, *hw_p));
- hw_p = here.hw_next;
+ hw_p = shadow_next_periodic(ehci, &here,
+ Q_NEXT_TYPE(ehci, *hw_p));
here = *prev_p;
}
/* an interrupt entry (at list end) could have been shared */
@@ -83,7 +98,7 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
*/
*prev_p = *periodic_next_shadow(ehci, &here,
Q_NEXT_TYPE(ehci, *hw_p));
- *hw_p = *here.hw_next;
+ *hw_p = *shadow_next_periodic(ehci, &here, Q_NEXT_TYPE(ehci, *hw_p));
}
/* how many of the uframe's 125 usecs are allocated? */
@@ -93,18 +108,20 @@ periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
__hc32 *hw_p = &ehci->periodic [frame];
union ehci_shadow *q = &ehci->pshadow [frame];
unsigned usecs = 0;
+ struct ehci_qh_hw *hw;
while (q->ptr) {
switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
case Q_TYPE_QH:
+ hw = q->qh->hw;
/* is it in the S-mask? */
- if (q->qh->hw_info2 & cpu_to_hc32(ehci, 1 << uframe))
+ if (hw->hw_info2 & cpu_to_hc32(ehci, 1 << uframe))
usecs += q->qh->usecs;
/* ... or C-mask? */
- if (q->qh->hw_info2 & cpu_to_hc32(ehci,
+ if (hw->hw_info2 & cpu_to_hc32(ehci,
1 << (8 + uframe)))
usecs += q->qh->c_usecs;
- hw_p = &q->qh->hw_next;
+ hw_p = &hw->hw_next;
q = &q->qh->qh_next;
break;
// case Q_TYPE_FSTN:
@@ -237,10 +254,10 @@ periodic_tt_usecs (
continue;
case Q_TYPE_QH:
if (same_tt(dev, q->qh->dev)) {
- uf = tt_start_uframe(ehci, q->qh->hw_info2);
+ uf = tt_start_uframe(ehci, q->qh->hw->hw_info2);
tt_usecs[uf] += q->qh->tt_usecs;
}
- hw_p = &q->qh->hw_next;
+ hw_p = &q->qh->hw->hw_next;
q = &q->qh->qh_next;
continue;
case Q_TYPE_SITD:
@@ -375,6 +392,7 @@ static int tt_no_collision (
for (; frame < ehci->periodic_size; frame += period) {
union ehci_shadow here;
__hc32 type;
+ struct ehci_qh_hw *hw;
here = ehci->pshadow [frame];
type = Q_NEXT_TYPE(ehci, ehci->periodic [frame]);
@@ -385,17 +403,18 @@ static int tt_no_collision (
here = here.itd->itd_next;
continue;
case Q_TYPE_QH:
+ hw = here.qh->hw;
if (same_tt (dev, here.qh->dev)) {
u32 mask;
mask = hc32_to_cpu(ehci,
- here.qh->hw_info2);
+ hw->hw_info2);
/* "knows" no gap is needed */
mask |= mask >> 8;
if (mask & uf_mask)
break;
}
- type = Q_NEXT_TYPE(ehci, here.qh->hw_next);
+ type = Q_NEXT_TYPE(ehci, hw->hw_next);
here = here.qh->qh_next;
continue;
case Q_TYPE_SITD:
@@ -498,7 +517,8 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
dev_dbg (&qh->dev->dev,
"link qh%d-%04x/%p start %d [%d/%d us]\n",
- period, hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK),
+ period, hc32_to_cpup(ehci, &qh->hw->hw_info2)
+ & (QH_CMASK | QH_SMASK),
qh, qh->start, qh->usecs, qh->c_usecs);
/* high bandwidth, or otherwise every microframe */
@@ -517,7 +537,7 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
if (type == cpu_to_hc32(ehci, Q_TYPE_QH))
break;
prev = periodic_next_shadow(ehci, prev, type);
- hw_p = &here.qh->hw_next;
+ hw_p = shadow_next_periodic(ehci, &here, type);
here = *prev;
}
@@ -528,14 +548,14 @@ static int qh_link_periodic (struct ehci_hcd *ehci, struct ehci_qh *qh)
if (qh->period > here.qh->period)
break;
prev = &here.qh->qh_next;
- hw_p = &here.qh->hw_next;
+ hw_p = &here.qh->hw->hw_next;
here = *prev;
}
/* link in this qh, unless some earlier pass did that */
if (qh != here.qh) {
qh->qh_next = here;
if (here.qh)
- qh->hw_next = *hw_p;
+ qh->hw->hw_next = *hw_p;
wmb ();
prev->qh = qh;
*hw_p = QH_NEXT (ehci, qh->qh_dma);
@@ -581,7 +601,7 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
dev_dbg (&qh->dev->dev,
"unlink qh%d-%04x/%p start %d [%d/%d us]\n",
qh->period,
- hc32_to_cpup(ehci, &qh->hw_info2) & (QH_CMASK | QH_SMASK),
+ hc32_to_cpup(ehci, &qh->hw->hw_info2) & (QH_CMASK | QH_SMASK),
qh, qh->start, qh->usecs, qh->c_usecs);
/* qh->qh_next still "live" to HC */
@@ -596,6 +616,7 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
unsigned wait;
+ struct ehci_qh_hw *hw = qh->hw;
qh_unlink_periodic (ehci, qh);
@@ -606,14 +627,14 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
*/
if (list_empty (&qh->qtd_list)
|| (cpu_to_hc32(ehci, QH_CMASK)
- & qh->hw_info2) != 0)
+ & hw->hw_info2) != 0)
wait = 2;
else
wait = 55; /* worst case: 3 * 1024 */
udelay (wait);
qh->qh_state = QH_STATE_IDLE;
- qh->hw_next = EHCI_LIST_END(ehci);
+ hw->hw_next = EHCI_LIST_END(ehci);
wmb ();
}
@@ -739,14 +760,15 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
unsigned uframe;
__hc32 c_mask;
unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */
+ struct ehci_qh_hw *hw = qh->hw;
qh_refresh(ehci, qh);
- qh->hw_next = EHCI_LIST_END(ehci);
+ hw->hw_next = EHCI_LIST_END(ehci);
frame = qh->start;
/* reuse the previous schedule slots, if we can */
if (frame < qh->period) {
- uframe = ffs(hc32_to_cpup(ehci, &qh->hw_info2) & QH_SMASK);
+ uframe = ffs(hc32_to_cpup(ehci, &hw->hw_info2) & QH_SMASK);
status = check_intr_schedule (ehci, frame, --uframe,
qh, &c_mask);
} else {
@@ -784,11 +806,11 @@ static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
qh->start = frame;
/* reset S-frame and (maybe) C-frame masks */
- qh->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
- qh->hw_info2 |= qh->period
+ hw->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
+ hw->hw_info2 |= qh->period
? cpu_to_hc32(ehci, 1 << uframe)
: cpu_to_hc32(ehci, QH_SMASK);
- qh->hw_info2 |= c_mask;
+ hw->hw_info2 |= c_mask;
} else
ehci_dbg (ehci, "reused qh %p schedule\n", qh);
@@ -2188,7 +2210,7 @@ restart:
case Q_TYPE_QH:
/* handle any completions */
temp.qh = qh_get (q.qh);
- type = Q_NEXT_TYPE(ehci, q.qh->hw_next);
+ type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next);
q = q.qh->qh_next;
modified = qh_completions (ehci, temp.qh);
if (unlikely (list_empty (&temp.qh->qtd_list)))
diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c
new file mode 100644
index 000000000000..cfa21ea20f82
--- /dev/null
+++ b/drivers/usb/host/ehci-w90x900.c
@@ -0,0 +1,181 @@
+/*
+ * linux/driver/usb/host/ehci-w90x900.c
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/platform_device.h>
+
+/*ebable phy0 and phy1 for w90p910*/
+#define ENPHY (0x01<<8)
+#define PHY0_CTR (0xA4)
+#define PHY1_CTR (0xA8)
+
+static int __devinit usb_w90x900_probe(const struct hc_driver *driver,
+ struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ struct resource *res;
+ int retval = 0, irq;
+ unsigned long val;
+
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ retval = -ENXIO;
+ goto err1;
+ }
+
+ hcd = usb_create_hcd(driver, &pdev->dev, "w90x900 EHCI");
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ retval = -EBUSY;
+ goto err2;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (hcd->regs == NULL) {
+ retval = -EFAULT;
+ goto err3;
+ }
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs +
+ HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+
+ /* enable PHY 0,1,the regs only apply to w90p910
+ * 0xA4,0xA8 were offsets of PHY0 and PHY1 controller of
+ * w90p910 IC relative to ehci->regs.
+ */
+ val = __raw_readl(ehci->regs+PHY0_CTR);
+ val |= ENPHY;
+ __raw_writel(val, ehci->regs+PHY0_CTR);
+
+ val = __raw_readl(ehci->regs+PHY1_CTR);
+ val |= ENPHY;
+ __raw_writel(val, ehci->regs+PHY1_CTR);
+
+ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+ ehci->sbrn = 0x20;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ goto err4;
+
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+ if (retval != 0)
+ goto err4;
+
+ ehci_writel(ehci, 1, &ehci->regs->configured_flag);
+
+ return retval;
+err4:
+ iounmap(hcd->regs);
+err3:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err2:
+ usb_put_hcd(hcd);
+err1:
+ return retval;
+}
+
+static
+void usb_w90x900_remove(struct usb_hcd *hcd, struct platform_device *pdev)
+{
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+}
+
+static const struct hc_driver ehci_w90x900_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Nuvoton w90x900 EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ehci_irq,
+ .flags = HCD_USB2|HCD_MEMORY,
+
+ /*
+ * basic lifecycle operations
+ */
+ .reset = ehci_init,
+ .start = ehci_run,
+
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ehci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+#ifdef CONFIG_PM
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+#endif
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+};
+
+static int __devinit ehci_w90x900_probe(struct platform_device *pdev)
+{
+ if (usb_disabled())
+ return -ENODEV;
+
+ return usb_w90x900_probe(&ehci_w90x900_hc_driver, pdev);
+}
+
+static int __devexit ehci_w90x900_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_w90x900_remove(hcd, pdev);
+
+ return 0;
+}
+
+static struct platform_driver ehci_hcd_w90x900_driver = {
+ .probe = ehci_w90x900_probe,
+ .remove = __devexit_p(ehci_w90x900_remove),
+ .driver = {
+ .name = "w90x900-ehci",
+ .owner = THIS_MODULE,
+ },
+};
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("w90p910 usb ehci driver!");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:w90p910-ehci");
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 48b9e889a18b..ec3dba6b8e48 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -126,6 +126,7 @@ struct ehci_hcd { /* one per controller */
unsigned big_endian_mmio:1;
unsigned big_endian_desc:1;
unsigned has_amcc_usb23:1;
+ unsigned need_io_watchdog:1;
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
@@ -135,6 +136,7 @@ struct ehci_hcd { /* one per controller */
#define OHCI_HCCTRL_OFFSET 0x4
#define OHCI_HCCTRL_LEN 0x4
__hc32 *ohci_hcctrl_reg;
+ unsigned has_hostpc:1;
u8 sbrn; /* packed release number */
@@ -298,8 +300,8 @@ union ehci_shadow {
* These appear in both the async and (for interrupt) periodic schedules.
*/
-struct ehci_qh {
- /* first part defined by EHCI spec */
+/* first part defined by EHCI spec */
+struct ehci_qh_hw {
__hc32 hw_next; /* see EHCI 3.6.1 */
__hc32 hw_info1; /* see EHCI 3.6.2 */
#define QH_HEAD 0x00008000
@@ -317,7 +319,10 @@ struct ehci_qh {
__hc32 hw_token;
__hc32 hw_buf [5];
__hc32 hw_buf_hi [5];
+} __attribute__ ((aligned(32)));
+struct ehci_qh {
+ struct ehci_qh_hw *hw;
/* the rest is HCD-private */
dma_addr_t qh_dma; /* address of qh */
union ehci_shadow qh_next; /* ptr to qh; or periodic */
@@ -357,7 +362,7 @@ struct ehci_qh {
struct usb_device *dev; /* access to TT */
unsigned clearing_tt:1; /* Clear-TT-Buf in progress */
-} __attribute__ ((aligned (32)));
+};
/*-------------------------------------------------------------------------*/
@@ -544,7 +549,7 @@ static inline unsigned int
ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
{
if (ehci_is_TDI(ehci)) {
- switch ((portsc>>26)&3) {
+ switch ((portsc >> (ehci->has_hostpc ? 25 : 26)) & 3) {
case 0:
return 0;
case 1:
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
new file mode 100644
index 000000000000..5819e10a146c
--- /dev/null
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -0,0 +1,2905 @@
+/*
+ * ISP1362 HCD (Host Controller Driver) for USB.
+ *
+ * Copyright (C) 2005 Lothar Wassmann <LW@KARO-electronics.de>
+ *
+ * Derived from the SL811 HCD, rewritten for ISP116x.
+ * Copyright (C) 2005 Olav Kongas <ok@artecdesign.ee>
+ *
+ * Portions:
+ * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
+ * Copyright (C) 2004 David Brownell
+ */
+
+/*
+ * The ISP1362 chip requires a large delay (300ns and 462ns) between
+ * accesses to the address and data register.
+ * The following timing options exist:
+ *
+ * 1. Configure your memory controller to add such delays if it can (the best)
+ * 2. Implement platform-specific delay function possibly
+ * combined with configuring the memory controller; see
+ * include/linux/usb_isp1362.h for more info.
+ * 3. Use ndelay (easiest, poorest).
+ *
+ * Use the corresponding macros USE_PLATFORM_DELAY and USE_NDELAY in the
+ * platform specific section of isp1362.h to select the appropriate variant.
+ *
+ * Also note that according to the Philips "ISP1362 Errata" document
+ * Rev 1.00 from 27 May data corruption may occur when the #WR signal
+ * is reasserted (even with #CS deasserted) within 132ns after a
+ * write cycle to any controller register. If the hardware doesn't
+ * implement the recommended fix (gating the #WR with #CS) software
+ * must ensure that no further write cycle (not necessarily to the chip!)
+ * is issued by the CPU within this interval.
+
+ * For PXA25x this can be ensured by using VLIO with the maximum
+ * recovery time (MSCx = 0x7f8c) with a memory clock of 99.53 MHz.
+ */
+
+#ifdef CONFIG_USB_DEBUG
+# define ISP1362_DEBUG
+#else
+# undef ISP1362_DEBUG
+#endif
+
+/*
+ * The PXA255 UDC apparently doesn't handle GET_STATUS, GET_CONFIG and
+ * GET_INTERFACE requests correctly when the SETUP and DATA stages of the
+ * requests are carried out in separate frames. This will delay any SETUP
+ * packets until the start of the next frame so that this situation is
+ * unlikely to occur (and makes usbtest happy running with a PXA255 target
+ * device).
+ */
+#undef BUGGY_PXA2XX_UDC_USBTEST
+
+#undef PTD_TRACE
+#undef URB_TRACE
+#undef VERBOSE
+#undef REGISTERS
+
+/* This enables a memory test on the ISP1362 chip memory to make sure the
+ * chip access timing is correct.
+ */
+#undef CHIP_BUFFER_TEST
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/usb/isp1362.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+static int dbg_level;
+#ifdef ISP1362_DEBUG
+module_param(dbg_level, int, 0644);
+#else
+module_param(dbg_level, int, 0);
+#define STUB_DEBUG_FILE
+#endif
+
+#include "../core/hcd.h"
+#include "../core/usb.h"
+#include "isp1362.h"
+
+
+#define DRIVER_VERSION "2005-04-04"
+#define DRIVER_DESC "ISP1362 USB Host Controller Driver"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static const char hcd_name[] = "isp1362-hcd";
+
+static void isp1362_hc_stop(struct usb_hcd *hcd);
+static int isp1362_hc_start(struct usb_hcd *hcd);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * When called from the interrupthandler only isp1362_hcd->irqenb is modified,
+ * since the interrupt handler will write isp1362_hcd->irqenb to HCuPINT upon
+ * completion.
+ * We don't need a 'disable' counterpart, since interrupts will be disabled
+ * only by the interrupt handler.
+ */
+static inline void isp1362_enable_int(struct isp1362_hcd *isp1362_hcd, u16 mask)
+{
+ if ((isp1362_hcd->irqenb | mask) == isp1362_hcd->irqenb)
+ return;
+ if (mask & ~isp1362_hcd->irqenb)
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, mask & ~isp1362_hcd->irqenb);
+ isp1362_hcd->irqenb |= mask;
+ if (isp1362_hcd->irq_active)
+ return;
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline struct isp1362_ep_queue *get_ptd_queue(struct isp1362_hcd *isp1362_hcd,
+ u16 offset)
+{
+ struct isp1362_ep_queue *epq = NULL;
+
+ if (offset < isp1362_hcd->istl_queue[1].buf_start)
+ epq = &isp1362_hcd->istl_queue[0];
+ else if (offset < isp1362_hcd->intl_queue.buf_start)
+ epq = &isp1362_hcd->istl_queue[1];
+ else if (offset < isp1362_hcd->atl_queue.buf_start)
+ epq = &isp1362_hcd->intl_queue;
+ else if (offset < isp1362_hcd->atl_queue.buf_start +
+ isp1362_hcd->atl_queue.buf_size)
+ epq = &isp1362_hcd->atl_queue;
+
+ if (epq)
+ DBG(1, "%s: PTD $%04x is on %s queue\n", __func__, offset, epq->name);
+ else
+ pr_warning("%s: invalid PTD $%04x\n", __func__, offset);
+
+ return epq;
+}
+
+static inline int get_ptd_offset(struct isp1362_ep_queue *epq, u8 index)
+{
+ int offset;
+
+ if (index * epq->blk_size > epq->buf_size) {
+ pr_warning("%s: Bad %s index %d(%d)\n", __func__, epq->name, index,
+ epq->buf_size / epq->blk_size);
+ return -EINVAL;
+ }
+ offset = epq->buf_start + index * epq->blk_size;
+ DBG(3, "%s: %s PTD[%02x] # %04x\n", __func__, epq->name, index, offset);
+
+ return offset;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline u16 max_transfer_size(struct isp1362_ep_queue *epq, size_t size,
+ int mps)
+{
+ u16 xfer_size = min_t(size_t, MAX_XFER_SIZE, size);
+
+ xfer_size = min_t(size_t, xfer_size, epq->buf_avail * epq->blk_size - PTD_HEADER_SIZE);
+ if (xfer_size < size && xfer_size % mps)
+ xfer_size -= xfer_size % mps;
+
+ return xfer_size;
+}
+
+static int claim_ptd_buffers(struct isp1362_ep_queue *epq,
+ struct isp1362_ep *ep, u16 len)
+{
+ int ptd_offset = -EINVAL;
+ int index;
+ int num_ptds = ((len + PTD_HEADER_SIZE - 1) / epq->blk_size) + 1;
+ int found = -1;
+ int last = -1;
+
+ BUG_ON(len > epq->buf_size);
+
+ if (!epq->buf_avail)
+ return -ENOMEM;
+
+ if (ep->num_ptds)
+ pr_err("%s: %s len %d/%d num_ptds %d buf_map %08lx skip_map %08lx\n", __func__,
+ epq->name, len, epq->blk_size, num_ptds, epq->buf_map, epq->skip_map);
+ BUG_ON(ep->num_ptds != 0);
+
+ for (index = 0; index <= epq->buf_count - num_ptds; index++) {
+ if (test_bit(index, &epq->buf_map))
+ continue;
+ found = index;
+ for (last = index + 1; last < index + num_ptds; last++) {
+ if (test_bit(last, &epq->buf_map)) {
+ found = -1;
+ break;
+ }
+ }
+ if (found >= 0)
+ break;
+ }
+ if (found < 0)
+ return -EOVERFLOW;
+
+ DBG(1, "%s: Found %d PTDs[%d] for %d/%d byte\n", __func__,
+ num_ptds, found, len, (int)(epq->blk_size - PTD_HEADER_SIZE));
+ ptd_offset = get_ptd_offset(epq, found);
+ WARN_ON(ptd_offset < 0);
+ ep->ptd_offset = ptd_offset;
+ ep->num_ptds += num_ptds;
+ epq->buf_avail -= num_ptds;
+ BUG_ON(epq->buf_avail > epq->buf_count);
+ ep->ptd_index = found;
+ for (index = found; index < last; index++)
+ __set_bit(index, &epq->buf_map);
+ DBG(1, "%s: Done %s PTD[%d] $%04x, avail %d count %d claimed %d %08lx:%08lx\n",
+ __func__, epq->name, ep->ptd_index, ep->ptd_offset,
+ epq->buf_avail, epq->buf_count, num_ptds, epq->buf_map, epq->skip_map);
+
+ return found;
+}
+
+static inline void release_ptd_buffers(struct isp1362_ep_queue *epq, struct isp1362_ep *ep)
+{
+ int index = ep->ptd_index;
+ int last = ep->ptd_index + ep->num_ptds;
+
+ if (last > epq->buf_count)
+ pr_err("%s: ep %p req %d len %d %s PTD[%d] $%04x num_ptds %d buf_count %d buf_avail %d buf_map %08lx skip_map %08lx\n",
+ __func__, ep, ep->num_req, ep->length, epq->name, ep->ptd_index,
+ ep->ptd_offset, ep->num_ptds, epq->buf_count, epq->buf_avail,
+ epq->buf_map, epq->skip_map);
+ BUG_ON(last > epq->buf_count);
+
+ for (; index < last; index++) {
+ __clear_bit(index, &epq->buf_map);
+ __set_bit(index, &epq->skip_map);
+ }
+ epq->buf_avail += ep->num_ptds;
+ epq->ptd_count--;
+
+ BUG_ON(epq->buf_avail > epq->buf_count);
+ BUG_ON(epq->ptd_count > epq->buf_count);
+
+ DBG(1, "%s: Done %s PTDs $%04x released %d avail %d count %d\n",
+ __func__, epq->name,
+ ep->ptd_offset, ep->num_ptds, epq->buf_avail, epq->buf_count);
+ DBG(1, "%s: buf_map %08lx skip_map %08lx\n", __func__,
+ epq->buf_map, epq->skip_map);
+
+ ep->num_ptds = 0;
+ ep->ptd_offset = -EINVAL;
+ ep->ptd_index = -EINVAL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ Set up PTD's.
+*/
+static void prepare_ptd(struct isp1362_hcd *isp1362_hcd, struct urb *urb,
+ struct isp1362_ep *ep, struct isp1362_ep_queue *epq,
+ u16 fno)
+{
+ struct ptd *ptd;
+ int toggle;
+ int dir;
+ u16 len;
+ size_t buf_len = urb->transfer_buffer_length - urb->actual_length;
+
+ DBG(3, "%s: %s ep %p\n", __func__, epq->name, ep);
+
+ ptd = &ep->ptd;
+
+ ep->data = (unsigned char *)urb->transfer_buffer + urb->actual_length;
+
+ switch (ep->nextpid) {
+ case USB_PID_IN:
+ toggle = usb_gettoggle(urb->dev, ep->epnum, 0);
+ dir = PTD_DIR_IN;
+ if (usb_pipecontrol(urb->pipe)) {
+ len = min_t(size_t, ep->maxpacket, buf_len);
+ } else if (usb_pipeisoc(urb->pipe)) {
+ len = min_t(size_t, urb->iso_frame_desc[fno].length, MAX_XFER_SIZE);
+ ep->data = urb->transfer_buffer + urb->iso_frame_desc[fno].offset;
+ } else
+ len = max_transfer_size(epq, buf_len, ep->maxpacket);
+ DBG(1, "%s: IN len %d/%d/%d from URB\n", __func__, len, ep->maxpacket,
+ (int)buf_len);
+ break;
+ case USB_PID_OUT:
+ toggle = usb_gettoggle(urb->dev, ep->epnum, 1);
+ dir = PTD_DIR_OUT;
+ if (usb_pipecontrol(urb->pipe))
+ len = min_t(size_t, ep->maxpacket, buf_len);
+ else if (usb_pipeisoc(urb->pipe))
+ len = min_t(size_t, urb->iso_frame_desc[0].length, MAX_XFER_SIZE);
+ else
+ len = max_transfer_size(epq, buf_len, ep->maxpacket);
+ if (len == 0)
+ pr_info("%s: Sending ZERO packet: %d\n", __func__,
+ urb->transfer_flags & URB_ZERO_PACKET);
+ DBG(1, "%s: OUT len %d/%d/%d from URB\n", __func__, len, ep->maxpacket,
+ (int)buf_len);
+ break;
+ case USB_PID_SETUP:
+ toggle = 0;
+ dir = PTD_DIR_SETUP;
+ len = sizeof(struct usb_ctrlrequest);
+ DBG(1, "%s: SETUP len %d\n", __func__, len);
+ ep->data = urb->setup_packet;
+ break;
+ case USB_PID_ACK:
+ toggle = 1;
+ len = 0;
+ dir = (urb->transfer_buffer_length && usb_pipein(urb->pipe)) ?
+ PTD_DIR_OUT : PTD_DIR_IN;
+ DBG(1, "%s: ACK len %d\n", __func__, len);
+ break;
+ default:
+ toggle = dir = len = 0;
+ pr_err("%s@%d: ep->nextpid %02x\n", __func__, __LINE__, ep->nextpid);
+ BUG_ON(1);
+ }
+
+ ep->length = len;
+ if (!len)
+ ep->data = NULL;
+
+ ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle);
+ ptd->mps = PTD_MPS(ep->maxpacket) | PTD_SPD(urb->dev->speed == USB_SPEED_LOW) |
+ PTD_EP(ep->epnum);
+ ptd->len = PTD_LEN(len) | PTD_DIR(dir);
+ ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe));
+
+ if (usb_pipeint(urb->pipe)) {
+ ptd->faddr |= PTD_SF_INT(ep->branch);
+ ptd->faddr |= PTD_PR(ep->interval ? __ffs(ep->interval) : 0);
+ }
+ if (usb_pipeisoc(urb->pipe))
+ ptd->faddr |= PTD_SF_ISO(fno);
+
+ DBG(1, "%s: Finished\n", __func__);
+}
+
+static void isp1362_write_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
+ struct isp1362_ep_queue *epq)
+{
+ struct ptd *ptd = &ep->ptd;
+ int len = PTD_GET_DIR(ptd) == PTD_DIR_IN ? 0 : ep->length;
+
+ _BUG_ON(ep->ptd_offset < 0);
+
+ prefetch(ptd);
+ isp1362_write_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE);
+ if (len)
+ isp1362_write_buffer(isp1362_hcd, ep->data,
+ ep->ptd_offset + PTD_HEADER_SIZE, len);
+
+ dump_ptd(ptd);
+ dump_ptd_out_data(ptd, ep->data);
+}
+
+static void isp1362_read_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
+ struct isp1362_ep_queue *epq)
+{
+ struct ptd *ptd = &ep->ptd;
+ int act_len;
+
+ WARN_ON(list_empty(&ep->active));
+ BUG_ON(ep->ptd_offset < 0);
+
+ list_del_init(&ep->active);
+ DBG(1, "%s: ep %p removed from active list %p\n", __func__, ep, &epq->active);
+
+ prefetchw(ptd);
+ isp1362_read_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE);
+ dump_ptd(ptd);
+ act_len = PTD_GET_COUNT(ptd);
+ if (PTD_GET_DIR(ptd) != PTD_DIR_IN || act_len == 0)
+ return;
+ if (act_len > ep->length)
+ pr_err("%s: ep %p PTD $%04x act_len %d ep->length %d\n", __func__, ep,
+ ep->ptd_offset, act_len, ep->length);
+ BUG_ON(act_len > ep->length);
+ /* Only transfer the amount of data that has actually been overwritten
+ * in the chip buffer. We don't want any data that doesn't belong to the
+ * transfer to leak out of the chip to the callers transfer buffer!
+ */
+ prefetchw(ep->data);
+ isp1362_read_buffer(isp1362_hcd, ep->data,
+ ep->ptd_offset + PTD_HEADER_SIZE, act_len);
+ dump_ptd_in_data(ptd, ep->data);
+}
+
+/*
+ * INT PTDs will stay in the chip until data is available.
+ * This function will remove a PTD from the chip when the URB is dequeued.
+ * Must be called with the spinlock held and IRQs disabled
+ */
+static void remove_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep)
+
+{
+ int index;
+ struct isp1362_ep_queue *epq;
+
+ DBG(1, "%s: ep %p PTD[%d] $%04x\n", __func__, ep, ep->ptd_index, ep->ptd_offset);
+ BUG_ON(ep->ptd_offset < 0);
+
+ epq = get_ptd_queue(isp1362_hcd, ep->ptd_offset);
+ BUG_ON(!epq);
+
+ /* put ep in remove_list for cleanup */
+ WARN_ON(!list_empty(&ep->remove_list));
+ list_add_tail(&ep->remove_list, &isp1362_hcd->remove_list);
+ /* let SOF interrupt handle the cleanup */
+ isp1362_enable_int(isp1362_hcd, HCuPINT_SOF);
+
+ index = ep->ptd_index;
+ if (index < 0)
+ /* ISO queues don't have SKIP registers */
+ return;
+
+ DBG(1, "%s: Disabling PTD[%02x] $%04x %08lx|%08x\n", __func__,
+ index, ep->ptd_offset, epq->skip_map, 1 << index);
+
+ /* prevent further processing of PTD (will be effective after next SOF) */
+ epq->skip_map |= 1 << index;
+ if (epq == &isp1362_hcd->atl_queue) {
+ DBG(2, "%s: ATLSKIP = %08x -> %08lx\n", __func__,
+ isp1362_read_reg32(isp1362_hcd, HCATLSKIP), epq->skip_map);
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP, epq->skip_map);
+ if (~epq->skip_map == 0)
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
+ } else if (epq == &isp1362_hcd->intl_queue) {
+ DBG(2, "%s: INTLSKIP = %08x -> %08lx\n", __func__,
+ isp1362_read_reg32(isp1362_hcd, HCINTLSKIP), epq->skip_map);
+ isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, epq->skip_map);
+ if (~epq->skip_map == 0)
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
+ }
+}
+
+/*
+ Take done or failed requests out of schedule. Give back
+ processed urbs.
+*/
+static void finish_request(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep,
+ struct urb *urb, int status)
+ __releases(isp1362_hcd->lock)
+ __acquires(isp1362_hcd->lock)
+{
+ urb->hcpriv = NULL;
+ ep->error_count = 0;
+
+ if (usb_pipecontrol(urb->pipe))
+ ep->nextpid = USB_PID_SETUP;
+
+ URB_DBG("%s: req %d FA %d ep%d%s %s: len %d/%d %s stat %d\n", __func__,
+ ep->num_req, usb_pipedevice(urb->pipe),
+ usb_pipeendpoint(urb->pipe),
+ !usb_pipein(urb->pipe) ? "out" : "in",
+ usb_pipecontrol(urb->pipe) ? "ctrl" :
+ usb_pipeint(urb->pipe) ? "int" :
+ usb_pipebulk(urb->pipe) ? "bulk" :
+ "iso",
+ urb->actual_length, urb->transfer_buffer_length,
+ !(urb->transfer_flags & URB_SHORT_NOT_OK) ?
+ "short_ok" : "", urb->status);
+
+
+ usb_hcd_unlink_urb_from_ep(isp1362_hcd_to_hcd(isp1362_hcd), urb);
+ spin_unlock(&isp1362_hcd->lock);
+ usb_hcd_giveback_urb(isp1362_hcd_to_hcd(isp1362_hcd), urb, status);
+ spin_lock(&isp1362_hcd->lock);
+
+ /* take idle endpoints out of the schedule right away */
+ if (!list_empty(&ep->hep->urb_list))
+ return;
+
+ /* async deschedule */
+ if (!list_empty(&ep->schedule)) {
+ list_del_init(&ep->schedule);
+ return;
+ }
+
+
+ if (ep->interval) {
+ /* periodic deschedule */
+ DBG(1, "deschedule qh%d/%p branch %d load %d bandwidth %d -> %d\n", ep->interval,
+ ep, ep->branch, ep->load,
+ isp1362_hcd->load[ep->branch],
+ isp1362_hcd->load[ep->branch] - ep->load);
+ isp1362_hcd->load[ep->branch] -= ep->load;
+ ep->branch = PERIODIC_SIZE;
+ }
+}
+
+/*
+ * Analyze transfer results, handle partial transfers and errors
+*/
+static void postproc_ep(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep)
+{
+ struct urb *urb = get_urb(ep);
+ struct usb_device *udev;
+ struct ptd *ptd;
+ int short_ok;
+ u16 len;
+ int urbstat = -EINPROGRESS;
+ u8 cc;
+
+ DBG(2, "%s: ep %p req %d\n", __func__, ep, ep->num_req);
+
+ udev = urb->dev;
+ ptd = &ep->ptd;
+ cc = PTD_GET_CC(ptd);
+ if (cc == PTD_NOTACCESSED) {
+ pr_err("%s: req %d PTD %p Untouched by ISP1362\n", __func__,
+ ep->num_req, ptd);
+ cc = PTD_DEVNOTRESP;
+ }
+
+ short_ok = !(urb->transfer_flags & URB_SHORT_NOT_OK);
+ len = urb->transfer_buffer_length - urb->actual_length;
+
+ /* Data underrun is special. For allowed underrun
+ we clear the error and continue as normal. For
+ forbidden underrun we finish the DATA stage
+ immediately while for control transfer,
+ we do a STATUS stage.
+ */
+ if (cc == PTD_DATAUNDERRUN) {
+ if (short_ok) {
+ DBG(1, "%s: req %d Allowed data underrun short_%sok %d/%d/%d byte\n",
+ __func__, ep->num_req, short_ok ? "" : "not_",
+ PTD_GET_COUNT(ptd), ep->maxpacket, len);
+ cc = PTD_CC_NOERROR;
+ urbstat = 0;
+ } else {
+ DBG(1, "%s: req %d Data Underrun %s nextpid %02x short_%sok %d/%d/%d byte\n",
+ __func__, ep->num_req,
+ usb_pipein(urb->pipe) ? "IN" : "OUT", ep->nextpid,
+ short_ok ? "" : "not_",
+ PTD_GET_COUNT(ptd), ep->maxpacket, len);
+ if (usb_pipecontrol(urb->pipe)) {
+ ep->nextpid = USB_PID_ACK;
+ /* save the data underrun error code for later and
+ * procede with the status stage
+ */
+ urb->actual_length += PTD_GET_COUNT(ptd);
+ BUG_ON(urb->actual_length > urb->transfer_buffer_length);
+
+ if (urb->status == -EINPROGRESS)
+ urb->status = cc_to_error[PTD_DATAUNDERRUN];
+ } else {
+ usb_settoggle(udev, ep->epnum, ep->nextpid == USB_PID_OUT,
+ PTD_GET_TOGGLE(ptd));
+ urbstat = cc_to_error[PTD_DATAUNDERRUN];
+ }
+ goto out;
+ }
+ }
+
+ if (cc != PTD_CC_NOERROR) {
+ if (++ep->error_count >= 3 || cc == PTD_CC_STALL || cc == PTD_DATAOVERRUN) {
+ urbstat = cc_to_error[cc];
+ DBG(1, "%s: req %d nextpid %02x, status %d, error %d, error_count %d\n",
+ __func__, ep->num_req, ep->nextpid, urbstat, cc,
+ ep->error_count);
+ }
+ goto out;
+ }
+
+ switch (ep->nextpid) {
+ case USB_PID_OUT:
+ if (PTD_GET_COUNT(ptd) != ep->length)
+ pr_err("%s: count=%d len=%d\n", __func__,
+ PTD_GET_COUNT(ptd), ep->length);
+ BUG_ON(PTD_GET_COUNT(ptd) != ep->length);
+ urb->actual_length += ep->length;
+ BUG_ON(urb->actual_length > urb->transfer_buffer_length);
+ usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd));
+ if (urb->actual_length == urb->transfer_buffer_length) {
+ DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__,
+ ep->num_req, len, ep->maxpacket, urbstat);
+ if (usb_pipecontrol(urb->pipe)) {
+ DBG(3, "%s: req %d %s Wait for ACK\n", __func__,
+ ep->num_req,
+ usb_pipein(urb->pipe) ? "IN" : "OUT");
+ ep->nextpid = USB_PID_ACK;
+ } else {
+ if (len % ep->maxpacket ||
+ !(urb->transfer_flags & URB_ZERO_PACKET)) {
+ urbstat = 0;
+ DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n",
+ __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT",
+ urbstat, len, ep->maxpacket, urb->actual_length);
+ }
+ }
+ }
+ break;
+ case USB_PID_IN:
+ len = PTD_GET_COUNT(ptd);
+ BUG_ON(len > ep->length);
+ urb->actual_length += len;
+ BUG_ON(urb->actual_length > urb->transfer_buffer_length);
+ usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd));
+ /* if transfer completed or (allowed) data underrun */
+ if ((urb->transfer_buffer_length == urb->actual_length) ||
+ len % ep->maxpacket) {
+ DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__,
+ ep->num_req, len, ep->maxpacket, urbstat);
+ if (usb_pipecontrol(urb->pipe)) {
+ DBG(3, "%s: req %d %s Wait for ACK\n", __func__,
+ ep->num_req,
+ usb_pipein(urb->pipe) ? "IN" : "OUT");
+ ep->nextpid = USB_PID_ACK;
+ } else {
+ urbstat = 0;
+ DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n",
+ __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT",
+ urbstat, len, ep->maxpacket, urb->actual_length);
+ }
+ }
+ break;
+ case USB_PID_SETUP:
+ if (urb->transfer_buffer_length == urb->actual_length) {
+ ep->nextpid = USB_PID_ACK;
+ } else if (usb_pipeout(urb->pipe)) {
+ usb_settoggle(udev, 0, 1, 1);
+ ep->nextpid = USB_PID_OUT;
+ } else {
+ usb_settoggle(udev, 0, 0, 1);
+ ep->nextpid = USB_PID_IN;
+ }
+ break;
+ case USB_PID_ACK:
+ DBG(3, "%s: req %d got ACK %d -> 0\n", __func__, ep->num_req,
+ urbstat);
+ WARN_ON(urbstat != -EINPROGRESS);
+ urbstat = 0;
+ ep->nextpid = 0;
+ break;
+ default:
+ BUG_ON(1);
+ }
+
+ out:
+ if (urbstat != -EINPROGRESS) {
+ DBG(2, "%s: Finishing ep %p req %d urb %p status %d\n", __func__,
+ ep, ep->num_req, urb, urbstat);
+ finish_request(isp1362_hcd, ep, urb, urbstat);
+ }
+}
+
+static void finish_unlinks(struct isp1362_hcd *isp1362_hcd)
+{
+ struct isp1362_ep *ep;
+ struct isp1362_ep *tmp;
+
+ list_for_each_entry_safe(ep, tmp, &isp1362_hcd->remove_list, remove_list) {
+ struct isp1362_ep_queue *epq =
+ get_ptd_queue(isp1362_hcd, ep->ptd_offset);
+ int index = ep->ptd_index;
+
+ BUG_ON(epq == NULL);
+ if (index >= 0) {
+ DBG(1, "%s: remove PTD[%d] $%04x\n", __func__, index, ep->ptd_offset);
+ BUG_ON(ep->num_ptds == 0);
+ release_ptd_buffers(epq, ep);
+ }
+ if (!list_empty(&ep->hep->urb_list)) {
+ struct urb *urb = get_urb(ep);
+
+ DBG(1, "%s: Finishing req %d ep %p from remove_list\n", __func__,
+ ep->num_req, ep);
+ finish_request(isp1362_hcd, ep, urb, -ESHUTDOWN);
+ }
+ WARN_ON(list_empty(&ep->active));
+ if (!list_empty(&ep->active)) {
+ list_del_init(&ep->active);
+ DBG(1, "%s: ep %p removed from active list\n", __func__, ep);
+ }
+ list_del_init(&ep->remove_list);
+ DBG(1, "%s: ep %p removed from remove_list\n", __func__, ep);
+ }
+ DBG(1, "%s: Done\n", __func__);
+}
+
+static inline void enable_atl_transfers(struct isp1362_hcd *isp1362_hcd, int count)
+{
+ if (count > 0) {
+ if (count < isp1362_hcd->atl_queue.ptd_count)
+ isp1362_write_reg16(isp1362_hcd, HCATLDTC, count);
+ isp1362_enable_int(isp1362_hcd, HCuPINT_ATL);
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP, isp1362_hcd->atl_queue.skip_map);
+ isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
+ } else
+ isp1362_enable_int(isp1362_hcd, HCuPINT_SOF);
+}
+
+static inline void enable_intl_transfers(struct isp1362_hcd *isp1362_hcd)
+{
+ isp1362_enable_int(isp1362_hcd, HCuPINT_INTL);
+ isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
+ isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, isp1362_hcd->intl_queue.skip_map);
+}
+
+static inline void enable_istl_transfers(struct isp1362_hcd *isp1362_hcd, int flip)
+{
+ isp1362_enable_int(isp1362_hcd, flip ? HCuPINT_ISTL1 : HCuPINT_ISTL0);
+ isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, flip ?
+ HCBUFSTAT_ISTL1_FULL : HCBUFSTAT_ISTL0_FULL);
+}
+
+static int submit_req(struct isp1362_hcd *isp1362_hcd, struct urb *urb,
+ struct isp1362_ep *ep, struct isp1362_ep_queue *epq)
+{
+ int index = epq->free_ptd;
+
+ prepare_ptd(isp1362_hcd, urb, ep, epq, 0);
+ index = claim_ptd_buffers(epq, ep, ep->length);
+ if (index == -ENOMEM) {
+ DBG(1, "%s: req %d No free %s PTD available: %d, %08lx:%08lx\n", __func__,
+ ep->num_req, epq->name, ep->num_ptds, epq->buf_map, epq->skip_map);
+ return index;
+ } else if (index == -EOVERFLOW) {
+ DBG(1, "%s: req %d Not enough space for %d byte %s PTD %d %08lx:%08lx\n",
+ __func__, ep->num_req, ep->length, epq->name, ep->num_ptds,
+ epq->buf_map, epq->skip_map);
+ return index;
+ } else
+ BUG_ON(index < 0);
+ list_add_tail(&ep->active, &epq->active);
+ DBG(1, "%s: ep %p req %d len %d added to active list %p\n", __func__,
+ ep, ep->num_req, ep->length, &epq->active);
+ DBG(1, "%s: Submitting %s PTD $%04x for ep %p req %d\n", __func__, epq->name,
+ ep->ptd_offset, ep, ep->num_req);
+ isp1362_write_ptd(isp1362_hcd, ep, epq);
+ __clear_bit(ep->ptd_index, &epq->skip_map);
+
+ return 0;
+}
+
+static void start_atl_transfers(struct isp1362_hcd *isp1362_hcd)
+{
+ int ptd_count = 0;
+ struct isp1362_ep_queue *epq = &isp1362_hcd->atl_queue;
+ struct isp1362_ep *ep;
+ int defer = 0;
+
+ if (atomic_read(&epq->finishing)) {
+ DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
+ return;
+ }
+
+ list_for_each_entry(ep, &isp1362_hcd->async, schedule) {
+ struct urb *urb = get_urb(ep);
+ int ret;
+
+ if (!list_empty(&ep->active)) {
+ DBG(2, "%s: Skipping active %s ep %p\n", __func__, epq->name, ep);
+ continue;
+ }
+
+ DBG(1, "%s: Processing %s ep %p req %d\n", __func__, epq->name,
+ ep, ep->num_req);
+
+ ret = submit_req(isp1362_hcd, urb, ep, epq);
+ if (ret == -ENOMEM) {
+ defer = 1;
+ break;
+ } else if (ret == -EOVERFLOW) {
+ defer = 1;
+ continue;
+ }
+#ifdef BUGGY_PXA2XX_UDC_USBTEST
+ defer = ep->nextpid == USB_PID_SETUP;
+#endif
+ ptd_count++;
+ }
+
+ /* Avoid starving of endpoints */
+ if (isp1362_hcd->async.next != isp1362_hcd->async.prev) {
+ DBG(2, "%s: Cycling ASYNC schedule %d\n", __func__, ptd_count);
+ list_move(&isp1362_hcd->async, isp1362_hcd->async.next);
+ }
+ if (ptd_count || defer)
+ enable_atl_transfers(isp1362_hcd, defer ? 0 : ptd_count);
+
+ epq->ptd_count += ptd_count;
+ if (epq->ptd_count > epq->stat_maxptds) {
+ epq->stat_maxptds = epq->ptd_count;
+ DBG(0, "%s: max_ptds: %d\n", __func__, epq->stat_maxptds);
+ }
+}
+
+static void start_intl_transfers(struct isp1362_hcd *isp1362_hcd)
+{
+ int ptd_count = 0;
+ struct isp1362_ep_queue *epq = &isp1362_hcd->intl_queue;
+ struct isp1362_ep *ep;
+
+ if (atomic_read(&epq->finishing)) {
+ DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
+ return;
+ }
+
+ list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) {
+ struct urb *urb = get_urb(ep);
+ int ret;
+
+ if (!list_empty(&ep->active)) {
+ DBG(1, "%s: Skipping active %s ep %p\n", __func__,
+ epq->name, ep);
+ continue;
+ }
+
+ DBG(1, "%s: Processing %s ep %p req %d\n", __func__,
+ epq->name, ep, ep->num_req);
+ ret = submit_req(isp1362_hcd, urb, ep, epq);
+ if (ret == -ENOMEM)
+ break;
+ else if (ret == -EOVERFLOW)
+ continue;
+ ptd_count++;
+ }
+
+ if (ptd_count) {
+ static int last_count;
+
+ if (ptd_count != last_count) {
+ DBG(0, "%s: ptd_count: %d\n", __func__, ptd_count);
+ last_count = ptd_count;
+ }
+ enable_intl_transfers(isp1362_hcd);
+ }
+
+ epq->ptd_count += ptd_count;
+ if (epq->ptd_count > epq->stat_maxptds)
+ epq->stat_maxptds = epq->ptd_count;
+}
+
+static inline int next_ptd(struct isp1362_ep_queue *epq, struct isp1362_ep *ep)
+{
+ u16 ptd_offset = ep->ptd_offset;
+ int num_ptds = (ep->length + PTD_HEADER_SIZE + (epq->blk_size - 1)) / epq->blk_size;
+
+ DBG(2, "%s: PTD offset $%04x + %04x => %d * %04x -> $%04x\n", __func__, ptd_offset,
+ ep->length, num_ptds, epq->blk_size, ptd_offset + num_ptds * epq->blk_size);
+
+ ptd_offset += num_ptds * epq->blk_size;
+ if (ptd_offset < epq->buf_start + epq->buf_size)
+ return ptd_offset;
+ else
+ return -ENOMEM;
+}
+
+static void start_iso_transfers(struct isp1362_hcd *isp1362_hcd)
+{
+ int ptd_count = 0;
+ int flip = isp1362_hcd->istl_flip;
+ struct isp1362_ep_queue *epq;
+ int ptd_offset;
+ struct isp1362_ep *ep;
+ struct isp1362_ep *tmp;
+ u16 fno = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
+
+ fill2:
+ epq = &isp1362_hcd->istl_queue[flip];
+ if (atomic_read(&epq->finishing)) {
+ DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name);
+ return;
+ }
+
+ if (!list_empty(&epq->active))
+ return;
+
+ ptd_offset = epq->buf_start;
+ list_for_each_entry_safe(ep, tmp, &isp1362_hcd->isoc, schedule) {
+ struct urb *urb = get_urb(ep);
+ s16 diff = fno - (u16)urb->start_frame;
+
+ DBG(1, "%s: Processing %s ep %p\n", __func__, epq->name, ep);
+
+ if (diff > urb->number_of_packets) {
+ /* time frame for this URB has elapsed */
+ finish_request(isp1362_hcd, ep, urb, -EOVERFLOW);
+ continue;
+ } else if (diff < -1) {
+ /* URB is not due in this frame or the next one.
+ * Comparing with '-1' instead of '0' accounts for double
+ * buffering in the ISP1362 which enables us to queue the PTD
+ * one frame ahead of time
+ */
+ } else if (diff == -1) {
+ /* submit PTD's that are due in the next frame */
+ prepare_ptd(isp1362_hcd, urb, ep, epq, fno);
+ if (ptd_offset + PTD_HEADER_SIZE + ep->length >
+ epq->buf_start + epq->buf_size) {
+ pr_err("%s: Not enough ISO buffer space for %d byte PTD\n",
+ __func__, ep->length);
+ continue;
+ }
+ ep->ptd_offset = ptd_offset;
+ list_add_tail(&ep->active, &epq->active);
+
+ ptd_offset = next_ptd(epq, ep);
+ if (ptd_offset < 0) {
+ pr_warning("%s: req %d No more %s PTD buffers available\n", __func__,
+ ep->num_req, epq->name);
+ break;
+ }
+ }
+ }
+ list_for_each_entry(ep, &epq->active, active) {
+ if (epq->active.next == &ep->active)
+ ep->ptd.mps |= PTD_LAST_MSK;
+ isp1362_write_ptd(isp1362_hcd, ep, epq);
+ ptd_count++;
+ }
+
+ if (ptd_count)
+ enable_istl_transfers(isp1362_hcd, flip);
+
+ epq->ptd_count += ptd_count;
+ if (epq->ptd_count > epq->stat_maxptds)
+ epq->stat_maxptds = epq->ptd_count;
+
+ /* check, whether the second ISTL buffer may also be filled */
+ if (!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) &
+ (flip ? HCBUFSTAT_ISTL0_FULL : HCBUFSTAT_ISTL1_FULL))) {
+ fno++;
+ ptd_count = 0;
+ flip = 1 - flip;
+ goto fill2;
+ }
+}
+
+static void finish_transfers(struct isp1362_hcd *isp1362_hcd, unsigned long done_map,
+ struct isp1362_ep_queue *epq)
+{
+ struct isp1362_ep *ep;
+ struct isp1362_ep *tmp;
+
+ if (list_empty(&epq->active)) {
+ DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name);
+ return;
+ }
+
+ DBG(1, "%s: Finishing %s transfers %08lx\n", __func__, epq->name, done_map);
+
+ atomic_inc(&epq->finishing);
+ list_for_each_entry_safe(ep, tmp, &epq->active, active) {
+ int index = ep->ptd_index;
+
+ DBG(1, "%s: Checking %s PTD[%02x] $%04x\n", __func__, epq->name,
+ index, ep->ptd_offset);
+
+ BUG_ON(index < 0);
+ if (__test_and_clear_bit(index, &done_map)) {
+ isp1362_read_ptd(isp1362_hcd, ep, epq);
+ epq->free_ptd = index;
+ BUG_ON(ep->num_ptds == 0);
+ release_ptd_buffers(epq, ep);
+
+ DBG(1, "%s: ep %p req %d removed from active list\n", __func__,
+ ep, ep->num_req);
+ if (!list_empty(&ep->remove_list)) {
+ list_del_init(&ep->remove_list);
+ DBG(1, "%s: ep %p removed from remove list\n", __func__, ep);
+ }
+ DBG(1, "%s: Postprocessing %s ep %p req %d\n", __func__, epq->name,
+ ep, ep->num_req);
+ postproc_ep(isp1362_hcd, ep);
+ }
+ if (!done_map)
+ break;
+ }
+ if (done_map)
+ pr_warning("%s: done_map not clear: %08lx:%08lx\n", __func__, done_map,
+ epq->skip_map);
+ atomic_dec(&epq->finishing);
+}
+
+static void finish_iso_transfers(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep_queue *epq)
+{
+ struct isp1362_ep *ep;
+ struct isp1362_ep *tmp;
+
+ if (list_empty(&epq->active)) {
+ DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name);
+ return;
+ }
+
+ DBG(1, "%s: Finishing %s transfers\n", __func__, epq->name);
+
+ atomic_inc(&epq->finishing);
+ list_for_each_entry_safe(ep, tmp, &epq->active, active) {
+ DBG(1, "%s: Checking PTD $%04x\n", __func__, ep->ptd_offset);
+
+ isp1362_read_ptd(isp1362_hcd, ep, epq);
+ DBG(1, "%s: Postprocessing %s ep %p\n", __func__, epq->name, ep);
+ postproc_ep(isp1362_hcd, ep);
+ }
+ WARN_ON(epq->blk_size != 0);
+ atomic_dec(&epq->finishing);
+}
+
+static irqreturn_t isp1362_irq(struct usb_hcd *hcd)
+{
+ int handled = 0;
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ u16 irqstat;
+ u16 svc_mask;
+
+ spin_lock(&isp1362_hcd->lock);
+
+ BUG_ON(isp1362_hcd->irq_active++);
+
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
+
+ irqstat = isp1362_read_reg16(isp1362_hcd, HCuPINT);
+ DBG(3, "%s: got IRQ %04x:%04x\n", __func__, irqstat, isp1362_hcd->irqenb);
+
+ /* only handle interrupts that are currently enabled */
+ irqstat &= isp1362_hcd->irqenb;
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, irqstat);
+ svc_mask = irqstat;
+
+ if (irqstat & HCuPINT_SOF) {
+ isp1362_hcd->irqenb &= ~HCuPINT_SOF;
+ isp1362_hcd->irq_stat[ISP1362_INT_SOF]++;
+ handled = 1;
+ svc_mask &= ~HCuPINT_SOF;
+ DBG(3, "%s: SOF\n", __func__);
+ isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
+ if (!list_empty(&isp1362_hcd->remove_list))
+ finish_unlinks(isp1362_hcd);
+ if (!list_empty(&isp1362_hcd->async) && !(irqstat & HCuPINT_ATL)) {
+ if (list_empty(&isp1362_hcd->atl_queue.active)) {
+ start_atl_transfers(isp1362_hcd);
+ } else {
+ isp1362_enable_int(isp1362_hcd, HCuPINT_ATL);
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP,
+ isp1362_hcd->atl_queue.skip_map);
+ isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
+ }
+ }
+ }
+
+ if (irqstat & HCuPINT_ISTL0) {
+ isp1362_hcd->irq_stat[ISP1362_INT_ISTL0]++;
+ handled = 1;
+ svc_mask &= ~HCuPINT_ISTL0;
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL0_FULL);
+ DBG(1, "%s: ISTL0\n", __func__);
+ WARN_ON((int)!!isp1362_hcd->istl_flip);
+ WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & HCBUFSTAT_ISTL0_ACTIVE);
+ WARN_ON(!isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & HCBUFSTAT_ISTL0_DONE);
+ isp1362_hcd->irqenb &= ~HCuPINT_ISTL0;
+ }
+
+ if (irqstat & HCuPINT_ISTL1) {
+ isp1362_hcd->irq_stat[ISP1362_INT_ISTL1]++;
+ handled = 1;
+ svc_mask &= ~HCuPINT_ISTL1;
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL1_FULL);
+ DBG(1, "%s: ISTL1\n", __func__);
+ WARN_ON(!(int)isp1362_hcd->istl_flip);
+ WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & HCBUFSTAT_ISTL1_ACTIVE);
+ WARN_ON(!isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & HCBUFSTAT_ISTL1_DONE);
+ isp1362_hcd->irqenb &= ~HCuPINT_ISTL1;
+ }
+
+ if (irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) {
+ WARN_ON((irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) ==
+ (HCuPINT_ISTL0 | HCuPINT_ISTL1));
+ finish_iso_transfers(isp1362_hcd,
+ &isp1362_hcd->istl_queue[isp1362_hcd->istl_flip]);
+ start_iso_transfers(isp1362_hcd);
+ isp1362_hcd->istl_flip = 1 - isp1362_hcd->istl_flip;
+ }
+
+ if (irqstat & HCuPINT_INTL) {
+ u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE);
+ u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCINTLSKIP);
+ isp1362_hcd->irq_stat[ISP1362_INT_INTL]++;
+
+ DBG(2, "%s: INTL\n", __func__);
+
+ svc_mask &= ~HCuPINT_INTL;
+
+ isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, skip_map | done_map);
+ if (~(done_map | skip_map) == 0)
+ /* All PTDs are finished, disable INTL processing entirely */
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE);
+
+ handled = 1;
+ WARN_ON(!done_map);
+ if (done_map) {
+ DBG(3, "%s: INTL done_map %08x\n", __func__, done_map);
+ finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue);
+ start_intl_transfers(isp1362_hcd);
+ }
+ }
+
+ if (irqstat & HCuPINT_ATL) {
+ u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE);
+ u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCATLSKIP);
+ isp1362_hcd->irq_stat[ISP1362_INT_ATL]++;
+
+ DBG(2, "%s: ATL\n", __func__);
+
+ svc_mask &= ~HCuPINT_ATL;
+
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP, skip_map | done_map);
+ if (~(done_map | skip_map) == 0)
+ isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE);
+ if (done_map) {
+ DBG(3, "%s: ATL done_map %08x\n", __func__, done_map);
+ finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue);
+ start_atl_transfers(isp1362_hcd);
+ }
+ handled = 1;
+ }
+
+ if (irqstat & HCuPINT_OPR) {
+ u32 intstat = isp1362_read_reg32(isp1362_hcd, HCINTSTAT);
+ isp1362_hcd->irq_stat[ISP1362_INT_OPR]++;
+
+ svc_mask &= ~HCuPINT_OPR;
+ DBG(2, "%s: OPR %08x:%08x\n", __func__, intstat, isp1362_hcd->intenb);
+ intstat &= isp1362_hcd->intenb;
+ if (intstat & OHCI_INTR_UE) {
+ pr_err("Unrecoverable error\n");
+ /* FIXME: do here reset or cleanup or whatever */
+ }
+ if (intstat & OHCI_INTR_RHSC) {
+ isp1362_hcd->rhstatus = isp1362_read_reg32(isp1362_hcd, HCRHSTATUS);
+ isp1362_hcd->rhport[0] = isp1362_read_reg32(isp1362_hcd, HCRHPORT1);
+ isp1362_hcd->rhport[1] = isp1362_read_reg32(isp1362_hcd, HCRHPORT2);
+ }
+ if (intstat & OHCI_INTR_RD) {
+ pr_info("%s: RESUME DETECTED\n", __func__);
+ isp1362_show_reg(isp1362_hcd, HCCONTROL);
+ usb_hcd_resume_root_hub(hcd);
+ }
+ isp1362_write_reg32(isp1362_hcd, HCINTSTAT, intstat);
+ irqstat &= ~HCuPINT_OPR;
+ handled = 1;
+ }
+
+ if (irqstat & HCuPINT_SUSP) {
+ isp1362_hcd->irq_stat[ISP1362_INT_SUSP]++;
+ handled = 1;
+ svc_mask &= ~HCuPINT_SUSP;
+
+ pr_info("%s: SUSPEND IRQ\n", __func__);
+ }
+
+ if (irqstat & HCuPINT_CLKRDY) {
+ isp1362_hcd->irq_stat[ISP1362_INT_CLKRDY]++;
+ handled = 1;
+ isp1362_hcd->irqenb &= ~HCuPINT_CLKRDY;
+ svc_mask &= ~HCuPINT_CLKRDY;
+ pr_info("%s: CLKRDY IRQ\n", __func__);
+ }
+
+ if (svc_mask)
+ pr_err("%s: Unserviced interrupt(s) %04x\n", __func__, svc_mask);
+
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
+ isp1362_hcd->irq_active--;
+ spin_unlock(&isp1362_hcd->lock);
+
+ return IRQ_RETVAL(handled);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#define MAX_PERIODIC_LOAD 900 /* out of 1000 usec */
+static int balance(struct isp1362_hcd *isp1362_hcd, u16 interval, u16 load)
+{
+ int i, branch = -ENOSPC;
+
+ /* search for the least loaded schedule branch of that interval
+ * which has enough bandwidth left unreserved.
+ */
+ for (i = 0; i < interval; i++) {
+ if (branch < 0 || isp1362_hcd->load[branch] > isp1362_hcd->load[i]) {
+ int j;
+
+ for (j = i; j < PERIODIC_SIZE; j += interval) {
+ if ((isp1362_hcd->load[j] + load) > MAX_PERIODIC_LOAD) {
+ pr_err("%s: new load %d load[%02x] %d max %d\n", __func__,
+ load, j, isp1362_hcd->load[j], MAX_PERIODIC_LOAD);
+ break;
+ }
+ }
+ if (j < PERIODIC_SIZE)
+ continue;
+ branch = i;
+ }
+ }
+ return branch;
+}
+
+/* NB! ALL the code above this point runs with isp1362_hcd->lock
+ held, irqs off
+*/
+
+/*-------------------------------------------------------------------------*/
+
+static int isp1362_urb_enqueue(struct usb_hcd *hcd,
+ struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ struct usb_device *udev = urb->dev;
+ unsigned int pipe = urb->pipe;
+ int is_out = !usb_pipein(pipe);
+ int type = usb_pipetype(pipe);
+ int epnum = usb_pipeendpoint(pipe);
+ struct usb_host_endpoint *hep = urb->ep;
+ struct isp1362_ep *ep = NULL;
+ unsigned long flags;
+ int retval = 0;
+
+ DBG(3, "%s: urb %p\n", __func__, urb);
+
+ if (type == PIPE_ISOCHRONOUS) {
+ pr_err("Isochronous transfers not supported\n");
+ return -ENOSPC;
+ }
+
+ URB_DBG("%s: FA %d ep%d%s %s: len %d %s%s\n", __func__,
+ usb_pipedevice(pipe), epnum,
+ is_out ? "out" : "in",
+ usb_pipecontrol(pipe) ? "ctrl" :
+ usb_pipeint(pipe) ? "int" :
+ usb_pipebulk(pipe) ? "bulk" :
+ "iso",
+ urb->transfer_buffer_length,
+ (urb->transfer_flags & URB_ZERO_PACKET) ? "ZERO_PACKET " : "",
+ !(urb->transfer_flags & URB_SHORT_NOT_OK) ?
+ "short_ok" : "");
+
+ /* avoid all allocations within spinlocks: request or endpoint */
+ if (!hep->hcpriv) {
+ ep = kcalloc(1, sizeof *ep, mem_flags);
+ if (!ep)
+ return -ENOMEM;
+ }
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ /* don't submit to a dead or disabled port */
+ if (!((isp1362_hcd->rhport[0] | isp1362_hcd->rhport[1]) &
+ (1 << USB_PORT_FEAT_ENABLE)) ||
+ !HC_IS_RUNNING(hcd->state)) {
+ kfree(ep);
+ retval = -ENODEV;
+ goto fail_not_linked;
+ }
+
+ retval = usb_hcd_link_urb_to_ep(hcd, urb);
+ if (retval) {
+ kfree(ep);
+ goto fail_not_linked;
+ }
+
+ if (hep->hcpriv) {
+ ep = hep->hcpriv;
+ } else {
+ INIT_LIST_HEAD(&ep->schedule);
+ INIT_LIST_HEAD(&ep->active);
+ INIT_LIST_HEAD(&ep->remove_list);
+ ep->udev = usb_get_dev(udev);
+ ep->hep = hep;
+ ep->epnum = epnum;
+ ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+ ep->ptd_offset = -EINVAL;
+ ep->ptd_index = -EINVAL;
+ usb_settoggle(udev, epnum, is_out, 0);
+
+ if (type == PIPE_CONTROL)
+ ep->nextpid = USB_PID_SETUP;
+ else if (is_out)
+ ep->nextpid = USB_PID_OUT;
+ else
+ ep->nextpid = USB_PID_IN;
+
+ switch (type) {
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ if (urb->interval > PERIODIC_SIZE)
+ urb->interval = PERIODIC_SIZE;
+ ep->interval = urb->interval;
+ ep->branch = PERIODIC_SIZE;
+ ep->load = usb_calc_bus_time(udev->speed, !is_out,
+ (type == PIPE_ISOCHRONOUS),
+ usb_maxpacket(udev, pipe, is_out)) / 1000;
+ break;
+ }
+ hep->hcpriv = ep;
+ }
+ ep->num_req = isp1362_hcd->req_serial++;
+
+ /* maybe put endpoint into schedule */
+ switch (type) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ if (list_empty(&ep->schedule)) {
+ DBG(1, "%s: Adding ep %p req %d to async schedule\n",
+ __func__, ep, ep->num_req);
+ list_add_tail(&ep->schedule, &isp1362_hcd->async);
+ }
+ break;
+ case PIPE_ISOCHRONOUS:
+ case PIPE_INTERRUPT:
+ urb->interval = ep->interval;
+
+ /* urb submitted for already existing EP */
+ if (ep->branch < PERIODIC_SIZE)
+ break;
+
+ retval = balance(isp1362_hcd, ep->interval, ep->load);
+ if (retval < 0) {
+ pr_err("%s: balance returned %d\n", __func__, retval);
+ goto fail;
+ }
+ ep->branch = retval;
+ retval = 0;
+ isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
+ DBG(1, "%s: Current frame %04x branch %02x start_frame %04x(%04x)\n",
+ __func__, isp1362_hcd->fmindex, ep->branch,
+ ((isp1362_hcd->fmindex + PERIODIC_SIZE - 1) &
+ ~(PERIODIC_SIZE - 1)) + ep->branch,
+ (isp1362_hcd->fmindex & (PERIODIC_SIZE - 1)) + ep->branch);
+
+ if (list_empty(&ep->schedule)) {
+ if (type == PIPE_ISOCHRONOUS) {
+ u16 frame = isp1362_hcd->fmindex;
+
+ frame += max_t(u16, 8, ep->interval);
+ frame &= ~(ep->interval - 1);
+ frame |= ep->branch;
+ if (frame_before(frame, isp1362_hcd->fmindex))
+ frame += ep->interval;
+ urb->start_frame = frame;
+
+ DBG(1, "%s: Adding ep %p to isoc schedule\n", __func__, ep);
+ list_add_tail(&ep->schedule, &isp1362_hcd->isoc);
+ } else {
+ DBG(1, "%s: Adding ep %p to periodic schedule\n", __func__, ep);
+ list_add_tail(&ep->schedule, &isp1362_hcd->periodic);
+ }
+ } else
+ DBG(1, "%s: ep %p already scheduled\n", __func__, ep);
+
+ DBG(2, "%s: load %d bandwidth %d -> %d\n", __func__,
+ ep->load / ep->interval, isp1362_hcd->load[ep->branch],
+ isp1362_hcd->load[ep->branch] + ep->load);
+ isp1362_hcd->load[ep->branch] += ep->load;
+ }
+
+ urb->hcpriv = hep;
+ ALIGNSTAT(isp1362_hcd, urb->transfer_buffer);
+
+ switch (type) {
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ start_atl_transfers(isp1362_hcd);
+ break;
+ case PIPE_INTERRUPT:
+ start_intl_transfers(isp1362_hcd);
+ break;
+ case PIPE_ISOCHRONOUS:
+ start_iso_transfers(isp1362_hcd);
+ break;
+ default:
+ BUG();
+ }
+ fail:
+ if (retval)
+ usb_hcd_unlink_urb_from_ep(hcd, urb);
+
+
+ fail_not_linked:
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (retval)
+ DBG(0, "%s: urb %p failed with %d\n", __func__, urb, retval);
+ return retval;
+}
+
+static int isp1362_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ struct usb_host_endpoint *hep;
+ unsigned long flags;
+ struct isp1362_ep *ep;
+ int retval = 0;
+
+ DBG(3, "%s: urb %p\n", __func__, urb);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ retval = usb_hcd_check_unlink_urb(hcd, urb, status);
+ if (retval)
+ goto done;
+
+ hep = urb->hcpriv;
+
+ if (!hep) {
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ return -EIDRM;
+ }
+
+ ep = hep->hcpriv;
+ if (ep) {
+ /* In front of queue? */
+ if (ep->hep->urb_list.next == &urb->urb_list) {
+ if (!list_empty(&ep->active)) {
+ DBG(1, "%s: urb %p ep %p req %d active PTD[%d] $%04x\n", __func__,
+ urb, ep, ep->num_req, ep->ptd_index, ep->ptd_offset);
+ /* disable processing and queue PTD for removal */
+ remove_ptd(isp1362_hcd, ep);
+ urb = NULL;
+ }
+ }
+ if (urb) {
+ DBG(1, "%s: Finishing ep %p req %d\n", __func__, ep,
+ ep->num_req);
+ finish_request(isp1362_hcd, ep, urb, status);
+ } else
+ DBG(1, "%s: urb %p active; wait4irq\n", __func__, urb);
+ } else {
+ pr_warning("%s: No EP in URB %p\n", __func__, urb);
+ retval = -EINVAL;
+ }
+done:
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ DBG(3, "%s: exit\n", __func__);
+
+ return retval;
+}
+
+static void isp1362_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
+{
+ struct isp1362_ep *ep = hep->hcpriv;
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+
+ DBG(1, "%s: ep %p\n", __func__, ep);
+ if (!ep)
+ return;
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ if (!list_empty(&hep->urb_list)) {
+ if (!list_empty(&ep->active) && list_empty(&ep->remove_list)) {
+ DBG(1, "%s: Removing ep %p req %d PTD[%d] $%04x\n", __func__,
+ ep, ep->num_req, ep->ptd_index, ep->ptd_offset);
+ remove_ptd(isp1362_hcd, ep);
+ pr_info("%s: Waiting for Interrupt to clean up\n", __func__);
+ }
+ }
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ /* Wait for interrupt to clear out active list */
+ while (!list_empty(&ep->active))
+ msleep(1);
+
+ DBG(1, "%s: Freeing EP %p\n", __func__, ep);
+
+ usb_put_dev(ep->udev);
+ kfree(ep);
+ hep->hcpriv = NULL;
+}
+
+static int isp1362_get_frame(struct usb_hcd *hcd)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ u32 fmnum;
+ unsigned long flags;
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ fmnum = isp1362_read_reg32(isp1362_hcd, HCFMNUM);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ return (int)fmnum;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Adapted from ohci-hub.c */
+static int isp1362_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ int ports, i, changed = 0;
+ unsigned long flags;
+
+ if (!HC_IS_RUNNING(hcd->state))
+ return -ESHUTDOWN;
+
+ /* Report no status change now, if we are scheduled to be
+ called later */
+ if (timer_pending(&hcd->rh_timer))
+ return 0;
+
+ ports = isp1362_hcd->rhdesca & RH_A_NDP;
+ BUG_ON(ports > 2);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ /* init status */
+ if (isp1362_hcd->rhstatus & (RH_HS_LPSC | RH_HS_OCIC))
+ buf[0] = changed = 1;
+ else
+ buf[0] = 0;
+
+ for (i = 0; i < ports; i++) {
+ u32 status = isp1362_hcd->rhport[i];
+
+ if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC |
+ RH_PS_OCIC | RH_PS_PRSC)) {
+ changed = 1;
+ buf[0] |= 1 << (i + 1);
+ continue;
+ }
+
+ if (!(status & RH_PS_CCS))
+ continue;
+ }
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ return changed;
+}
+
+static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd,
+ struct usb_hub_descriptor *desc)
+{
+ u32 reg = isp1362_hcd->rhdesca;
+
+ DBG(3, "%s: enter\n", __func__);
+
+ desc->bDescriptorType = 0x29;
+ desc->bDescLength = 9;
+ desc->bHubContrCurrent = 0;
+ desc->bNbrPorts = reg & 0x3;
+ /* Power switching, device type, overcurrent. */
+ desc->wHubCharacteristics = cpu_to_le16((reg >> 8) & 0x1f);
+ DBG(0, "%s: hubcharacteristics = %02x\n", __func__, cpu_to_le16((reg >> 8) & 0x1f));
+ desc->bPwrOn2PwrGood = (reg >> 24) & 0xff;
+ /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */
+ desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
+ desc->bitmap[1] = ~0;
+
+ DBG(3, "%s: exit\n", __func__);
+}
+
+/* Adapted from ohci-hub.c */
+static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ int retval = 0;
+ unsigned long flags;
+ unsigned long t1;
+ int ports = isp1362_hcd->rhdesca & RH_A_NDP;
+ u32 tmp = 0;
+
+ switch (typeReq) {
+ case ClearHubFeature:
+ DBG(0, "ClearHubFeature: ");
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ _DBG(0, "C_HUB_OVER_CURRENT\n");
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_OCIC);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ case C_HUB_LOCAL_POWER:
+ _DBG(0, "C_HUB_LOCAL_POWER\n");
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case SetHubFeature:
+ DBG(0, "SetHubFeature: ");
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ _DBG(0, "C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n");
+ break;
+ default:
+ goto error;
+ }
+ break;
+ case GetHubDescriptor:
+ DBG(0, "GetHubDescriptor\n");
+ isp1362_hub_descriptor(isp1362_hcd, (struct usb_hub_descriptor *)buf);
+ break;
+ case GetHubStatus:
+ DBG(0, "GetHubStatus\n");
+ put_unaligned(cpu_to_le32(0), (__le32 *) buf);
+ break;
+ case GetPortStatus:
+#ifndef VERBOSE
+ DBG(0, "GetPortStatus\n");
+#endif
+ if (!wIndex || wIndex > ports)
+ goto error;
+ tmp = isp1362_hcd->rhport[--wIndex];
+ put_unaligned(cpu_to_le32(tmp), (__le32 *) buf);
+ break;
+ case ClearPortFeature:
+ DBG(0, "ClearPortFeature: ");
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ _DBG(0, "USB_PORT_FEAT_ENABLE\n");
+ tmp = RH_PS_CCS;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ _DBG(0, "USB_PORT_FEAT_C_ENABLE\n");
+ tmp = RH_PS_PESC;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ _DBG(0, "USB_PORT_FEAT_SUSPEND\n");
+ tmp = RH_PS_POCI;
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ _DBG(0, "USB_PORT_FEAT_C_SUSPEND\n");
+ tmp = RH_PS_PSSC;
+ break;
+ case USB_PORT_FEAT_POWER:
+ _DBG(0, "USB_PORT_FEAT_POWER\n");
+ tmp = RH_PS_LSDA;
+
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ _DBG(0, "USB_PORT_FEAT_C_CONNECTION\n");
+ tmp = RH_PS_CSC;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ _DBG(0, "USB_PORT_FEAT_C_OVER_CURRENT\n");
+ tmp = RH_PS_OCIC;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ _DBG(0, "USB_PORT_FEAT_C_RESET\n");
+ tmp = RH_PS_PRSC;
+ break;
+ default:
+ goto error;
+ }
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, tmp);
+ isp1362_hcd->rhport[wIndex] =
+ isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ break;
+ case SetPortFeature:
+ DBG(0, "SetPortFeature: ");
+ if (!wIndex || wIndex > ports)
+ goto error;
+ wIndex--;
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ _DBG(0, "USB_PORT_FEAT_SUSPEND\n");
+#ifdef CONFIG_USB_OTG
+ if (ohci->hcd.self.otg_port == (wIndex + 1) &&
+ ohci->hcd.self.b_hnp_enable) {
+ start_hnp(ohci);
+ break;
+ }
+#endif
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PSS);
+ isp1362_hcd->rhport[wIndex] =
+ isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ break;
+ case USB_PORT_FEAT_POWER:
+ _DBG(0, "USB_PORT_FEAT_POWER\n");
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PPS);
+ isp1362_hcd->rhport[wIndex] =
+ isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ break;
+ case USB_PORT_FEAT_RESET:
+ _DBG(0, "USB_PORT_FEAT_RESET\n");
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ t1 = jiffies + msecs_to_jiffies(USB_RESET_WIDTH);
+ while (time_before(jiffies, t1)) {
+ /* spin until any current reset finishes */
+ for (;;) {
+ tmp = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex);
+ if (!(tmp & RH_PS_PRS))
+ break;
+ udelay(500);
+ }
+ if (!(tmp & RH_PS_CCS))
+ break;
+ /* Reset lasts 10ms (claims datasheet) */
+ isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, (RH_PS_PRS));
+
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ msleep(10);
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ }
+
+ isp1362_hcd->rhport[wIndex] = isp1362_read_reg32(isp1362_hcd,
+ HCRHPORT1 + wIndex);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ break;
+ default:
+ goto error;
+ }
+ break;
+
+ default:
+ error:
+ /* "protocol stall" on error */
+ _DBG(0, "PROTOCOL STALL\n");
+ retval = -EPIPE;
+ }
+
+ return retval;
+}
+
+#ifdef CONFIG_PM
+static int isp1362_bus_suspend(struct usb_hcd *hcd)
+{
+ int status = 0;
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+
+ if (time_before(jiffies, isp1362_hcd->next_statechange))
+ msleep(5);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
+ switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_RESUME:
+ DBG(0, "%s: resume/suspend?\n", __func__);
+ isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS;
+ isp1362_hcd->hc_control |= OHCI_USB_RESET;
+ isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
+ /* FALL THROUGH */
+ case OHCI_USB_RESET:
+ status = -EBUSY;
+ pr_warning("%s: needs reinit!\n", __func__);
+ goto done;
+ case OHCI_USB_SUSPEND:
+ pr_warning("%s: already suspended?\n", __func__);
+ goto done;
+ }
+ DBG(0, "%s: suspend root hub\n", __func__);
+
+ /* First stop any processing */
+ hcd->state = HC_STATE_QUIESCING;
+ if (!list_empty(&isp1362_hcd->atl_queue.active) ||
+ !list_empty(&isp1362_hcd->intl_queue.active) ||
+ !list_empty(&isp1362_hcd->istl_queue[0] .active) ||
+ !list_empty(&isp1362_hcd->istl_queue[1] .active)) {
+ int limit;
+
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0);
+ isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0);
+ isp1362_write_reg16(isp1362_hcd, HCBUFSTAT, 0);
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
+ isp1362_write_reg32(isp1362_hcd, HCINTSTAT, OHCI_INTR_SF);
+
+ DBG(0, "%s: stopping schedules ...\n", __func__);
+ limit = 2000;
+ while (limit > 0) {
+ udelay(250);
+ limit -= 250;
+ if (isp1362_read_reg32(isp1362_hcd, HCINTSTAT) & OHCI_INTR_SF)
+ break;
+ }
+ mdelay(7);
+ if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ATL) {
+ u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE);
+ finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue);
+ }
+ if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_INTL) {
+ u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE);
+ finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue);
+ }
+ if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL0)
+ finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[0]);
+ if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL1)
+ finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[1]);
+ }
+ DBG(0, "%s: HCINTSTAT: %08x\n", __func__,
+ isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
+ isp1362_write_reg32(isp1362_hcd, HCINTSTAT,
+ isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
+
+ /* Suspend hub */
+ isp1362_hcd->hc_control = OHCI_USB_SUSPEND;
+ isp1362_show_reg(isp1362_hcd, HCCONTROL);
+ isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
+ isp1362_show_reg(isp1362_hcd, HCCONTROL);
+
+#if 1
+ isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
+ if ((isp1362_hcd->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_SUSPEND) {
+ pr_err("%s: controller won't suspend %08x\n", __func__,
+ isp1362_hcd->hc_control);
+ status = -EBUSY;
+ } else
+#endif
+ {
+ /* no resumes until devices finish suspending */
+ isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(5);
+ }
+done:
+ if (status == 0) {
+ hcd->state = HC_STATE_SUSPENDED;
+ DBG(0, "%s: HCD suspended: %08x\n", __func__,
+ isp1362_read_reg32(isp1362_hcd, HCCONTROL));
+ }
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ return status;
+}
+
+static int isp1362_bus_resume(struct usb_hcd *hcd)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ u32 port;
+ unsigned long flags;
+ int status = -EINPROGRESS;
+
+ if (time_before(jiffies, isp1362_hcd->next_statechange))
+ msleep(5);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL);
+ pr_info("%s: HCCONTROL: %08x\n", __func__, isp1362_hcd->hc_control);
+ if (hcd->state == HC_STATE_RESUMING) {
+ pr_warning("%s: duplicate resume\n", __func__);
+ status = 0;
+ } else
+ switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_SUSPEND:
+ DBG(0, "%s: resume root hub\n", __func__);
+ isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS;
+ isp1362_hcd->hc_control |= OHCI_USB_RESUME;
+ isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
+ break;
+ case OHCI_USB_RESUME:
+ /* HCFS changes sometime after INTR_RD */
+ DBG(0, "%s: remote wakeup\n", __func__);
+ break;
+ case OHCI_USB_OPER:
+ DBG(0, "%s: odd resume\n", __func__);
+ status = 0;
+ hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+ break;
+ default: /* RESET, we lost power */
+ DBG(0, "%s: root hub hardware reset\n", __func__);
+ status = -EBUSY;
+ }
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (status == -EBUSY) {
+ DBG(0, "%s: Restarting HC\n", __func__);
+ isp1362_hc_stop(hcd);
+ return isp1362_hc_start(hcd);
+ }
+ if (status != -EINPROGRESS)
+ return status;
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ port = isp1362_read_reg32(isp1362_hcd, HCRHDESCA) & RH_A_NDP;
+ while (port--) {
+ u32 stat = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + port);
+
+ /* force global, not selective, resume */
+ if (!(stat & RH_PS_PSS)) {
+ DBG(0, "%s: Not Resuming RH port %d\n", __func__, port);
+ continue;
+ }
+ DBG(0, "%s: Resuming RH port %d\n", __func__, port);
+ isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + port, RH_PS_POCI);
+ }
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ /* Some controllers (lucent) need extra-long delays */
+ hcd->state = HC_STATE_RESUMING;
+ mdelay(20 /* usb 11.5.1.10 */ + 15);
+
+ isp1362_hcd->hc_control = OHCI_USB_OPER;
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_show_reg(isp1362_hcd, HCCONTROL);
+ isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ /* TRSMRCY */
+ msleep(10);
+
+ /* keep it alive for ~5x suspend + resume costs */
+ isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(250);
+
+ hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+ hcd->state = HC_STATE_RUNNING;
+ return 0;
+}
+#else
+#define isp1362_bus_suspend NULL
+#define isp1362_bus_resume NULL
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef STUB_DEBUG_FILE
+
+static inline void create_debug_file(struct isp1362_hcd *isp1362_hcd)
+{
+}
+static inline void remove_debug_file(struct isp1362_hcd *isp1362_hcd)
+{
+}
+
+#else
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+static void dump_irq(struct seq_file *s, char *label, u16 mask)
+{
+ seq_printf(s, "%-15s %04x%s%s%s%s%s%s\n", label, mask,
+ mask & HCuPINT_CLKRDY ? " clkrdy" : "",
+ mask & HCuPINT_SUSP ? " susp" : "",
+ mask & HCuPINT_OPR ? " opr" : "",
+ mask & HCuPINT_EOT ? " eot" : "",
+ mask & HCuPINT_ATL ? " atl" : "",
+ mask & HCuPINT_SOF ? " sof" : "");
+}
+
+static void dump_int(struct seq_file *s, char *label, u32 mask)
+{
+ seq_printf(s, "%-15s %08x%s%s%s%s%s%s%s\n", label, mask,
+ mask & OHCI_INTR_MIE ? " MIE" : "",
+ mask & OHCI_INTR_RHSC ? " rhsc" : "",
+ mask & OHCI_INTR_FNO ? " fno" : "",
+ mask & OHCI_INTR_UE ? " ue" : "",
+ mask & OHCI_INTR_RD ? " rd" : "",
+ mask & OHCI_INTR_SF ? " sof" : "",
+ mask & OHCI_INTR_SO ? " so" : "");
+}
+
+static void dump_ctrl(struct seq_file *s, char *label, u32 mask)
+{
+ seq_printf(s, "%-15s %08x%s%s%s\n", label, mask,
+ mask & OHCI_CTRL_RWC ? " rwc" : "",
+ mask & OHCI_CTRL_RWE ? " rwe" : "",
+ ({
+ char *hcfs;
+ switch (mask & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ hcfs = " oper";
+ break;
+ case OHCI_USB_RESET:
+ hcfs = " reset";
+ break;
+ case OHCI_USB_RESUME:
+ hcfs = " resume";
+ break;
+ case OHCI_USB_SUSPEND:
+ hcfs = " suspend";
+ break;
+ default:
+ hcfs = " ?";
+ }
+ hcfs;
+ }));
+}
+
+static void dump_regs(struct seq_file *s, struct isp1362_hcd *isp1362_hcd)
+{
+ seq_printf(s, "HCREVISION [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCREVISION),
+ isp1362_read_reg32(isp1362_hcd, HCREVISION));
+ seq_printf(s, "HCCONTROL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCONTROL),
+ isp1362_read_reg32(isp1362_hcd, HCCONTROL));
+ seq_printf(s, "HCCMDSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCMDSTAT),
+ isp1362_read_reg32(isp1362_hcd, HCCMDSTAT));
+ seq_printf(s, "HCINTSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTSTAT),
+ isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
+ seq_printf(s, "HCINTENB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTENB),
+ isp1362_read_reg32(isp1362_hcd, HCINTENB));
+ seq_printf(s, "HCFMINTVL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMINTVL),
+ isp1362_read_reg32(isp1362_hcd, HCFMINTVL));
+ seq_printf(s, "HCFMREM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMREM),
+ isp1362_read_reg32(isp1362_hcd, HCFMREM));
+ seq_printf(s, "HCFMNUM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMNUM),
+ isp1362_read_reg32(isp1362_hcd, HCFMNUM));
+ seq_printf(s, "HCLSTHRESH [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCLSTHRESH),
+ isp1362_read_reg32(isp1362_hcd, HCLSTHRESH));
+ seq_printf(s, "HCRHDESCA [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCA),
+ isp1362_read_reg32(isp1362_hcd, HCRHDESCA));
+ seq_printf(s, "HCRHDESCB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCB),
+ isp1362_read_reg32(isp1362_hcd, HCRHDESCB));
+ seq_printf(s, "HCRHSTATUS [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHSTATUS),
+ isp1362_read_reg32(isp1362_hcd, HCRHSTATUS));
+ seq_printf(s, "HCRHPORT1 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT1),
+ isp1362_read_reg32(isp1362_hcd, HCRHPORT1));
+ seq_printf(s, "HCRHPORT2 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT2),
+ isp1362_read_reg32(isp1362_hcd, HCRHPORT2));
+ seq_printf(s, "\n");
+ seq_printf(s, "HCHWCFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCHWCFG),
+ isp1362_read_reg16(isp1362_hcd, HCHWCFG));
+ seq_printf(s, "HCDMACFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCDMACFG),
+ isp1362_read_reg16(isp1362_hcd, HCDMACFG));
+ seq_printf(s, "HCXFERCTR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCXFERCTR),
+ isp1362_read_reg16(isp1362_hcd, HCXFERCTR));
+ seq_printf(s, "HCuPINT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINT),
+ isp1362_read_reg16(isp1362_hcd, HCuPINT));
+ seq_printf(s, "HCuPINTENB [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINTENB),
+ isp1362_read_reg16(isp1362_hcd, HCuPINTENB));
+ seq_printf(s, "HCCHIPID [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCCHIPID),
+ isp1362_read_reg16(isp1362_hcd, HCCHIPID));
+ seq_printf(s, "HCSCRATCH [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCSCRATCH),
+ isp1362_read_reg16(isp1362_hcd, HCSCRATCH));
+ seq_printf(s, "HCBUFSTAT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCBUFSTAT),
+ isp1362_read_reg16(isp1362_hcd, HCBUFSTAT));
+ seq_printf(s, "HCDIRADDR [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCDIRADDR),
+ isp1362_read_reg32(isp1362_hcd, HCDIRADDR));
+#if 0
+ seq_printf(s, "HCDIRDATA [%02x] %04x\n", ISP1362_REG_NO(HCDIRDATA),
+ isp1362_read_reg16(isp1362_hcd, HCDIRDATA));
+#endif
+ seq_printf(s, "HCISTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLBUFSZ),
+ isp1362_read_reg16(isp1362_hcd, HCISTLBUFSZ));
+ seq_printf(s, "HCISTLRATE [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLRATE),
+ isp1362_read_reg16(isp1362_hcd, HCISTLRATE));
+ seq_printf(s, "\n");
+ seq_printf(s, "HCINTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBUFSZ),
+ isp1362_read_reg16(isp1362_hcd, HCINTLBUFSZ));
+ seq_printf(s, "HCINTLBLKSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBLKSZ),
+ isp1362_read_reg16(isp1362_hcd, HCINTLBLKSZ));
+ seq_printf(s, "HCINTLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLDONE),
+ isp1362_read_reg32(isp1362_hcd, HCINTLDONE));
+ seq_printf(s, "HCINTLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLSKIP),
+ isp1362_read_reg32(isp1362_hcd, HCINTLSKIP));
+ seq_printf(s, "HCINTLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLLAST),
+ isp1362_read_reg32(isp1362_hcd, HCINTLLAST));
+ seq_printf(s, "HCINTLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLCURR),
+ isp1362_read_reg16(isp1362_hcd, HCINTLCURR));
+ seq_printf(s, "\n");
+ seq_printf(s, "HCATLBUFSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBUFSZ),
+ isp1362_read_reg16(isp1362_hcd, HCATLBUFSZ));
+ seq_printf(s, "HCATLBLKSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBLKSZ),
+ isp1362_read_reg16(isp1362_hcd, HCATLBLKSZ));
+#if 0
+ seq_printf(s, "HCATLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDONE),
+ isp1362_read_reg32(isp1362_hcd, HCATLDONE));
+#endif
+ seq_printf(s, "HCATLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLSKIP),
+ isp1362_read_reg32(isp1362_hcd, HCATLSKIP));
+ seq_printf(s, "HCATLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLLAST),
+ isp1362_read_reg32(isp1362_hcd, HCATLLAST));
+ seq_printf(s, "HCATLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLCURR),
+ isp1362_read_reg16(isp1362_hcd, HCATLCURR));
+ seq_printf(s, "\n");
+ seq_printf(s, "HCATLDTC [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTC),
+ isp1362_read_reg16(isp1362_hcd, HCATLDTC));
+ seq_printf(s, "HCATLDTCTO [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTCTO),
+ isp1362_read_reg16(isp1362_hcd, HCATLDTCTO));
+}
+
+static int proc_isp1362_show(struct seq_file *s, void *unused)
+{
+ struct isp1362_hcd *isp1362_hcd = s->private;
+ struct isp1362_ep *ep;
+ int i;
+
+ seq_printf(s, "%s\n%s version %s\n",
+ isp1362_hcd_to_hcd(isp1362_hcd)->product_desc, hcd_name, DRIVER_VERSION);
+
+ /* collect statistics to help estimate potential win for
+ * DMA engines that care about alignment (PXA)
+ */
+ seq_printf(s, "alignment: 16b/%ld 8b/%ld 4b/%ld 2b/%ld 1b/%ld\n",
+ isp1362_hcd->stat16, isp1362_hcd->stat8, isp1362_hcd->stat4,
+ isp1362_hcd->stat2, isp1362_hcd->stat1);
+ seq_printf(s, "max # ptds in ATL fifo: %d\n", isp1362_hcd->atl_queue.stat_maxptds);
+ seq_printf(s, "max # ptds in INTL fifo: %d\n", isp1362_hcd->intl_queue.stat_maxptds);
+ seq_printf(s, "max # ptds in ISTL fifo: %d\n",
+ max(isp1362_hcd->istl_queue[0] .stat_maxptds,
+ isp1362_hcd->istl_queue[1] .stat_maxptds));
+
+ /* FIXME: don't show the following in suspended state */
+ spin_lock_irq(&isp1362_hcd->lock);
+
+ dump_irq(s, "hc_irq_enable", isp1362_read_reg16(isp1362_hcd, HCuPINTENB));
+ dump_irq(s, "hc_irq_status", isp1362_read_reg16(isp1362_hcd, HCuPINT));
+ dump_int(s, "ohci_int_enable", isp1362_read_reg32(isp1362_hcd, HCINTENB));
+ dump_int(s, "ohci_int_status", isp1362_read_reg32(isp1362_hcd, HCINTSTAT));
+ dump_ctrl(s, "ohci_control", isp1362_read_reg32(isp1362_hcd, HCCONTROL));
+
+ for (i = 0; i < NUM_ISP1362_IRQS; i++)
+ if (isp1362_hcd->irq_stat[i])
+ seq_printf(s, "%-15s: %d\n",
+ ISP1362_INT_NAME(i), isp1362_hcd->irq_stat[i]);
+
+ dump_regs(s, isp1362_hcd);
+ list_for_each_entry(ep, &isp1362_hcd->async, schedule) {
+ struct urb *urb;
+
+ seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep, ep->epnum,
+ ({
+ char *s;
+ switch (ep->nextpid) {
+ case USB_PID_IN:
+ s = "in";
+ break;
+ case USB_PID_OUT:
+ s = "out";
+ break;
+ case USB_PID_SETUP:
+ s = "setup";
+ break;
+ case USB_PID_ACK:
+ s = "status";
+ break;
+ default:
+ s = "?";
+ break;
+ };
+ s;}), ep->maxpacket) ;
+ list_for_each_entry(urb, &ep->hep->urb_list, urb_list) {
+ seq_printf(s, " urb%p, %d/%d\n", urb,
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ }
+ }
+ if (!list_empty(&isp1362_hcd->async))
+ seq_printf(s, "\n");
+ dump_ptd_queue(&isp1362_hcd->atl_queue);
+
+ seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
+
+ list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) {
+ seq_printf(s, "branch:%2d load:%3d PTD[%d] $%04x:\n", ep->branch,
+ isp1362_hcd->load[ep->branch], ep->ptd_index, ep->ptd_offset);
+
+ seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n",
+ ep->interval, ep,
+ (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ",
+ ep->udev->devnum, ep->epnum,
+ (ep->epnum == 0) ? "" :
+ ((ep->nextpid == USB_PID_IN) ?
+ "in" : "out"), ep->maxpacket);
+ }
+ dump_ptd_queue(&isp1362_hcd->intl_queue);
+
+ seq_printf(s, "ISO:\n");
+
+ list_for_each_entry(ep, &isp1362_hcd->isoc, schedule) {
+ seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n",
+ ep->interval, ep,
+ (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ",
+ ep->udev->devnum, ep->epnum,
+ (ep->epnum == 0) ? "" :
+ ((ep->nextpid == USB_PID_IN) ?
+ "in" : "out"), ep->maxpacket);
+ }
+
+ spin_unlock_irq(&isp1362_hcd->lock);
+ seq_printf(s, "\n");
+
+ return 0;
+}
+
+static int proc_isp1362_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, proc_isp1362_show, PDE(inode)->data);
+}
+
+static const struct file_operations proc_ops = {
+ .open = proc_isp1362_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* expect just one isp1362_hcd per system */
+static const char proc_filename[] = "driver/isp1362";
+
+static void create_debug_file(struct isp1362_hcd *isp1362_hcd)
+{
+ struct proc_dir_entry *pde;
+
+ pde = create_proc_entry(proc_filename, 0, NULL);
+ if (pde == NULL) {
+ pr_warning("%s: Failed to create debug file '%s'\n", __func__, proc_filename);
+ return;
+ }
+
+ pde->proc_fops = &proc_ops;
+ pde->data = isp1362_hcd;
+ isp1362_hcd->pde = pde;
+}
+
+static void remove_debug_file(struct isp1362_hcd *isp1362_hcd)
+{
+ if (isp1362_hcd->pde)
+ remove_proc_entry(proc_filename, 0);
+}
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static void isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd)
+{
+ int tmp = 20;
+ unsigned long flags;
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ isp1362_write_reg16(isp1362_hcd, HCSWRES, HCSWRES_MAGIC);
+ isp1362_write_reg32(isp1362_hcd, HCCMDSTAT, OHCI_HCR);
+ while (--tmp) {
+ mdelay(1);
+ if (!(isp1362_read_reg32(isp1362_hcd, HCCMDSTAT) & OHCI_HCR))
+ break;
+ }
+ if (!tmp)
+ pr_err("Software reset timeout\n");
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+}
+
+static int isp1362_mem_config(struct usb_hcd *hcd)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+ u32 total;
+ u16 istl_size = ISP1362_ISTL_BUFSIZE;
+ u16 intl_blksize = ISP1362_INTL_BLKSIZE + PTD_HEADER_SIZE;
+ u16 intl_size = ISP1362_INTL_BUFFERS * intl_blksize;
+ u16 atl_blksize = ISP1362_ATL_BLKSIZE + PTD_HEADER_SIZE;
+ u16 atl_buffers = (ISP1362_BUF_SIZE - (istl_size + intl_size)) / atl_blksize;
+ u16 atl_size;
+ int i;
+
+ WARN_ON(istl_size & 3);
+ WARN_ON(atl_blksize & 3);
+ WARN_ON(intl_blksize & 3);
+ WARN_ON(atl_blksize < PTD_HEADER_SIZE);
+ WARN_ON(intl_blksize < PTD_HEADER_SIZE);
+
+ BUG_ON((unsigned)ISP1362_INTL_BUFFERS > 32);
+ if (atl_buffers > 32)
+ atl_buffers = 32;
+ atl_size = atl_buffers * atl_blksize;
+ total = atl_size + intl_size + istl_size;
+ dev_info(hcd->self.controller, "ISP1362 Memory usage:\n");
+ dev_info(hcd->self.controller, " ISTL: 2 * %4d: %4d @ $%04x:$%04x\n",
+ istl_size / 2, istl_size, 0, istl_size / 2);
+ dev_info(hcd->self.controller, " INTL: %4d * (%3u+8): %4d @ $%04x\n",
+ ISP1362_INTL_BUFFERS, intl_blksize - PTD_HEADER_SIZE,
+ intl_size, istl_size);
+ dev_info(hcd->self.controller, " ATL : %4d * (%3u+8): %4d @ $%04x\n",
+ atl_buffers, atl_blksize - PTD_HEADER_SIZE,
+ atl_size, istl_size + intl_size);
+ dev_info(hcd->self.controller, " USED/FREE: %4d %4d\n", total,
+ ISP1362_BUF_SIZE - total);
+
+ if (total > ISP1362_BUF_SIZE) {
+ dev_err(hcd->self.controller, "%s: Memory requested: %d, available %d\n",
+ __func__, total, ISP1362_BUF_SIZE);
+ return -ENOMEM;
+ }
+
+ total = istl_size + intl_size + atl_size;
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ for (i = 0; i < 2; i++) {
+ isp1362_hcd->istl_queue[i].buf_start = i * istl_size / 2,
+ isp1362_hcd->istl_queue[i].buf_size = istl_size / 2;
+ isp1362_hcd->istl_queue[i].blk_size = 4;
+ INIT_LIST_HEAD(&isp1362_hcd->istl_queue[i].active);
+ snprintf(isp1362_hcd->istl_queue[i].name,
+ sizeof(isp1362_hcd->istl_queue[i].name), "ISTL%d", i);
+ DBG(3, "%s: %5s buf $%04x %d\n", __func__,
+ isp1362_hcd->istl_queue[i].name,
+ isp1362_hcd->istl_queue[i].buf_start,
+ isp1362_hcd->istl_queue[i].buf_size);
+ }
+ isp1362_write_reg16(isp1362_hcd, HCISTLBUFSZ, istl_size / 2);
+
+ isp1362_hcd->intl_queue.buf_start = istl_size;
+ isp1362_hcd->intl_queue.buf_size = intl_size;
+ isp1362_hcd->intl_queue.buf_count = ISP1362_INTL_BUFFERS;
+ isp1362_hcd->intl_queue.blk_size = intl_blksize;
+ isp1362_hcd->intl_queue.buf_avail = isp1362_hcd->intl_queue.buf_count;
+ isp1362_hcd->intl_queue.skip_map = ~0;
+ INIT_LIST_HEAD(&isp1362_hcd->intl_queue.active);
+
+ isp1362_write_reg16(isp1362_hcd, HCINTLBUFSZ,
+ isp1362_hcd->intl_queue.buf_size);
+ isp1362_write_reg16(isp1362_hcd, HCINTLBLKSZ,
+ isp1362_hcd->intl_queue.blk_size - PTD_HEADER_SIZE);
+ isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0);
+ isp1362_write_reg32(isp1362_hcd, HCINTLLAST,
+ 1 << (ISP1362_INTL_BUFFERS - 1));
+
+ isp1362_hcd->atl_queue.buf_start = istl_size + intl_size;
+ isp1362_hcd->atl_queue.buf_size = atl_size;
+ isp1362_hcd->atl_queue.buf_count = atl_buffers;
+ isp1362_hcd->atl_queue.blk_size = atl_blksize;
+ isp1362_hcd->atl_queue.buf_avail = isp1362_hcd->atl_queue.buf_count;
+ isp1362_hcd->atl_queue.skip_map = ~0;
+ INIT_LIST_HEAD(&isp1362_hcd->atl_queue.active);
+
+ isp1362_write_reg16(isp1362_hcd, HCATLBUFSZ,
+ isp1362_hcd->atl_queue.buf_size);
+ isp1362_write_reg16(isp1362_hcd, HCATLBLKSZ,
+ isp1362_hcd->atl_queue.blk_size - PTD_HEADER_SIZE);
+ isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0);
+ isp1362_write_reg32(isp1362_hcd, HCATLLAST,
+ 1 << (atl_buffers - 1));
+
+ snprintf(isp1362_hcd->atl_queue.name,
+ sizeof(isp1362_hcd->atl_queue.name), "ATL");
+ snprintf(isp1362_hcd->intl_queue.name,
+ sizeof(isp1362_hcd->intl_queue.name), "INTL");
+ DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__,
+ isp1362_hcd->intl_queue.name,
+ isp1362_hcd->intl_queue.buf_start,
+ ISP1362_INTL_BUFFERS, isp1362_hcd->intl_queue.blk_size,
+ isp1362_hcd->intl_queue.buf_size);
+ DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__,
+ isp1362_hcd->atl_queue.name,
+ isp1362_hcd->atl_queue.buf_start,
+ atl_buffers, isp1362_hcd->atl_queue.blk_size,
+ isp1362_hcd->atl_queue.buf_size);
+
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ return 0;
+}
+
+static int isp1362_hc_reset(struct usb_hcd *hcd)
+{
+ int ret = 0;
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long t;
+ unsigned long timeout = 100;
+ unsigned long flags;
+ int clkrdy = 0;
+
+ pr_info("%s:\n", __func__);
+
+ if (isp1362_hcd->board && isp1362_hcd->board->reset) {
+ isp1362_hcd->board->reset(hcd->self.controller, 1);
+ msleep(20);
+ if (isp1362_hcd->board->clock)
+ isp1362_hcd->board->clock(hcd->self.controller, 1);
+ isp1362_hcd->board->reset(hcd->self.controller, 0);
+ } else
+ isp1362_sw_reset(isp1362_hcd);
+
+ /* chip has been reset. First we need to see a clock */
+ t = jiffies + msecs_to_jiffies(timeout);
+ while (!clkrdy && time_before_eq(jiffies, t)) {
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ clkrdy = isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_CLKRDY;
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (!clkrdy)
+ msleep(4);
+ }
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_CLKRDY);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (!clkrdy) {
+ pr_err("Clock not ready after %lums\n", timeout);
+ ret = -ENODEV;
+ }
+ return ret;
+}
+
+static void isp1362_hc_stop(struct usb_hcd *hcd)
+{
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+ u32 tmp;
+
+ pr_info("%s:\n", __func__);
+
+ del_timer_sync(&hcd->rh_timer);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
+
+ /* Switch off power for all ports */
+ tmp = isp1362_read_reg32(isp1362_hcd, HCRHDESCA);
+ tmp &= ~(RH_A_NPS | RH_A_PSM);
+ isp1362_write_reg32(isp1362_hcd, HCRHDESCA, tmp);
+ isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS);
+
+ /* Reset the chip */
+ if (isp1362_hcd->board && isp1362_hcd->board->reset)
+ isp1362_hcd->board->reset(hcd->self.controller, 1);
+ else
+ isp1362_sw_reset(isp1362_hcd);
+
+ if (isp1362_hcd->board && isp1362_hcd->board->clock)
+ isp1362_hcd->board->clock(hcd->self.controller, 0);
+
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+}
+
+#ifdef CHIP_BUFFER_TEST
+static int isp1362_chip_test(struct isp1362_hcd *isp1362_hcd)
+{
+ int ret = 0;
+ u16 *ref;
+ unsigned long flags;
+
+ ref = kmalloc(2 * ISP1362_BUF_SIZE, GFP_KERNEL);
+ if (ref) {
+ int offset;
+ u16 *tst = &ref[ISP1362_BUF_SIZE / 2];
+
+ for (offset = 0; offset < ISP1362_BUF_SIZE / 2; offset++) {
+ ref[offset] = ~offset;
+ tst[offset] = offset;
+ }
+
+ for (offset = 0; offset < 4; offset++) {
+ int j;
+
+ for (j = 0; j < 8; j++) {
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_buffer(isp1362_hcd, (u8 *)ref + offset, 0, j);
+ isp1362_read_buffer(isp1362_hcd, (u8 *)tst + offset, 0, j);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ if (memcmp(ref, tst, j)) {
+ ret = -ENODEV;
+ pr_err("%s: memory check with %d byte offset %d failed\n",
+ __func__, j, offset);
+ dump_data((u8 *)ref + offset, j);
+ dump_data((u8 *)tst + offset, j);
+ }
+ }
+ }
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_buffer(isp1362_hcd, ref, 0, ISP1362_BUF_SIZE);
+ isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ if (memcmp(ref, tst, ISP1362_BUF_SIZE)) {
+ ret = -ENODEV;
+ pr_err("%s: memory check failed\n", __func__);
+ dump_data((u8 *)tst, ISP1362_BUF_SIZE / 2);
+ }
+
+ for (offset = 0; offset < 256; offset++) {
+ int test_size = 0;
+
+ yield();
+
+ memset(tst, 0, ISP1362_BUF_SIZE);
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
+ isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (memcmp(tst, tst + (ISP1362_BUF_SIZE / (2 * sizeof(*tst))),
+ ISP1362_BUF_SIZE / 2)) {
+ pr_err("%s: Failed to clear buffer\n", __func__);
+ dump_data((u8 *)tst, ISP1362_BUF_SIZE);
+ break;
+ }
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_buffer(isp1362_hcd, ref, offset * 2, PTD_HEADER_SIZE);
+ isp1362_write_buffer(isp1362_hcd, ref + PTD_HEADER_SIZE / sizeof(*ref),
+ offset * 2 + PTD_HEADER_SIZE, test_size);
+ isp1362_read_buffer(isp1362_hcd, tst, offset * 2,
+ PTD_HEADER_SIZE + test_size);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) {
+ dump_data(((u8 *)ref) + offset, PTD_HEADER_SIZE + test_size);
+ dump_data((u8 *)tst, PTD_HEADER_SIZE + test_size);
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_read_buffer(isp1362_hcd, tst, offset * 2,
+ PTD_HEADER_SIZE + test_size);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) {
+ ret = -ENODEV;
+ pr_err("%s: memory check with offset %02x failed\n",
+ __func__, offset);
+ break;
+ }
+ pr_warning("%s: memory check with offset %02x ok after second read\n",
+ __func__, offset);
+ }
+ }
+ kfree(ref);
+ }
+ return ret;
+}
+#endif
+
+static int isp1362_hc_start(struct usb_hcd *hcd)
+{
+ int ret;
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ struct isp1362_platform_data *board = isp1362_hcd->board;
+ u16 hwcfg;
+ u16 chipid;
+ unsigned long flags;
+
+ pr_info("%s:\n", __func__);
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ if ((chipid & HCCHIPID_MASK) != HCCHIPID_MAGIC) {
+ pr_err("%s: Invalid chip ID %04x\n", __func__, chipid);
+ return -ENODEV;
+ }
+
+#ifdef CHIP_BUFFER_TEST
+ ret = isp1362_chip_test(isp1362_hcd);
+ if (ret)
+ return -ENODEV;
+#endif
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ /* clear interrupt status and disable all interrupt sources */
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, 0xff);
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0);
+
+ /* HW conf */
+ hwcfg = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1);
+ if (board->sel15Kres)
+ hwcfg |= HCHWCFG_PULLDOWN_DS2 |
+ (MAX_ROOT_PORTS > 1) ? HCHWCFG_PULLDOWN_DS1 : 0;
+ if (board->clknotstop)
+ hwcfg |= HCHWCFG_CLKNOTSTOP;
+ if (board->oc_enable)
+ hwcfg |= HCHWCFG_ANALOG_OC;
+ if (board->int_act_high)
+ hwcfg |= HCHWCFG_INT_POL;
+ if (board->int_edge_triggered)
+ hwcfg |= HCHWCFG_INT_TRIGGER;
+ if (board->dreq_act_high)
+ hwcfg |= HCHWCFG_DREQ_POL;
+ if (board->dack_act_high)
+ hwcfg |= HCHWCFG_DACK_POL;
+ isp1362_write_reg16(isp1362_hcd, HCHWCFG, hwcfg);
+ isp1362_show_reg(isp1362_hcd, HCHWCFG);
+ isp1362_write_reg16(isp1362_hcd, HCDMACFG, 0);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ ret = isp1362_mem_config(hcd);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+
+ /* Root hub conf */
+ isp1362_hcd->rhdesca = 0;
+ if (board->no_power_switching)
+ isp1362_hcd->rhdesca |= RH_A_NPS;
+ if (board->power_switching_mode)
+ isp1362_hcd->rhdesca |= RH_A_PSM;
+ if (board->potpg)
+ isp1362_hcd->rhdesca |= (board->potpg << 24) & RH_A_POTPGT;
+ else
+ isp1362_hcd->rhdesca |= (25 << 24) & RH_A_POTPGT;
+
+ isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca & ~RH_A_OCPM);
+ isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca | RH_A_OCPM);
+ isp1362_hcd->rhdesca = isp1362_read_reg32(isp1362_hcd, HCRHDESCA);
+
+ isp1362_hcd->rhdescb = RH_B_PPCM;
+ isp1362_write_reg32(isp1362_hcd, HCRHDESCB, isp1362_hcd->rhdescb);
+ isp1362_hcd->rhdescb = isp1362_read_reg32(isp1362_hcd, HCRHDESCB);
+
+ isp1362_read_reg32(isp1362_hcd, HCFMINTVL);
+ isp1362_write_reg32(isp1362_hcd, HCFMINTVL, (FSMP(FI) << 16) | FI);
+ isp1362_write_reg32(isp1362_hcd, HCLSTHRESH, LSTHRESH);
+
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ isp1362_hcd->hc_control = OHCI_USB_OPER;
+ hcd->state = HC_STATE_RUNNING;
+
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ /* Set up interrupts */
+ isp1362_hcd->intenb = OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE;
+ isp1362_hcd->intenb |= OHCI_INTR_RD;
+ isp1362_hcd->irqenb = HCuPINT_OPR | HCuPINT_SUSP;
+ isp1362_write_reg32(isp1362_hcd, HCINTENB, isp1362_hcd->intenb);
+ isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb);
+
+ /* Go operational */
+ isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control);
+ /* enable global power */
+ isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC | RH_HS_DRWE);
+
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct hc_driver isp1362_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "ISP1362 Host Controller",
+ .hcd_priv_size = sizeof(struct isp1362_hcd),
+
+ .irq = isp1362_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ .reset = isp1362_hc_reset,
+ .start = isp1362_hc_start,
+ .stop = isp1362_hc_stop,
+
+ .urb_enqueue = isp1362_urb_enqueue,
+ .urb_dequeue = isp1362_urb_dequeue,
+ .endpoint_disable = isp1362_endpoint_disable,
+
+ .get_frame_number = isp1362_get_frame,
+
+ .hub_status_data = isp1362_hub_status_data,
+ .hub_control = isp1362_hub_control,
+ .bus_suspend = isp1362_bus_suspend,
+ .bus_resume = isp1362_bus_resume,
+};
+
+/*-------------------------------------------------------------------------*/
+
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+
+static int __devexit isp1362_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ struct resource *res;
+
+ remove_debug_file(isp1362_hcd);
+ DBG(0, "%s: Removing HCD\n", __func__);
+ usb_remove_hcd(hcd);
+
+ DBG(0, "%s: Unmapping data_reg @ %08x\n", __func__,
+ (u32)isp1362_hcd->data_reg);
+ iounmap(isp1362_hcd->data_reg);
+
+ DBG(0, "%s: Unmapping addr_reg @ %08x\n", __func__,
+ (u32)isp1362_hcd->addr_reg);
+ iounmap(isp1362_hcd->addr_reg);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ DBG(0, "%s: release mem_region: %08lx\n", __func__, (long unsigned int)res->start);
+ if (res)
+ release_mem_region(res->start, resource_len(res));
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ DBG(0, "%s: release mem_region: %08lx\n", __func__, (long unsigned int)res->start);
+ if (res)
+ release_mem_region(res->start, resource_len(res));
+
+ DBG(0, "%s: put_hcd\n", __func__);
+ usb_put_hcd(hcd);
+ DBG(0, "%s: Done\n", __func__);
+
+ return 0;
+}
+
+static int __init isp1362_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct isp1362_hcd *isp1362_hcd;
+ struct resource *addr, *data;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+ int irq;
+ int retval = 0;
+
+ /* basic sanity checks first. board-specific init logic should
+ * have initialized this the three resources and probably board
+ * specific platform_data. we don't probe for IRQs, and do only
+ * minimal sanity checking.
+ */
+ if (pdev->num_resources < 3) {
+ retval = -ENODEV;
+ goto err1;
+ }
+
+ data = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ addr = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ irq = platform_get_irq(pdev, 0);
+ if (!addr || !data || irq < 0) {
+ retval = -ENODEV;
+ goto err1;
+ }
+
+#ifdef CONFIG_USB_HCD_DMA
+ if (pdev->dev.dma_mask) {
+ struct resource *dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+
+ if (!dma_res) {
+ retval = -ENODEV;
+ goto err1;
+ }
+ isp1362_hcd->data_dma = dma_res->start;
+ isp1362_hcd->max_dma_size = resource_len(dma_res);
+ }
+#else
+ if (pdev->dev.dma_mask) {
+ DBG(1, "won't do DMA");
+ retval = -ENODEV;
+ goto err1;
+ }
+#endif
+
+ if (!request_mem_region(addr->start, resource_len(addr), hcd_name)) {
+ retval = -EBUSY;
+ goto err1;
+ }
+ addr_reg = ioremap(addr->start, resource_len(addr));
+ if (addr_reg == NULL) {
+ retval = -ENOMEM;
+ goto err2;
+ }
+
+ if (!request_mem_region(data->start, resource_len(data), hcd_name)) {
+ retval = -EBUSY;
+ goto err3;
+ }
+ data_reg = ioremap(data->start, resource_len(data));
+ if (data_reg == NULL) {
+ retval = -ENOMEM;
+ goto err4;
+ }
+
+ /* allocate and initialize hcd */
+ hcd = usb_create_hcd(&isp1362_hc_driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err5;
+ }
+ hcd->rsrc_start = data->start;
+ isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ isp1362_hcd->data_reg = data_reg;
+ isp1362_hcd->addr_reg = addr_reg;
+
+ isp1362_hcd->next_statechange = jiffies;
+ spin_lock_init(&isp1362_hcd->lock);
+ INIT_LIST_HEAD(&isp1362_hcd->async);
+ INIT_LIST_HEAD(&isp1362_hcd->periodic);
+ INIT_LIST_HEAD(&isp1362_hcd->isoc);
+ INIT_LIST_HEAD(&isp1362_hcd->remove_list);
+ isp1362_hcd->board = pdev->dev.platform_data;
+#if USE_PLATFORM_DELAY
+ if (!isp1362_hcd->board->delay) {
+ dev_err(hcd->self.controller, "No platform delay function given\n");
+ retval = -ENODEV;
+ goto err6;
+ }
+#endif
+
+#ifdef CONFIG_ARM
+ if (isp1362_hcd->board)
+ set_irq_type(irq, isp1362_hcd->board->int_act_high ? IRQT_RISING : IRQT_FALLING);
+#endif
+
+ retval = usb_add_hcd(hcd, irq, IRQF_TRIGGER_LOW | IRQF_DISABLED | IRQF_SHARED);
+ if (retval != 0)
+ goto err6;
+ pr_info("%s, irq %d\n", hcd->product_desc, irq);
+
+ create_debug_file(isp1362_hcd);
+
+ return 0;
+
+ err6:
+ DBG(0, "%s: Freeing dev %08x\n", __func__, (u32)isp1362_hcd);
+ usb_put_hcd(hcd);
+ err5:
+ DBG(0, "%s: Unmapping data_reg @ %08x\n", __func__, (u32)data_reg);
+ iounmap(data_reg);
+ err4:
+ DBG(0, "%s: Releasing mem region %08lx\n", __func__, (long unsigned int)data->start);
+ release_mem_region(data->start, resource_len(data));
+ err3:
+ DBG(0, "%s: Unmapping addr_reg @ %08x\n", __func__, (u32)addr_reg);
+ iounmap(addr_reg);
+ err2:
+ DBG(0, "%s: Releasing mem region %08lx\n", __func__, (long unsigned int)addr->start);
+ release_mem_region(addr->start, resource_len(addr));
+ err1:
+ pr_err("%s: init error, %d\n", __func__, retval);
+
+ return retval;
+}
+
+#ifdef CONFIG_PM
+static int isp1362_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+ int retval = 0;
+
+ DBG(0, "%s: Suspending device\n", __func__);
+
+ if (state.event == PM_EVENT_FREEZE) {
+ DBG(0, "%s: Suspending root hub\n", __func__);
+ retval = isp1362_bus_suspend(hcd);
+ } else {
+ DBG(0, "%s: Suspending RH ports\n", __func__);
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ }
+ if (retval == 0)
+ pdev->dev.power.power_state = state;
+ return retval;
+}
+
+static int isp1362_resume(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd);
+ unsigned long flags;
+
+ DBG(0, "%s: Resuming\n", __func__);
+
+ if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
+ DBG(0, "%s: Resume RH ports\n", __func__);
+ spin_lock_irqsave(&isp1362_hcd->lock, flags);
+ isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC);
+ spin_unlock_irqrestore(&isp1362_hcd->lock, flags);
+ return 0;
+ }
+
+ pdev->dev.power.power_state = PMSG_ON;
+
+ return isp1362_bus_resume(isp1362_hcd_to_hcd(isp1362_hcd));
+}
+#else
+#define isp1362_suspend NULL
+#define isp1362_resume NULL
+#endif
+
+static struct platform_driver isp1362_driver = {
+ .probe = isp1362_probe,
+ .remove = __devexit_p(isp1362_remove),
+
+ .suspend = isp1362_suspend,
+ .resume = isp1362_resume,
+ .driver = {
+ .name = (char *)hcd_name,
+ .owner = THIS_MODULE,
+ },
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init isp1362_init(void)
+{
+ if (usb_disabled())
+ return -ENODEV;
+ pr_info("driver %s, %s\n", hcd_name, DRIVER_VERSION);
+ return platform_driver_register(&isp1362_driver);
+}
+module_init(isp1362_init);
+
+static void __exit isp1362_cleanup(void)
+{
+ platform_driver_unregister(&isp1362_driver);
+}
+module_exit(isp1362_cleanup);
diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h
new file mode 100644
index 000000000000..26e44fc8776f
--- /dev/null
+++ b/drivers/usb/host/isp1362.h
@@ -0,0 +1,1079 @@
+/*
+ * ISP1362 HCD (Host Controller Driver) for USB.
+ *
+ * COPYRIGHT (C) by L. Wassmann <LW@KARO-electronics.de>
+ */
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Platform specific compile time options
+ */
+#if defined(CONFIG_ARCH_KARO)
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/karo.h>
+
+#define USE_32BIT 1
+
+
+/* These options are mutually eclusive */
+#define USE_PLATFORM_DELAY 1
+#define USE_NDELAY 0
+/*
+ * MAX_ROOT_PORTS: Number of downstream ports
+ *
+ * The chip has two USB ports, one of which can be configured as
+ * an USB device port, so the value of this constant is implementation
+ * specific.
+ */
+#define MAX_ROOT_PORTS 2
+#define DUMMY_DELAY_ACCESS do {} while (0)
+
+/* insert platform specific definitions for other machines here */
+#elif defined(CONFIG_BLACKFIN)
+
+#include <linux/io.h>
+#define USE_32BIT 0
+#define MAX_ROOT_PORTS 2
+#define USE_PLATFORM_DELAY 0
+#define USE_NDELAY 1
+
+#define DUMMY_DELAY_ACCESS \
+ do { \
+ bfin_read16(ASYNC_BANK0_BASE); \
+ bfin_read16(ASYNC_BANK0_BASE); \
+ bfin_read16(ASYNC_BANK0_BASE); \
+ } while (0)
+
+#undef insw
+#undef outsw
+
+#define insw delayed_insw
+#define outsw delayed_outsw
+
+static inline void delayed_outsw(unsigned int addr, void *buf, int len)
+{
+ unsigned short *bp = (unsigned short *)buf;
+ while (len--) {
+ DUMMY_DELAY_ACCESS;
+ outw(*bp++, addr);
+ }
+}
+
+static inline void delayed_insw(unsigned int addr, void *buf, int len)
+{
+ unsigned short *bp = (unsigned short *)buf;
+ while (len--) {
+ DUMMY_DELAY_ACCESS;
+ *bp++ = inw((void *)addr);
+ }
+}
+
+#else
+
+#define MAX_ROOT_PORTS 2
+
+#define USE_32BIT 0
+
+/* These options are mutually eclusive */
+#define USE_PLATFORM_DELAY 0
+#define USE_NDELAY 0
+
+#define DUMMY_DELAY_ACCESS do {} while (0)
+
+#endif
+
+
+/* ------------------------------------------------------------------------- */
+
+#define USB_RESET_WIDTH 50
+#define MAX_XFER_SIZE 1023
+
+/* Buffer sizes */
+#define ISP1362_BUF_SIZE 4096
+#define ISP1362_ISTL_BUFSIZE 512
+#define ISP1362_INTL_BLKSIZE 64
+#define ISP1362_INTL_BUFFERS 16
+#define ISP1362_ATL_BLKSIZE 64
+
+#define ISP1362_REG_WRITE_OFFSET 0x80
+
+#ifdef ISP1362_DEBUG
+typedef const unsigned int isp1362_reg_t;
+
+#define REG_WIDTH_16 0x000
+#define REG_WIDTH_32 0x100
+#define REG_WIDTH_MASK 0x100
+#define REG_NO_MASK 0x0ff
+
+#define REG_ACCESS_R 0x200
+#define REG_ACCESS_W 0x400
+#define REG_ACCESS_RW 0x600
+#define REG_ACCESS_MASK 0x600
+
+#define ISP1362_REG_NO(r) ((r) & REG_NO_MASK)
+
+#define _BUG_ON(x) BUG_ON(x)
+#define _WARN_ON(x) WARN_ON(x)
+
+#define ISP1362_REG(name, addr, width, rw) \
+static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw))
+
+#define REG_ACCESS_TEST(r) BUG_ON(((r) & ISP1362_REG_WRITE_OFFSET) && !((r) & REG_ACCESS_W))
+#define REG_WIDTH_TEST(r, w) BUG_ON(((r) & REG_WIDTH_MASK) != (w))
+#else
+typedef const unsigned char isp1362_reg_t;
+#define ISP1362_REG_NO(r) (r)
+#define _BUG_ON(x) do {} while (0)
+#define _WARN_ON(x) do {} while (0)
+
+#define ISP1362_REG(name, addr, width, rw) \
+static isp1362_reg_t ISP1362_REG_##name = addr
+
+#define REG_ACCESS_TEST(r) do {} while (0)
+#define REG_WIDTH_TEST(r, w) do {} while (0)
+#endif
+
+/* OHCI compatible registers */
+/*
+ * Note: Some of the ISP1362 'OHCI' registers implement only
+ * a subset of the bits defined in the OHCI spec.
+ *
+ * Bitmasks for the individual bits of these registers are defined in "ohci.h"
+ */
+ISP1362_REG(HCREVISION, 0x00, REG_WIDTH_32, REG_ACCESS_R);
+ISP1362_REG(HCCONTROL, 0x01, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCCMDSTAT, 0x02, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCINTSTAT, 0x03, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCINTENB, 0x04, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCINTDIS, 0x05, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCFMINTVL, 0x0d, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCFMREM, 0x0e, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCFMNUM, 0x0f, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCLSTHRESH, 0x11, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCRHDESCA, 0x12, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCRHDESCB, 0x13, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCRHSTATUS, 0x14, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCRHPORT1, 0x15, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCRHPORT2, 0x16, REG_WIDTH_32, REG_ACCESS_RW);
+
+/* Philips ISP1362 specific registers */
+ISP1362_REG(HCHWCFG, 0x20, REG_WIDTH_16, REG_ACCESS_RW);
+#define HCHWCFG_DISABLE_SUSPEND (1 << 15)
+#define HCHWCFG_GLOBAL_PWRDOWN (1 << 14)
+#define HCHWCFG_PULLDOWN_DS1 (1 << 13)
+#define HCHWCFG_PULLDOWN_DS2 (1 << 12)
+#define HCHWCFG_CLKNOTSTOP (1 << 11)
+#define HCHWCFG_ANALOG_OC (1 << 10)
+#define HCHWCFG_ONEINT (1 << 9)
+#define HCHWCFG_DACK_MODE (1 << 8)
+#define HCHWCFG_ONEDMA (1 << 7)
+#define HCHWCFG_DACK_POL (1 << 6)
+#define HCHWCFG_DREQ_POL (1 << 5)
+#define HCHWCFG_DBWIDTH_MASK (0x03 << 3)
+#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK)
+#define HCHWCFG_INT_POL (1 << 2)
+#define HCHWCFG_INT_TRIGGER (1 << 1)
+#define HCHWCFG_INT_ENABLE (1 << 0)
+
+ISP1362_REG(HCDMACFG, 0x21, REG_WIDTH_16, REG_ACCESS_RW);
+#define HCDMACFG_CTR_ENABLE (1 << 7)
+#define HCDMACFG_BURST_LEN_MASK (0x03 << 5)
+#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK)
+#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0)
+#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1)
+#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2)
+#define HCDMACFG_DMA_ENABLE (1 << 4)
+#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1)
+#define HCDMACFG_BUF_TYPE(n) (((n) << 1) & HCDMACFG_BUF_TYPE_MASK)
+#define HCDMACFG_BUF_ISTL0 HCDMACFG_BUF_TYPE(0)
+#define HCDMACFG_BUF_ISTL1 HCDMACFG_BUF_TYPE(1)
+#define HCDMACFG_BUF_INTL HCDMACFG_BUF_TYPE(2)
+#define HCDMACFG_BUF_ATL HCDMACFG_BUF_TYPE(3)
+#define HCDMACFG_BUF_DIRECT HCDMACFG_BUF_TYPE(4)
+#define HCDMACFG_DMA_RW_SELECT (1 << 0)
+
+ISP1362_REG(HCXFERCTR, 0x22, REG_WIDTH_16, REG_ACCESS_RW);
+
+ISP1362_REG(HCuPINT, 0x24, REG_WIDTH_16, REG_ACCESS_RW);
+#define HCuPINT_SOF (1 << 0)
+#define HCuPINT_ISTL0 (1 << 1)
+#define HCuPINT_ISTL1 (1 << 2)
+#define HCuPINT_EOT (1 << 3)
+#define HCuPINT_OPR (1 << 4)
+#define HCuPINT_SUSP (1 << 5)
+#define HCuPINT_CLKRDY (1 << 6)
+#define HCuPINT_INTL (1 << 7)
+#define HCuPINT_ATL (1 << 8)
+#define HCuPINT_OTG (1 << 9)
+
+ISP1362_REG(HCuPINTENB, 0x25, REG_WIDTH_16, REG_ACCESS_RW);
+/* same bit definitions apply as for HCuPINT */
+
+ISP1362_REG(HCCHIPID, 0x27, REG_WIDTH_16, REG_ACCESS_R);
+#define HCCHIPID_MASK 0xff00
+#define HCCHIPID_MAGIC 0x3600
+
+ISP1362_REG(HCSCRATCH, 0x28, REG_WIDTH_16, REG_ACCESS_RW);
+
+ISP1362_REG(HCSWRES, 0x29, REG_WIDTH_16, REG_ACCESS_W);
+#define HCSWRES_MAGIC 0x00f6
+
+ISP1362_REG(HCBUFSTAT, 0x2c, REG_WIDTH_16, REG_ACCESS_RW);
+#define HCBUFSTAT_ISTL0_FULL (1 << 0)
+#define HCBUFSTAT_ISTL1_FULL (1 << 1)
+#define HCBUFSTAT_INTL_ACTIVE (1 << 2)
+#define HCBUFSTAT_ATL_ACTIVE (1 << 3)
+#define HCBUFSTAT_RESET_HWPP (1 << 4)
+#define HCBUFSTAT_ISTL0_ACTIVE (1 << 5)
+#define HCBUFSTAT_ISTL1_ACTIVE (1 << 6)
+#define HCBUFSTAT_ISTL0_DONE (1 << 8)
+#define HCBUFSTAT_ISTL1_DONE (1 << 9)
+#define HCBUFSTAT_PAIRED_PTDPP (1 << 10)
+
+ISP1362_REG(HCDIRADDR, 0x32, REG_WIDTH_32, REG_ACCESS_RW);
+#define HCDIRADDR_ADDR_MASK 0x0000ffff
+#define HCDIRADDR_ADDR(n) (((n) << 0) & HCDIRADDR_ADDR_MASK)
+#define HCDIRADDR_COUNT_MASK 0xffff0000
+#define HCDIRADDR_COUNT(n) (((n) << 16) & HCDIRADDR_COUNT_MASK)
+ISP1362_REG(HCDIRDATA, 0x45, REG_WIDTH_16, REG_ACCESS_RW);
+
+ISP1362_REG(HCISTLBUFSZ, 0x30, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCISTL0PORT, 0x40, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCISTL1PORT, 0x42, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCISTLRATE, 0x47, REG_WIDTH_16, REG_ACCESS_RW);
+
+ISP1362_REG(HCINTLBUFSZ, 0x33, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCINTLPORT, 0x43, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCINTLBLKSZ, 0x53, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCINTLDONE, 0x17, REG_WIDTH_32, REG_ACCESS_R);
+ISP1362_REG(HCINTLSKIP, 0x18, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCINTLLAST, 0x19, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCINTLCURR, 0x1a, REG_WIDTH_16, REG_ACCESS_R);
+
+ISP1362_REG(HCATLBUFSZ, 0x34, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCATLPORT, 0x44, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCATLBLKSZ, 0x54, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCATLDONE, 0x1b, REG_WIDTH_32, REG_ACCESS_R);
+ISP1362_REG(HCATLSKIP, 0x1c, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCATLLAST, 0x1d, REG_WIDTH_32, REG_ACCESS_RW);
+ISP1362_REG(HCATLCURR, 0x1e, REG_WIDTH_16, REG_ACCESS_R);
+
+ISP1362_REG(HCATLDTC, 0x51, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(HCATLDTCTO, 0x52, REG_WIDTH_16, REG_ACCESS_RW);
+
+
+ISP1362_REG(OTGCONTROL, 0x62, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(OTGSTATUS, 0x67, REG_WIDTH_16, REG_ACCESS_R);
+ISP1362_REG(OTGINT, 0x68, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(OTGINTENB, 0x69, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(OTGTIMER, 0x6A, REG_WIDTH_16, REG_ACCESS_RW);
+ISP1362_REG(OTGALTTMR, 0x6C, REG_WIDTH_16, REG_ACCESS_RW);
+
+/* Philips transfer descriptor, cpu-endian */
+struct ptd {
+ u16 count;
+#define PTD_COUNT_MSK (0x3ff << 0)
+#define PTD_TOGGLE_MSK (1 << 10)
+#define PTD_ACTIVE_MSK (1 << 11)
+#define PTD_CC_MSK (0xf << 12)
+ u16 mps;
+#define PTD_MPS_MSK (0x3ff << 0)
+#define PTD_SPD_MSK (1 << 10)
+#define PTD_LAST_MSK (1 << 11)
+#define PTD_EP_MSK (0xf << 12)
+ u16 len;
+#define PTD_LEN_MSK (0x3ff << 0)
+#define PTD_DIR_MSK (3 << 10)
+#define PTD_DIR_SETUP (0)
+#define PTD_DIR_OUT (1)
+#define PTD_DIR_IN (2)
+ u16 faddr;
+#define PTD_FA_MSK (0x7f << 0)
+/* PTD Byte 7: [StartingFrame (if ISO PTD) | StartingFrame[0..4], PollingRate[0..2] (if INT PTD)] */
+#define PTD_SF_ISO_MSK (0xff << 8)
+#define PTD_SF_INT_MSK (0x1f << 8)
+#define PTD_PR_MSK (0x07 << 13)
+} __attribute__ ((packed, aligned(2)));
+#define PTD_HEADER_SIZE sizeof(struct ptd)
+
+/* ------------------------------------------------------------------------- */
+/* Copied from ohci.h: */
+/*
+ * Hardware transfer status codes -- CC from PTD
+ */
+#define PTD_CC_NOERROR 0x00
+#define PTD_CC_CRC 0x01
+#define PTD_CC_BITSTUFFING 0x02
+#define PTD_CC_DATATOGGLEM 0x03
+#define PTD_CC_STALL 0x04
+#define PTD_DEVNOTRESP 0x05
+#define PTD_PIDCHECKFAIL 0x06
+#define PTD_UNEXPECTEDPID 0x07
+#define PTD_DATAOVERRUN 0x08
+#define PTD_DATAUNDERRUN 0x09
+ /* 0x0A, 0x0B reserved for hardware */
+#define PTD_BUFFEROVERRUN 0x0C
+#define PTD_BUFFERUNDERRUN 0x0D
+ /* 0x0E, 0x0F reserved for HCD */
+#define PTD_NOTACCESSED 0x0F
+
+
+/* map OHCI TD status codes (CC) to errno values */
+static const int cc_to_error[16] = {
+ /* No Error */ 0,
+ /* CRC Error */ -EILSEQ,
+ /* Bit Stuff */ -EPROTO,
+ /* Data Togg */ -EILSEQ,
+ /* Stall */ -EPIPE,
+ /* DevNotResp */ -ETIMEDOUT,
+ /* PIDCheck */ -EPROTO,
+ /* UnExpPID */ -EPROTO,
+ /* DataOver */ -EOVERFLOW,
+ /* DataUnder */ -EREMOTEIO,
+ /* (for hw) */ -EIO,
+ /* (for hw) */ -EIO,
+ /* BufferOver */ -ECOMM,
+ /* BuffUnder */ -ENOSR,
+ /* (for HCD) */ -EALREADY,
+ /* (for HCD) */ -EALREADY
+};
+
+
+/*
+ * HcControl (control) register masks
+ */
+#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */
+#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */
+#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */
+
+/* pre-shifted values for HCFS */
+# define OHCI_USB_RESET (0 << 6)
+# define OHCI_USB_RESUME (1 << 6)
+# define OHCI_USB_OPER (2 << 6)
+# define OHCI_USB_SUSPEND (3 << 6)
+
+/*
+ * HcCommandStatus (cmdstatus) register masks
+ */
+#define OHCI_HCR (1 << 0) /* host controller reset */
+#define OHCI_SOC (3 << 16) /* scheduling overrun count */
+
+/*
+ * masks used with interrupt registers:
+ * HcInterruptStatus (intrstatus)
+ * HcInterruptEnable (intrenable)
+ * HcInterruptDisable (intrdisable)
+ */
+#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */
+#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */
+#define OHCI_INTR_SF (1 << 2) /* start frame */
+#define OHCI_INTR_RD (1 << 3) /* resume detect */
+#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */
+#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */
+#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */
+#define OHCI_INTR_OC (1 << 30) /* ownership change */
+#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */
+
+/* roothub.portstatus [i] bits */
+#define RH_PS_CCS 0x00000001 /* current connect status */
+#define RH_PS_PES 0x00000002 /* port enable status*/
+#define RH_PS_PSS 0x00000004 /* port suspend status */
+#define RH_PS_POCI 0x00000008 /* port over current indicator */
+#define RH_PS_PRS 0x00000010 /* port reset status */
+#define RH_PS_PPS 0x00000100 /* port power status */
+#define RH_PS_LSDA 0x00000200 /* low speed device attached */
+#define RH_PS_CSC 0x00010000 /* connect status change */
+#define RH_PS_PESC 0x00020000 /* port enable status change */
+#define RH_PS_PSSC 0x00040000 /* port suspend status change */
+#define RH_PS_OCIC 0x00080000 /* over current indicator change */
+#define RH_PS_PRSC 0x00100000 /* port reset status change */
+
+/* roothub.status bits */
+#define RH_HS_LPS 0x00000001 /* local power status */
+#define RH_HS_OCI 0x00000002 /* over current indicator */
+#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */
+#define RH_HS_LPSC 0x00010000 /* local power status change */
+#define RH_HS_OCIC 0x00020000 /* over current indicator change */
+#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */
+
+/* roothub.b masks */
+#define RH_B_DR 0x0000ffff /* device removable flags */
+#define RH_B_PPCM 0xffff0000 /* port power control mask */
+
+/* roothub.a masks */
+#define RH_A_NDP (0xff << 0) /* number of downstream ports */
+#define RH_A_PSM (1 << 8) /* power switching mode */
+#define RH_A_NPS (1 << 9) /* no power switching */
+#define RH_A_DT (1 << 10) /* device type (mbz) */
+#define RH_A_OCPM (1 << 11) /* over current protection mode */
+#define RH_A_NOCP (1 << 12) /* no over current protection */
+#define RH_A_POTPGT (0xff << 24) /* power on to power good time */
+
+#define FI 0x2edf /* 12000 bits per frame (-1) */
+#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7))
+#define LSTHRESH 0x628 /* lowspeed bit threshold */
+
+/* ------------------------------------------------------------------------- */
+
+/* PTD accessor macros. */
+#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0)
+#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK)
+#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10)
+#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK)
+#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11)
+#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK)
+#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12)
+#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK)
+#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0)
+#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK)
+#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10)
+#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK)
+#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11)
+#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK)
+#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12)
+#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK)
+#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0)
+#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK)
+#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10)
+#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK)
+#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0)
+#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK)
+#define PTD_GET_SF_INT(p) (((p)->faddr & PTD_SF_INT_MSK) >> 8)
+#define PTD_SF_INT(v) (((v) << 8) & PTD_SF_INT_MSK)
+#define PTD_GET_SF_ISO(p) (((p)->faddr & PTD_SF_ISO_MSK) >> 8)
+#define PTD_SF_ISO(v) (((v) << 8) & PTD_SF_ISO_MSK)
+#define PTD_GET_PR(p) (((p)->faddr & PTD_PR_MSK) >> 13)
+#define PTD_PR(v) (((v) << 13) & PTD_PR_MSK)
+
+#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */
+#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE)
+
+struct isp1362_ep {
+ struct usb_host_endpoint *hep;
+ struct usb_device *udev;
+
+ /* philips transfer descriptor */
+ struct ptd ptd;
+
+ u8 maxpacket;
+ u8 epnum;
+ u8 nextpid;
+ u16 error_count;
+ u16 length; /* of current packet */
+ s16 ptd_offset; /* buffer offset in ISP1362 where
+ PTD has been stored
+ (for access thru HCDIRDATA) */
+ int ptd_index;
+ int num_ptds;
+ void *data; /* to databuf */
+ /* queue of active EPs (the ones transmitted to the chip) */
+ struct list_head active;
+
+ /* periodic schedule */
+ u8 branch;
+ u16 interval;
+ u16 load;
+ u16 last_iso;
+
+ /* async schedule */
+ struct list_head schedule; /* list of all EPs that need processing */
+ struct list_head remove_list;
+ int num_req;
+};
+
+struct isp1362_ep_queue {
+ struct list_head active; /* list of PTDs currently processed by HC */
+ atomic_t finishing;
+ unsigned long buf_map;
+ unsigned long skip_map;
+ int free_ptd;
+ u16 buf_start;
+ u16 buf_size;
+ u16 blk_size; /* PTD buffer block size for ATL and INTL */
+ u8 buf_count;
+ u8 buf_avail;
+ char name[16];
+
+ /* for statistical tracking */
+ u8 stat_maxptds; /* Max # of ptds seen simultaneously in fifo */
+ u8 ptd_count; /* number of ptds submitted to this queue */
+};
+
+struct isp1362_hcd {
+ spinlock_t lock;
+ void __iomem *addr_reg;
+ void __iomem *data_reg;
+
+ struct isp1362_platform_data *board;
+
+ struct proc_dir_entry *pde;
+ unsigned long stat1, stat2, stat4, stat8, stat16;
+
+ /* HC registers */
+ u32 intenb; /* "OHCI" interrupts */
+ u16 irqenb; /* uP interrupts */
+
+ /* Root hub registers */
+ u32 rhdesca;
+ u32 rhdescb;
+ u32 rhstatus;
+ u32 rhport[MAX_ROOT_PORTS];
+ unsigned long next_statechange;
+
+ /* HC control reg shadow copy */
+ u32 hc_control;
+
+ /* async schedule: control, bulk */
+ struct list_head async;
+
+ /* periodic schedule: int */
+ u16 load[PERIODIC_SIZE];
+ struct list_head periodic;
+ u16 fmindex;
+
+ /* periodic schedule: isochronous */
+ struct list_head isoc;
+ int istl_flip:1;
+ int irq_active:1;
+
+ /* Schedules for the current frame */
+ struct isp1362_ep_queue atl_queue;
+ struct isp1362_ep_queue intl_queue;
+ struct isp1362_ep_queue istl_queue[2];
+
+ /* list of PTDs retrieved from HC */
+ struct list_head remove_list;
+ enum {
+ ISP1362_INT_SOF,
+ ISP1362_INT_ISTL0,
+ ISP1362_INT_ISTL1,
+ ISP1362_INT_EOT,
+ ISP1362_INT_OPR,
+ ISP1362_INT_SUSP,
+ ISP1362_INT_CLKRDY,
+ ISP1362_INT_INTL,
+ ISP1362_INT_ATL,
+ ISP1362_INT_OTG,
+ NUM_ISP1362_IRQS
+ } IRQ_NAMES;
+ unsigned int irq_stat[NUM_ISP1362_IRQS];
+ int req_serial;
+};
+
+static inline const char *ISP1362_INT_NAME(int n)
+{
+ switch (n) {
+ case ISP1362_INT_SOF: return "SOF";
+ case ISP1362_INT_ISTL0: return "ISTL0";
+ case ISP1362_INT_ISTL1: return "ISTL1";
+ case ISP1362_INT_EOT: return "EOT";
+ case ISP1362_INT_OPR: return "OPR";
+ case ISP1362_INT_SUSP: return "SUSP";
+ case ISP1362_INT_CLKRDY: return "CLKRDY";
+ case ISP1362_INT_INTL: return "INTL";
+ case ISP1362_INT_ATL: return "ATL";
+ case ISP1362_INT_OTG: return "OTG";
+ default: return "unknown";
+ }
+}
+
+static inline void ALIGNSTAT(struct isp1362_hcd *isp1362_hcd, void *ptr)
+{
+ unsigned p = (unsigned)ptr;
+ if (!(p & 0xf))
+ isp1362_hcd->stat16++;
+ else if (!(p & 0x7))
+ isp1362_hcd->stat8++;
+ else if (!(p & 0x3))
+ isp1362_hcd->stat4++;
+ else if (!(p & 0x1))
+ isp1362_hcd->stat2++;
+ else
+ isp1362_hcd->stat1++;
+}
+
+static inline struct isp1362_hcd *hcd_to_isp1362_hcd(struct usb_hcd *hcd)
+{
+ return (struct isp1362_hcd *) (hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd)
+{
+ return container_of((void *)isp1362_hcd, struct usb_hcd, hcd_priv);
+}
+
+#define frame_before(f1, f2) ((s16)((u16)f1 - (u16)f2) < 0)
+
+/*
+ * ISP1362 HW Interface
+ */
+
+#ifdef ISP1362_DEBUG
+#define DBG(level, fmt...) \
+ do { \
+ if (dbg_level > level) \
+ pr_debug(fmt); \
+ } while (0)
+#define _DBG(level, fmt...) \
+ do { \
+ if (dbg_level > level) \
+ printk(fmt); \
+ } while (0)
+#else
+#define DBG(fmt...) do {} while (0)
+#define _DBG DBG
+#endif
+
+#ifdef VERBOSE
+# define VDBG(fmt...) DBG(3, fmt)
+#else
+# define VDBG(fmt...) do {} while (0)
+#endif
+
+#ifdef REGISTERS
+# define RDBG(fmt...) DBG(1, fmt)
+#else
+# define RDBG(fmt...) do {} while (0)
+#endif
+
+#ifdef URB_TRACE
+#define URB_DBG(fmt...) DBG(0, fmt)
+#else
+#define URB_DBG(fmt...) do {} while (0)
+#endif
+
+
+#if USE_PLATFORM_DELAY
+#if USE_NDELAY
+#error USE_PLATFORM_DELAY and USE_NDELAY defined simultaneously.
+#endif
+#define isp1362_delay(h, d) (h)->board->delay(isp1362_hcd_to_hcd(h)->self.controller, d)
+#elif USE_NDELAY
+#define isp1362_delay(h, d) ndelay(d)
+#else
+#define isp1362_delay(h, d) do {} while (0)
+#endif
+
+#define get_urb(ep) ({ \
+ BUG_ON(list_empty(&ep->hep->urb_list)); \
+ container_of(ep->hep->urb_list.next, struct urb, urb_list); \
+})
+
+/* basic access functions for ISP1362 chip registers */
+/* NOTE: The contents of the address pointer register cannot be read back! The driver must ensure,
+ * that all register accesses are performed with interrupts disabled, since the interrupt
+ * handler has no way of restoring the previous state.
+ */
+static void isp1362_write_addr(struct isp1362_hcd *isp1362_hcd, isp1362_reg_t reg)
+{
+ /*_BUG_ON((reg & ISP1362_REG_WRITE_OFFSET) && !(reg & REG_ACCESS_W));*/
+ REG_ACCESS_TEST(reg);
+ _BUG_ON(!irqs_disabled());
+ DUMMY_DELAY_ACCESS;
+ writew(ISP1362_REG_NO(reg), isp1362_hcd->addr_reg);
+ DUMMY_DELAY_ACCESS;
+ isp1362_delay(isp1362_hcd, 1);
+}
+
+static void isp1362_write_data16(struct isp1362_hcd *isp1362_hcd, u16 val)
+{
+ _BUG_ON(!irqs_disabled());
+ DUMMY_DELAY_ACCESS;
+ writew(val, isp1362_hcd->data_reg);
+}
+
+static u16 isp1362_read_data16(struct isp1362_hcd *isp1362_hcd)
+{
+ u16 val;
+
+ _BUG_ON(!irqs_disabled());
+ DUMMY_DELAY_ACCESS;
+ val = readw(isp1362_hcd->data_reg);
+
+ return val;
+}
+
+static void isp1362_write_data32(struct isp1362_hcd *isp1362_hcd, u32 val)
+{
+ _BUG_ON(!irqs_disabled());
+#if USE_32BIT
+ DUMMY_DELAY_ACCESS;
+ writel(val, isp1362_hcd->data_reg);
+#else
+ DUMMY_DELAY_ACCESS;
+ writew((u16)val, isp1362_hcd->data_reg);
+ DUMMY_DELAY_ACCESS;
+ writew(val >> 16, isp1362_hcd->data_reg);
+#endif
+}
+
+static u32 isp1362_read_data32(struct isp1362_hcd *isp1362_hcd)
+{
+ u32 val;
+
+ _BUG_ON(!irqs_disabled());
+#if USE_32BIT
+ DUMMY_DELAY_ACCESS;
+ val = readl(isp1362_hcd->data_reg);
+#else
+ DUMMY_DELAY_ACCESS;
+ val = (u32)readw(isp1362_hcd->data_reg);
+ DUMMY_DELAY_ACCESS;
+ val |= (u32)readw(isp1362_hcd->data_reg) << 16;
+#endif
+ return val;
+}
+
+/* use readsw/writesw to access the fifo whenever possible */
+/* assume HCDIRDATA or XFERCTR & addr_reg have been set up */
+static void isp1362_read_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len)
+{
+ u8 *dp = buf;
+ u16 data;
+
+ if (!len)
+ return;
+
+ _BUG_ON(!irqs_disabled());
+
+ RDBG("%s: Reading %d byte from fifo to mem @ %p\n", __func__, len, buf);
+#if USE_32BIT
+ if (len >= 4) {
+ RDBG("%s: Using readsl for %d dwords\n", __func__, len >> 2);
+ readsl(isp1362_hcd->data_reg, dp, len >> 2);
+ dp += len & ~3;
+ len &= 3;
+ }
+#endif
+ if (len >= 2) {
+ RDBG("%s: Using readsw for %d words\n", __func__, len >> 1);
+ insw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1);
+ dp += len & ~1;
+ len &= 1;
+ }
+
+ BUG_ON(len & ~1);
+ if (len > 0) {
+ data = isp1362_read_data16(isp1362_hcd);
+ RDBG("%s: Reading trailing byte %02x to mem @ %08x\n", __func__,
+ (u8)data, (u32)dp);
+ *dp = (u8)data;
+ }
+}
+
+static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len)
+{
+ u8 *dp = buf;
+ u16 data;
+
+ if (!len)
+ return;
+
+ if ((unsigned)dp & 0x1) {
+ /* not aligned */
+ for (; len > 1; len -= 2) {
+ data = *dp++;
+ data |= *dp++ << 8;
+ isp1362_write_data16(isp1362_hcd, data);
+ }
+ if (len)
+ isp1362_write_data16(isp1362_hcd, *dp);
+ return;
+ }
+
+ _BUG_ON(!irqs_disabled());
+
+ RDBG("%s: Writing %d byte to fifo from memory @%p\n", __func__, len, buf);
+#if USE_32BIT
+ if (len >= 4) {
+ RDBG("%s: Using writesl for %d dwords\n", __func__, len >> 2);
+ writesl(isp1362_hcd->data_reg, dp, len >> 2);
+ dp += len & ~3;
+ len &= 3;
+ }
+#endif
+ if (len >= 2) {
+ RDBG("%s: Using writesw for %d words\n", __func__, len >> 1);
+ outsw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1);
+ dp += len & ~1;
+ len &= 1;
+ }
+
+ BUG_ON(len & ~1);
+ if (len > 0) {
+ /* finally write any trailing byte; we don't need to care
+ * about the high byte of the last word written
+ */
+ data = (u16)*dp;
+ RDBG("%s: Sending trailing byte %02x from mem @ %08x\n", __func__,
+ data, (u32)dp);
+ isp1362_write_data16(isp1362_hcd, data);
+ }
+}
+
+#define isp1362_read_reg16(d, r) ({ \
+ u16 __v; \
+ REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \
+ isp1362_write_addr(d, ISP1362_REG_##r); \
+ __v = isp1362_read_data16(d); \
+ RDBG("%s: Read %04x from %s[%02x]\n", __func__, __v, #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r)); \
+ __v; \
+})
+
+#define isp1362_read_reg32(d, r) ({ \
+ u32 __v; \
+ REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \
+ isp1362_write_addr(d, ISP1362_REG_##r); \
+ __v = isp1362_read_data32(d); \
+ RDBG("%s: Read %08x from %s[%02x]\n", __func__, __v, #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r)); \
+ __v; \
+})
+
+#define isp1362_write_reg16(d, r, v) { \
+ REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \
+ isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \
+ isp1362_write_data16(d, (u16)(v)); \
+ RDBG("%s: Wrote %04x to %s[%02x]\n", __func__, (u16)(v), #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r)); \
+}
+
+#define isp1362_write_reg32(d, r, v) { \
+ REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \
+ isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \
+ isp1362_write_data32(d, (u32)(v)); \
+ RDBG("%s: Wrote %08x to %s[%02x]\n", __func__, (u32)(v), #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r)); \
+}
+
+#define isp1362_set_mask16(d, r, m) { \
+ u16 __v; \
+ __v = isp1362_read_reg16(d, r); \
+ if ((__v | m) != __v) \
+ isp1362_write_reg16(d, r, __v | m); \
+}
+
+#define isp1362_clr_mask16(d, r, m) { \
+ u16 __v; \
+ __v = isp1362_read_reg16(d, r); \
+ if ((__v & ~m) != __v) \
+ isp1362_write_reg16(d, r, __v & ~m); \
+}
+
+#define isp1362_set_mask32(d, r, m) { \
+ u32 __v; \
+ __v = isp1362_read_reg32(d, r); \
+ if ((__v | m) != __v) \
+ isp1362_write_reg32(d, r, __v | m); \
+}
+
+#define isp1362_clr_mask32(d, r, m) { \
+ u32 __v; \
+ __v = isp1362_read_reg32(d, r); \
+ if ((__v & ~m) != __v) \
+ isp1362_write_reg32(d, r, __v & ~m); \
+}
+
+#ifdef ISP1362_DEBUG
+#define isp1362_show_reg(d, r) { \
+ if ((ISP1362_REG_##r & REG_WIDTH_MASK) == REG_WIDTH_32) \
+ DBG(0, "%-12s[%02x]: %08x\n", #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg32(d, r)); \
+ else \
+ DBG(0, "%-12s[%02x]: %04x\n", #r, \
+ ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \
+}
+#else
+#define isp1362_show_reg(d, r) do {} while (0)
+#endif
+
+static void __attribute__((__unused__)) isp1362_show_regs(struct isp1362_hcd *isp1362_hcd)
+{
+ isp1362_show_reg(isp1362_hcd, HCREVISION);
+ isp1362_show_reg(isp1362_hcd, HCCONTROL);
+ isp1362_show_reg(isp1362_hcd, HCCMDSTAT);
+ isp1362_show_reg(isp1362_hcd, HCINTSTAT);
+ isp1362_show_reg(isp1362_hcd, HCINTENB);
+ isp1362_show_reg(isp1362_hcd, HCFMINTVL);
+ isp1362_show_reg(isp1362_hcd, HCFMREM);
+ isp1362_show_reg(isp1362_hcd, HCFMNUM);
+ isp1362_show_reg(isp1362_hcd, HCLSTHRESH);
+ isp1362_show_reg(isp1362_hcd, HCRHDESCA);
+ isp1362_show_reg(isp1362_hcd, HCRHDESCB);
+ isp1362_show_reg(isp1362_hcd, HCRHSTATUS);
+ isp1362_show_reg(isp1362_hcd, HCRHPORT1);
+ isp1362_show_reg(isp1362_hcd, HCRHPORT2);
+
+ isp1362_show_reg(isp1362_hcd, HCHWCFG);
+ isp1362_show_reg(isp1362_hcd, HCDMACFG);
+ isp1362_show_reg(isp1362_hcd, HCXFERCTR);
+ isp1362_show_reg(isp1362_hcd, HCuPINT);
+
+ if (in_interrupt())
+ DBG(0, "%-12s[%02x]: %04x\n", "HCuPINTENB",
+ ISP1362_REG_NO(ISP1362_REG_HCuPINTENB), isp1362_hcd->irqenb);
+ else
+ isp1362_show_reg(isp1362_hcd, HCuPINTENB);
+ isp1362_show_reg(isp1362_hcd, HCCHIPID);
+ isp1362_show_reg(isp1362_hcd, HCSCRATCH);
+ isp1362_show_reg(isp1362_hcd, HCBUFSTAT);
+ isp1362_show_reg(isp1362_hcd, HCDIRADDR);
+ /* Access would advance fifo
+ * isp1362_show_reg(isp1362_hcd, HCDIRDATA);
+ */
+ isp1362_show_reg(isp1362_hcd, HCISTLBUFSZ);
+ isp1362_show_reg(isp1362_hcd, HCISTLRATE);
+ isp1362_show_reg(isp1362_hcd, HCINTLBUFSZ);
+ isp1362_show_reg(isp1362_hcd, HCINTLBLKSZ);
+ isp1362_show_reg(isp1362_hcd, HCINTLDONE);
+ isp1362_show_reg(isp1362_hcd, HCINTLSKIP);
+ isp1362_show_reg(isp1362_hcd, HCINTLLAST);
+ isp1362_show_reg(isp1362_hcd, HCINTLCURR);
+ isp1362_show_reg(isp1362_hcd, HCATLBUFSZ);
+ isp1362_show_reg(isp1362_hcd, HCATLBLKSZ);
+ /* only valid after ATL_DONE interrupt
+ * isp1362_show_reg(isp1362_hcd, HCATLDONE);
+ */
+ isp1362_show_reg(isp1362_hcd, HCATLSKIP);
+ isp1362_show_reg(isp1362_hcd, HCATLLAST);
+ isp1362_show_reg(isp1362_hcd, HCATLCURR);
+ isp1362_show_reg(isp1362_hcd, HCATLDTC);
+ isp1362_show_reg(isp1362_hcd, HCATLDTCTO);
+}
+
+static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len)
+{
+ _BUG_ON(offset & 1);
+ _BUG_ON(offset >= ISP1362_BUF_SIZE);
+ _BUG_ON(len > ISP1362_BUF_SIZE);
+ _BUG_ON(offset + len > ISP1362_BUF_SIZE);
+ len = (len + 1) & ~1;
+
+ isp1362_clr_mask16(isp1362_hcd, HCDMACFG, HCDMACFG_CTR_ENABLE);
+ isp1362_write_reg32(isp1362_hcd, HCDIRADDR,
+ HCDIRADDR_ADDR(offset) | HCDIRADDR_COUNT(len));
+}
+
+static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len)
+{
+ _BUG_ON(offset & 1);
+
+ isp1362_write_diraddr(isp1362_hcd, offset, len);
+
+ DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %08x\n", __func__,
+ len, offset, (u32)buf);
+
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
+ _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT));
+
+ isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA);
+
+ isp1362_read_fifo(isp1362_hcd, buf, len);
+ _WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT));
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
+ _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT));
+}
+
+static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len)
+{
+ _BUG_ON(offset & 1);
+
+ isp1362_write_diraddr(isp1362_hcd, offset, len);
+
+ DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %08x\n", __func__,
+ len, offset, (u32)buf);
+
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
+ _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT));
+
+ isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA | ISP1362_REG_WRITE_OFFSET);
+ isp1362_write_fifo(isp1362_hcd, buf, len);
+
+ _WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT));
+ isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT);
+ _WARN_ON((isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_EOT));
+}
+
+static void __attribute__((unused)) dump_data(char *buf, int len)
+{
+ if (dbg_level > 0) {
+ int k;
+ int lf = 0;
+
+ for (k = 0; k < len; ++k) {
+ if (!lf)
+ DBG(0, "%04x:", k);
+ printk(" %02x", ((u8 *) buf)[k]);
+ lf = 1;
+ if (!k)
+ continue;
+ if (k % 16 == 15) {
+ printk("\n");
+ lf = 0;
+ continue;
+ }
+ if (k % 8 == 7)
+ printk(" ");
+ if (k % 4 == 3)
+ printk(" ");
+ }
+ if (lf)
+ printk("\n");
+ }
+}
+
+#if defined(ISP1362_DEBUG) && defined(PTD_TRACE)
+
+static void dump_ptd(struct ptd *ptd)
+{
+ DBG(0, "EP %p: CC=%x EP=%d DIR=%x CNT=%d LEN=%d MPS=%d TGL=%x ACT=%x FA=%d SPD=%x SF=%x PR=%x LST=%x\n",
+ container_of(ptd, struct isp1362_ep, ptd),
+ PTD_GET_CC(ptd), PTD_GET_EP(ptd), PTD_GET_DIR(ptd),
+ PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd),
+ PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd), PTD_GET_FA(ptd),
+ PTD_GET_SPD(ptd), PTD_GET_SF_INT(ptd), PTD_GET_PR(ptd), PTD_GET_LAST(ptd));
+ DBG(0, " %04x %04x %04x %04x\n", ptd->count, ptd->mps, ptd->len, ptd->faddr);
+}
+
+static void dump_ptd_out_data(struct ptd *ptd, u8 *buf)
+{
+ if (dbg_level > 0) {
+ if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) {
+ DBG(0, "--out->\n");
+ dump_data(buf, PTD_GET_LEN(ptd));
+ }
+ }
+}
+
+static void dump_ptd_in_data(struct ptd *ptd, u8 *buf)
+{
+ if (dbg_level > 0) {
+ if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) {
+ DBG(0, "<--in--\n");
+ dump_data(buf, PTD_GET_COUNT(ptd));
+ }
+ DBG(0, "-----\n");
+ }
+}
+
+static void dump_ptd_queue(struct isp1362_ep_queue *epq)
+{
+ struct isp1362_ep *ep;
+ int dbg = dbg_level;
+
+ dbg_level = 1;
+ list_for_each_entry(ep, &epq->active, active) {
+ dump_ptd(&ep->ptd);
+ dump_data(ep->data, ep->length);
+ }
+ dbg_level = dbg;
+}
+#else
+#define dump_ptd(ptd) do {} while (0)
+#define dump_ptd_in_data(ptd, buf) do {} while (0)
+#define dump_ptd_out_data(ptd, buf) do {} while (0)
+#define dump_ptd_data(ptd, buf) do {} while (0)
+#define dump_ptd_queue(epq) do {} while (0)
+#endif
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index 15438469f21a..9600a58299db 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -386,6 +386,10 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
hwmode |= HW_DACK_POL_HIGH;
if (priv->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
hwmode |= HW_DREQ_POL_HIGH;
+ if (priv->devflags & ISP1760_FLAG_INTR_POL_HIGH)
+ hwmode |= HW_INTR_HIGH_ACT;
+ if (priv->devflags & ISP1760_FLAG_INTR_EDGE_TRIG)
+ hwmode |= HW_INTR_EDGE_TRIG;
/*
* We have to set this first in case we're in 16-bit mode.
diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h
index 462f4943cb1b..6931ef5c9650 100644
--- a/drivers/usb/host/isp1760-hcd.h
+++ b/drivers/usb/host/isp1760-hcd.h
@@ -142,6 +142,8 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */
#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */
#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */
+#define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */
+#define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */
/* chip memory management */
struct memory_chunk {
diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c
index d4feebfc63bd..1c9f977a5c9c 100644
--- a/drivers/usb/host/isp1760-if.c
+++ b/drivers/usb/host/isp1760-if.c
@@ -3,6 +3,7 @@
* Currently there is support for
* - OpenFirmware
* - PCI
+ * - PDEV (generic platform device centralized driver model)
*
* (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
*
@@ -11,6 +12,7 @@
#include <linux/usb.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/usb/isp1760.h>
#include "../core/hcd.h"
#include "isp1760-hcd.h"
@@ -308,6 +310,8 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev)
struct resource *mem_res;
struct resource *irq_res;
resource_size_t mem_size;
+ struct isp1760_platform_data *priv = pdev->dev.platform_data;
+ unsigned int devflags = 0;
unsigned long irqflags = IRQF_SHARED | IRQF_DISABLED;
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -330,8 +334,23 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev)
}
irqflags |= irq_res->flags & IRQF_TRIGGER_MASK;
+ if (priv) {
+ if (priv->is_isp1761)
+ devflags |= ISP1760_FLAG_ISP1761;
+ if (priv->bus_width_16)
+ devflags |= ISP1760_FLAG_BUS_WIDTH_16;
+ if (priv->port1_otg)
+ devflags |= ISP1760_FLAG_OTG_EN;
+ if (priv->analog_oc)
+ devflags |= ISP1760_FLAG_ANALOG_OC;
+ if (priv->dack_polarity_high)
+ devflags |= ISP1760_FLAG_DACK_POL_HIGH;
+ if (priv->dreq_polarity_high)
+ devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
+ }
+
hcd = isp1760_register(mem_res->start, mem_size, irq_res->start,
- irqflags, &pdev->dev, dev_name(&pdev->dev), 0);
+ irqflags, &pdev->dev, dev_name(&pdev->dev), devflags);
if (IS_ERR(hcd)) {
pr_warning("isp1760: Failed to register the HCD device\n");
ret = -ENODEV;
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index bb5e6f671578..7ccffcbe7b6f 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -148,7 +148,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
at91_start_hc(pdev);
ohci_hcd_init(hcd_to_ohci(hcd));
- retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED);
+ retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
if (retval == 0)
return retval;
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index 2ac4e022a13f..e4380082ebb1 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -248,10 +248,9 @@ static int ohci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *pdev,
- pm_message_t message)
+static int ohci_hcd_au1xxx_drv_suspend(struct device *dev)
{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
unsigned long flags;
int rc;
@@ -274,10 +273,6 @@ static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *pdev,
ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
(void)ohci_readl(ohci, &ohci->regs->intrdisable);
- /* make sure snapshot being resumed re-enumerates everything */
- if (message.event == PM_EVENT_PRETHAW)
- ohci_usb_reset(ohci);
-
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
au1xxx_stop_ohc();
@@ -287,9 +282,9 @@ bail:
return rc;
}
-static int ohci_hcd_au1xxx_drv_resume(struct platform_device *pdev)
+static int ohci_hcd_au1xxx_drv_resume(struct device *dev)
{
- struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = dev_get_drvdata(dev);
au1xxx_start_ohc();
@@ -298,20 +293,26 @@ static int ohci_hcd_au1xxx_drv_resume(struct platform_device *pdev)
return 0;
}
+
+static struct dev_pm_ops au1xxx_ohci_pmops = {
+ .suspend = ohci_hcd_au1xxx_drv_suspend,
+ .resume = ohci_hcd_au1xxx_drv_resume,
+};
+
+#define AU1XXX_OHCI_PMOPS &au1xxx_ohci_pmops
+
#else
-#define ohci_hcd_au1xxx_drv_suspend NULL
-#define ohci_hcd_au1xxx_drv_resume NULL
+#define AU1XXX_OHCI_PMOPS NULL
#endif
static struct platform_driver ohci_hcd_au1xxx_driver = {
.probe = ohci_hcd_au1xxx_drv_probe,
.remove = ohci_hcd_au1xxx_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
- .suspend = ohci_hcd_au1xxx_drv_suspend,
- .resume = ohci_hcd_au1xxx_drv_resume,
.driver = {
.name = "au1xxx-ohci",
.owner = THIS_MODULE,
+ .pm = AU1XXX_OHCI_PMOPS,
},
};
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 58151687d351..78bb7710f36d 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -34,7 +34,6 @@
#include <linux/usb/otg.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
-#include <linux/reboot.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index 61800742fc07..fd318bebc365 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -177,9 +177,13 @@ static inline void pxa27x_setup_hc(struct pxa27x_ohci *ohci,
if (inf->flags & NO_OC_PROTECTION)
uhcrhda |= UHCRHDA_NOCP;
+ else
+ uhcrhda &= ~UHCRHDA_NOCP;
if (inf->flags & OC_MODE_PERPORT)
uhcrhda |= UHCRHDA_OCPM;
+ else
+ uhcrhda &= ~UHCRHDA_OCPM;
if (inf->power_on_delay) {
uhcrhda &= ~UHCRHDA_POTPGT(0xff);
@@ -498,6 +502,9 @@ static int ohci_hcd_pxa27x_drv_resume(struct device *dev)
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd);
int status;
+ struct pxaohci_platform_data *inf;
+
+ inf = pdev->dev.platform_data;
if (time_before(jiffies, ohci->ohci.next_statechange))
msleep(5);
@@ -506,6 +513,13 @@ static int ohci_hcd_pxa27x_drv_resume(struct device *dev)
if ((status = pxa27x_start_hc(ohci, dev)) < 0)
return status;
+ if (inf) {
+ /* Select Power Management Mode */
+ pxa27x_ohci_select_pmm(ohci, inf->port_mode);
+ } else {
+ pr_err("No platform info on resume");
+ }
+
ohci_finish_controller_resume(hcd);
return 0;
}
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index 5ac489ee3dab..50f57f468836 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -33,7 +33,6 @@
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
-#include <linux/reboot.h>
#include <linux/usb.h>
#include <linux/moduleparam.h>
#include <linux/dma-mapping.h>
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index 64e57bfe236b..acd582c02802 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -1422,7 +1422,6 @@ static int uhci_urb_enqueue(struct usb_hcd *hcd,
goto err_submit_failed;
/* Add this URB to the QH */
- urbp->qh = qh;
list_add_tail(&urbp->node, &qh->queue);
/* If the new URB is the first and only one on this QH then either
diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c
index c2050785a819..c632437c7649 100644
--- a/drivers/usb/host/whci/asl.c
+++ b/drivers/usb/host/whci/asl.c
@@ -227,11 +227,21 @@ void scan_async_work(struct work_struct *work)
/*
* Now that the ASL is updated, complete the removal of any
* removed qsets.
+ *
+ * If the qset was to be reset, do so and reinsert it into the
+ * ASL if it has pending transfers.
*/
spin_lock_irq(&whc->lock);
list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) {
qset_remove_complete(whc, qset);
+ if (qset->reset) {
+ qset_reset(whc, qset);
+ if (!list_empty(&qset->stds)) {
+ asl_qset_insert_begin(whc, qset);
+ queue_work(whc->workqueue, &whc->async_work);
+ }
+ }
}
spin_unlock_irq(&whc->lock);
@@ -267,7 +277,7 @@ int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
else
err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
if (!err) {
- if (!qset->in_sw_list)
+ if (!qset->in_sw_list && !qset->remove)
asl_qset_insert_begin(whc, qset);
} else
usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb);
diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c
index e019a5058ab8..687b622a1612 100644
--- a/drivers/usb/host/whci/hcd.c
+++ b/drivers/usb/host/whci/hcd.c
@@ -192,19 +192,23 @@ static void whc_endpoint_reset(struct usb_hcd *usb_hcd,
struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
struct whc *whc = wusbhc_to_whc(wusbhc);
struct whc_qset *qset;
+ unsigned long flags;
+
+ spin_lock_irqsave(&whc->lock, flags);
qset = ep->hcpriv;
if (qset) {
qset->remove = 1;
+ qset->reset = 1;
if (usb_endpoint_xfer_bulk(&ep->desc)
|| usb_endpoint_xfer_control(&ep->desc))
queue_work(whc->workqueue, &whc->async_work);
else
queue_work(whc->workqueue, &whc->periodic_work);
-
- qset_reset(whc, qset);
}
+
+ spin_unlock_irqrestore(&whc->lock, flags);
}
diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c
index ff4ef9e910d9..a9e05bac6646 100644
--- a/drivers/usb/host/whci/pzl.c
+++ b/drivers/usb/host/whci/pzl.c
@@ -255,11 +255,21 @@ void scan_periodic_work(struct work_struct *work)
/*
* Now that the PZL is updated, complete the removal of any
* removed qsets.
+ *
+ * If the qset was to be reset, do so and reinsert it into the
+ * PZL if it has pending transfers.
*/
spin_lock_irq(&whc->lock);
list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) {
qset_remove_complete(whc, qset);
+ if (qset->reset) {
+ qset_reset(whc, qset);
+ if (!list_empty(&qset->stds)) {
+ qset_insert_in_sw_list(whc, qset);
+ queue_work(whc->workqueue, &whc->periodic_work);
+ }
+ }
}
spin_unlock_irq(&whc->lock);
@@ -295,7 +305,7 @@ int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
else
err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
if (!err) {
- if (!qset->in_sw_list)
+ if (!qset->in_sw_list && !qset->remove)
qset_insert_in_sw_list(whc, qset);
} else
usb_hcd_unlink_urb_from_ep(&whc->wusbhc.usb_hcd, urb);
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c
index 640b38fbd051..1b9dc1571570 100644
--- a/drivers/usb/host/whci/qset.c
+++ b/drivers/usb/host/whci/qset.c
@@ -103,7 +103,6 @@ static void qset_fill_qh(struct whc_qset *qset, struct urb *urb)
void qset_clear(struct whc *whc, struct whc_qset *qset)
{
qset->td_start = qset->td_end = qset->ntds = 0;
- qset->remove = 0;
qset->qh.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T);
qset->qh.status = qset->qh.status & QH_STATUS_SEQ_MASK;
@@ -125,7 +124,7 @@ void qset_clear(struct whc *whc, struct whc_qset *qset)
*/
void qset_reset(struct whc *whc, struct whc_qset *qset)
{
- wait_for_completion(&qset->remove_complete);
+ qset->reset = 0;
qset->qh.status &= ~QH_STATUS_SEQ_MASK;
qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1);
@@ -156,6 +155,7 @@ struct whc_qset *get_qset(struct whc *whc, struct urb *urb,
void qset_remove_complete(struct whc *whc, struct whc_qset *qset)
{
+ qset->remove = 0;
list_del_init(&qset->list_node);
complete(&qset->remove_complete);
}
diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h
index 794dba0d0f0a..e8d0001605be 100644
--- a/drivers/usb/host/whci/whci-hc.h
+++ b/drivers/usb/host/whci/whci-hc.h
@@ -264,6 +264,7 @@ struct whc_qset {
unsigned in_sw_list:1;
unsigned in_hw_list:1;
unsigned remove:1;
+ unsigned reset:1;
struct urb *pause_after_urb;
struct completion remove_complete;
int max_burst;
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index 6da8887538c7..1337a9ce80b9 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -96,6 +96,8 @@ static int idmouse_probe(struct usb_interface *interface,
const struct usb_device_id *id);
static void idmouse_disconnect(struct usb_interface *interface);
+static int idmouse_suspend(struct usb_interface *intf, pm_message_t message);
+static int idmouse_resume(struct usb_interface *intf);
/* file operation pointers */
static const struct file_operations idmouse_fops = {
@@ -117,7 +119,11 @@ static struct usb_driver idmouse_driver = {
.name = DRIVER_SHORT,
.probe = idmouse_probe,
.disconnect = idmouse_disconnect,
+ .suspend = idmouse_suspend,
+ .resume = idmouse_resume,
+ .reset_resume = idmouse_resume,
.id_table = idmouse_table,
+ .supports_autosuspend = 1,
};
static int idmouse_create_image(struct usb_idmouse *dev)
@@ -197,6 +203,17 @@ reset:
return result;
}
+/* PM operations are nops as this driver does IO only during open() */
+static int idmouse_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ return 0;
+}
+
+static int idmouse_resume(struct usb_interface *intf)
+{
+ return 0;
+}
+
static inline void idmouse_delete(struct usb_idmouse *dev)
{
kfree(dev->bulk_in_buffer);
@@ -235,9 +252,13 @@ static int idmouse_open(struct inode *inode, struct file *file)
} else {
/* create a new image and check for success */
+ result = usb_autopm_get_interface(interface);
+ if (result)
+ goto error;
result = idmouse_create_image (dev);
if (result)
goto error;
+ usb_autopm_put_interface(interface);
/* increment our usage count for the driver */
++dev->open;
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index ad4fb15b5dcb..90f130126c10 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -412,6 +412,9 @@ static unsigned int ld_usb_poll(struct file *file, poll_table *wait)
dev = file->private_data;
+ if (!dev->intf)
+ return POLLERR | POLLHUP;
+
poll_wait(file, &dev->read_wait, wait);
poll_wait(file, &dev->write_wait, wait);
@@ -767,6 +770,9 @@ static void ld_usb_disconnect(struct usb_interface *intf)
ld_usb_delete(dev);
} else {
dev->intf = NULL;
+ /* wake up pollers */
+ wake_up_interruptible_all(&dev->read_wait);
+ wake_up_interruptible_all(&dev->write_wait);
mutex_unlock(&dev->mutex);
}
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index c1e2433f640d..046b8633864c 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -552,6 +552,9 @@ static unsigned int tower_poll (struct file *file, poll_table *wait)
dev = file->private_data;
+ if (!dev->udev)
+ return POLLERR | POLLHUP;
+
poll_wait(file, &dev->read_wait, wait);
poll_wait(file, &dev->write_wait, wait);
@@ -1025,6 +1028,9 @@ static void tower_disconnect (struct usb_interface *interface)
tower_delete (dev);
} else {
dev->udev = NULL;
+ /* wake up pollers */
+ wake_up_interruptible_all(&dev->read_wait);
+ wake_up_interruptible_all(&dev->write_wait);
mutex_unlock(&dev->lock);
}
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index b4ec716de7da..0025847743f3 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -79,14 +79,12 @@ sisusb_free_buffers(struct sisusb_usb_data *sisusb)
for (i = 0; i < NUMOBUFS; i++) {
if (sisusb->obuf[i]) {
- usb_buffer_free(sisusb->sisusb_dev, sisusb->obufsize,
- sisusb->obuf[i], sisusb->transfer_dma_out[i]);
+ kfree(sisusb->obuf[i]);
sisusb->obuf[i] = NULL;
}
}
if (sisusb->ibuf) {
- usb_buffer_free(sisusb->sisusb_dev, sisusb->ibufsize,
- sisusb->ibuf, sisusb->transfer_dma_in);
+ kfree(sisusb->ibuf);
sisusb->ibuf = NULL;
}
}
@@ -230,8 +228,7 @@ sisusb_bulk_completeout(struct urb *urb)
static int
sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data,
- int len, int *actual_length, int timeout, unsigned int tflags,
- dma_addr_t transfer_dma)
+ int len, int *actual_length, int timeout, unsigned int tflags)
{
struct urb *urb = sisusb->sisurbout[index];
int retval, byteswritten = 0;
@@ -245,9 +242,6 @@ sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe,
urb->transfer_flags |= tflags;
urb->actual_length = 0;
- if ((urb->transfer_dma = transfer_dma))
- urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
/* Set up context */
sisusb->urbout_context[index].actual_length = (timeout) ?
NULL : actual_length;
@@ -297,8 +291,8 @@ sisusb_bulk_completein(struct urb *urb)
}
static int
-sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, int len,
- int *actual_length, int timeout, unsigned int tflags, dma_addr_t transfer_dma)
+sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data,
+ int len, int *actual_length, int timeout, unsigned int tflags)
{
struct urb *urb = sisusb->sisurbin;
int retval, readbytes = 0;
@@ -311,9 +305,6 @@ sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data,
urb->transfer_flags |= tflags;
urb->actual_length = 0;
- if ((urb->transfer_dma = transfer_dma))
- urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
sisusb->completein = 0;
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval == 0) {
@@ -422,8 +413,7 @@ static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
thispass,
&transferred_len,
async ? 0 : 5 * HZ,
- tflags,
- sisusb->transfer_dma_out[index]);
+ tflags);
if (result == -ETIMEDOUT) {
@@ -432,29 +422,16 @@ static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
return -ETIME;
continue;
+ }
- } else if ((result == 0) && !async && transferred_len) {
+ if ((result == 0) && !async && transferred_len) {
thispass -= transferred_len;
- if (thispass) {
- if (sisusb->transfer_dma_out) {
- /* If DMA, copy remaining
- * to beginning of buffer
- */
- memcpy(buffer,
- buffer + transferred_len,
- thispass);
- } else {
- /* If not DMA, simply increase
- * the pointer
- */
- buffer += transferred_len;
- }
- }
+ buffer += transferred_len;
} else
break;
- };
+ }
if (result)
return result;
@@ -530,8 +507,7 @@ static int sisusb_recv_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len,
thispass,
&transferred_len,
5 * HZ,
- tflags,
- sisusb->transfer_dma_in);
+ tflags);
if (transferred_len)
thispass = transferred_len;
@@ -3132,8 +3108,7 @@ static int sisusb_probe(struct usb_interface *intf,
/* Allocate buffers */
sisusb->ibufsize = SISUSB_IBUF_SIZE;
- if (!(sisusb->ibuf = usb_buffer_alloc(dev, SISUSB_IBUF_SIZE,
- GFP_KERNEL, &sisusb->transfer_dma_in))) {
+ if (!(sisusb->ibuf = kmalloc(SISUSB_IBUF_SIZE, GFP_KERNEL))) {
dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for input buffer");
retval = -ENOMEM;
goto error_2;
@@ -3142,9 +3117,7 @@ static int sisusb_probe(struct usb_interface *intf,
sisusb->numobufs = 0;
sisusb->obufsize = SISUSB_OBUF_SIZE;
for (i = 0; i < NUMOBUFS; i++) {
- if (!(sisusb->obuf[i] = usb_buffer_alloc(dev, SISUSB_OBUF_SIZE,
- GFP_KERNEL,
- &sisusb->transfer_dma_out[i]))) {
+ if (!(sisusb->obuf[i] = kmalloc(SISUSB_OBUF_SIZE, GFP_KERNEL))) {
if (i == 0) {
dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate memory for output buffer\n");
retval = -ENOMEM;
diff --git a/drivers/usb/misc/sisusbvga/sisusb.h b/drivers/usb/misc/sisusbvga/sisusb.h
index cf0b4a5883f6..55492a5930bd 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.h
+++ b/drivers/usb/misc/sisusbvga/sisusb.h
@@ -123,8 +123,6 @@ struct sisusb_usb_data {
int numobufs; /* number of obufs = number of out urbs */
char *obuf[NUMOBUFS], *ibuf; /* transfer buffers */
int obufsize, ibufsize;
- dma_addr_t transfer_dma_out[NUMOBUFS];
- dma_addr_t transfer_dma_in;
struct urb *sisurbout[NUMOBUFS];
struct urb *sisurbin;
unsigned char urbstatus[NUMOBUFS];
diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c
index 28a6a3a09538..3db255537e79 100644
--- a/drivers/usb/misc/usbsevseg.c
+++ b/drivers/usb/misc/usbsevseg.c
@@ -38,6 +38,7 @@ static char *display_textmodes[] = {"raw", "hex", "ascii", NULL};
struct usb_sevsegdev {
struct usb_device *udev;
+ struct usb_interface *intf;
u8 powered;
u8 mode_msb;
@@ -46,6 +47,8 @@ struct usb_sevsegdev {
u8 textmode;
u8 text[MAXLEN];
u16 textlength;
+
+ u8 shadow_power; /* for PM */
};
/* sysfs_streq can't replace this completely
@@ -65,6 +68,12 @@ static void update_display_powered(struct usb_sevsegdev *mydev)
{
int rc;
+ if (!mydev->shadow_power && mydev->powered) {
+ rc = usb_autopm_get_interface(mydev->intf);
+ if (rc < 0)
+ return;
+ }
+
rc = usb_control_msg(mydev->udev,
usb_sndctrlpipe(mydev->udev, 0),
0x12,
@@ -76,12 +85,18 @@ static void update_display_powered(struct usb_sevsegdev *mydev)
2000);
if (rc < 0)
dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc);
+
+ if (mydev->shadow_power && !mydev->powered)
+ usb_autopm_put_interface(mydev->intf);
}
static void update_display_mode(struct usb_sevsegdev *mydev)
{
int rc;
+ if(mydev->shadow_power != 1)
+ return;
+
rc = usb_control_msg(mydev->udev,
usb_sndctrlpipe(mydev->udev, 0),
0x12,
@@ -96,14 +111,17 @@ static void update_display_mode(struct usb_sevsegdev *mydev)
dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc);
}
-static void update_display_visual(struct usb_sevsegdev *mydev)
+static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf)
{
int rc;
int i;
unsigned char *buffer;
u8 decimals = 0;
- buffer = kzalloc(MAXLEN, GFP_KERNEL);
+ if(mydev->shadow_power != 1)
+ return;
+
+ buffer = kzalloc(MAXLEN, mf);
if (!buffer) {
dev_err(&mydev->udev->dev, "out of memory\n");
return;
@@ -163,7 +181,7 @@ static ssize_t set_attr_##name(struct device *dev, \
struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \
\
mydev->name = simple_strtoul(buf, NULL, 10); \
- update_fcn(mydev); \
+ update_fcn(mydev); \
\
return count; \
} \
@@ -194,7 +212,7 @@ static ssize_t set_attr_text(struct device *dev,
if (end > 0)
memcpy(mydev->text, buf, end);
- update_display_visual(mydev);
+ update_display_visual(mydev, GFP_KERNEL);
return count;
}
@@ -242,7 +260,7 @@ static ssize_t set_attr_decimals(struct device *dev,
if (buf[i] == '1')
mydev->decimals[end-1-i] = 1;
- update_display_visual(mydev);
+ update_display_visual(mydev, GFP_KERNEL);
return count;
}
@@ -286,7 +304,7 @@ static ssize_t set_attr_textmode(struct device *dev,
for (i = 0; display_textmodes[i]; i++) {
if (sysfs_streq(display_textmodes[i], buf)) {
mydev->textmode = i;
- update_display_visual(mydev);
+ update_display_visual(mydev, GFP_KERNEL);
return count;
}
}
@@ -330,6 +348,7 @@ static int sevseg_probe(struct usb_interface *interface,
}
mydev->udev = usb_get_dev(udev);
+ mydev->intf = interface;
usb_set_intfdata(interface, mydev);
/*set defaults */
@@ -364,11 +383,49 @@ static void sevseg_disconnect(struct usb_interface *interface)
dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
}
+static int sevseg_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct usb_sevsegdev *mydev;
+
+ mydev = usb_get_intfdata(intf);
+ mydev->shadow_power = 0;
+
+ return 0;
+}
+
+static int sevseg_resume(struct usb_interface *intf)
+{
+ struct usb_sevsegdev *mydev;
+
+ mydev = usb_get_intfdata(intf);
+ mydev->shadow_power = 1;
+ update_display_mode(mydev);
+ update_display_visual(mydev, GFP_NOIO);
+
+ return 0;
+}
+
+static int sevseg_reset_resume(struct usb_interface *intf)
+{
+ struct usb_sevsegdev *mydev;
+
+ mydev = usb_get_intfdata(intf);
+ mydev->shadow_power = 1;
+ update_display_mode(mydev);
+ update_display_visual(mydev, GFP_NOIO);
+
+ return 0;
+}
+
static struct usb_driver sevseg_driver = {
.name = "usbsevseg",
.probe = sevseg_probe,
.disconnect = sevseg_disconnect,
+ .suspend = sevseg_suspend,
+ .resume = sevseg_resume,
+ .reset_resume = sevseg_reset_resume,
.id_table = id_table,
+ .supports_autosuspend = 1,
};
static int __init usb_sevseg_init(void)
diff --git a/drivers/usb/mon/Kconfig b/drivers/usb/mon/Kconfig
index f28f350cd96a..635745f57fbd 100644
--- a/drivers/usb/mon/Kconfig
+++ b/drivers/usb/mon/Kconfig
@@ -5,11 +5,9 @@
config USB_MON
tristate "USB Monitor"
depends on USB
- default y if USB=y
- default m if USB=m
help
If you select this option, a component which captures the USB traffic
between peripheral-specific drivers and HC drivers will be built.
For more information, see <file:Documentation/usb/usbmon.txt>.
- If unsure, say Y (if allowed), otherwise M.
+ If unsure, say Y, if allowed, otherwise M.
diff --git a/drivers/usb/mon/Makefile b/drivers/usb/mon/Makefile
index c6516b566731..384b198faa7c 100644
--- a/drivers/usb/mon/Makefile
+++ b/drivers/usb/mon/Makefile
@@ -2,6 +2,6 @@
# Makefile for USB monitor
#
-usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_bin.o mon_dma.o
+usbmon-objs := mon_main.o mon_stat.o mon_text.o mon_bin.o
obj-$(CONFIG_USB_MON) += usbmon.o
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 0f7a30b7d2d1..dfdc43e2e00d 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -220,9 +220,8 @@ static void mon_free_buff(struct mon_pgmap *map, int npages);
/*
* This is a "chunked memcpy". It does not manipulate any counters.
- * But it returns the new offset for repeated application.
*/
-unsigned int mon_copy_to_buff(const struct mon_reader_bin *this,
+static void mon_copy_to_buff(const struct mon_reader_bin *this,
unsigned int off, const unsigned char *from, unsigned int length)
{
unsigned int step_len;
@@ -247,7 +246,6 @@ unsigned int mon_copy_to_buff(const struct mon_reader_bin *this,
from += step_len;
length -= step_len;
}
- return off;
}
/*
@@ -400,15 +398,8 @@ static char mon_bin_get_data(const struct mon_reader_bin *rp,
unsigned int offset, struct urb *urb, unsigned int length)
{
- if (urb->dev->bus->uses_dma &&
- (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
- mon_dmapeek_vec(rp, offset, urb->transfer_dma, length);
- return 0;
- }
-
if (urb->transfer_buffer == NULL)
return 'Z';
-
mon_copy_to_buff(rp, offset, urb->transfer_buffer, length);
return 0;
}
@@ -635,7 +626,6 @@ static int mon_bin_open(struct inode *inode, struct file *file)
spin_lock_init(&rp->b_lock);
init_waitqueue_head(&rp->b_wait);
mutex_init(&rp->fetch_lock);
-
rp->b_size = BUFF_DFL;
size = sizeof(struct mon_pgmap) * (rp->b_size/CHUNK_SIZE);
diff --git a/drivers/usb/mon/mon_dma.c b/drivers/usb/mon/mon_dma.c
deleted file mode 100644
index 140cc80bd2b1..000000000000
--- a/drivers/usb/mon/mon_dma.c
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * The USB Monitor, inspired by Dave Harding's USBMon.
- *
- * mon_dma.c: Library which snoops on DMA areas.
- *
- * Copyright (C) 2005 Pete Zaitcev (zaitcev@redhat.com)
- */
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/highmem.h>
-#include <asm/page.h>
-
-#include <linux/usb.h> /* Only needed for declarations in usb_mon.h */
-#include "usb_mon.h"
-
-/*
- * PC-compatibles, are, fortunately, sufficiently cache-coherent for this.
- */
-#if defined(__i386__) || defined(__x86_64__) /* CONFIG_ARCH_I386 doesn't exit */
-#define MON_HAS_UNMAP 1
-
-#define phys_to_page(phys) pfn_to_page((phys) >> PAGE_SHIFT)
-
-char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
-{
- struct page *pg;
- unsigned long flags;
- unsigned char *map;
- unsigned char *ptr;
-
- /*
- * On i386, a DMA handle is the "physical" address of a page.
- * In other words, the bus address is equal to physical address.
- * There is no IOMMU.
- */
- pg = phys_to_page(dma_addr);
-
- /*
- * We are called from hardware IRQs in case of callbacks.
- * But we can be called from softirq or process context in case
- * of submissions. In such case, we need to protect KM_IRQ0.
- */
- local_irq_save(flags);
- map = kmap_atomic(pg, KM_IRQ0);
- ptr = map + (dma_addr & (PAGE_SIZE-1));
- memcpy(dst, ptr, len);
- kunmap_atomic(map, KM_IRQ0);
- local_irq_restore(flags);
- return 0;
-}
-
-void mon_dmapeek_vec(const struct mon_reader_bin *rp,
- unsigned int offset, dma_addr_t dma_addr, unsigned int length)
-{
- unsigned long flags;
- unsigned int step_len;
- struct page *pg;
- unsigned char *map;
- unsigned long page_off, page_len;
-
- local_irq_save(flags);
- while (length) {
- /* compute number of bytes we are going to copy in this page */
- step_len = length;
- page_off = dma_addr & (PAGE_SIZE-1);
- page_len = PAGE_SIZE - page_off;
- if (page_len < step_len)
- step_len = page_len;
-
- /* copy data and advance pointers */
- pg = phys_to_page(dma_addr);
- map = kmap_atomic(pg, KM_IRQ0);
- offset = mon_copy_to_buff(rp, offset, map + page_off, step_len);
- kunmap_atomic(map, KM_IRQ0);
- dma_addr += step_len;
- length -= step_len;
- }
- local_irq_restore(flags);
-}
-
-#endif /* __i386__ */
-
-#ifndef MON_HAS_UNMAP
-char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len)
-{
- return 'D';
-}
-
-void mon_dmapeek_vec(const struct mon_reader_bin *rp,
- unsigned int offset, dma_addr_t dma_addr, unsigned int length)
-{
- ;
-}
-
-#endif /* MON_HAS_UNMAP */
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index 5e0ab4201c00..e0c2db3b767b 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -361,7 +361,6 @@ static int __init mon_init(void)
}
// MOD_INC_USE_COUNT(which_module?);
-
mutex_lock(&usb_bus_list_lock);
list_for_each_entry (ubus, &usb_bus_list, bus_list) {
mon_bus_init(ubus);
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index a7eb4c99342c..9f1a9227ebe6 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -150,20 +150,6 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
return '>';
}
- /*
- * The check to see if it's safe to poke at data has an enormous
- * number of corner cases, but it seems that the following is
- * more or less safe.
- *
- * We do not even try to look at transfer_buffer, because it can
- * contain non-NULL garbage in case the upper level promised to
- * set DMA for the HCD.
- */
- if (urb->dev->bus->uses_dma &&
- (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) {
- return mon_dmapeek(ep->data, urb->transfer_dma, len);
- }
-
if (urb->transfer_buffer == NULL)
return 'Z'; /* '0' would be not as pretty. */
diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h
index f5d84ff8c101..df9a4df342c7 100644
--- a/drivers/usb/mon/usb_mon.h
+++ b/drivers/usb/mon/usb_mon.h
@@ -65,20 +65,6 @@ int __init mon_bin_init(void);
void mon_bin_exit(void);
/*
- * DMA interface.
- *
- * XXX The vectored side needs a serious re-thinking. Abstracting vectors,
- * like in Paolo's original patch, produces a double pkmap. We need an idea.
-*/
-extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len);
-
-struct mon_reader_bin;
-extern void mon_dmapeek_vec(const struct mon_reader_bin *rp,
- unsigned int offset, dma_addr_t dma_addr, unsigned int len);
-extern unsigned int mon_copy_to_buff(const struct mon_reader_bin *rp,
- unsigned int offset, const unsigned char *from, unsigned int len);
-
-/*
*/
extern struct mutex mon_lock;
diff --git a/drivers/usb/serial/moto_modem.c b/drivers/usb/serial/moto_modem.c
index b66b71ccd12b..99bd00f5188a 100644
--- a/drivers/usb/serial/moto_modem.c
+++ b/drivers/usb/serial/moto_modem.c
@@ -8,7 +8,7 @@
* published by the Free Software Foundation.
*
* {sigh}
- * Mororola should be using the CDC ACM USB spec, but instead
+ * Motorola should be using the CDC ACM USB spec, but instead
* they try to just "do their own thing"... This driver should handle a
* few phones in which a basic "dumb serial connection" is needed to be
* able to get a connection through to them.
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index fe47051dbef2..0ce86e665edd 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -592,6 +592,7 @@ static struct usb_driver option_driver = {
#ifdef CONFIG_PM
.suspend = usb_serial_suspend,
.resume = usb_serial_resume,
+ .supports_autosuspend = 1,
#endif
.id_table = option_ids,
.no_dynamic_id = 1,
@@ -639,6 +640,12 @@ static int debug;
#define IN_BUFLEN 4096
#define OUT_BUFLEN 4096
+struct option_intf_private {
+ spinlock_t susp_lock;
+ unsigned int suspended:1;
+ int in_flight;
+};
+
struct option_port_private {
/* Input endpoints and buffer for this port */
struct urb *in_urbs[N_IN_URB];
@@ -647,6 +654,8 @@ struct option_port_private {
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) */
@@ -693,12 +702,17 @@ module_exit(option_exit);
static int option_probe(struct usb_serial *serial,
const struct usb_device_id *id)
{
+ struct option_intf_private *data;
/* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */
if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID &&
serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 &&
serial->interface->cur_altsetting->desc.bInterfaceClass == 0x8)
return -ENODEV;
+ data = serial->private = kzalloc(sizeof(struct option_intf_private), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ spin_lock_init(&data->susp_lock);
return 0;
}
@@ -755,12 +769,15 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
{
struct option_port_private *portdata;
+ struct option_intf_private *intfdata;
int i;
int left, todo;
struct urb *this_urb = NULL; /* spurious */
int err;
+ unsigned long flags;
portdata = usb_get_serial_port_data(port);
+ intfdata = port->serial->private;
dbg("%s: write (%d chars)", __func__, count);
@@ -782,17 +799,33 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
dbg("%s: endpoint %d buf %d", __func__,
usb_pipeendpoint(this_urb->pipe), i);
+ err = usb_autopm_get_interface_async(port->serial->interface);
+ if (err < 0)
+ break;
+
/* send the data */
memcpy(this_urb->transfer_buffer, buf, todo);
this_urb->transfer_buffer_length = todo;
- err = usb_submit_urb(this_urb, GFP_ATOMIC);
- if (err) {
- dbg("usb_submit_urb %p (write bulk) failed "
- "(%d)", this_urb, err);
- clear_bit(i, &portdata->out_busy);
- continue;
+ spin_lock_irqsave(&intfdata->susp_lock, flags);
+ if (intfdata->suspended) {
+ usb_anchor_urb(this_urb, &portdata->delayed);
+ spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+ } else {
+ intfdata->in_flight++;
+ spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+ err = usb_submit_urb(this_urb, GFP_ATOMIC);
+ if (err) {
+ dbg("usb_submit_urb %p (write bulk) failed "
+ "(%d)", this_urb, err);
+ clear_bit(i, &portdata->out_busy);
+ spin_lock_irqsave(&intfdata->susp_lock, flags);
+ intfdata->in_flight--;
+ spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+ continue;
+ }
}
+
portdata->tx_start_time[i] = jiffies;
buf += todo;
left -= todo;
@@ -836,7 +869,10 @@ static void option_indat_callback(struct urb *urb)
if (err)
printk(KERN_ERR "%s: resubmit read urb failed. "
"(%d)", __func__, err);
+ else
+ usb_mark_last_busy(port->serial->dev);
}
+
}
return;
}
@@ -845,15 +881,21 @@ static void option_outdat_callback(struct urb *urb)
{
struct usb_serial_port *port;
struct option_port_private *portdata;
+ struct option_intf_private *intfdata;
int i;
dbg("%s", __func__);
port = urb->context;
+ intfdata = port->serial->private;
usb_serial_port_softint(port);
-
+ usb_autopm_put_interface_async(port->serial->interface);
portdata = usb_get_serial_port_data(port);
+ spin_lock(&intfdata->susp_lock);
+ intfdata->in_flight--;
+ spin_unlock(&intfdata->susp_lock);
+
for (i = 0; i < N_OUT_URB; ++i) {
if (portdata->out_urbs[i] == urb) {
smp_mb__before_clear_bit();
@@ -963,10 +1005,13 @@ static int option_chars_in_buffer(struct tty_struct *tty)
static int option_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct option_port_private *portdata;
+ struct option_intf_private *intfdata;
+ struct usb_serial *serial = port->serial;
int i, err;
struct urb *urb;
portdata = usb_get_serial_port_data(port);
+ intfdata = serial->private;
dbg("%s", __func__);
@@ -985,6 +1030,12 @@ static int option_open(struct tty_struct *tty, struct usb_serial_port *port)
option_send_setup(port);
+ serial->interface->needs_remote_wakeup = 1;
+ spin_lock_irq(&intfdata->susp_lock);
+ portdata->opened = 1;
+ spin_unlock_irq(&intfdata->susp_lock);
+ usb_autopm_put_interface(serial->interface);
+
return 0;
}
@@ -1009,16 +1060,23 @@ static void option_close(struct usb_serial_port *port)
int i;
struct usb_serial *serial = port->serial;
struct option_port_private *portdata;
+ struct option_intf_private *intfdata = port->serial->private;
dbg("%s", __func__);
portdata = usb_get_serial_port_data(port);
if (serial->dev) {
/* Stop reading/writing urbs */
+ spin_lock_irq(&intfdata->susp_lock);
+ portdata->opened = 0;
+ spin_unlock_irq(&intfdata->susp_lock);
+
for (i = 0; i < N_IN_URB; i++)
usb_kill_urb(portdata->in_urbs[i]);
for (i = 0; i < N_OUT_URB; i++)
usb_kill_urb(portdata->out_urbs[i]);
+ usb_autopm_get_interface(serial->interface);
+ serial->interface->needs_remote_wakeup = 0;
}
}
@@ -1123,6 +1181,7 @@ static int option_startup(struct usb_serial *serial)
__func__, i);
return 1;
}
+ init_usb_anchor(&portdata->delayed);
for (j = 0; j < N_IN_URB; j++) {
buffer = (u8 *)__get_free_page(GFP_KERNEL);
@@ -1225,18 +1284,52 @@ static void option_release(struct usb_serial *serial)
#ifdef CONFIG_PM
static int option_suspend(struct usb_serial *serial, pm_message_t message)
{
+ struct option_intf_private *intfdata = serial->private;
+ int b;
+
dbg("%s entered", __func__);
+
+ if (serial->dev->auto_pm) {
+ spin_lock_irq(&intfdata->susp_lock);
+ b = intfdata->in_flight;
+ spin_unlock_irq(&intfdata->susp_lock);
+
+ if (b)
+ return -EBUSY;
+ }
+
+ spin_lock_irq(&intfdata->susp_lock);
+ intfdata->suspended = 1;
+ spin_unlock_irq(&intfdata->susp_lock);
stop_read_write_urbs(serial);
return 0;
}
+static void play_delayed(struct usb_serial_port *port)
+{
+ struct option_intf_private *data;
+ struct option_port_private *portdata;
+ struct urb *urb;
+ int err;
+
+ portdata = usb_get_serial_port_data(port);
+ data = port->serial->private;
+ while ((urb = usb_get_from_anchor(&portdata->delayed))) {
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (!err)
+ data->in_flight++;
+ }
+}
+
static int option_resume(struct usb_serial *serial)
{
- int err, i, j;
+ int i, j;
struct usb_serial_port *port;
- struct urb *urb;
+ struct option_intf_private *intfdata = serial->private;
struct option_port_private *portdata;
+ struct urb *urb;
+ int err = 0;
dbg("%s entered", __func__);
/* get the interrupt URBs resubmitted unconditionally */
@@ -1251,7 +1344,7 @@ static int option_resume(struct usb_serial *serial)
if (err < 0) {
err("%s: Error %d for interrupt URB of port%d",
__func__, err, i);
- return err;
+ goto err_out;
}
}
@@ -1259,27 +1352,32 @@ static int option_resume(struct usb_serial *serial)
/* walk all ports */
port = serial->port[i];
portdata = usb_get_serial_port_data(port);
- mutex_lock(&port->mutex);
/* skip closed ports */
- if (!port->port.count) {
- mutex_unlock(&port->mutex);
+ spin_lock_irq(&intfdata->susp_lock);
+ if (!portdata->opened) {
+ spin_unlock_irq(&intfdata->susp_lock);
continue;
}
for (j = 0; j < N_IN_URB; j++) {
urb = portdata->in_urbs[j];
- err = usb_submit_urb(urb, GFP_NOIO);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
- mutex_unlock(&port->mutex);
err("%s: Error %d for bulk URB %d",
__func__, err, i);
- return err;
+ spin_unlock_irq(&intfdata->susp_lock);
+ goto err_out;
}
}
- mutex_unlock(&port->mutex);
+ play_delayed(port);
+ spin_unlock_irq(&intfdata->susp_lock);
}
- return 0;
+ spin_lock_irq(&intfdata->susp_lock);
+ intfdata->suspended = 0;
+ spin_unlock_irq(&intfdata->susp_lock);
+err_out:
+ return err;
}
#endif
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 80c1f4d8e910..192710352638 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -292,8 +292,6 @@ bailout_serial_put:
static void serial_do_down(struct usb_serial_port *port)
{
struct usb_serial_driver *drv = port->serial->type;
- struct usb_serial *serial;
- struct module *owner;
/* The console is magical, do not hang up the console hardware
or there will be tears */
@@ -301,12 +299,8 @@ static void serial_do_down(struct usb_serial_port *port)
return;
mutex_lock(&port->mutex);
- serial = port->serial;
- owner = serial->type->driver.owner;
-
if (drv->close)
drv->close(port);
-
mutex_unlock(&port->mutex);
}
@@ -1221,15 +1215,19 @@ int usb_serial_suspend(struct usb_interface *intf, pm_message_t message)
serial->suspending = 1;
+ if (serial->type->suspend) {
+ r = serial->type->suspend(serial, message);
+ if (r < 0)
+ goto err_out;
+ }
+
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
if (port)
kill_traffic(port);
}
- if (serial->type->suspend)
- r = serial->type->suspend(serial, message);
-
+err_out:
return r;
}
EXPORT_SYMBOL(usb_serial_suspend);
diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
index 2b6e565262c2..ded836b02d7b 100644
--- a/drivers/usb/storage/datafab.c
+++ b/drivers/usb/storage/datafab.c
@@ -334,7 +334,7 @@ static int datafab_determine_lun(struct us_data *us,
unsigned char *buf;
int count = 0, rc;
- if (!us || !info)
+ if (!info)
return USB_STOR_TRANSPORT_ERROR;
memcpy(command, scommand, 8);
@@ -399,7 +399,7 @@ static int datafab_id_device(struct us_data *us,
unsigned char *reply;
int rc;
- if (!us || !info)
+ if (!info)
return USB_STOR_TRANSPORT_ERROR;
if (info->lun == -1) {
diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
index 1c69420e3acf..6168596c5ac6 100644
--- a/drivers/usb/storage/jumpshot.c
+++ b/drivers/usb/storage/jumpshot.c
@@ -335,7 +335,7 @@ static int jumpshot_id_device(struct us_data *us,
unsigned char *reply;
int rc;
- if (!us || !info)
+ if (!info)
return USB_STOR_TRANSPORT_ERROR;
command[0] = 0xE0;
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 5c60e58a8bae..e3c46598bbae 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -195,7 +195,7 @@ struct usb_interface {
struct device dev; /* interface specific device info */
struct device *usb_dev;
- int pm_usage_cnt; /* usage counter for autosuspend */
+ atomic_t pm_usage_cnt; /* usage counter for autosuspend */
struct work_struct reset_ws; /* for resets in atomic context */
};
#define to_usb_interface(d) container_of(d, struct usb_interface, dev)
@@ -551,13 +551,13 @@ extern void usb_autopm_put_interface_async(struct usb_interface *intf);
static inline void usb_autopm_enable(struct usb_interface *intf)
{
- intf->pm_usage_cnt = 0;
+ atomic_set(&intf->pm_usage_cnt, 0);
usb_autopm_set_interface(intf);
}
static inline void usb_autopm_disable(struct usb_interface *intf)
{
- intf->pm_usage_cnt = 1;
+ atomic_set(&intf->pm_usage_cnt, 1);
usb_autopm_set_interface(intf);
}
@@ -1036,9 +1036,10 @@ typedef void (*usb_complete_t)(struct urb *);
* @transfer_flags: A variety of flags may be used to affect how URB
* submission, unlinking, or operation are handled. Different
* kinds of URB can use different flags.
- * @transfer_buffer: This identifies the buffer to (or from) which
- * the I/O request will be performed (unless URB_NO_TRANSFER_DMA_MAP
- * is set). This buffer must be suitable for DMA; allocate it with
+ * @transfer_buffer: This identifies the buffer to (or from) which the I/O
+ * request will be performed unless URB_NO_TRANSFER_DMA_MAP is set
+ * (however, do not leave garbage in transfer_buffer even then).
+ * This buffer must be suitable for DMA; allocate it with
* kmalloc() or equivalent. For transfers to "in" endpoints, contents
* of this buffer will be modified. This buffer is used for the data
* stage of control transfers.
@@ -1104,9 +1105,15 @@ typedef void (*usb_complete_t)(struct urb *);
* allocate a DMA buffer with usb_buffer_alloc() or call usb_buffer_map().
* When these transfer flags are provided, host controller drivers will
* attempt to use the dma addresses found in the transfer_dma and/or
- * setup_dma fields rather than determining a dma address themselves. (Note
- * that transfer_buffer and setup_packet must still be set because not all
- * host controllers use DMA, nor do virtual root hubs).
+ * setup_dma fields rather than determining a dma address themselves.
+ *
+ * Note that transfer_buffer must still be set if the controller
+ * does not support DMA (as indicated by bus.uses_dma) and when talking
+ * to root hub. If you have to trasfer between highmem zone and the device
+ * on such controller, create a bounce buffer or bail out with an error.
+ * If transfer_buffer cannot be set (is in highmem) and the controller is DMA
+ * capable, assign NULL to it, so that usbmon knows not to use the value.
+ * The setup_packet must always be set, so it cannot be located in highmem.
*
* Initialization:
*
diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h
index b5744bc218ab..eaf9dffe0a01 100644
--- a/include/linux/usb/audio.h
+++ b/include/linux/usb/audio.h
@@ -24,88 +24,78 @@
#define USB_SUBCLASS_AUDIOCONTROL 0x01
#define USB_SUBCLASS_AUDIOSTREAMING 0x02
#define USB_SUBCLASS_MIDISTREAMING 0x03
-#define USB_SUBCLASS_VENDOR_SPEC 0xff
-
-/* A.5 Audio Class-Specific AC interface Descriptor Subtypes*/
-#define HEADER 0x01
-#define INPUT_TERMINAL 0x02
-#define OUTPUT_TERMINAL 0x03
-#define MIXER_UNIT 0x04
-#define SELECTOR_UNIT 0x05
-#define FEATURE_UNIT 0x06
-#define PROCESSING_UNIT 0x07
-#define EXTENSION_UNIT 0x08
-
-#define AS_GENERAL 0x01
-#define FORMAT_TYPE 0x02
-#define FORMAT_SPECIFIC 0x03
-
-#define EP_GENERAL 0x01
-
-#define MS_GENERAL 0x01
-#define MIDI_IN_JACK 0x02
-#define MIDI_OUT_JACK 0x03
-
-/* endpoint attributes */
-#define EP_ATTR_MASK 0x0c
-#define EP_ATTR_ASYNC 0x04
-#define EP_ATTR_ADAPTIVE 0x08
-#define EP_ATTR_SYNC 0x0c
-
-/* cs endpoint attributes */
-#define EP_CS_ATTR_SAMPLE_RATE 0x01
-#define EP_CS_ATTR_PITCH_CONTROL 0x02
-#define EP_CS_ATTR_FILL_MAX 0x80
-
-/* Audio Class specific Request Codes */
-#define USB_AUDIO_SET_INTF 0x21
-#define USB_AUDIO_SET_ENDPOINT 0x22
-#define USB_AUDIO_GET_INTF 0xa1
-#define USB_AUDIO_GET_ENDPOINT 0xa2
-
-#define SET_ 0x00
-#define GET_ 0x80
-
-#define _CUR 0x1
-#define _MIN 0x2
-#define _MAX 0x3
-#define _RES 0x4
-#define _MEM 0x5
-
-#define SET_CUR (SET_ | _CUR)
-#define GET_CUR (GET_ | _CUR)
-#define SET_MIN (SET_ | _MIN)
-#define GET_MIN (GET_ | _MIN)
-#define SET_MAX (SET_ | _MAX)
-#define GET_MAX (GET_ | _MAX)
-#define SET_RES (SET_ | _RES)
-#define GET_RES (GET_ | _RES)
-#define SET_MEM (SET_ | _MEM)
-#define GET_MEM (GET_ | _MEM)
-
-#define GET_STAT 0xff
-
-#define USB_AC_TERMINAL_UNDEFINED 0x100
-#define USB_AC_TERMINAL_STREAMING 0x101
-#define USB_AC_TERMINAL_VENDOR_SPEC 0x1FF
+
+/* A.5 Audio Class-Specific AC Interface Descriptor Subtypes */
+#define UAC_HEADER 0x01
+#define UAC_INPUT_TERMINAL 0x02
+#define UAC_OUTPUT_TERMINAL 0x03
+#define UAC_MIXER_UNIT 0x04
+#define UAC_SELECTOR_UNIT 0x05
+#define UAC_FEATURE_UNIT 0x06
+#define UAC_PROCESSING_UNIT 0x07
+#define UAC_EXTENSION_UNIT 0x08
+
+/* A.6 Audio Class-Specific AS Interface Descriptor Subtypes */
+#define UAC_AS_GENERAL 0x01
+#define UAC_FORMAT_TYPE 0x02
+#define UAC_FORMAT_SPECIFIC 0x03
+
+/* A.8 Audio Class-Specific Endpoint Descriptor Subtypes */
+#define UAC_EP_GENERAL 0x01
+
+/* A.9 Audio Class-Specific Request Codes */
+#define UAC_SET_ 0x00
+#define UAC_GET_ 0x80
+
+#define UAC__CUR 0x1
+#define UAC__MIN 0x2
+#define UAC__MAX 0x3
+#define UAC__RES 0x4
+#define UAC__MEM 0x5
+
+#define UAC_SET_CUR (UAC_SET_ | UAC__CUR)
+#define UAC_GET_CUR (UAC_GET_ | UAC__CUR)
+#define UAC_SET_MIN (UAC_SET_ | UAC__MIN)
+#define UAC_GET_MIN (UAC_GET_ | UAC__MIN)
+#define UAC_SET_MAX (UAC_SET_ | UAC__MAX)
+#define UAC_GET_MAX (UAC_GET_ | UAC__MAX)
+#define UAC_SET_RES (UAC_SET_ | UAC__RES)
+#define UAC_GET_RES (UAC_GET_ | UAC__RES)
+#define UAC_SET_MEM (UAC_SET_ | UAC__MEM)
+#define UAC_GET_MEM (UAC_GET_ | UAC__MEM)
+
+#define UAC_GET_STAT 0xff
+
+/* MIDI - A.1 MS Class-Specific Interface Descriptor Subtypes */
+#define UAC_MS_HEADER 0x01
+#define UAC_MIDI_IN_JACK 0x02
+#define UAC_MIDI_OUT_JACK 0x03
+
+/* MIDI - A.1 MS Class-Specific Endpoint Descriptor Subtypes */
+#define UAC_MS_GENERAL 0x01
+
+/* Terminals - 2.1 USB Terminal Types */
+#define UAC_TERMINAL_UNDEFINED 0x100
+#define UAC_TERMINAL_STREAMING 0x101
+#define UAC_TERMINAL_VENDOR_SPEC 0x1FF
/* Terminal Control Selectors */
/* 4.3.2 Class-Specific AC Interface Descriptor */
-struct usb_ac_header_descriptor {
+struct uac_ac_header_descriptor {
__u8 bLength; /* 8 + n */
__u8 bDescriptorType; /* USB_DT_CS_INTERFACE */
- __u8 bDescriptorSubtype; /* USB_MS_HEADER */
+ __u8 bDescriptorSubtype; /* UAC_MS_HEADER */
__le16 bcdADC; /* 0x0100 */
__le16 wTotalLength; /* includes Unit and Terminal desc. */
__u8 bInCollection; /* n */
__u8 baInterfaceNr[]; /* [n] */
} __attribute__ ((packed));
-#define USB_DT_AC_HEADER_SIZE(n) (8 + (n))
+#define UAC_DT_AC_HEADER_SIZE(n) (8 + (n))
/* As above, but more useful for defining your own descriptors: */
-#define DECLARE_USB_AC_HEADER_DESCRIPTOR(n) \
-struct usb_ac_header_descriptor_##n { \
+#define DECLARE_UAC_AC_HEADER_DESCRIPTOR(n) \
+struct uac_ac_header_descriptor_##n { \
__u8 bLength; \
__u8 bDescriptorType; \
__u8 bDescriptorSubtype; \
@@ -116,7 +106,7 @@ struct usb_ac_header_descriptor_##n { \
} __attribute__ ((packed))
/* 4.3.2.1 Input Terminal Descriptor */
-struct usb_input_terminal_descriptor {
+struct uac_input_terminal_descriptor {
__u8 bLength; /* in bytes: 12 */
__u8 bDescriptorType; /* CS_INTERFACE descriptor type */
__u8 bDescriptorSubtype; /* INPUT_TERMINAL descriptor subtype */
@@ -129,18 +119,19 @@ struct usb_input_terminal_descriptor {
__u8 iTerminal;
} __attribute__ ((packed));
-#define USB_DT_AC_INPUT_TERMINAL_SIZE 12
+#define UAC_DT_INPUT_TERMINAL_SIZE 12
-#define USB_AC_INPUT_TERMINAL_UNDEFINED 0x200
-#define USB_AC_INPUT_TERMINAL_MICROPHONE 0x201
-#define USB_AC_INPUT_TERMINAL_DESKTOP_MICROPHONE 0x202
-#define USB_AC_INPUT_TERMINAL_PERSONAL_MICROPHONE 0x203
-#define USB_AC_INPUT_TERMINAL_OMNI_DIR_MICROPHONE 0x204
-#define USB_AC_INPUT_TERMINAL_MICROPHONE_ARRAY 0x205
-#define USB_AC_INPUT_TERMINAL_PROC_MICROPHONE_ARRAY 0x206
+/* Terminals - 2.2 Input Terminal Types */
+#define UAC_INPUT_TERMINAL_UNDEFINED 0x200
+#define UAC_INPUT_TERMINAL_MICROPHONE 0x201
+#define UAC_INPUT_TERMINAL_DESKTOP_MICROPHONE 0x202
+#define UAC_INPUT_TERMINAL_PERSONAL_MICROPHONE 0x203
+#define UAC_INPUT_TERMINAL_OMNI_DIR_MICROPHONE 0x204
+#define UAC_INPUT_TERMINAL_MICROPHONE_ARRAY 0x205
+#define UAC_INPUT_TERMINAL_PROC_MICROPHONE_ARRAY 0x206
/* 4.3.2.2 Output Terminal Descriptor */
-struct usb_output_terminal_descriptor {
+struct uac_output_terminal_descriptor {
__u8 bLength; /* in bytes: 9 */
__u8 bDescriptorType; /* CS_INTERFACE descriptor type */
__u8 bDescriptorSubtype; /* OUTPUT_TERMINAL descriptor subtype */
@@ -151,23 +142,24 @@ struct usb_output_terminal_descriptor {
__u8 iTerminal;
} __attribute__ ((packed));
-#define USB_DT_AC_OUTPUT_TERMINAL_SIZE 9
+#define UAC_DT_OUTPUT_TERMINAL_SIZE 9
-#define USB_AC_OUTPUT_TERMINAL_UNDEFINED 0x300
-#define USB_AC_OUTPUT_TERMINAL_SPEAKER 0x301
-#define USB_AC_OUTPUT_TERMINAL_HEADPHONES 0x302
-#define USB_AC_OUTPUT_TERMINAL_HEAD_MOUNTED_DISPLAY_AUDIO 0x303
-#define USB_AC_OUTPUT_TERMINAL_DESKTOP_SPEAKER 0x304
-#define USB_AC_OUTPUT_TERMINAL_ROOM_SPEAKER 0x305
-#define USB_AC_OUTPUT_TERMINAL_COMMUNICATION_SPEAKER 0x306
-#define USB_AC_OUTPUT_TERMINAL_LOW_FREQ_EFFECTS_SPEAKER 0x307
+/* Terminals - 2.3 Output Terminal Types */
+#define UAC_OUTPUT_TERMINAL_UNDEFINED 0x300
+#define UAC_OUTPUT_TERMINAL_SPEAKER 0x301
+#define UAC_OUTPUT_TERMINAL_HEADPHONES 0x302
+#define UAC_OUTPUT_TERMINAL_HEAD_MOUNTED_DISPLAY_AUDIO 0x303
+#define UAC_OUTPUT_TERMINAL_DESKTOP_SPEAKER 0x304
+#define UAC_OUTPUT_TERMINAL_ROOM_SPEAKER 0x305
+#define UAC_OUTPUT_TERMINAL_COMMUNICATION_SPEAKER 0x306
+#define UAC_OUTPUT_TERMINAL_LOW_FREQ_EFFECTS_SPEAKER 0x307
/* Set bControlSize = 2 as default setting */
-#define USB_DT_AC_FEATURE_UNIT_SIZE(ch) (7 + ((ch) + 1) * 2)
+#define UAC_DT_FEATURE_UNIT_SIZE(ch) (7 + ((ch) + 1) * 2)
/* As above, but more useful for defining your own descriptors: */
-#define DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(ch) \
-struct usb_ac_feature_unit_descriptor_##ch { \
+#define DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(ch) \
+struct uac_feature_unit_descriptor_##ch { \
__u8 bLength; \
__u8 bDescriptorType; \
__u8 bDescriptorSubtype; \
@@ -179,7 +171,7 @@ struct usb_ac_feature_unit_descriptor_##ch { \
} __attribute__ ((packed))
/* 4.5.2 Class-Specific AS Interface Descriptor */
-struct usb_as_header_descriptor {
+struct uac_as_header_descriptor {
__u8 bLength; /* in bytes: 7 */
__u8 bDescriptorType; /* USB_DT_CS_INTERFACE */
__u8 bDescriptorSubtype; /* AS_GENERAL */
@@ -188,16 +180,17 @@ struct usb_as_header_descriptor {
__le16 wFormatTag; /* The Audio Data Format */
} __attribute__ ((packed));
-#define USB_DT_AS_HEADER_SIZE 7
+#define UAC_DT_AS_HEADER_SIZE 7
-#define USB_AS_AUDIO_FORMAT_TYPE_I_UNDEFINED 0x0
-#define USB_AS_AUDIO_FORMAT_TYPE_I_PCM 0x1
-#define USB_AS_AUDIO_FORMAT_TYPE_I_PCM8 0x2
-#define USB_AS_AUDIO_FORMAT_TYPE_I_IEEE_FLOAT 0x3
-#define USB_AS_AUDIO_FORMAT_TYPE_I_ALAW 0x4
-#define USB_AS_AUDIO_FORMAT_TYPE_I_MULAW 0x5
+/* Formats - A.1.1 Audio Data Format Type I Codes */
+#define UAC_FORMAT_TYPE_I_UNDEFINED 0x0
+#define UAC_FORMAT_TYPE_I_PCM 0x1
+#define UAC_FORMAT_TYPE_I_PCM8 0x2
+#define UAC_FORMAT_TYPE_I_IEEE_FLOAT 0x3
+#define UAC_FORMAT_TYPE_I_ALAW 0x4
+#define UAC_FORMAT_TYPE_I_MULAW 0x5
-struct usb_as_format_type_i_continuous_descriptor {
+struct uac_format_type_i_continuous_descriptor {
__u8 bLength; /* in bytes: 8 + (ns * 3) */
__u8 bDescriptorType; /* USB_DT_CS_INTERFACE */
__u8 bDescriptorSubtype; /* FORMAT_TYPE */
@@ -210,9 +203,9 @@ struct usb_as_format_type_i_continuous_descriptor {
__u8 tUpperSamFreq[3];
} __attribute__ ((packed));
-#define USB_AS_FORMAT_TYPE_I_CONTINUOUS_DESC_SIZE 14
+#define UAC_FORMAT_TYPE_I_CONTINUOUS_DESC_SIZE 14
-struct usb_as_formate_type_i_discrete_descriptor {
+struct uac_format_type_i_discrete_descriptor {
__u8 bLength; /* in bytes: 8 + (ns * 3) */
__u8 bDescriptorType; /* USB_DT_CS_INTERFACE */
__u8 bDescriptorSubtype; /* FORMAT_TYPE */
@@ -224,8 +217,8 @@ struct usb_as_formate_type_i_discrete_descriptor {
__u8 tSamFreq[][3];
} __attribute__ ((packed));
-#define DECLARE_USB_AS_FORMAT_TYPE_I_DISCRETE_DESC(n) \
-struct usb_as_formate_type_i_discrete_descriptor_##n { \
+#define DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(n) \
+struct uac_format_type_i_discrete_descriptor_##n { \
__u8 bLength; \
__u8 bDescriptorType; \
__u8 bDescriptorSubtype; \
@@ -237,18 +230,15 @@ struct usb_as_formate_type_i_discrete_descriptor_##n { \
__u8 tSamFreq[n][3]; \
} __attribute__ ((packed))
-#define USB_AS_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n) (8 + (n * 3))
-
-#define USB_AS_FORMAT_TYPE_UNDEFINED 0x0
-#define USB_AS_FORMAT_TYPE_I 0x1
-#define USB_AS_FORMAT_TYPE_II 0x2
-#define USB_AS_FORMAT_TYPE_III 0x3
+#define UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n) (8 + (n * 3))
-#define USB_AS_ENDPOINT_ASYNC (1 << 2)
-#define USB_AS_ENDPOINT_ADAPTIVE (2 << 2)
-#define USB_AS_ENDPOINT_SYNC (3 << 2)
+/* Formats - A.2 Format Type Codes */
+#define UAC_FORMAT_TYPE_UNDEFINED 0x0
+#define UAC_FORMAT_TYPE_I 0x1
+#define UAC_FORMAT_TYPE_II 0x2
+#define UAC_FORMAT_TYPE_III 0x3
-struct usb_as_iso_endpoint_descriptor {
+struct uac_iso_endpoint_descriptor {
__u8 bLength; /* in bytes: 7 */
__u8 bDescriptorType; /* USB_DT_CS_ENDPOINT */
__u8 bDescriptorSubtype; /* EP_GENERAL */
@@ -256,30 +246,37 @@ struct usb_as_iso_endpoint_descriptor {
__u8 bLockDelayUnits;
__le16 wLockDelay;
};
-#define USB_AS_ISO_ENDPOINT_DESC_SIZE 7
-
-#define FU_CONTROL_UNDEFINED 0x00
-#define MUTE_CONTROL 0x01
-#define VOLUME_CONTROL 0x02
-#define BASS_CONTROL 0x03
-#define MID_CONTROL 0x04
-#define TREBLE_CONTROL 0x05
-#define GRAPHIC_EQUALIZER_CONTROL 0x06
-#define AUTOMATIC_GAIN_CONTROL 0x07
-#define DELAY_CONTROL 0x08
-#define BASS_BOOST_CONTROL 0x09
-#define LOUDNESS_CONTROL 0x0a
-
-#define FU_MUTE (1 << (MUTE_CONTROL - 1))
-#define FU_VOLUME (1 << (VOLUME_CONTROL - 1))
-#define FU_BASS (1 << (BASS_CONTROL - 1))
-#define FU_MID (1 << (MID_CONTROL - 1))
-#define FU_TREBLE (1 << (TREBLE_CONTROL - 1))
-#define FU_GRAPHIC_EQ (1 << (GRAPHIC_EQUALIZER_CONTROL - 1))
-#define FU_AUTO_GAIN (1 << (AUTOMATIC_GAIN_CONTROL - 1))
-#define FU_DELAY (1 << (DELAY_CONTROL - 1))
-#define FU_BASS_BOOST (1 << (BASS_BOOST_CONTROL - 1))
-#define FU_LOUDNESS (1 << (LOUDNESS_CONTROL - 1))
+#define UAC_ISO_ENDPOINT_DESC_SIZE 7
+
+#define UAC_EP_CS_ATTR_SAMPLE_RATE 0x01
+#define UAC_EP_CS_ATTR_PITCH_CONTROL 0x02
+#define UAC_EP_CS_ATTR_FILL_MAX 0x80
+
+/* A.10.2 Feature Unit Control Selectors */
+#define UAC_FU_CONTROL_UNDEFINED 0x00
+#define UAC_MUTE_CONTROL 0x01
+#define UAC_VOLUME_CONTROL 0x02
+#define UAC_BASS_CONTROL 0x03
+#define UAC_MID_CONTROL 0x04
+#define UAC_TREBLE_CONTROL 0x05
+#define UAC_GRAPHIC_EQUALIZER_CONTROL 0x06
+#define UAC_AUTOMATIC_GAIN_CONTROL 0x07
+#define UAC_DELAY_CONTROL 0x08
+#define UAC_BASS_BOOST_CONTROL 0x09
+#define UAC_LOUDNESS_CONTROL 0x0a
+
+#define UAC_FU_MUTE (1 << (UAC_MUTE_CONTROL - 1))
+#define UAC_FU_VOLUME (1 << (UAC_VOLUME_CONTROL - 1))
+#define UAC_FU_BASS (1 << (UAC_BASS_CONTROL - 1))
+#define UAC_FU_MID (1 << (UAC_MID_CONTROL - 1))
+#define UAC_FU_TREBLE (1 << (UAC_TREBLE_CONTROL - 1))
+#define UAC_FU_GRAPHIC_EQ (1 << (UAC_GRAPHIC_EQUALIZER_CONTROL - 1))
+#define UAC_FU_AUTO_GAIN (1 << (UAC_AUTOMATIC_GAIN_CONTROL - 1))
+#define UAC_FU_DELAY (1 << (UAC_DELAY_CONTROL - 1))
+#define UAC_FU_BASS_BOOST (1 << (UAC_BASS_BOOST_CONTROL - 1))
+#define UAC_FU_LOUDNESS (1 << (UAC_LOUDNESS_CONTROL - 1))
+
+#ifdef __KERNEL__
struct usb_audio_control {
struct list_head list;
@@ -290,18 +287,6 @@ struct usb_audio_control {
int (*get)(struct usb_audio_control *con, u8 cmd);
};
-static inline int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
-{
- con->data[cmd] = value;
-
- return 0;
-}
-
-static inline int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
-{
- return con->data[cmd];
-}
-
struct usb_audio_control_selector {
struct list_head list;
struct list_head control;
@@ -311,4 +296,6 @@ struct usb_audio_control_selector {
struct usb_descriptor_header *desc;
};
+#endif /* __KERNEL__ */
+
#endif /* __LINUX_USB_AUDIO_H */
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index 93223638f702..94012e649d86 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -258,6 +258,8 @@ struct usb_device_descriptor {
#define USB_CLASS_APP_SPEC 0xfe
#define USB_CLASS_VENDOR_SPEC 0xff
+#define USB_SUBCLASS_VENDOR_SPEC 0xff
+
/*-------------------------------------------------------------------------*/
/* USB_DT_CONFIG: Configuration descriptor information.
@@ -348,6 +350,12 @@ struct usb_endpoint_descriptor {
#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
#define USB_ENDPOINT_DIR_MASK 0x80
+#define USB_ENDPOINT_SYNCTYPE 0x0c
+#define USB_ENDPOINT_SYNC_NONE (0 << 2)
+#define USB_ENDPOINT_SYNC_ASYNC (1 << 2)
+#define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2)
+#define USB_ENDPOINT_SYNC_SYNC (3 << 2)
+
#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h
index 5b88e36c9103..4c4b701ff265 100644
--- a/include/linux/usb/ehci_def.h
+++ b/include/linux/usb/ehci_def.h
@@ -132,6 +132,19 @@ struct ehci_regs {
#define USBMODE_CM_HC (3<<0) /* host controller mode */
#define USBMODE_CM_IDLE (0<<0) /* idle state */
+/* Moorestown has some non-standard registers, partially due to the fact that
+ * its EHCI controller has both TT and LPM support. HOSTPCx are extentions to
+ * PORTSCx
+ */
+#define HOSTPC0 0x84 /* HOSTPC extension */
+#define HOSTPC_PHCD (1<<22) /* Phy clock disable */
+#define HOSTPC_PSPD (3<<25) /* Port speed detection */
+#define USBMODE_EX 0xc8 /* USB Device mode extension */
+#define USBMODE_EX_VBPS (1<<5) /* VBus Power Select On */
+#define USBMODE_EX_HC (3<<0) /* host controller mode */
+#define TXFILLTUNING 0x24 /* TX FIFO Tuning register */
+#define TXFIFO_DEFAULT (8<<16) /* FIFO burst threshold 8 */
+
/* Appendix C, Debug port ... intended for use with special "debug devices"
* that can help if there's no serial console. (nonstandard enumeration.)
*/
diff --git a/include/linux/usb/isp1362.h b/include/linux/usb/isp1362.h
new file mode 100644
index 000000000000..642684bb9292
--- /dev/null
+++ b/include/linux/usb/isp1362.h
@@ -0,0 +1,46 @@
+/*
+ * board initialization code should put one of these into dev->platform_data
+ * and place the isp1362 onto platform_bus.
+ */
+
+#ifndef __LINUX_USB_ISP1362_H__
+#define __LINUX_USB_ISP1362_H__
+
+struct isp1362_platform_data {
+ /* Enable internal pulldown resistors on downstream ports */
+ unsigned sel15Kres:1;
+ /* Clock cannot be stopped */
+ unsigned clknotstop:1;
+ /* On-chip overcurrent protection */
+ unsigned oc_enable:1;
+ /* INT output polarity */
+ unsigned int_act_high:1;
+ /* INT edge or level triggered */
+ unsigned int_edge_triggered:1;
+ /* DREQ output polarity */
+ unsigned dreq_act_high:1;
+ /* DACK input polarity */
+ unsigned dack_act_high:1;
+ /* chip can be resumed via H_WAKEUP pin */
+ unsigned remote_wakeup_connected:1;
+ /* Switch or not to switch (keep always powered) */
+ unsigned no_power_switching:1;
+ /* Ganged port power switching (0) or individual port power switching (1) */
+ unsigned power_switching_mode:1;
+ /* Given port_power, msec/2 after power on till power good */
+ u8 potpg;
+ /* Hardware reset set/clear */
+ void (*reset) (struct device *dev, int set);
+ /* Clock start/stop */
+ void (*clock) (struct device *dev, int start);
+ /* Inter-io delay (ns). The chip is picky about access timings; it
+ * expects at least:
+ * 110ns delay between consecutive accesses to DATA_REG,
+ * 300ns delay between access to ADDR_REG and DATA_REG (registers)
+ * 462ns delay between access to ADDR_REG and DATA_REG (buffer memory)
+ * WE MUST NOT be activated during these intervals (even without CS!)
+ */
+ void (*delay) (struct device *dev, unsigned int delay);
+};
+
+#endif
diff --git a/include/linux/usb/isp1760.h b/include/linux/usb/isp1760.h
new file mode 100644
index 000000000000..de7de53c5531
--- /dev/null
+++ b/include/linux/usb/isp1760.h
@@ -0,0 +1,18 @@
+/*
+ * board initialization should put one of these into dev->platform_data
+ * and place the isp1760 onto platform_bus named "isp1760-hcd".
+ */
+
+#ifndef __LINUX_USB_ISP1760_H
+#define __LINUX_USB_ISP1760_H
+
+struct isp1760_platform_data {
+ unsigned is_isp1761:1; /* Chip is ISP1761 */
+ unsigned bus_width_16:1; /* 16/32-bit data bus width */
+ unsigned port1_otg:1; /* Port 1 supports OTG */
+ unsigned analog_oc:1; /* Analog overcurrent */
+ unsigned dack_polarity_high:1; /* DACK active high */
+ unsigned dreq_polarity_high:1; /* DREQ active high */
+};
+
+#endif /* __LINUX_USB_ISP1760_H */
diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h
index 0044d9b4cb85..00ceebeb9e5c 100644
--- a/include/linux/usbdevice_fs.h
+++ b/include/linux/usbdevice_fs.h
@@ -175,4 +175,6 @@ struct usbdevfs_ioctl32 {
#define USBDEVFS_CLEAR_HALT _IOR('U', 21, unsigned int)
#define USBDEVFS_DISCONNECT _IO('U', 22)
#define USBDEVFS_CONNECT _IO('U', 23)
+#define USBDEVFS_CLAIM_PORT _IOR('U', 24, unsigned int)
+#define USBDEVFS_RELEASE_PORT _IOR('U', 25, unsigned int)
#endif /* _LINUX_USBDEVICE_FS_H */