diff options
author | Steve French <sfrench@us.ibm.com> | 2009-07-16 04:21:39 +0000 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2009-07-16 04:21:39 +0000 |
commit | f6c43385435640e056424034caac0d765c45e370 (patch) | |
tree | 54c2ba0a7d3abb156bc51f6b9f02e00f0a260f85 /drivers/usb | |
parent | 65bc98b0059360e458aebd208587be44641227c1 (diff) | |
parent | 35b5c55fee08e6e4001ba98060a2d0b82f70b5f4 (diff) |
Merge branch 'master' of /pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/usb')
58 files changed, 628 insertions, 2243 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 3f104599347..5b15d9d8896 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -387,6 +387,7 @@ static void acm_rx_tasklet(unsigned long _acm) struct acm_ru *rcv; unsigned long flags; unsigned char throttled; + struct usb_host_endpoint *ep; dbg("Entering acm_rx_tasklet"); @@ -462,11 +463,20 @@ urbs: rcv->buffer = buf; - usb_fill_bulk_urb(rcv->urb, acm->dev, - acm->rx_endpoint, - buf->base, - acm->readsize, - acm_read_bulk, rcv); + ep = (usb_pipein(acm->rx_endpoint) ? acm->dev->ep_in : acm->dev->ep_out) + [usb_pipeendpoint(acm->rx_endpoint)]; + if (usb_endpoint_xfer_int(&ep->desc)) + usb_fill_int_urb(rcv->urb, acm->dev, + acm->rx_endpoint, + buf->base, + acm->readsize, + acm_read_bulk, rcv, ep->desc.bInterval); + else + usb_fill_bulk_urb(rcv->urb, acm->dev, + acm->rx_endpoint, + buf->base, + acm->readsize, + acm_read_bulk, rcv); rcv->urb->transfer_dma = buf->dma; rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -1227,9 +1237,14 @@ made_compressed_probe: goto alloc_fail7; } - usb_fill_bulk_urb(snd->urb, usb_dev, - usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), - NULL, acm->writesize, acm_write_bulk, snd); + if (usb_endpoint_xfer_int(epwrite)) + usb_fill_int_urb(snd->urb, usb_dev, + usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), + NULL, acm->writesize, acm_write_bulk, snd, epwrite->bInterval); + else + usb_fill_bulk_urb(snd->urb, usb_dev, + usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress), + NULL, acm->writesize, acm_write_bulk, snd); snd->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; snd->instance = acm; } diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 0fe434505ac..ba589d4ca8b 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -15,7 +15,6 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/module.h> -#include <linux/smp_lock.h> #include <linux/mutex.h> #include <linux/uaccess.h> #include <linux/bitops.h> diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 3703789d0d2..b09a527f734 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -751,7 +751,7 @@ static int get_capabilities(struct usbtmc_device_data *data) { struct device *dev = &data->usb_dev->dev; char *buffer; - int rv; + int rv = 0; buffer = kmalloc(0x18, GFP_KERNEL); if (!buffer) @@ -763,7 +763,7 @@ static int get_capabilities(struct usbtmc_device_data *data) 0, 0, buffer, 0x18, USBTMC_TIMEOUT); if (rv < 0) { dev_err(dev, "usb_control_msg returned %d\n", rv); - return rv; + goto err_out; } dev_dbg(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); @@ -773,7 +773,8 @@ static int get_capabilities(struct usbtmc_device_data *data) dev_dbg(dev, "USB488 device capabilities are %x\n", buffer[15]); if (buffer[0] != USBTMC_STATUS_SUCCESS) { dev_err(dev, "GET_CAPABILITIES returned %x\n", buffer[0]); - return -EPERM; + rv = -EPERM; + goto err_out; } data->capabilities.interface_capabilities = buffer[4]; @@ -781,8 +782,9 @@ static int get_capabilities(struct usbtmc_device_data *data) data->capabilities.usb488_interface_capabilities = buffer[14]; data->capabilities.usb488_device_capabilities = buffer[15]; +err_out: kfree(buffer); - return 0; + return rv; } #define capability_attribute(name) \ diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 69280c35b5c..ad925946f86 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -28,7 +28,7 @@ comment "Miscellaneous USB options" depends on USB config USB_DEVICEFS - bool "USB device filesystem (DEPRECATED)" if EMBEDDED + bool "USB device filesystem (DEPRECATED)" depends on USB ---help--- If you say Y here (and to "/proc file system support" in the "File diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 73c108d117b..96f11715cd2 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -136,17 +136,19 @@ static const struct class_info clas_info[] = {USB_CLASS_AUDIO, "audio"}, {USB_CLASS_COMM, "comm."}, {USB_CLASS_HID, "HID"}, - {USB_CLASS_HUB, "hub"}, {USB_CLASS_PHYSICAL, "PID"}, + {USB_CLASS_STILL_IMAGE, "still"}, {USB_CLASS_PRINTER, "print"}, {USB_CLASS_MASS_STORAGE, "stor."}, + {USB_CLASS_HUB, "hub"}, {USB_CLASS_CDC_DATA, "data"}, - {USB_CLASS_APP_SPEC, "app."}, - {USB_CLASS_VENDOR_SPEC, "vend."}, - {USB_CLASS_STILL_IMAGE, "still"}, {USB_CLASS_CSCID, "scard"}, {USB_CLASS_CONTENT_SEC, "c-sec"}, {USB_CLASS_VIDEO, "video"}, + {USB_CLASS_WIRELESS_CONTROLLER, "wlcon"}, + {USB_CLASS_MISC, "misc"}, + {USB_CLASS_APP_SPEC, "app."}, + {USB_CLASS_VENDOR_SPEC, "vend."}, {-1, "unk."} /* leave as last */ }; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 308609039c7..38b8bce782d 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -325,21 +325,34 @@ static void async_completed(struct urb *urb) struct async *as = urb->context; struct dev_state *ps = as->ps; struct siginfo sinfo; + struct pid *pid = NULL; + uid_t uid = 0; + uid_t euid = 0; + u32 secid = 0; + int signr; spin_lock(&ps->lock); list_move_tail(&as->asynclist, &ps->async_completed); - spin_unlock(&ps->lock); as->status = urb->status; - if (as->signr) { + signr = as->signr; + if (signr) { sinfo.si_signo = as->signr; sinfo.si_errno = as->status; sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = as->userurb; - kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid, - as->euid, as->secid); + pid = as->pid; + uid = as->uid; + euid = as->euid; + secid = as->secid; } snoop(&urb->dev->dev, "urb complete\n"); snoop_urb(urb, as->userurb); + spin_unlock(&ps->lock); + + if (signr) + kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid, + euid, secid); + wake_up(&ps->wait); } @@ -982,7 +995,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, USBDEVFS_URB_ZERO_PACKET | USBDEVFS_URB_NO_INTERRUPT)) return -EINVAL; - if (!uurb->buffer) + if (uurb->buffer_length > 0 && !uurb->buffer) return -EINVAL; if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL && (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) { @@ -1038,11 +1051,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, is_in = 0; uurb->endpoint &= ~USB_DIR_IN; } - if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, - uurb->buffer, uurb->buffer_length)) { - kfree(dr); - return -EFAULT; - } snoop(&ps->dev->dev, "control urb: bRequest=%02x " "bRrequestType=%02x wValue=%04x " "wIndex=%04x wLength=%04x\n", @@ -1062,9 +1070,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; - if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, - uurb->buffer, uurb->buffer_length)) - return -EFAULT; snoop(&ps->dev->dev, "bulk urb\n"); break; @@ -1106,28 +1111,35 @@ 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; - if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, - uurb->buffer, uurb->buffer_length)) - return -EFAULT; snoop(&ps->dev->dev, "interrupt urb\n"); break; default: return -EINVAL; } - as = alloc_async(uurb->number_of_packets); - if (!as) { + if (uurb->buffer_length > 0 && + !access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, + uurb->buffer, uurb->buffer_length)) { kfree(isopkt); kfree(dr); - return -ENOMEM; + return -EFAULT; } - as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL); - if (!as->urb->transfer_buffer) { + as = alloc_async(uurb->number_of_packets); + if (!as) { kfree(isopkt); kfree(dr); - free_async(as); return -ENOMEM; } + if (uurb->buffer_length > 0) { + as->urb->transfer_buffer = kmalloc(uurb->buffer_length, + GFP_KERNEL); + if (!as->urb->transfer_buffer) { + kfree(isopkt); + kfree(dr); + free_async(as); + return -ENOMEM; + } + } as->urb->dev = ps->dev; as->urb->pipe = (uurb->type << 30) | __create_pipe(ps->dev, uurb->endpoint & 0xf) | @@ -1169,7 +1181,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, kfree(isopkt); as->ps = ps; as->userurb = arg; - if (uurb->endpoint & USB_DIR_IN) + if (is_in && uurb->buffer_length > 0) as->userbuffer = uurb->buffer; else as->userbuffer = NULL; @@ -1179,9 +1191,9 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->uid = cred->uid; as->euid = cred->euid; security_task_getsecid(current, &as->secid); - if (!is_in) { + if (!is_in && uurb->buffer_length > 0) { if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, - as->urb->transfer_buffer_length)) { + uurb->buffer_length)) { free_async(as); return -EFAULT; } @@ -1231,22 +1243,22 @@ static int processcompl(struct async *as, void __user * __user *arg) if (as->userbuffer) if (copy_to_user(as->userbuffer, urb->transfer_buffer, urb->transfer_buffer_length)) - return -EFAULT; + goto err_out; if (put_user(as->status, &userurb->status)) - return -EFAULT; + goto err_out; if (put_user(urb->actual_length, &userurb->actual_length)) - return -EFAULT; + goto err_out; if (put_user(urb->error_count, &userurb->error_count)) - return -EFAULT; + goto err_out; if (usb_endpoint_xfer_isoc(&urb->ep->desc)) { for (i = 0; i < urb->number_of_packets; i++) { if (put_user(urb->iso_frame_desc[i].actual_length, &userurb->iso_frame_desc[i].actual_length)) - return -EFAULT; + goto err_out; if (put_user(urb->iso_frame_desc[i].status, &userurb->iso_frame_desc[i].status)) - return -EFAULT; + goto err_out; } } @@ -1255,6 +1267,10 @@ static int processcompl(struct async *as, void __user * __user *arg) if (put_user(addr, (void __user * __user *)arg)) return -EFAULT; return 0; + +err_out: + free_async(as); + return -EFAULT; } static struct async *reap_as(struct dev_state *ps) diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index d397ecfd5b1..ec5c67ea07b 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -227,6 +227,10 @@ struct hc_driver { /* has a port been handed over to a companion? */ int (*port_handed_over)(struct usb_hcd *, int); + /* CLEAR_TT_BUFFER completion callback */ + void (*clear_tt_buffer_complete)(struct usb_hcd *, + struct usb_host_endpoint *); + /* xHCI specific functions */ /* Called by usb_alloc_dev to alloc HC device structures */ int (*alloc_dev)(struct usb_hcd *, struct usb_device *); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 2af3b4f0605..71f86c60d83 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -450,10 +450,10 @@ hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt) * talking to TTs must queue control transfers (not just bulk and iso), so * both can talk to the same hub concurrently. */ -static void hub_tt_kevent (struct work_struct *work) +static void hub_tt_work(struct work_struct *work) { struct usb_hub *hub = - container_of(work, struct usb_hub, tt.kevent); + container_of(work, struct usb_hub, tt.clear_work); unsigned long flags; int limit = 100; @@ -462,6 +462,7 @@ static void hub_tt_kevent (struct work_struct *work) struct list_head *next; struct usb_tt_clear *clear; struct usb_device *hdev = hub->hdev; + const struct hc_driver *drv; int status; next = hub->tt.clear_list.next; @@ -471,21 +472,25 @@ static void hub_tt_kevent (struct work_struct *work) /* drop lock so HCD can concurrently report other TT errors */ spin_unlock_irqrestore (&hub->tt.lock, flags); status = hub_clear_tt_buffer (hdev, clear->devinfo, clear->tt); - spin_lock_irqsave (&hub->tt.lock, flags); - if (status) dev_err (&hdev->dev, "clear tt %d (%04x) error %d\n", clear->tt, clear->devinfo, status); + + /* Tell the HCD, even if the operation failed */ + drv = clear->hcd->driver; + if (drv->clear_tt_buffer_complete) + (drv->clear_tt_buffer_complete)(clear->hcd, clear->ep); + kfree(clear); + spin_lock_irqsave(&hub->tt.lock, flags); } spin_unlock_irqrestore (&hub->tt.lock, flags); } /** - * usb_hub_tt_clear_buffer - clear control/bulk TT state in high speed hub - * @udev: the device whose split transaction failed - * @pipe: identifies the endpoint of the failed transaction + * usb_hub_clear_tt_buffer - clear control/bulk TT state in high speed hub + * @urb: an URB associated with the failed or incomplete split transaction * * High speed HCDs use this to tell the hub driver that some split control or * bulk transaction failed in a way that requires clearing internal state of @@ -495,8 +500,10 @@ static void hub_tt_kevent (struct work_struct *work) * It may not be possible for that hub to handle additional full (or low) * speed transactions until that state is fully cleared out. */ -void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) +int usb_hub_clear_tt_buffer(struct urb *urb) { + struct usb_device *udev = urb->dev; + int pipe = urb->pipe; struct usb_tt *tt = udev->tt; unsigned long flags; struct usb_tt_clear *clear; @@ -508,7 +515,7 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) if ((clear = kmalloc (sizeof *clear, GFP_ATOMIC)) == NULL) { dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n"); /* FIXME recover somehow ... RESET_TT? */ - return; + return -ENOMEM; } /* info that CLEAR_TT_BUFFER needs */ @@ -520,14 +527,19 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) : (USB_ENDPOINT_XFER_BULK << 11); if (usb_pipein (pipe)) clear->devinfo |= 1 << 15; - + + /* info for completion callback */ + clear->hcd = bus_to_hcd(udev->bus); + clear->ep = urb->ep; + /* tell keventd to clear state for this TT */ spin_lock_irqsave (&tt->lock, flags); list_add_tail (&clear->clear_list, &tt->clear_list); - schedule_work (&tt->kevent); + schedule_work(&tt->clear_work); spin_unlock_irqrestore (&tt->lock, flags); + return 0; } -EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer); +EXPORT_SYMBOL_GPL(usb_hub_clear_tt_buffer); /* If do_delay is false, return the number of milliseconds the caller * needs to delay. @@ -818,7 +830,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) if (hub->has_indicators) cancel_delayed_work_sync(&hub->leds); if (hub->tt.hub) - cancel_work_sync(&hub->tt.kevent); + cancel_work_sync(&hub->tt.clear_work); } /* caller has locked the hub device */ @@ -935,7 +947,7 @@ static int hub_configure(struct usb_hub *hub, spin_lock_init (&hub->tt.lock); INIT_LIST_HEAD (&hub->tt.clear_list); - INIT_WORK (&hub->tt.kevent, hub_tt_kevent); + INIT_WORK(&hub->tt.clear_work, hub_tt_work); switch (hdev->descriptor.bDeviceProtocol) { case 0: break; diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 889c0f32a40..de8081f065e 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -188,16 +188,18 @@ struct usb_tt { /* for control/bulk error recovery (CLEAR_TT_BUFFER) */ spinlock_t lock; struct list_head clear_list; /* of usb_tt_clear */ - struct work_struct kevent; + struct work_struct clear_work; }; struct usb_tt_clear { struct list_head clear_list; unsigned tt; u16 devinfo; + struct usb_hcd *hcd; + struct usb_host_endpoint *ep; }; -extern void usb_hub_tt_clear_buffer(struct usb_device *dev, int pipe); +extern int usb_hub_clear_tt_buffer(struct urb *urb); extern void usb_ep0_reinit(struct usb_device *); #endif /* __LINUX_HUB_H */ diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 2bed83caacb..9720e699f47 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -806,6 +806,48 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, return rc; } +static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) +{ + int err; + + if (dev->have_langid) + return 0; + + if (dev->string_langid < 0) + return -EPIPE; + + err = usb_string_sub(dev, 0, 0, tbuf); + + /* If the string was reported but is malformed, default to english + * (0x0409) */ + if (err == -ENODATA || (err > 0 && err < 4)) { + dev->string_langid = 0x0409; + dev->have_langid = 1; + dev_err(&dev->dev, + "string descriptor 0 malformed (err = %d), " + "defaulting to 0x%04x\n", + err, dev->string_langid); + return 0; + } + + /* In case of all other errors, we assume the device is not able to + * deal with strings at all. Set string_langid to -1 in order to + * prevent any string to be retrieved from the device */ + if (err < 0) { + dev_err(&dev->dev, "string descriptor 0 read error: %d\n", + err); + dev->string_langid = -1; + return -EPIPE; + } + + /* always use the first langid listed */ + dev->string_langid = tbuf[2] | (tbuf[3] << 8); + dev->have_langid = 1; + dev_dbg(&dev->dev, "default language 0x%04x\n", + dev->string_langid); + return 0; +} + /** * usb_string - returns UTF-8 version of a string descriptor * @dev: the device whose string descriptor is being retrieved @@ -837,24 +879,9 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) if (!tbuf) return -ENOMEM; - /* get langid for strings if it's not yet known */ - if (!dev->have_langid) { - err = usb_string_sub(dev, 0, 0, tbuf); - if (err < 0) { - dev_err(&dev->dev, - "string descriptor 0 read error: %d\n", - err); - } else if (err < 4) { - dev_err(&dev->dev, "string descriptor 0 too short\n"); - } else { - dev->string_langid = tbuf[2] | (tbuf[3] << 8); - /* always use the first langid listed */ - dev_dbg(&dev->dev, "default language 0x%04x\n", - dev->string_langid); - } - - dev->have_langid = 1; - } + err = usb_get_langid(dev, tbuf); + if (err < 0) + goto errout; err = usb_string_sub(dev, dev->string_langid, index, tbuf); if (err < 0) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5d1ddf485d1..7f8e83a954a 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -286,6 +286,27 @@ config USB_S3C_HSOTG default USB_GADGET select USB_GADGET_SELECTED +config USB_GADGET_IMX + boolean "Freescale IMX USB Peripheral Controller" + depends on ARCH_MX1 + help + Freescale's IMX series include an integrated full speed + USB 1.1 device controller. The controller in the IMX series + is register-compatible. + + It has Six fixed-function endpoints, as well as endpoint + zero (for control transfers). + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "imx_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_IMX + tristate + depends on USB_GADGET_IMX + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_S3C2410 boolean "S3C2410 USB Device Controller" depends on ARCH_S3C2410 @@ -321,27 +342,6 @@ config USB_GADGET_MUSB_HDRC This OTG-capable silicon IP is used in dual designs including the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin -config USB_GADGET_IMX - boolean "Freescale IMX USB Peripheral Controller" - depends on ARCH_MX1 - help - Freescale's IMX series include an integrated full speed - USB 1.1 device controller. The controller in the IMX series - is register-compatible. - - It has Six fixed-function endpoints, as well as endpoint - zero (for control transfers). - - Say "y" to link the driver statically, or "m" to build a - dynamically linked module called "imx_udc" and force all - gadget drivers to also be dynamically linked. - -config USB_IMX - tristate - depends on USB_GADGET_IMX - default USB_GADGET - select USB_GADGET_SELECTED - config USB_GADGET_M66592 boolean "Renesas M66592 USB Peripheral Controller" select USB_GADGET_DUALSPEED @@ -604,6 +604,7 @@ config USB_ZERO_HNPTEST config USB_AUDIO tristate "Audio Gadget (EXPERIMENTAL)" depends on SND + select SND_PCM help Gadget Audio is compatible with USB Audio Class specification 1.0. It will include at least one AudioControl interface, zero or more diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 826f3adde5d..77352ccc245 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -48,7 +48,6 @@ #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/timer.h> diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c index 94de7e86461..9f80f4e970b 100644 --- a/drivers/usb/gadget/audio.c +++ b/drivers/usb/gadget/audio.c @@ -42,9 +42,9 @@ * Instead: allocate your own, using normal USB-IF procedures. */ -/* Thanks to NetChip Technologies for donating this product ID. */ -#define AUDIO_VENDOR_NUM 0x0525 /* NetChip */ -#define AUDIO_PRODUCT_NUM 0xa4a1 /* Linux-USB Audio Gadget */ +/* Thanks to Linux Foundation for donating this product ID. */ +#define AUDIO_VENDOR_NUM 0x1d6b /* Linux Foundation */ +#define AUDIO_PRODUCT_NUM 0x0101 /* Linux-USB Audio Gadget */ /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index d006dc652e0..bd102f5052b 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -293,15 +293,16 @@ static int __init eth_bind(struct usb_composite_dev *cdev) /* CDC Subset */ eth_config_driver.label = "CDC Subset/SAFE"; - device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM), - device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM), - device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; + device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM); + device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM); + if (!has_rndis()) + device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; } if (has_rndis()) { /* RNDIS plus ECM-or-Subset */ - device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM), - device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM), + device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM); + device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM); device_desc.bNumConfigurations = 2; } diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index 6829d596135..a3913519fd5 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -34,7 +34,6 @@ #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/timer.h> diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 0ce4e281984..ed21e263f83 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -139,7 +139,7 @@ static int is_vbus_present(void) { struct pxa2xx_udc_mach_info *mach = the_controller->mach; - if (mach->gpio_vbus) { + if (gpio_is_valid(mach->gpio_vbus)) { int value = gpio_get_value(mach->gpio_vbus); if (mach->gpio_vbus_inverted) @@ -158,7 +158,7 @@ static void pullup_off(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; int off_level = mach->gpio_pullup_inverted; - if (mach->gpio_pullup) + if (gpio_is_valid(mach->gpio_pullup)) gpio_set_value(mach->gpio_pullup, off_level); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); @@ -169,7 +169,7 @@ static void pullup_on(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; int on_level = !mach->gpio_pullup_inverted; - if (mach->gpio_pullup) + if (gpio_is_valid(mach->gpio_pullup)) gpio_set_value(mach->gpio_pullup, on_level); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_CONNECT); @@ -1000,7 +1000,7 @@ static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) udc = container_of(_gadget, struct pxa25x_udc, gadget); /* not all boards support pullup control */ - if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) return -EOPNOTSUPP; udc->pullup = (is_active != 0); @@ -1802,11 +1802,13 @@ pxa25x_udc_irq(int irq, void *_dev) USIR0 |= tmp; handled = 1; } +#ifndef CONFIG_USB_PXA25X_SMALL if (usir1 & tmp) { handle_ep(&dev->ep[i+8]); USIR1 |= tmp; handled = 1; } +#endif } } @@ -2160,7 +2162,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; - if (dev->mach->gpio_vbus) { + if (gpio_is_valid(dev->mach->gpio_vbus)) { if ((retval = gpio_request(dev->mach->gpio_vbus, "pxa25x_udc GPIO VBUS"))) { dev_dbg(&pdev->dev, @@ -2173,7 +2175,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) } else vbus_irq = 0; - if (dev->mach->gpio_pullup) { + if (gpio_is_valid(dev->mach->gpio_pullup)) { if ((retval = gpio_request(dev->mach->gpio_pullup, "pca25x_udc GPIO PULLUP"))) { dev_dbg(&pdev->dev, @@ -2256,10 +2258,10 @@ lubbock_fail0: #endif free_irq(irq, dev); err_irq1: - if (dev->mach->gpio_pullup) + if (gpio_is_valid(dev->mach->gpio_pullup)) gpio_free(dev->mach->gpio_pullup); err_gpio_pullup: - if (dev->mach->gpio_vbus) + if (gpio_is_valid(dev->mach->gpio_vbus)) gpio_free(dev->mach->gpio_vbus); err_gpio_vbus: clk_put(dev->clk); @@ -2294,11 +2296,11 @@ static int __exit pxa25x_udc_remove(struct platform_device *pdev) free_irq(LUBBOCK_USB_IRQ, dev); } #endif - if (dev->mach->gpio_vbus) { + if (gpio_is_valid(dev->mach->gpio_vbus)) { free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev); gpio_free(dev->mach->gpio_vbus); } - if (dev->mach->gpio_pullup) + if (gpio_is_valid(dev->mach->gpio_pullup)) gpio_free(dev->mach->gpio_pullup); clk_put(dev->clk); @@ -2329,7 +2331,7 @@ static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state) struct pxa25x_udc *udc = platform_get_drvdata(dev); unsigned long flags; - if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command) WARNING("USB host won't detect disconnect!\n"); udc->suspended = 1; diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 2b4660e08c4..ca41b0b5afb 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -442,6 +442,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len, case OID_802_3_MAC_OPTIONS: pr_debug("%s: OID_802_3_MAC_OPTIONS\n", __func__); + *outbuf = cpu_to_le32(0); + retval = 0; break; /* ieee802.3 statistics OIDs (table 4-4) */ diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 9a2b8920532..a9b452fe622 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -28,7 +28,6 @@ #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/timer.h> diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 0c03471f0d4..1a920c70b5a 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -181,26 +181,27 @@ config USB_OHCI_HCD_PPC_SOC Enables support for the USB controller on the MPC52xx or STB03xxx processor chip. If unsure, say Y. -config USB_OHCI_HCD_PPC_OF - bool "OHCI support for PPC USB controller on OF platform bus" - depends on USB_OHCI_HCD && PPC_OF - default y - ---help--- - Enables support for the USB controller PowerPC present on the - OpenFirmware platform bus. - config USB_OHCI_HCD_PPC_OF_BE - bool "Support big endian HC" - depends on USB_OHCI_HCD_PPC_OF - default y + bool "OHCI support for OF platform bus (big endian)" + depends on USB_OHCI_HCD && PPC_OF select USB_OHCI_BIG_ENDIAN_DESC select USB_OHCI_BIG_ENDIAN_MMIO + ---help--- + Enables support for big-endian USB controllers present on the + OpenFirmware platform bus. config USB_OHCI_HCD_PPC_OF_LE - bool "Support little endian HC" - depends on USB_OHCI_HCD_PPC_OF - default n + bool "OHCI support for OF platform bus (little endian)" + depends on USB_OHCI_HCD && PPC_OF select USB_OHCI_LITTLE_ENDIAN + ---help--- + Enables support for little-endian USB controllers present on the + OpenFirmware platform bus. + +config USB_OHCI_HCD_PPC_OF + bool + depends on USB_OHCI_HCD && PPC_OF + default USB_OHCI_HCD_PPC_OF_BE || USB_OHCI_HCD_PPC_OF_LE config USB_OHCI_HCD_PCI bool "OHCI support for PCI-bus USB controllers" diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index c3a778bd359..59d208d94d4 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -113,6 +113,8 @@ static const struct hc_driver ehci_au1xxx_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index bf86809c512..991174937db 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -325,6 +325,8 @@ static const struct hc_driver ehci_fsl_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ehci_fsl_drv_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2b72473544d..7d03549c333 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1003,6 +1003,8 @@ idle_timeout: schedule_timeout_uninterruptible(1); goto rescan; case QH_STATE_IDLE: /* fully unlinked */ + if (qh->clearing_tt) + goto idle_timeout; if (list_empty (&qh->qtd_list)) { qh_put (qh); break; @@ -1030,12 +1032,14 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_qh *qh; int eptype = usb_endpoint_type(&ep->desc); + int epnum = usb_endpoint_num(&ep->desc); + int is_out = usb_endpoint_dir_out(&ep->desc); + unsigned long flags; if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) return; - rescan: - spin_lock_irq(&ehci->lock); + spin_lock_irqsave(&ehci->lock, flags); qh = ep->hcpriv; /* For Bulk and Interrupt endpoints we maintain the toggle state @@ -1044,29 +1048,24 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) * the toggle bit in the QH. */ if (qh) { + usb_settoggle(qh->dev, epnum, is_out, 0); if (!list_empty(&qh->qtd_list)) { WARN_ONCE(1, "clear_halt for a busy endpoint\n"); - } else if (qh->qh_state == QH_STATE_IDLE) { - qh->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE); - } else { - /* It's not safe to write into the overlay area - * while the QH is active. Unlink it first and - * wait for the unlink to complete. + } else if (qh->qh_state == QH_STATE_LINKED) { + + /* The toggle value in the QH can't be updated + * while the QH is active. Unlink it now; + * re-linking will call qh_refresh(). */ - if (qh->qh_state == QH_STATE_LINKED) { - if (eptype == USB_ENDPOINT_XFER_BULK) { - unlink_async(ehci, qh); - } else { - intr_deschedule(ehci, qh); - (void) qh_schedule(ehci, qh); - } + if (eptype == USB_ENDPOINT_XFER_BULK) { + unlink_async(ehci, qh); + } else { + intr_deschedule(ehci, qh); + (void) qh_schedule(ehci, qh); } - spin_unlock_irq(&ehci->lock); - schedule_timeout_uninterruptible(1); - goto rescan; } } - spin_unlock_irq(&ehci->lock); + spin_unlock_irqrestore(&ehci->lock, flags); } static int ehci_get_frame (struct usb_hcd *hcd) diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c index a44bb4a9495..89b7c70c6ed 100644 --- a/drivers/usb/host/ehci-ixp4xx.c +++ b/drivers/usb/host/ehci-ixp4xx.c @@ -61,6 +61,8 @@ static const struct hc_driver ixp4xx_ehci_hc_driver = { #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ixp4xx_ehci_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 770dd9aba62..dc2ac613a9d 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -165,6 +165,8 @@ static const struct hc_driver ehci_orion_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static void __init diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index f3683e1da16..c2f1b7df918 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -404,6 +404,8 @@ static const struct hc_driver ehci_pci_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index fbd272288fc..36f96da129f 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -79,6 +79,8 @@ static const struct hc_driver ehci_ppc_of_hc_driver = { #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 93f7035d00a..1dee33b9139 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -75,6 +75,8 @@ static const struct hc_driver ps3_ehci_hc_driver = { #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int __devinit ps3_ehci_probe(struct ps3_system_bus_device *dev) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 3192f683f80..9a1384747f3 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -93,6 +93,22 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) qh->hw_qtd_next = QTD_NEXT(ehci, qtd->qtd_dma); qh->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))) { + 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; + if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) { + qh->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); @@ -123,6 +139,55 @@ qh_refresh (struct ehci_hcd *ehci, struct ehci_qh *qh) /*-------------------------------------------------------------------------*/ +static void qh_link_async(struct ehci_hcd *ehci, struct ehci_qh *qh); + +static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct ehci_qh *qh = ep->hcpriv; + unsigned long flags; + + spin_lock_irqsave(&ehci->lock, flags); + qh->clearing_tt = 0; + if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) + && HC_IS_RUNNING(hcd->state)) + qh_link_async(ehci, qh); + spin_unlock_irqrestore(&ehci->lock, flags); +} + +static void ehci_clear_tt_buffer(struct ehci_hcd *ehci, struct ehci_qh *qh, + struct urb *urb, u32 token) +{ + + /* If an async split transaction gets an error or is unlinked, + * the TT buffer may be left in an indeterminate state. We + * have to clear the TT buffer. + * + * Note: this routine is never called for Isochronous transfers. + */ + if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { +#ifdef DEBUG + struct usb_device *tt = urb->dev->tt->hub; + dev_dbg(&tt->dev, + "clear tt buffer port %d, a%d ep%d t%08x\n", + urb->dev->ttport, urb->dev->devnum, + usb_pipeendpoint(urb->pipe), token); +#endif /* DEBUG */ + if (!ehci_is_TDI(ehci) + || urb->dev->tt->hub != + ehci_to_hcd(ehci)->self.root_hub) { + if (usb_hub_clear_tt_buffer(urb) == 0) + qh->clearing_tt = 1; + } else { + + /* REVISIT ARC-derived cores don't clear the root + * hub TT buffer in this way... + */ + } + } +} + static int qtd_copy_status ( struct ehci_hcd *ehci, struct urb *urb, @@ -149,6 +214,14 @@ static int qtd_copy_status ( if (token & QTD_STS_BABBLE) { /* FIXME "must" disable babbling device's port too */ status = -EOVERFLOW; + /* CERR nonzero + halt --> stall */ + } else if (QTD_CERR(token)) { + status = -EPIPE; + + /* In theory, more than one of the following bits can be set + * since they are sticky and the transaction is retried. + * Which to test first is rather arbitrary. + */ } else if (token & QTD_STS_MMF) { /* fs/ls interrupt xfer missed the complete-split */ status = -EPROTO; @@ -157,21 +230,15 @@ static int qtd_copy_status ( ? -ENOSR /* hc couldn't read data */ : -ECOMM; /* hc couldn't write data */ } else if (token & QTD_STS_XACT) { - /* timeout, bad crc, wrong PID, etc; retried */ - if (QTD_CERR (token)) - status = -EPIPE; - else { - ehci_dbg (ehci, "devpath %s ep%d%s 3strikes\n", - urb->dev->devpath, - usb_pipeendpoint (urb->pipe), - usb_pipein (urb->pipe) ? "in" : "out"); - status = -EPROTO; - } - /* CERR nonzero + no errors + halt --> stall */ - } else if (QTD_CERR (token)) - status = -EPIPE; - else /* unknown */ + /* timeout, bad CRC, wrong PID, etc */ + ehci_dbg(ehci, "devpath %s ep%d%s 3strikes\n", + urb->dev->devpath, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out"); + status = -EPROTO; + } else { /* unknown */ status = -EPROTO; + } ehci_vdbg (ehci, "dev%d ep%d%s qtd token %08x --> status %d\n", @@ -179,28 +246,6 @@ static int qtd_copy_status ( usb_pipeendpoint (urb->pipe), usb_pipein (urb->pipe) ? "in" : "out", token, status); - - /* if async CSPLIT failed, try cleaning out the TT buffer */ - if (status != -EPIPE - && urb->dev->tt - && !usb_pipeint(urb->pipe) - && ((token & QTD_STS_MMF) != 0 - || QTD_CERR(token) == 0) - && (!ehci_is_TDI(ehci) - || urb->dev->tt->hub != - ehci_to_hcd(ehci)->self.root_hub)) { -#ifdef DEBUG - struct usb_device *tt = urb->dev->tt->hub; - dev_dbg (&tt->dev, - "clear tt buffer port %d, a%d ep%d t%08x\n", - urb->dev->ttport, urb->dev->devnum, - usb_pipeendpoint (urb->pipe), token); -#endif /* DEBUG */ - /* REVISIT ARC-derived cores don't clear the root - * hub TT buffer in this way... - */ - usb_hub_tt_clear_buffer (urb->dev, urb->pipe); - } } return status; @@ -391,9 +436,16 @@ 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) + == qh->hw_current) { token = hc32_to_cpu(ehci, qh->hw_token); + /* An unlink may leave an incomplete + * async transaction in the TT buffer. + * We have to clear it. + */ + ehci_clear_tt_buffer(ehci, qh, urb, token); + } + /* force halt for unlinked or blocked qh, so we'll * patch the qh later and so that completions can't * activate it while we "know" it's stopped. @@ -419,6 +471,13 @@ halt: && (qtd->hw_alt_next & EHCI_LIST_END(ehci))) last_status = -EINPROGRESS; + + /* As part of low/full-speed endpoint-halt processing + * we must clear the TT buffer (11.17.5). + */ + if (unlikely(last_status != -EINPROGRESS && + last_status != -EREMOTEIO)) + ehci_clear_tt_buffer(ehci, qh, urb, token); } /* if we're removing something not at the queue head, @@ -834,6 +893,7 @@ done: qh->qh_state = QH_STATE_IDLE; qh->hw_info1 = cpu_to_hc32(ehci, info1); qh->hw_info2 = cpu_to_hc32(ehci, info2); + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); qh_refresh (ehci, qh); return qh; } @@ -847,6 +907,10 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) __hc32 dma = QH_NEXT(ehci, qh->qh_dma); struct ehci_qh *head; + /* Don't link a QH if there's a Clear-TT-Buffer pending */ + if (unlikely(qh->clearing_tt)) + return; + /* (re)start the async schedule? */ head = ehci->async; timer_action_done (ehci, TIMER_ASYNC_OFF); @@ -864,7 +928,7 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) } } - /* clear halt and maybe recover from silicon quirk */ + /* clear halt and/or toggle; and maybe recover from silicon quirk */ if (qh->qh_state == QH_STATE_IDLE) qh_refresh (ehci, qh); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 9d1babc7ff6..74f7f83b29a 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1619,11 +1619,14 @@ itd_complete ( desc->status = -EPROTO; /* HC need not update length with this error */ - if (!(t & EHCI_ISOC_BABBLE)) - desc->actual_length = EHCI_ITD_LENGTH (t); + if (!(t & EHCI_ISOC_BABBLE)) { + desc->actual_length = EHCI_ITD_LENGTH(t); + urb->actual_length += desc->actual_length; + } } else if (likely ((t & EHCI_ISOC_ACTIVE) == 0)) { desc->status = 0; - desc->actual_length = EHCI_ITD_LENGTH (t); + desc->actual_length = EHCI_ITD_LENGTH(t); + urb->actual_length += desc->actual_length; } else { /* URB was too late */ desc->status = -EXDEV; @@ -2014,7 +2017,8 @@ sitd_complete ( desc->status = -EPROTO; } else { desc->status = 0; - desc->actual_length = desc->length - SITD_LENGTH (t); + desc->actual_length = desc->length - SITD_LENGTH(t); + urb->actual_length += desc->actual_length; } stream->depth -= stream->interval << 3; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 90ad3395bb2..2bfff30f470 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -354,7 +354,9 @@ struct ehci_qh { unsigned short period; /* polling interval */ unsigned short start; /* where polling starts */ #define NO_FRAME ((unsigned short)~0) /* pick new start */ + struct usb_device *dev; /* access to TT */ + unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index bb63b68ddb7..62a226b6167 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -576,9 +576,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd) out_be16(&usb->fhci->regs->usb_event, usb->saved_msk); } else if (usb->port_status == FHCI_PORT_DISABLED) { - if (fhci_ioports_check_bus_state(fhci) == 1 && - usb->port_status != FHCI_PORT_LOW && - usb->port_status != FHCI_PORT_FULL) + if (fhci_ioports_check_bus_state(fhci) == 1) fhci_device_connected_interrupt(fhci); } usb_er &= ~USB_E_RESET_MASK; @@ -605,9 +603,7 @@ irqreturn_t fhci_irq(struct usb_hcd *hcd) } if (usb_er & USB_E_IDLE_MASK) { - if (usb->port_status == FHCI_PORT_DISABLED && - usb->port_status != FHCI_PORT_LOW && - usb->port_status != FHCI_PORT_FULL) { + if (usb->port_status == FHCI_PORT_DISABLED) { usb_er &= ~USB_E_RESET_MASK; fhci_device_connected_interrupt(fhci); } else if (usb->port_status == diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 3fa3a170279..d4feebfc63b 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -361,7 +361,7 @@ static int __devexit isp1760_plat_remove(struct platform_device *pdev) static struct platform_driver isp1760_plat_driver = { .probe = isp1760_plat_probe, - .remove = isp1760_plat_remove, + .remove = __devexit_p(isp1760_plat_remove), .driver = { .name = "isp1760", }, diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 56976cc0352..e18f74946e6 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -26,7 +26,6 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/timer.h> diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 3c5fe5cee05..90e1a8dedfa 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -18,6 +18,7 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/sched.h> +#include <linux/smp_lock.h> #include <linux/poll.h> #include <linux/usb/iowarrior.h> diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index deb95bb49fd..d645f3899fe 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -32,6 +32,7 @@ #include <linux/kernel.h> #include <linux/signal.h> #include <linux/sched.h> +#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/random.h> #include <linux/poll.h> diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index e0ff9ccd866..29092b8e59c 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -16,6 +16,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/mutex.h> #include <asm/uaccess.h> diff --git a/drivers/usb/musb/cppi_dma.h b/drivers/usb/musb/cppi_dma.h index 8a39de3e6e4..59bf949e589 100644 --- a/drivers/usb/musb/cppi_dma.h +++ b/drivers/usb/musb/cppi_dma.h @@ -5,7 +5,6 @@ #include <linux/slab.h> #include <linux/list.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/dmapool.h> diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 180d7daa409..e16ff605c45 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -35,13 +35,14 @@ #include <mach/hardware.h> #include <mach/memory.h> #include <mach/gpio.h> +#include <mach/cputype.h> #include <asm/mach-types.h> #include "musb_core.h" #ifdef CONFIG_MACH_DAVINCI_EVM -#define GPIO_nVBUS_DRV 87 +#define GPIO_nVBUS_DRV 144 #endif #include "davinci.h" @@ -329,7 +330,6 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ); WARNING("VBUS error workaround (delay coming)\n"); } else if (is_host_enabled(musb) && drvvbus) { - musb->is_active = 1; MUSB_HST_MODE(musb); musb->xceiv->default_a = 1; musb->xceiv->state = OTG_STATE_A_WAIT_VRISE; @@ -343,7 +343,9 @@ static irqreturn_t davinci_interrupt(int irq, void *__hci) portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); } - /* NOTE: this must complete poweron within 100 msec */ + /* NOTE: this must complete poweron within 100 msec + * (OTG_TIME_A_WAIT_VRISE) but we don't check for that. + */ davinci_source_power(musb, drvvbus, 0); DBG(2, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", @@ -411,6 +413,21 @@ int __init musb_platform_init(struct musb *musb) __raw_writel(phy_ctrl, USB_PHY_CTRL); } + /* On dm355, the default-A state machine needs DRVVBUS control. + * If we won't be a host, there's no need to turn it on. + */ + if (cpu_is_davinci_dm355()) { + u32 deepsleep = __raw_readl(DM355_DEEPSLEEP); + + if (is_host_enabled(musb)) { + deepsleep &= ~DRVVBUS_OVERRIDE; + } else { + deepsleep &= ~DRVVBUS_FORCE; + deepsleep |= DRVVBUS_OVERRIDE; + } + __raw_writel(deepsleep, DM355_DEEPSLEEP); + } + /* reset the controller */ musb_writel(tibase, DAVINCI_USB_CTRL_REG, 0x1); @@ -437,6 +454,15 @@ int musb_platform_exit(struct musb *musb) if (is_host_enabled(musb)) del_timer_sync(&otg_workaround); + /* force VBUS off */ + if (cpu_is_davinci_dm355()) { + u32 deepsleep = __raw_readl(DM355_DEEPSLEEP); + + deepsleep &= ~DRVVBUS_FORCE; + deepsleep |= DRVVBUS_OVERRIDE; + __raw_writel(deepsleep, DM355_DEEPSLEEP); + } + davinci_source_power(musb, 0 /*off*/, 1); /* delay, to avoid problems with module reload */ diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index f3772ca3b2c..381d648a36b 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -38,7 +38,6 @@ #include <linux/slab.h> #include <linux/list.h> #include <linux/interrupt.h> -#include <linux/smp_lock.h> #include <linux/errno.h> #include <linux/timer.h> #include <linux/clk.h> diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 94a2a350a41..cf94511485f 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -373,7 +373,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, musb_save_toggle(qh, is_in, urb); break; case USB_ENDPOINT_XFER_ISOC: - if (urb->error_count) + if (status == 0 && urb->error_count) status = -EXDEV; break; } @@ -2235,13 +2235,30 @@ static void musb_h_stop(struct usb_hcd *hcd) static int musb_bus_suspend(struct usb_hcd *hcd) { struct musb *musb = hcd_to_musb(hcd); + u8 devctl; - if (musb->xceiv->state == OTG_STATE_A_SUSPEND) + if (!is_host_active(musb)) return 0; - if (is_host_active(musb) && musb->is_active) { - WARNING("trying to suspend as %s is_active=%i\n", - otg_state_string(musb), musb->is_active); + switch (musb->xceiv->state) { + case OTG_STATE_A_SUSPEND: + return 0; + case OTG_STATE_A_WAIT_VRISE: + /* ID could be grounded even if there's no device + * on the other end of the cable. NOTE that the + * A_WAIT_VRISE timers are messy with MUSB... + */ + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) + musb->xceiv->state = OTG_STATE_A_WAIT_BCON; + break; + default: + break; + } + + if (musb->is_active) { + WARNING("trying to suspend as %s while active\n", + otg_state_string(musb)); return -EBUSY; } else return 0; diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 69feeec1628..aa884d072f0 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -59,18 +59,4 @@ config NOP_USB_XCEIV built-in with usb ip or which are autonomous and doesn't require any phy programming such as ISP1x04 etc. -config USB_LANGWELL_OTG - tristate "Intel Langwell USB OTG dual-role support" - depends on USB && MRST - select USB_OTG - select USB_OTG_UTILS - help - Say Y here if you want to build Intel Langwell USB OTG - transciever driver in kernel. This driver implements role - switch between EHCI host driver and Langwell USB OTG - client driver. - - To compile this driver as a module, choose M here: the - module will be called langwell_otg. - endif # USB || OTG diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index 6d1abdd3c0a..20816785652 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -9,7 +9,6 @@ obj-$(CONFIG_USB_OTG_UTILS) += otg.o obj-$(CONFIG_USB_GPIO_VBUS) += gpio_vbus.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TWL4030_USB) += twl4030-usb.o -obj-$(CONFIG_USB_LANGWELL_OTG) += langwell_otg.o obj-$(CONFIG_NOP_USB_XCEIV) += nop-usb-xceiv.o ccflags-$(CONFIG_USB_DEBUG) += -DDEBUG diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c deleted file mode 100644 index 6f628d0e9f3..00000000000 --- a/drivers/usb/otg/langwell_otg.c +++ /dev/null @@ -1,1915 +0,0 @@ -/* - * Intel Langwell USB OTG transceiver driver - * Copyright (C) 2008 - 2009, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - */ -/* This driver helps to switch Langwell OTG controller function between host - * and peripheral. It works with EHCI driver and Langwell client controller - * driver together. - */ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/errno.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/device.h> -#include <linux/moduleparam.h> -#include <linux/usb/ch9.h> -#include <linux/usb/gadget.h> -#include <linux/usb.h> -#include <linux/usb/otg.h> -#include <linux/notifier.h> -#include <asm/ipc_defs.h> -#include <linux/delay.h> -#include "../core/hcd.h" - -#include <linux/usb/langwell_otg.h> - -#define DRIVER_DESC "Intel Langwell USB OTG transceiver driver" -#define DRIVER_VERSION "3.0.0.32L.0002" - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR("Henry Yuan <hang.yuan@intel.com>, Hao Wu <hao.wu@intel.com>"); -MODULE_VERSION(DRIVER_VERSION); -MODULE_LICENSE("GPL"); - -static const char driver_name[] = "langwell_otg"; - -static int langwell_otg_probe(struct pci_dev *pdev, - const struct pci_device_id *id); -static void langwell_otg_remove(struct pci_dev *pdev); -static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message); -static int langwell_otg_resume(struct pci_dev *pdev); - -static int langwell_otg_set_host(struct otg_transceiver *otg, - struct usb_bus *host); -static int langwell_otg_set_peripheral(struct otg_transceiver *otg, - struct usb_gadget *gadget); -static int langwell_otg_start_srp(struct otg_transceiver *otg); - -static const struct pci_device_id pci_ids[] = {{ - .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), - .class_mask = ~0, - .vendor = 0x8086, - .device = 0x0811, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, -}, { /* end: all zeroes */ } -}; - -static struct pci_driver otg_pci_driver = { - .name = (char *) driver_name, - .id_table = pci_ids, - - .probe = langwell_otg_probe, - .remove = langwell_otg_remove, - - .suspend = langwell_otg_suspend, - .resume = langwell_otg_resume, -}; - -static const char *state_string(enum usb_otg_state state) -{ - switch (state) { - case OTG_STATE_A_IDLE: - return "a_idle"; - case OTG_STATE_A_WAIT_VRISE: - return "a_wait_vrise"; - case OTG_STATE_A_WAIT_BCON: - return "a_wait_bcon"; - case OTG_STATE_A_HOST: - return "a_host"; - case OTG_STATE_A_SUSPEND: - return "a_suspend"; - case OTG_STATE_A_PERIPHERAL: - return "a_peripheral"; - case OTG_STATE_A_WAIT_VFALL: - return "a_wait_vfall"; - case OTG_STATE_A_VBUS_ERR: - return "a_vbus_err"; - case OTG_STATE_B_IDLE: - return "b_idle"; - case OTG_STATE_B_SRP_INIT: - return "b_srp_init"; - case OTG_STATE_B_PERIPHERAL: - return "b_peripheral"; - case OTG_STATE_B_WAIT_ACON: - return "b_wait_acon"; - case OTG_STATE_B_HOST: - return "b_host"; - default: - return "UNDEFINED"; - } -} - -/* HSM timers */ -static inline struct langwell_otg_timer *otg_timer_initializer -(void (*function)(unsigned long), unsigned long expires, unsigned long data) -{ - struct langwell_otg_timer *timer; - timer = kmalloc(sizeof(struct langwell_otg_timer), GFP_KERNEL); - timer->function = function; - timer->expires = expires; - timer->data = data; - return timer; -} - -static struct langwell_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, - *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_res_tmr, - *b_bus_suspend_tmr; - -static struct list_head active_timers; - -static struct langwell_otg *the_transceiver; - -/* host/client notify transceiver when event affects HNP state */ -void langwell_update_transceiver() -{ - otg_dbg("transceiver driver is notified\n"); - queue_work(the_transceiver->qwork, &the_transceiver->work); -} -EXPORT_SYMBOL(langwell_update_transceiver); - -static int langwell_otg_set_host(struct otg_transceiver *otg, - struct usb_bus *host) -{ - otg->host = host; - - return 0; -} - -static int langwell_otg_set_peripheral(struct otg_transceiver *otg, - struct usb_gadget *gadget) -{ - otg->gadget = gadget; - - return 0; -} - -static int langwell_otg_set_power(struct otg_transceiver *otg, - unsigned mA) -{ - return 0; -} - -/* A-device drives vbus, controlled through PMIC CHRGCNTL register*/ -static void langwell_otg_drv_vbus(int on) -{ - struct ipc_pmic_reg_data pmic_data = {0}; - struct ipc_pmic_reg_data battery_data; - - /* Check if battery is attached or not */ - battery_data.pmic_reg_data[0].register_address = 0xd2; - battery_data.ioc = 0; - battery_data.num_entries = 1; - if (ipc_pmic_register_read(&battery_data)) { - otg_dbg("Failed to read PMIC register 0xd2.\n"); - return; - } - - if ((battery_data.pmic_reg_data[0].value & 0x20) == 0) { - otg_dbg("no battery attached\n"); - return; - } - - /* Workaround for battery attachment issue */ - if (battery_data.pmic_reg_data[0].value == 0x34) { - otg_dbg("battery \n"); - return; - } - - otg_dbg("battery attached\n"); - - pmic_data.ioc = 0; - pmic_data.pmic_reg_data[0].register_address = 0xD4; - pmic_data.num_entries = 1; - if (on) - pmic_data.pmic_reg_data[0].value = 0x20; - else - pmic_data.pmic_reg_data[0].value = 0xc0; - - if (ipc_pmic_register_write(&pmic_data, TRUE)) - otg_dbg("Failed to write PMIC.\n"); - -} - -/* charge vbus or discharge vbus through a resistor to ground */ -static void langwell_otg_chrg_vbus(int on) -{ - - u32 val; - - val = readl(the_transceiver->regs + CI_OTGSC); - - if (on) - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VC, - the_transceiver->regs + CI_OTGSC); - else - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_VD, - the_transceiver->regs + CI_OTGSC); - -} - -/* Start SRP */ -static int langwell_otg_start_srp(struct otg_transceiver *otg) -{ - u32 val; - - otg_dbg("Start SRP ->\n"); - - val = readl(the_transceiver->regs + CI_OTGSC); - - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HADP, - the_transceiver->regs + CI_OTGSC); - - /* Check if the data plus is finished or not */ - msleep(8); - val = readl(the_transceiver->regs + CI_OTGSC); - if (val & (OTGSC_HADP | OTGSC_DP)) - otg_dbg("DataLine SRP Error\n"); - - /* FIXME: VBus SRP */ - - return 0; -} - - -/* stop SOF via bus_suspend */ -static void langwell_otg_loc_sof(int on) -{ - struct usb_hcd *hcd; - int err; - - otg_dbg("loc_sof -> %d\n", on); - - hcd = bus_to_hcd(the_transceiver->otg.host); - if (on) - err = hcd->driver->bus_resume(hcd); - else - err = hcd->driver->bus_suspend(hcd); - - if (err) - otg_dbg("Failed to resume/suspend bus - %d\n", err); -} - -static void langwell_otg_phy_low_power(int on) -{ - u32 val; - - otg_dbg("phy low power mode-> %d\n", on); - - val = readl(the_transceiver->regs + CI_HOSTPC1); - if (on) - writel(val | HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1); - else - writel(val & ~HOSTPC1_PHCD, the_transceiver->regs + CI_HOSTPC1); -} - -/* Enable/Disable OTG interrupt */ -static void langwell_otg_intr(int on) -{ - u32 val; - - otg_dbg("interrupt -> %d\n", on); - - val = readl(the_transceiver->regs + CI_OTGSC); - if (on) { - val = val | (OTGSC_INTEN_MASK | OTGSC_IDPU); - writel(val, the_transceiver->regs + CI_OTGSC); - } else { - val = val & ~(OTGSC_INTEN_MASK | OTGSC_IDPU); - writel(val, the_transceiver->regs + CI_OTGSC); - } -} - -/* set HAAR: Hardware Assist Auto-Reset */ -static void langwell_otg_HAAR(int on) -{ - u32 val; - - otg_dbg("HAAR -> %d\n", on); - - val = readl(the_transceiver->regs + CI_OTGSC); - if (on) - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HAAR, - the_transceiver->regs + CI_OTGSC); - else - writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HAAR, - the_transceiver->regs + CI_OTGSC); -} - -/* set HABA: Hardware Assist B-Disconnect to A-Connect */ -static void langwell_otg_HABA(int on) -{ - u32 val; - - otg_dbg("HABA -> %d\n", on); - - val = readl(the_transceiver->regs + CI_OTGSC); - if (on) - writel((val & ~OTGSC_INTSTS_MASK) | OTGSC_HABA, - the_transceiver->regs + CI_OTGSC); - else - writel((val & ~OTGSC_INTSTS_MASK) & ~OTGSC_HABA, - the_transceiver->regs + CI_OTGSC); -} - -static int langwell_otg_check_se0_srp(int on) -{ - u32 val; - - int delay_time = TB_SE0_SRP * 10; /* step is 100us */ - - otg_dbg("check_se0_srp -> \n"); - - do { - udelay(100); - if (!delay_time--) - break; - val = readl(the_transceiver->regs + CI_PORTSC1); - val &= PORTSC_LS; - } while (!val); - - otg_dbg("check_se0_srp <- \n"); - return val; -} - -/* The timeout callback function to set time out bit */ -static void set_tmout(unsigned long indicator) -{ - *(int *)indicator = 1; -} - -void langwell_otg_nsf_msg(unsigned long indicator) -{ - switch (indicator) { - case 2: - case 4: - case 6: - case 7: - printk(KERN_ERR "OTG:NSF-%lu - deivce not responding\n", - indicator); - break; - case 3: - printk(KERN_ERR "OTG:NSF-%lu - deivce not supported\n", - indicator); - break; - default: - printk(KERN_ERR "Do not have this kind of NSF\n"); - break; - } -} - -/* Initialize timers */ -static void langwell_otg_init_timers(struct otg_hsm *hsm) -{ - /* HSM used timers */ - a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, - (unsigned long)&hsm->a_wait_vrise_tmout); - a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, - (unsigned long)&hsm->a_wait_bcon_tmout); - a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, - (unsigned long)&hsm->a_aidl_bdis_tmout); - b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, - (unsigned long)&hsm->b_ase0_brst_tmout); - b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, - (unsigned long)&hsm->b_se0_srp); - b_srp_res_tmr = otg_timer_initializer(&set_tmout, TB_SRP_RES, - (unsigned long)&hsm->b_srp_res_tmout); - b_bus_suspend_tmr = otg_timer_initializer(&set_tmout, TB_BUS_SUSPEND, - (unsigned long)&hsm->b_bus_suspend_tmout); -} - -/* Free timers */ -static void langwell_otg_free_timers(void) -{ - kfree(a_wait_vrise_tmr); - kfree(a_wait_bcon_tmr); - kfree(a_aidl_bdis_tmr); - kfree(b_ase0_brst_tmr); - kfree(b_se0_srp_tmr); - kfree(b_srp_res_tmr); - kfree(b_bus_suspend_tmr); -} - -/* Add timer to timer list */ -static void langwell_otg_add_timer(void *gtimer) -{ - struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; - struct langwell_otg_timer *tmp_timer; - u32 val32; - - /* Check if the timer is already in the active list, - * if so update timer count - */ - list_for_each_entry(tmp_timer, &active_timers, list) - if (tmp_timer == timer) { - timer->count = timer->expires; - return; - } - timer->count = timer->expires; - - if (list_empty(&active_timers)) { - val32 = readl(the_transceiver->regs + CI_OTGSC); - writel(val32 | OTGSC_1MSE, the_transceiver->regs + CI_OTGSC); - } - - list_add_tail(&timer->list, &active_timers); -} - -/* Remove timer from the timer list; clear timeout status */ -static void langwell_otg_del_timer(void *gtimer) -{ - struct langwell_otg_timer *timer = (struct langwell_otg_timer *)gtimer; - struct langwell_otg_timer *tmp_timer, *del_tmp; - u32 val32; - - list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) - if (tmp_timer == timer) - list_del(&timer->list); - - if (list_empty(&active_timers)) { - val32 = readl(the_transceiver->regs + CI_OTGSC); - writel(val32 & ~OTGSC_1MSE, the_transceiver->regs + CI_OTGSC); - } -} - -/* Reduce timer count by 1, and find timeout conditions.*/ -static int langwell_otg_tick_timer(u32 *int_sts) -{ - struct langwell_otg_timer *tmp_timer, *del_tmp; - int expired = 0; - - list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { - tmp_timer->count--; - /* check if timer expires */ - if (!tmp_timer->count) { - list_del(&tmp_timer->list); - tmp_timer->function(tmp_timer->data); - expired = 1; - } - } - - if (list_empty(&active_timers)) { - otg_dbg("tick timer: disable 1ms int\n"); - *int_sts = *int_sts & ~OTGSC_1MSE; - } - return expired; -} - -static void reset_otg(void) -{ - u32 val; - int delay_time = 1000; - - otg_dbg("reseting OTG controller ...\n"); - val = readl(the_transceiver->regs + CI_USBCMD); - writel(val | USBCMD_RST, the_transceiver->regs + CI_USBCMD); - do { - udelay(100); - if (!delay_time--) - otg_dbg("reset timeout\n"); - val = readl(the_transceiver->regs + CI_USBCMD); - val &= USBCMD_RST; - } while (val != 0); - otg_dbg("reset done.\n"); -} - -static void set_host_mode(void) -{ - u32 val; - - reset_otg(); - val = readl(the_transceiver->regs + CI_USBMODE); - val = (val & (~USBMODE_CM)) | USBMODE_HOST; - writel(val, the_transceiver->regs + CI_USBMODE); -} - -static void set_client_mode(void) -{ - u32 val; - - reset_otg(); - val = readl(the_transceiver->regs + CI_USBMODE); - val = (val & (~USBMODE_CM)) | USBMODE_DEVICE; - writel(val, the_transceiver->regs + CI_USBMODE); -} - -static void init_hsm(void) -{ - struct langwell_otg *langwell = the_transceiver; - u32 val32; - - /* read OTGSC after reset */ - val32 = readl(langwell->regs + CI_OTGSC); - otg_dbg("%s: OTGSC init value = 0x%x\n", __func__, val32); - - /* set init state */ - if (val32 & OTGSC_ID) { - langwell->hsm.id = 1; - langwell->otg.default_a = 0; - set_client_mode(); - langwell->otg.state = OTG_STATE_B_IDLE; - langwell_otg_drv_vbus(0); - } else { - langwell->hsm.id = 0; - langwell->otg.default_a = 1; - set_host_mode(); - langwell->otg.state = OTG_STATE_A_IDLE; - } - - /* set session indicator */ - if (val32 & OTGSC_BSE) - langwell->hsm.b_sess_end = 1; - if (val32 & OTGSC_BSV) - langwell->hsm.b_sess_vld = 1; - if (val32 & OTGSC_ASV) - langwell->hsm.a_sess_vld = 1; - if (val32 & OTGSC_AVV) - langwell->hsm.a_vbus_vld = 1; - - /* defautly power the bus */ - langwell->hsm.a_bus_req = 1; - langwell->hsm.a_bus_drop = 0; - /* defautly don't request bus as B device */ - langwell->hsm.b_bus_req = 0; - /* no system error */ - langwell->hsm.a_clr_err = 0; -} - -static irqreturn_t otg_dummy_irq(int irq, void *_dev) -{ - void __iomem *reg_base = _dev; - u32 val; - u32 int_mask = 0; - - val = readl(reg_base + CI_USBMODE); - if ((val & USBMODE_CM) != USBMODE_DEVICE) - return IRQ_NONE; - - val = readl(reg_base + CI_USBSTS); - int_mask = val & INTR_DUMMY_MASK; - - if (int_mask == 0) - return IRQ_NONE; - - /* clear hsm.b_conn here since host driver can't detect it - * otg_dummy_irq called means B-disconnect happened. - */ - if (the_transceiver->hsm.b_conn) { - the_transceiver->hsm.b_conn = 0; - if (spin_trylock(&the_transceiver->wq_lock)) { - queue_work(the_transceiver->qwork, - &the_transceiver->work); - spin_unlock(&the_transceiver->wq_lock); - } - } - /* Clear interrupts */ - writel(int_mask, reg_base + CI_USBSTS); - return IRQ_HANDLED; -} - -static irqreturn_t otg_irq(int irq, void *_dev) -{ - struct langwell_otg *langwell = _dev; - u32 int_sts, int_en; - u32 int_mask = 0; - int flag = 0; - - int_sts = readl(langwell->regs + CI_OTGSC); - int_en = (int_sts & OTGSC_INTEN_MASK) >> 8; - int_mask = int_sts & int_en; - if (int_mask == 0) - return IRQ_NONE; - - if (int_mask & OTGSC_IDIS) { - otg_dbg("%s: id change int\n", __func__); - langwell->hsm.id = (int_sts & OTGSC_ID) ? 1 : 0; - flag = 1; - } - if (int_mask & OTGSC_DPIS) { - otg_dbg("%s: data pulse int\n", __func__); - langwell->hsm.a_srp_det = (int_sts & OTGSC_DPS) ? 1 : 0; - flag = 1; - } - if (int_mask & OTGSC_BSEIS) { - otg_dbg("%s: b session end int\n", __func__); - langwell->hsm.b_sess_end = (int_sts & OTGSC_BSE) ? 1 : 0; - flag = 1; - } - if (int_mask & OTGSC_BSVIS) { - otg_dbg("%s: b session valid int\n", __func__); - langwell->hsm.b_sess_vld = (int_sts & OTGSC_BSV) ? 1 : 0; - flag = 1; - } - if (int_mask & OTGSC_ASVIS) { - otg_dbg("%s: a session valid int\n", __func__); - langwell->hsm.a_sess_vld = (int_sts & OTGSC_ASV) ? 1 : 0; - flag = 1; - } - if (int_mask & OTGSC_AVVIS) { - otg_dbg("%s: a vbus valid int\n", __func__); - langwell->hsm.a_vbus_vld = (int_sts & OTGSC_AVV) ? 1 : 0; - flag = 1; - } - - if (int_mask & OTGSC_1MSS) { - /* need to schedule otg_work if any timer is expired */ - if (langwell_otg_tick_timer(&int_sts)) - flag = 1; - } - - writel((int_sts & ~OTGSC_INTSTS_MASK) | int_mask, - langwell->regs + CI_OTGSC); - if (flag) - queue_work(langwell->qwork, &langwell->work); - - return IRQ_HANDLED; -} - -static void langwell_otg_work(struct work_struct *work) -{ - struct langwell_otg *langwell = container_of(work, - struct langwell_otg, work); - int retval; - - otg_dbg("%s: old state = %s\n", __func__, - state_string(langwell->otg.state)); - - switch (langwell->otg.state) { - case OTG_STATE_UNDEFINED: - case OTG_STATE_B_IDLE: - if (!langwell->hsm.id) { - langwell_otg_del_timer(b_srp_res_tmr); - langwell->otg.default_a = 1; - langwell->hsm.a_srp_det = 0; - - langwell_otg_chrg_vbus(0); - langwell_otg_drv_vbus(0); - - set_host_mode(); - langwell->otg.state = OTG_STATE_A_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.b_srp_res_tmout) { - langwell->hsm.b_srp_res_tmout = 0; - langwell->hsm.b_bus_req = 0; - langwell_otg_nsf_msg(6); - } else if (langwell->hsm.b_sess_vld) { - langwell_otg_del_timer(b_srp_res_tmr); - langwell->hsm.b_sess_end = 0; - langwell->hsm.a_bus_suspend = 0; - - langwell_otg_chrg_vbus(0); - if (langwell->client_ops) { - langwell->client_ops->resume(langwell->pdev); - langwell->otg.state = OTG_STATE_B_PERIPHERAL; - } else - otg_dbg("client driver not loaded.\n"); - - } else if (langwell->hsm.b_bus_req && - (langwell->hsm.b_sess_end)) { - /* workaround for b_se0_srp detection */ - retval = langwell_otg_check_se0_srp(0); - if (retval) { - langwell->hsm.b_bus_req = 0; - otg_dbg("LS is not SE0, try again later\n"); - } else { - /* Start SRP */ - langwell_otg_start_srp(&langwell->otg); - langwell_otg_add_timer(b_srp_res_tmr); - } - } - break; - case OTG_STATE_B_SRP_INIT: - if (!langwell->hsm.id) { - langwell->otg.default_a = 1; - langwell->hsm.a_srp_det = 0; - - langwell_otg_drv_vbus(0); - langwell_otg_chrg_vbus(0); - - langwell->otg.state = OTG_STATE_A_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.b_sess_vld) { - langwell_otg_chrg_vbus(0); - if (langwell->client_ops) { - langwell->client_ops->resume(langwell->pdev); - langwell->otg.state = OTG_STATE_B_PERIPHERAL; - } else - otg_dbg("client driver not loaded.\n"); - } - break; - case OTG_STATE_B_PERIPHERAL: - if (!langwell->hsm.id) { - langwell->otg.default_a = 1; - langwell->hsm.a_srp_det = 0; - - langwell_otg_drv_vbus(0); - langwell_otg_chrg_vbus(0); - set_host_mode(); - - if (langwell->client_ops) { - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - } else - otg_dbg("client driver has been removed.\n"); - - langwell->otg.state = OTG_STATE_A_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.b_sess_vld) { - langwell->hsm.b_hnp_enable = 0; - - if (langwell->client_ops) { - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - } else - otg_dbg("client driver has been removed.\n"); - - langwell->otg.state = OTG_STATE_B_IDLE; - } else if (langwell->hsm.b_bus_req && langwell->hsm.b_hnp_enable - && langwell->hsm.a_bus_suspend) { - - if (langwell->client_ops) { - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - } else - otg_dbg("client driver has been removed.\n"); - - langwell_otg_HAAR(1); - langwell->hsm.a_conn = 0; - - if (langwell->host_ops) { - langwell->host_ops->probe(langwell->pdev, - langwell->host_ops->id_table); - langwell->otg.state = OTG_STATE_B_WAIT_ACON; - } else - otg_dbg("host driver not loaded.\n"); - - langwell->hsm.a_bus_resume = 0; - langwell->hsm.b_ase0_brst_tmout = 0; - langwell_otg_add_timer(b_ase0_brst_tmr); - } - break; - - case OTG_STATE_B_WAIT_ACON: - if (!langwell->hsm.id) { - langwell_otg_del_timer(b_ase0_brst_tmr); - langwell->otg.default_a = 1; - langwell->hsm.a_srp_det = 0; - - langwell_otg_drv_vbus(0); - langwell_otg_chrg_vbus(0); - set_host_mode(); - - langwell_otg_HAAR(0); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->otg.state = OTG_STATE_A_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.b_sess_vld) { - langwell_otg_del_timer(b_ase0_brst_tmr); - langwell->hsm.b_hnp_enable = 0; - langwell->hsm.b_bus_req = 0; - langwell_otg_chrg_vbus(0); - langwell_otg_HAAR(0); - - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->otg.state = OTG_STATE_B_IDLE; - } else if (langwell->hsm.a_conn) { - langwell_otg_del_timer(b_ase0_brst_tmr); - langwell_otg_HAAR(0); - langwell->otg.state = OTG_STATE_B_HOST; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_bus_resume || - langwell->hsm.b_ase0_brst_tmout) { - langwell_otg_del_timer(b_ase0_brst_tmr); - langwell_otg_HAAR(0); - langwell_otg_nsf_msg(7); - - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - - langwell->hsm.a_bus_suspend = 0; - langwell->hsm.b_bus_req = 0; - - if (langwell->client_ops) - langwell->client_ops->resume(langwell->pdev); - else - otg_dbg("client driver not loaded.\n"); - - langwell->otg.state = OTG_STATE_B_PERIPHERAL; - } - break; - - case OTG_STATE_B_HOST: - if (!langwell->hsm.id) { - langwell->otg.default_a = 1; - langwell->hsm.a_srp_det = 0; - - langwell_otg_drv_vbus(0); - langwell_otg_chrg_vbus(0); - set_host_mode(); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->otg.state = OTG_STATE_A_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.b_sess_vld) { - langwell->hsm.b_hnp_enable = 0; - langwell->hsm.b_bus_req = 0; - langwell_otg_chrg_vbus(0); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->otg.state = OTG_STATE_B_IDLE; - } else if ((!langwell->hsm.b_bus_req) || - (!langwell->hsm.a_conn)) { - langwell->hsm.b_bus_req = 0; - langwell_otg_loc_sof(0); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - - langwell->hsm.a_bus_suspend = 0; - - if (langwell->client_ops) - langwell->client_ops->resume(langwell->pdev); - else - otg_dbg("client driver not loaded.\n"); - - langwell->otg.state = OTG_STATE_B_PERIPHERAL; - } - break; - - case OTG_STATE_A_IDLE: - langwell->otg.default_a = 1; - if (langwell->hsm.id) { - langwell->otg.default_a = 0; - langwell->hsm.b_bus_req = 0; - langwell_otg_drv_vbus(0); - langwell_otg_chrg_vbus(0); - - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_sess_vld) { - langwell_otg_drv_vbus(1); - langwell->hsm.a_srp_det = 1; - langwell->hsm.a_wait_vrise_tmout = 0; - langwell_otg_add_timer(a_wait_vrise_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_VRISE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.a_bus_drop && - (langwell->hsm.a_srp_det || langwell->hsm.a_bus_req)) { - langwell_otg_drv_vbus(1); - langwell->hsm.a_wait_vrise_tmout = 0; - langwell_otg_add_timer(a_wait_vrise_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_VRISE; - queue_work(langwell->qwork, &langwell->work); - } - break; - case OTG_STATE_A_WAIT_VRISE: - if (langwell->hsm.id) { - langwell_otg_del_timer(a_wait_vrise_tmr); - langwell->hsm.b_bus_req = 0; - langwell->otg.default_a = 0; - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_B_IDLE; - } else if (langwell->hsm.a_vbus_vld) { - langwell_otg_del_timer(a_wait_vrise_tmr); - if (langwell->host_ops) - langwell->host_ops->probe(langwell->pdev, - langwell->host_ops->id_table); - else - otg_dbg("host driver not loaded.\n"); - langwell->hsm.b_conn = 0; - langwell->hsm.a_set_b_hnp_en = 0; - langwell->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_BCON; - } else if (langwell->hsm.a_wait_vrise_tmout) { - if (langwell->hsm.a_vbus_vld) { - if (langwell->host_ops) - langwell->host_ops->probe( - langwell->pdev, - langwell->host_ops->id_table); - else - otg_dbg("host driver not loaded.\n"); - langwell->hsm.b_conn = 0; - langwell->hsm.a_set_b_hnp_en = 0; - langwell->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_BCON; - } else { - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_VBUS_ERR; - } - } - break; - case OTG_STATE_A_WAIT_BCON: - if (langwell->hsm.id) { - langwell_otg_del_timer(a_wait_bcon_tmr); - - langwell->otg.default_a = 0; - langwell->hsm.b_bus_req = 0; - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.a_vbus_vld) { - langwell_otg_del_timer(a_wait_bcon_tmr); - - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_VBUS_ERR; - } else if (langwell->hsm.a_bus_drop || - (langwell->hsm.a_wait_bcon_tmout && - !langwell->hsm.a_bus_req)) { - langwell_otg_del_timer(a_wait_bcon_tmr); - - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (langwell->hsm.b_conn) { - langwell_otg_del_timer(a_wait_bcon_tmr); - - langwell->hsm.a_suspend_req = 0; - langwell->otg.state = OTG_STATE_A_HOST; - if (!langwell->hsm.a_bus_req && - langwell->hsm.a_set_b_hnp_en) { - /* It is not safe enough to do a fast - * transistion from A_WAIT_BCON to - * A_SUSPEND */ - msleep(10000); - if (langwell->hsm.a_bus_req) - break; - - if (request_irq(langwell->pdev->irq, - otg_dummy_irq, IRQF_SHARED, - driver_name, langwell->regs) != 0) { - otg_dbg("request interrupt %d fail\n", - langwell->pdev->irq); - } - - langwell_otg_HABA(1); - langwell->hsm.b_bus_resume = 0; - langwell->hsm.a_aidl_bdis_tmout = 0; - langwell_otg_add_timer(a_aidl_bdis_tmr); - - langwell_otg_loc_sof(0); - langwell->otg.state = OTG_STATE_A_SUSPEND; - } else if (!langwell->hsm.a_bus_req && - !langwell->hsm.a_set_b_hnp_en) { - struct pci_dev *pdev = langwell->pdev; - if (langwell->host_ops) - langwell->host_ops->remove(pdev); - else - otg_dbg("host driver removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - } - } - break; - case OTG_STATE_A_HOST: - if (langwell->hsm.id) { - langwell->otg.default_a = 0; - langwell->hsm.b_bus_req = 0; - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_bus_drop || - (!langwell->hsm.a_set_b_hnp_en && !langwell->hsm.a_bus_req)) { - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (!langwell->hsm.a_vbus_vld) { - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_VBUS_ERR; - } else if (langwell->hsm.a_set_b_hnp_en - && !langwell->hsm.a_bus_req) { - /* Set HABA to enable hardware assistance to signal - * A-connect after receiver B-disconnect. Hardware - * will then set client mode and enable URE, SLE and - * PCE after the assistance. otg_dummy_irq is used to - * clean these ints when client driver is not resumed. - */ - if (request_irq(langwell->pdev->irq, - otg_dummy_irq, IRQF_SHARED, driver_name, - langwell->regs) != 0) { - otg_dbg("request interrupt %d failed\n", - langwell->pdev->irq); - } - - /* set HABA */ - langwell_otg_HABA(1); - langwell->hsm.b_bus_resume = 0; - langwell->hsm.a_aidl_bdis_tmout = 0; - langwell_otg_add_timer(a_aidl_bdis_tmr); - langwell_otg_loc_sof(0); - langwell->otg.state = OTG_STATE_A_SUSPEND; - } else if (!langwell->hsm.b_conn || !langwell->hsm.a_bus_req) { - langwell->hsm.a_wait_bcon_tmout = 0; - langwell->hsm.a_set_b_hnp_en = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_BCON; - } - break; - case OTG_STATE_A_SUSPEND: - if (langwell->hsm.id) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(langwell->pdev->irq, langwell->regs); - langwell->otg.default_a = 0; - langwell->hsm.b_bus_req = 0; - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_bus_req || - langwell->hsm.b_bus_resume) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(langwell->pdev->irq, langwell->regs); - langwell->hsm.a_suspend_req = 0; - langwell_otg_loc_sof(1); - langwell->otg.state = OTG_STATE_A_HOST; - } else if (langwell->hsm.a_aidl_bdis_tmout || - langwell->hsm.a_bus_drop) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(langwell->pdev->irq, langwell->regs); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (!langwell->hsm.b_conn && - langwell->hsm.a_set_b_hnp_en) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(langwell->pdev->irq, langwell->regs); - - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - - langwell->hsm.b_bus_suspend = 0; - langwell->hsm.b_bus_suspend_vld = 0; - langwell->hsm.b_bus_suspend_tmout = 0; - - /* msleep(200); */ - if (langwell->client_ops) - langwell->client_ops->resume(langwell->pdev); - else - otg_dbg("client driver not loaded.\n"); - - langwell_otg_add_timer(b_bus_suspend_tmr); - langwell->otg.state = OTG_STATE_A_PERIPHERAL; - break; - } else if (!langwell->hsm.a_vbus_vld) { - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - free_irq(langwell->pdev->irq, langwell->regs); - if (langwell->host_ops) - langwell->host_ops->remove(langwell->pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_VBUS_ERR; - } - break; - case OTG_STATE_A_PERIPHERAL: - if (langwell->hsm.id) { - langwell_otg_del_timer(b_bus_suspend_tmr); - langwell->otg.default_a = 0; - langwell->hsm.b_bus_req = 0; - if (langwell->client_ops) - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - else - otg_dbg("client driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (!langwell->hsm.a_vbus_vld) { - langwell_otg_del_timer(b_bus_suspend_tmr); - if (langwell->client_ops) - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - else - otg_dbg("client driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_VBUS_ERR; - } else if (langwell->hsm.a_bus_drop) { - langwell_otg_del_timer(b_bus_suspend_tmr); - if (langwell->client_ops) - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - else - otg_dbg("client driver has been removed.\n"); - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - } else if (langwell->hsm.b_bus_suspend) { - langwell_otg_del_timer(b_bus_suspend_tmr); - if (langwell->client_ops) - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - else - otg_dbg("client driver has been removed.\n"); - - if (langwell->host_ops) - langwell->host_ops->probe(langwell->pdev, - langwell->host_ops->id_table); - else - otg_dbg("host driver not loaded.\n"); - langwell->hsm.a_set_b_hnp_en = 0; - langwell->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_BCON; - } else if (langwell->hsm.b_bus_suspend_tmout) { - u32 val; - val = readl(langwell->regs + CI_PORTSC1); - if (!(val & PORTSC_SUSP)) - break; - if (langwell->client_ops) - langwell->client_ops->suspend(langwell->pdev, - PMSG_FREEZE); - else - otg_dbg("client driver has been removed.\n"); - if (langwell->host_ops) - langwell->host_ops->probe(langwell->pdev, - langwell->host_ops->id_table); - else - otg_dbg("host driver not loaded.\n"); - langwell->hsm.a_set_b_hnp_en = 0; - langwell->hsm.a_wait_bcon_tmout = 0; - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_BCON; - } - break; - case OTG_STATE_A_VBUS_ERR: - if (langwell->hsm.id) { - langwell->otg.default_a = 0; - langwell->hsm.a_clr_err = 0; - langwell->hsm.a_srp_det = 0; - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_clr_err) { - langwell->hsm.a_clr_err = 0; - langwell->hsm.a_srp_det = 0; - reset_otg(); - init_hsm(); - if (langwell->otg.state == OTG_STATE_A_IDLE) - queue_work(langwell->qwork, &langwell->work); - } - break; - case OTG_STATE_A_WAIT_VFALL: - if (langwell->hsm.id) { - langwell->otg.default_a = 0; - langwell->otg.state = OTG_STATE_B_IDLE; - queue_work(langwell->qwork, &langwell->work); - } else if (langwell->hsm.a_bus_req) { - langwell_otg_drv_vbus(1); - langwell->hsm.a_wait_vrise_tmout = 0; - langwell_otg_add_timer(a_wait_vrise_tmr); - langwell->otg.state = OTG_STATE_A_WAIT_VRISE; - } else if (!langwell->hsm.a_sess_vld) { - langwell->hsm.a_srp_det = 0; - langwell_otg_drv_vbus(0); - set_host_mode(); - langwell->otg.state = OTG_STATE_A_IDLE; - } - break; - default: - ; - } - - otg_dbg("%s: new state = %s\n", __func__, - state_string(langwell->otg.state)); -} - - static ssize_t -show_registers(struct device *_dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *langwell; - char *next; - unsigned size; - unsigned t; - - langwell = the_transceiver; - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, - "\n" - "USBCMD = 0x%08x \n" - "USBSTS = 0x%08x \n" - "USBINTR = 0x%08x \n" - "ASYNCLISTADDR = 0x%08x \n" - "PORTSC1 = 0x%08x \n" - "HOSTPC1 = 0x%08x \n" - "OTGSC = 0x%08x \n" - "USBMODE = 0x%08x \n", - readl(langwell->regs + 0x30), - readl(langwell->regs + 0x34), - readl(langwell->regs + 0x38), - readl(langwell->regs + 0x48), - readl(langwell->regs + 0x74), - readl(langwell->regs + 0xb4), - readl(langwell->regs + 0xf4), - readl(langwell->regs + 0xf8) - ); - size -= t; - next += t; - - return PAGE_SIZE - size; -} -static DEVICE_ATTR(registers, S_IRUGO, show_registers, NULL); - -static ssize_t -show_hsm(struct device *_dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *langwell; - char *next; - unsigned size; - unsigned t; - - langwell = the_transceiver; - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, - "\n" - "current state = %s\n" - "a_bus_resume = \t%d\n" - "a_bus_suspend = \t%d\n" - "a_conn = \t%d\n" - "a_sess_vld = \t%d\n" - "a_srp_det = \t%d\n" - "a_vbus_vld = \t%d\n" - "b_bus_resume = \t%d\n" - "b_bus_suspend = \t%d\n" - "b_conn = \t%d\n" - "b_se0_srp = \t%d\n" - "b_sess_end = \t%d\n" - "b_sess_vld = \t%d\n" - "id = \t%d\n" - "a_set_b_hnp_en = \t%d\n" - "b_srp_done = \t%d\n" - "b_hnp_enable = \t%d\n" - "a_wait_vrise_tmout = \t%d\n" - "a_wait_bcon_tmout = \t%d\n" - "a_aidl_bdis_tmout = \t%d\n" - "b_ase0_brst_tmout = \t%d\n" - "a_bus_drop = \t%d\n" - "a_bus_req = \t%d\n" - "a_clr_err = \t%d\n" - "a_suspend_req = \t%d\n" - "b_bus_req = \t%d\n" - "b_bus_suspend_tmout = \t%d\n" - "b_bus_suspend_vld = \t%d\n", - state_string(langwell->otg.state), - langwell->hsm.a_bus_resume, - langwell->hsm.a_bus_suspend, - langwell->hsm.a_conn, - langwell->hsm.a_sess_vld, - langwell->hsm.a_srp_det, - langwell->hsm.a_vbus_vld, - langwell->hsm.b_bus_resume, - langwell->hsm.b_bus_suspend, - langwell->hsm.b_conn, - langwell->hsm.b_se0_srp, - langwell->hsm.b_sess_end, - langwell->hsm.b_sess_vld, - langwell->hsm.id, - langwell->hsm.a_set_b_hnp_en, - langwell->hsm.b_srp_done, - langwell->hsm.b_hnp_enable, - langwell->hsm.a_wait_vrise_tmout, - langwell->hsm.a_wait_bcon_tmout, - langwell->hsm.a_aidl_bdis_tmout, - langwell->hsm.b_ase0_brst_tmout, - langwell->hsm.a_bus_drop, - langwell->hsm.a_bus_req, - langwell->hsm.a_clr_err, - langwell->hsm.a_suspend_req, - langwell->hsm.b_bus_req, - langwell->hsm.b_bus_suspend_tmout, - langwell->hsm.b_bus_suspend_vld - ); - size -= t; - next += t; - - return PAGE_SIZE - size; -} -static DEVICE_ATTR(hsm, S_IRUGO, show_hsm, NULL); - -static ssize_t -get_a_bus_req(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *langwell; - char *next; - unsigned size; - unsigned t; - - langwell = the_transceiver; - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, "%d", langwell->hsm.a_bus_req); - size -= t; - next += t; - - return PAGE_SIZE - size; -} - -static ssize_t -set_a_bus_req(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *langwell; - langwell = the_transceiver; - if (!langwell->otg.default_a) - return -1; - if (count > 2) - return -1; - - if (buf[0] == '0') { - langwell->hsm.a_bus_req = 0; - otg_dbg("a_bus_req = 0\n"); - } else if (buf[0] == '1') { - /* If a_bus_drop is TRUE, a_bus_req can't be set */ - if (langwell->hsm.a_bus_drop) - return -1; - langwell->hsm.a_bus_req = 1; - otg_dbg("a_bus_req = 1\n"); - } - if (spin_trylock(&langwell->wq_lock)) { - queue_work(langwell->qwork, &langwell->work); - spin_unlock(&langwell->wq_lock); - } - return count; -} -static DEVICE_ATTR(a_bus_req, S_IRUGO | S_IWUGO, get_a_bus_req, set_a_bus_req); - -static ssize_t -get_a_bus_drop(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *langwell; - char *next; - unsigned size; - unsigned t; - - langwell = the_transceiver; - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, "%d", langwell->hsm.a_bus_drop); - size -= t; - next += t; - - return PAGE_SIZE - size; -} - -static ssize_t -set_a_bus_drop(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *langwell; - langwell = the_transceiver; - if (!langwell->otg.default_a) - return -1; - if (count > 2) - return -1; - - if (buf[0] == '0') { - langwell->hsm.a_bus_drop = 0; - otg_dbg("a_bus_drop = 0\n"); - } else if (buf[0] == '1') { - langwell->hsm.a_bus_drop = 1; - langwell->hsm.a_bus_req = 0; - otg_dbg("a_bus_drop = 1, then a_bus_req = 0\n"); - } - if (spin_trylock(&langwell->wq_lock)) { - queue_work(langwell->qwork, &langwell->work); - spin_unlock(&langwell->wq_lock); - } - return count; -} -static DEVICE_ATTR(a_bus_drop, S_IRUGO | S_IWUGO, - get_a_bus_drop, set_a_bus_drop); - -static ssize_t -get_b_bus_req(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct langwell_otg *langwell; - char *next; - unsigned size; - unsigned t; - - langwell = the_transceiver; - next = buf; - size = PAGE_SIZE; - - t = scnprintf(next, size, "%d", langwell->hsm.b_bus_req); - size -= t; - next += t; - - return PAGE_SIZE - size; -} - -static ssize_t -set_b_bus_req(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *langwell; - langwell = the_transceiver; - - if (langwell->otg.default_a) - return -1; - - if (count > 2) - return -1; - - if (buf[0] == '0') { - langwell->hsm.b_bus_req = 0; - otg_dbg("b_bus_req = 0\n"); - } else if (buf[0] == '1') { - langwell->hsm.b_bus_req = 1; - otg_dbg("b_bus_req = 1\n"); - } - if (spin_trylock(&langwell->wq_lock)) { - queue_work(langwell->qwork, &langwell->work); - spin_unlock(&langwell->wq_lock); - } - return count; -} -static DEVICE_ATTR(b_bus_req, S_IRUGO | S_IWUGO, get_b_bus_req, set_b_bus_req); - -static ssize_t -set_a_clr_err(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct langwell_otg *langwell; - langwell = the_transceiver; - - if (!langwell->otg.default_a) - return -1; - if (count > 2) - return -1; - - if (buf[0] == '1') { - langwell->hsm.a_clr_err = 1; - otg_dbg("a_clr_err = 1\n"); - } - if (spin_trylock(&langwell->wq_lock)) { - queue_work(langwell->qwork, &langwell->work); - spin_unlock(&langwell->wq_lock); - } - return count; -} -static DEVICE_ATTR(a_clr_err, S_IWUGO, NULL, set_a_clr_err); - -static struct attribute *inputs_attrs[] = { - &dev_attr_a_bus_req.attr, - &dev_attr_a_bus_drop.attr, - &dev_attr_b_bus_req.attr, - &dev_attr_a_clr_err.attr, - NULL, -}; - -static struct attribute_group debug_dev_attr_group = { - .name = "inputs", - .attrs = inputs_attrs, -}; - -int langwell_register_host(struct pci_driver *host_driver) -{ - int ret = 0; - - the_transceiver->host_ops = host_driver; - queue_work(the_transceiver->qwork, &the_transceiver->work); - otg_dbg("host controller driver is registered\n"); - - return ret; -} -EXPORT_SYMBOL(langwell_register_host); - -void langwell_unregister_host(struct pci_driver *host_driver) -{ - if (the_transceiver->host_ops) - the_transceiver->host_ops->remove(the_transceiver->pdev); - the_transceiver->host_ops = NULL; - the_transceiver->hsm.a_bus_drop = 1; - queue_work(the_transceiver->qwork, &the_transceiver->work); - otg_dbg("host controller driver is unregistered\n"); -} -EXPORT_SYMBOL(langwell_unregister_host); - -int langwell_register_peripheral(struct pci_driver *client_driver) -{ - int ret = 0; - - if (client_driver) - ret = client_driver->probe(the_transceiver->pdev, - client_driver->id_table); - if (!ret) { - the_transceiver->client_ops = client_driver; - queue_work(the_transceiver->qwork, &the_transceiver->work); - otg_dbg("client controller driver is registered\n"); - } - - return ret; -} -EXPORT_SYMBOL(langwell_register_peripheral); - -void langwell_unregister_peripheral(struct pci_driver *client_driver) -{ - if (the_transceiver->client_ops) - the_transceiver->client_ops->remove(the_transceiver->pdev); - the_transceiver->client_ops = NULL; - the_transceiver->hsm.b_bus_req = 0; - queue_work(the_transceiver->qwork, &the_transceiver->work); - otg_dbg("client controller driver is unregistered\n"); -} -EXPORT_SYMBOL(langwell_unregister_peripheral); - -static int langwell_otg_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - unsigned long resource, len; - void __iomem *base = NULL; - int retval; - u32 val32; - struct langwell_otg *langwell; - char qname[] = "langwell_otg_queue"; - - retval = 0; - otg_dbg("\notg controller is detected.\n"); - if (pci_enable_device(pdev) < 0) { - retval = -ENODEV; - goto done; - } - - langwell = kzalloc(sizeof *langwell, GFP_KERNEL); - if (langwell == NULL) { - retval = -ENOMEM; - goto done; - } - the_transceiver = langwell; - - /* control register: BAR 0 */ - resource = pci_resource_start(pdev, 0); - len = pci_resource_len(pdev, 0); - if (!request_mem_region(resource, len, driver_name)) { - retval = -EBUSY; - goto err; - } - langwell->region = 1; - - base = ioremap_nocache(resource, len); - if (base == NULL) { - retval = -EFAULT; - goto err; - } - langwell->regs = base; - - if (!pdev->irq) { - otg_dbg("No IRQ.\n"); - retval = -ENODEV; - goto err; - } - - langwell->qwork = create_workqueue(qname); - if (!langwell->qwork) { - otg_dbg("cannot create workqueue %s\n", qname); - retval = -ENOMEM; - goto err; - } - INIT_WORK(&langwell->work, langwell_otg_work); - - /* OTG common part */ - langwell->pdev = pdev; - langwell->otg.dev = &pdev->dev; - langwell->otg.label = driver_name; - langwell->otg.set_host = langwell_otg_set_host; - langwell->otg.set_peripheral = langwell_otg_set_peripheral; - langwell->otg.set_power = langwell_otg_set_power; - langwell->otg.start_srp = langwell_otg_start_srp; - langwell->otg.state = OTG_STATE_UNDEFINED; - if (otg_set_transceiver(&langwell->otg)) { - otg_dbg("can't set transceiver\n"); - retval = -EBUSY; - goto err; - } - - reset_otg(); - init_hsm(); - - spin_lock_init(&langwell->lock); - spin_lock_init(&langwell->wq_lock); - INIT_LIST_HEAD(&active_timers); - langwell_otg_init_timers(&langwell->hsm); - - if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, - driver_name, langwell) != 0) { - otg_dbg("request interrupt %d failed\n", pdev->irq); - retval = -EBUSY; - goto err; - } - - /* enable OTGSC int */ - val32 = OTGSC_DPIE | OTGSC_BSEIE | OTGSC_BSVIE | - OTGSC_ASVIE | OTGSC_AVVIE | OTGSC_IDIE | OTGSC_IDPU; - writel(val32, langwell->regs + CI_OTGSC); - - retval = device_create_file(&pdev->dev, &dev_attr_registers); - if (retval < 0) { - otg_dbg("Can't register sysfs attribute: %d\n", retval); - goto err; - } - - retval = device_create_file(&pdev->dev, &dev_attr_hsm); - if (retval < 0) { - otg_dbg("Can't hsm sysfs attribute: %d\n", retval); - goto err; - } - - retval = sysfs_create_group(&pdev->dev.kobj, &debug_dev_attr_group); - if (retval < 0) { - otg_dbg("Can't register sysfs attr group: %d\n", retval); - goto err; - } - - if (langwell->otg.state == OTG_STATE_A_IDLE) - queue_work(langwell->qwork, &langwell->work); - - return 0; - -err: - if (the_transceiver) - langwell_otg_remove(pdev); -done: - return retval; -} - -static void langwell_otg_remove(struct pci_dev *pdev) -{ - struct langwell_otg *langwell; - - langwell = the_transceiver; - - if (langwell->qwork) { - flush_workqueue(langwell->qwork); - destroy_workqueue(langwell->qwork); - } - langwell_otg_free_timers(); - - /* disable OTGSC interrupt as OTGSC doesn't change in reset */ - writel(0, langwell->regs + CI_OTGSC); - - if (pdev->irq) - free_irq(pdev->irq, langwell); - if (langwell->regs) - iounmap(langwell->regs); - if (langwell->region) - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - - otg_set_transceiver(NULL); - pci_disable_device(pdev); - sysfs_remove_group(&pdev->dev.kobj, &debug_dev_attr_group); - device_remove_file(&pdev->dev, &dev_attr_hsm); - device_remove_file(&pdev->dev, &dev_attr_registers); - kfree(langwell); - langwell = NULL; -} - -static void transceiver_suspend(struct pci_dev *pdev) -{ - pci_save_state(pdev); - pci_set_power_state(pdev, PCI_D3hot); - langwell_otg_phy_low_power(1); -} - -static int langwell_otg_suspend(struct pci_dev *pdev, pm_message_t message) -{ - int ret = 0; - struct langwell_otg *langwell; - - langwell = the_transceiver; - - /* Disbale OTG interrupts */ - langwell_otg_intr(0); - - if (pdev->irq) - free_irq(pdev->irq, langwell); - - /* Prevent more otg_work */ - flush_workqueue(langwell->qwork); - spin_lock(&langwell->wq_lock); - - /* start actions */ - switch (langwell->otg.state) { - case OTG_STATE_A_IDLE: - case OTG_STATE_B_IDLE: - case OTG_STATE_A_WAIT_VFALL: - case OTG_STATE_A_VBUS_ERR: - transceiver_suspend(pdev); - break; - case OTG_STATE_A_WAIT_VRISE: - langwell_otg_del_timer(a_wait_vrise_tmr); - langwell->hsm.a_srp_det = 0; - langwell_otg_drv_vbus(0); - langwell->otg.state = OTG_STATE_A_IDLE; - transceiver_suspend(pdev); - break; - case OTG_STATE_A_WAIT_BCON: - langwell_otg_del_timer(a_wait_bcon_tmr); - if (langwell->host_ops) - ret = langwell->host_ops->suspend(pdev, message); - langwell_otg_drv_vbus(0); - break; - case OTG_STATE_A_HOST: - if (langwell->host_ops) - ret = langwell->host_ops->suspend(pdev, message); - langwell_otg_drv_vbus(0); - langwell_otg_phy_low_power(1); - break; - case OTG_STATE_A_SUSPEND: - langwell_otg_del_timer(a_aidl_bdis_tmr); - langwell_otg_HABA(0); - if (langwell->host_ops) - langwell->host_ops->remove(pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell_otg_drv_vbus(0); - transceiver_suspend(pdev); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - break; - case OTG_STATE_A_PERIPHERAL: - if (langwell->client_ops) - ret = langwell->client_ops->suspend(pdev, message); - else - otg_dbg("client driver has been removed.\n"); - langwell_otg_drv_vbus(0); - transceiver_suspend(pdev); - langwell->otg.state = OTG_STATE_A_WAIT_VFALL; - break; - case OTG_STATE_B_HOST: - if (langwell->host_ops) - langwell->host_ops->remove(pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->hsm.b_bus_req = 0; - transceiver_suspend(pdev); - langwell->otg.state = OTG_STATE_B_IDLE; - break; - case OTG_STATE_B_PERIPHERAL: - if (langwell->client_ops) - ret = langwell->client_ops->suspend(pdev, message); - else - otg_dbg("client driver has been removed.\n"); - break; - case OTG_STATE_B_WAIT_ACON: - langwell_otg_del_timer(b_ase0_brst_tmr); - langwell_otg_HAAR(0); - if (langwell->host_ops) - langwell->host_ops->remove(pdev); - else - otg_dbg("host driver has been removed.\n"); - langwell->hsm.b_bus_req = 0; - langwell->otg.state = OTG_STATE_B_IDLE; - transceiver_suspend(pdev); - break; - default: - otg_dbg("error state before suspend\n "); - break; - } - spin_unlock(&langwell->wq_lock); - - return ret; -} - -static void transceiver_resume(struct pci_dev *pdev) -{ - pci_restore_state(pdev); - pci_set_power_state(pdev, PCI_D0); - langwell_otg_phy_low_power(0); -} - -static int langwell_otg_resume(struct pci_dev *pdev) -{ - int ret = 0; - struct langwell_otg *langwell; - - langwell = the_transceiver; - - spin_lock(&langwell->wq_lock); - - switch (langwell->otg.state) { - case OTG_STATE_A_IDLE: - case OTG_STATE_B_IDLE: - case OTG_STATE_A_WAIT_VFALL: - case OTG_STATE_A_VBUS_ERR: - transceiver_resume(pdev); - break; - case OTG_STATE_A_WAIT_BCON: - langwell_otg_add_timer(a_wait_bcon_tmr); - langwell_otg_drv_vbus(1); - if (langwell->host_ops) - ret = langwell->host_ops->resume(pdev); - break; - case OTG_STATE_A_HOST: - langwell_otg_drv_vbus(1); - langwell_otg_phy_low_power(0); - if (langwell->host_ops) - ret = langwell->host_ops->resume(pdev); - break; - case OTG_STATE_B_PERIPHERAL: - if (langwell->client_ops) - ret = langwell->client_ops->resume(pdev); - else - otg_dbg("client driver not loaded.\n"); - break; - default: - otg_dbg("error state before suspend\n "); - break; - } - - if (request_irq(pdev->irq, otg_irq, IRQF_SHARED, - driver_name, the_transceiver) != 0) { - otg_dbg("request interrupt %d failed\n", pdev->irq); - ret = -EBUSY; - } - - /* enable OTG interrupts */ - langwell_otg_intr(1); - - spin_unlock(&langwell->wq_lock); - - queue_work(langwell->qwork, &langwell->work); - - - return ret; -} - -static int __init langwell_otg_init(void) -{ - return pci_register_driver(&otg_pci_driver); -} -module_init(langwell_otg_init); - -static void __exit langwell_otg_cleanup(void) -{ - pci_unregister_driver(&otg_pci_driver); -} -module_exit(langwell_otg_cleanup); diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c index 9ed5ea56867..af456b48985 100644 --- a/drivers/usb/otg/nop-usb-xceiv.c +++ b/drivers/usb/otg/nop-usb-xceiv.c @@ -53,6 +53,7 @@ EXPORT_SYMBOL(usb_nop_xceiv_register); void usb_nop_xceiv_unregister(void) { platform_device_unregister(pd); + pd = NULL; } EXPORT_SYMBOL(usb_nop_xceiv_unregister); diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 247b61bfb7f..0e4f2e41ace 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -169,9 +169,11 @@ static int usb_console_setup(struct console *co, char *options) kfree(tty); } } - /* So we know not to kill the hardware on a hangup on this - port. We have also bumped the use count by one so it won't go - idle */ + /* Now that any required fake tty operations are completed restore + * the tty port count */ + --port->port.count; + /* The console is special in terms of closing the device so + * indicate this port is now acting as a system console. */ port->console = 1; retval = 0; @@ -204,7 +206,7 @@ static void usb_console_write(struct console *co, dbg("%s - port %d, %d byte(s)", __func__, port->number, count); - if (!port->port.count) { + if (!port->console) { dbg("%s - port not opened", __func__); return; } @@ -300,8 +302,7 @@ void usb_serial_console_exit(void) { if (usbcons_info.port) { unregister_console(&usbcons); - if (usbcons_info.port->port.count) - usbcons_info.port->port.count--; + usbcons_info.port->console = 0; usbcons_info.port = NULL; } } diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 2b9eeda62bf..e9a40b820fd 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -67,6 +67,8 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ { USB_DEVICE(0x10C4, 0x0F91) }, /* Vstabi */ + { USB_DEVICE(0x10C4, 0x1101) }, /* Arkham Technology DS101 Bus Monitor */ + { USB_DEVICE(0x10C4, 0x1601) }, /* Arkham Technology DS101 Adapter */ { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 9734085fd2f..59adfe12311 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1228,8 +1228,8 @@ static void cypress_read_int_callback(struct urb *urb) /* precursor to disconnect so just go away */ return; case -EPIPE: - usb_clear_halt(port->serial->dev, 0x81); - break; + /* Can't call usb_clear_halt while in_interrupt */ + /* FALLS THROUGH */ default: /* something ugly is going on... */ dev_err(&urb->dev->dev, diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 3dc3768ca71..60c64cc5be2 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -33,6 +33,7 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> @@ -107,6 +108,7 @@ struct ftdi_sio_quirk { static int ftdi_jtag_probe(struct usb_serial *serial); static int ftdi_mtxorb_hack_setup(struct usb_serial *serial); +static int ftdi_NDI_device_setup(struct usb_serial *serial); static void ftdi_USB_UIRT_setup(struct ftdi_private *priv); static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv); @@ -118,6 +120,10 @@ static struct ftdi_sio_quirk ftdi_mtxorb_hack_quirk = { .probe = ftdi_mtxorb_hack_setup, }; +static struct ftdi_sio_quirk ftdi_NDI_device_quirk = { + .probe = ftdi_NDI_device_setup, +}; + static struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = { .port_probe = ftdi_USB_UIRT_setup, }; @@ -191,6 +197,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_MTXORB_4_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MTXORB_5_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MTXORB_6_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_R2000KU_TRUE_RNG) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0100_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0101_PID) }, { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0102_PID) }, @@ -579,6 +586,9 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CCSMACHX_2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSLOAD_N_GO_3_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSICDU64_4_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSPRIME8_5_PID) }, { USB_DEVICE(FTDI_VID, INSIDE_ACCESSO) }, { USB_DEVICE(INTREPID_VID, INTREPID_VALUECAN_PID) }, { USB_DEVICE(INTREPID_VID, INTREPID_NEOVI_PID) }, @@ -644,6 +654,16 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID), + .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_SPECTRA_SCU_PID), + .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_2_PID), + .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_FUTURE_3_PID), + .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_NDI_AURORA_SCU_PID), + .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk }, { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PHI_FISCO_PID) }, @@ -660,6 +680,8 @@ static struct usb_device_id id_table_combined [] = { .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID), + .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) }, { USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) }, { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) }, @@ -667,7 +689,6 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) }, { USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) }, { USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) }, - { USB_DEVICE(FTDI_VID, FTDI_NDI_HUC_PID) }, { USB_DEVICE(ATMEL_VID, STK541_PID) }, { USB_DEVICE(DE_VID, STB_PID) }, { USB_DEVICE(DE_VID, WHT_PID) }, @@ -1023,6 +1044,16 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty, case FT2232C: /* FT2232C chip */ case FT232RL: if (baud <= 3000000) { + __u16 product_id = le16_to_cpu( + port->serial->dev->descriptor.idProduct); + if (((FTDI_NDI_HUC_PID == product_id) || + (FTDI_NDI_SPECTRA_SCU_PID == product_id) || + (FTDI_NDI_FUTURE_2_PID == product_id) || + (FTDI_NDI_FUTURE_3_PID == product_id) || + (FTDI_NDI_AURORA_SCU_PID == product_id)) && + (baud == 19200)) { + baud = 1200000; + } div_value = ftdi_232bm_baud_to_divisor(baud); } else { dbg("%s - Baud rate too high!", __func__); @@ -1554,6 +1585,39 @@ static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv) } /* ftdi_HE_TIRA1_setup */ /* + * Module parameter to control latency timer for NDI FTDI-based USB devices. + * If this value is not set in modprobe.conf.local its value will be set to 1ms. + */ +static int ndi_latency_timer = 1; + +/* Setup for the NDI FTDI-based USB devices, which requires hardwired + * baudrate (19200 gets mapped to 1200000). + * + * Called from usbserial:serial_probe. + */ +static int ftdi_NDI_device_setup(struct usb_serial *serial) +{ + struct usb_device *udev = serial->dev; + int latency = ndi_latency_timer; + int rv = 0; + char buf[1]; + + if (latency == 0) + latency = 1; + if (latency > 99) + latency = 99; + + dbg("%s setting NDI device latency to %d", __func__, latency); + dev_info(&udev->dev, "NDI device with a latency value of %d", latency); + + rv = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + FTDI_SIO_SET_LATENCY_TIMER_REQUEST, + FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE, + latency, 0, buf, 0, WDR_TIMEOUT); + return 0; +} + +/* * First port on JTAG adaptors such as Olimex arm-usb-ocd or the FIC/OpenMoko * Neo1973 Debug Board is reserved for JTAG interface and can be accessed from * userspace using openocd. @@ -2121,7 +2185,7 @@ static void ftdi_process_read(struct work_struct *work) /* Note that the error flag is duplicated for every character received since we don't know which character it applied to */ - if (!usb_serial_handle_sysrq_char(port, + if (!usb_serial_handle_sysrq_char(tty, port, data[packet_offset + i])) tty_insert_flip_char(tty, data[packet_offset + i], @@ -2622,3 +2686,5 @@ MODULE_PARM_DESC(vendor, "User specified vendor ID (default=" module_param(product, ushort, 0); MODULE_PARM_DESC(product, "User specified product ID"); +module_param(ndi_latency_timer, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ndi_latency_timer, "NDI device latency timer override"); diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index f1d440a728a..c9fbd741509 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -506,6 +506,7 @@ * * Armin Laeuger originally sent the PID for the UM 100 module. */ +#define FTDI_R2000KU_TRUE_RNG 0xFB80 /* R2000KU TRUE RNG */ #define FTDI_ELV_UR100_PID 0xFB58 /* USB-RS232-Umsetzer (UR 100) */ #define FTDI_ELV_UM100_PID 0xFB5A /* USB-Modul UM 100 */ #define FTDI_ELV_UO100_PID 0xFB5B /* USB-Modul UO 100 */ @@ -614,6 +615,9 @@ #define FTDI_CCSICDU20_0_PID 0xF9D0 #define FTDI_CCSICDU40_1_PID 0xF9D1 #define FTDI_CCSMACHX_2_PID 0xF9D2 +#define FTDI_CCSLOAD_N_GO_3_PID 0xF9D3 +#define FTDI_CCSICDU64_4_PID 0xF9D4 +#define FTDI_CCSPRIME8_5_PID 0xF9D5 /* Inside Accesso contactless reader (http://www.insidefr.com) */ #define INSIDE_ACCESSO 0xFAD0 @@ -736,6 +740,15 @@ #define FTDI_PYRAMID_PID 0xE6C8 /* Pyramid Appliance Display */ /* + * NDI (www.ndigital.com) product ids + */ +#define FTDI_NDI_HUC_PID 0xDA70 /* NDI Host USB Converter */ +#define FTDI_NDI_SPECTRA_SCU_PID 0xDA71 /* NDI Spectra SCU */ +#define FTDI_NDI_FUTURE_2_PID 0xDA72 /* NDI future device #2 */ +#define FTDI_NDI_FUTURE_3_PID 0xDA73 /* NDI future device #3 */ +#define FTDI_NDI_AURORA_SCU_PID 0xDA74 /* NDI Aurora SCU */ + +/* * Posiflex inc retail equipment (http://www.posiflex.com.tw) */ #define POSIFLEX_VID 0x0d3a /* Vendor ID */ @@ -848,9 +861,6 @@ #define TML_VID 0x1B91 /* Vendor ID */ #define TML_USB_SERIAL_PID 0x0064 /* USB - Serial Converter */ -/* NDI Polaris System */ -#define FTDI_NDI_HUC_PID 0xDA70 - /* Propox devices */ #define FTDI_PROPOX_JTAGCABLEII_PID 0xD738 @@ -934,6 +944,8 @@ #define MARVELL_VID 0x9e88 #define MARVELL_SHEEVAPLUG_PID 0x9e8f +#define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmBH */ + /* * BmRequestType: 1100 0000b * bRequest: FTDI_E2_READ diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 932d6241b78..ce57f6a32bd 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -424,10 +424,17 @@ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) if (!tty) goto done; - /* Push data to tty */ - for (i = 0; i < urb->actual_length; i++, ch++) { - if (!usb_serial_handle_sysrq_char(port, *ch)) - tty_insert_flip_char(tty, *ch, TTY_NORMAL); + /* The per character mucking around with sysrq path it too slow for + stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases + where the USB serial is not a console anyway */ + if (!port->console || !port->sysrq) + tty_insert_flip_string(tty, ch, urb->actual_length); + else { + /* Push data to tty */ + for (i = 0; i < urb->actual_length; i++, ch++) { + if (!usb_serial_handle_sysrq_char(tty, port, *ch)) + tty_insert_flip_char(tty, *ch, TTY_NORMAL); + } } tty_flip_buffer_push(tty); tty_kref_put(tty); @@ -527,11 +534,12 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty) } } -int usb_serial_handle_sysrq_char(struct usb_serial_port *port, unsigned int ch) +int usb_serial_handle_sysrq_char(struct tty_struct *tty, + struct usb_serial_port *port, unsigned int ch) { if (port->sysrq && port->console) { if (ch && time_before(jiffies, port->sysrq)) { - handle_sysrq(ch, tty_port_tty_get(&port->port)); + handle_sysrq(ch, tty); port->sysrq = 0; return 1; } diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index c40f95c1951..c31940a307f 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -26,6 +26,7 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 575816e6ba3..98262dd552b 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -206,6 +206,7 @@ static int option_resume(struct usb_serial *serial); #define NOVATELWIRELESS_PRODUCT_MC950D 0x4400 #define NOVATELWIRELESS_PRODUCT_U727 0x5010 #define NOVATELWIRELESS_PRODUCT_MC760 0x6000 +#define NOVATELWIRELESS_PRODUCT_OVMC760 0x6002 /* FUTURE NOVATEL PRODUCTS */ #define NOVATELWIRELESS_PRODUCT_EVDO_HIGHSPEED 0X6001 @@ -307,11 +308,20 @@ static int option_resume(struct usb_serial *serial); #define DLINK_VENDOR_ID 0x1186 #define DLINK_PRODUCT_DWM_652 0x3e04 +#define QISDA_VENDOR_ID 0x1da5 +#define QISDA_PRODUCT_H21_4512 0x4512 +#define QISDA_PRODUCT_H21_4523 0x4523 +#define QISDA_PRODUCT_H20_4515 0x4515 +#define QISDA_PRODUCT_H20_4519 0x4519 + /* TOSHIBA PRODUCTS */ #define TOSHIBA_VENDOR_ID 0x0930 #define TOSHIBA_PRODUCT_HSDPA_MINICARD 0x1302 +#define ALINK_VENDOR_ID 0x1e0e +#define ALINK_PRODUCT_3GU 0x9200 + static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -430,6 +440,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC727) }, /* Novatel MC727/U727/USB727 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_U727) }, /* Novatel MC727/U727/USB727 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_MC760) }, /* Novatel MC760/U760/USB760 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_OVMC760) }, /* Novatel Ovation MC760 */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_FULLSPEED) }, /* Novatel HSPA product */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_EVDO_EMBEDDED_FULLSPEED) }, /* Novatel EVDO Embedded product */ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_HSPA_EMBEDDED_FULLSPEED) }, /* Novatel HSPA Embedded product */ @@ -529,8 +540,13 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) }, - { USB_DEVICE(0x1da5, 0x4515) }, /* BenQ H20 */ + { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4512) }, + { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H21_4523) }, + { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4515) }, + { USB_DEVICE(QISDA_VENDOR_ID, QISDA_PRODUCT_H20_4519) }, { USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_HSDPA_MINICARD ) }, /* Toshiba 3G HSDPA == Novatel Expedite EU870D MiniCard */ + { USB_DEVICE(ALINK_VENDOR_ID, 0x9000) }, + { USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -732,7 +748,6 @@ static int option_write(struct tty_struct *tty, struct usb_serial_port *port, memcpy(this_urb->transfer_buffer, buf, todo); this_urb->transfer_buffer_length = todo; - this_urb->dev = port->serial->dev; err = usb_submit_urb(this_urb, GFP_ATOMIC); if (err) { dbg("usb_submit_urb %p (write bulk) failed " @@ -860,7 +875,6 @@ static void option_instat_callback(struct urb *urb) /* Resubmit urb so we continue receiving IRQ data */ if (status != -ESHUTDOWN && status != -ENOENT) { - urb->dev = serial->dev; err = usb_submit_urb(urb, GFP_ATOMIC); if (err) dbg("%s: resubmit intr urb failed. (%d)", @@ -921,23 +935,11 @@ static int option_open(struct tty_struct *tty, dbg("%s", __func__); - /* Reset low level data toggle and start reading from endpoints */ + /* Start reading from the IN endpoint */ for (i = 0; i < N_IN_URB; i++) { urb = portdata->in_urbs[i]; if (!urb) continue; - if (urb->dev != serial->dev) { - dbg("%s: dev %p != %p", __func__, - urb->dev, serial->dev); - continue; - } - - /* - * make sure endpoint data toggle is synchronized with the - * device - */ - usb_clear_halt(urb->dev, urb->pipe); - err = usb_submit_urb(urb, GFP_KERNEL); if (err) { dbg("%s: submit urb %d failed (%d) %d", @@ -946,16 +948,6 @@ static int option_open(struct tty_struct *tty, } } - /* Reset low level data toggle on out endpoints */ - for (i = 0; i < N_OUT_URB; i++) { - urb = portdata->out_urbs[i]; - if (!urb) - continue; - urb->dev = serial->dev; - /* usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), 0); */ - } - option_send_setup(port); return 0; @@ -1218,7 +1210,6 @@ static int option_resume(struct usb_serial *serial) dbg("%s: No interrupt URB for port %d\n", __func__, i); continue; } - port->interrupt_in_urb->dev = serial->dev; err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO); dbg("Submitted interrupt URB for port %d (result %d)", i, err); if (err < 0) { diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ec6c132a25b..7d15bfa7c2d 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -94,6 +94,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(YCCABLE_VENDOR_ID, YCCABLE_PRODUCT_ID) }, { USB_DEVICE(SUPERIAL_VENDOR_ID, SUPERIAL_PRODUCT_ID) }, { USB_DEVICE(HP_VENDOR_ID, HP_LD220_PRODUCT_ID) }, + { USB_DEVICE(CRESSI_VENDOR_ID, CRESSI_EDY_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -971,18 +972,46 @@ exit: __func__, retval); } +static void pl2303_push_data(struct tty_struct *tty, + struct usb_serial_port *port, struct urb *urb, + u8 line_status) +{ + unsigned char *data = urb->transfer_buffer; + /* get tty_flag from status */ + char tty_flag = TTY_NORMAL; + /* break takes precedence over parity, */ + /* which takes precedence over framing errors */ + if (line_status & UART_BREAK_ERROR) + tty_flag = TTY_BREAK; + else if (line_status & UART_PARITY_ERROR) + tty_flag = TTY_PARITY; + else if (line_status & UART_FRAME_ERROR) + tty_flag = TTY_FRAME; + dbg("%s - tty_flag = %d", __func__, tty_flag); + + tty_buffer_request_room(tty, urb->actual_length + 1); + /* overrun is special, not associated with a char */ + if (line_status & UART_OVERRUN_ERROR) + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + if (port->console && port->sysrq) { + int i; + for (i = 0; i < urb->actual_length; ++i) + if (!usb_serial_handle_sysrq_char(tty, port, data[i])) + tty_insert_flip_char(tty, data[i], tty_flag); + } else + tty_insert_flip_string(tty, data, urb->actual_length); + tty_flip_buffer_push(tty); +} + static void pl2303_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; struct pl2303_private *priv = usb_get_serial_port_data(port); struct tty_struct *tty; - unsigned char *data = urb->transfer_buffer; unsigned long flags; - int i; int result; int status = urb->status; u8 line_status; - char tty_flag; dbg("%s - port %d", __func__, port->number); @@ -1010,10 +1039,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) } usb_serial_debug_data(debug, &port->dev, __func__, - urb->actual_length, data); - - /* get tty_flag from status */ - tty_flag = TTY_NORMAL; + urb->actual_length, urb->transfer_buffer); spin_lock_irqsave(&priv->lock, flags); line_status = priv->line_status; @@ -1021,26 +1047,9 @@ static void pl2303_read_bulk_callback(struct urb *urb) spin_unlock_irqrestore(&priv->lock, flags); wake_up_interruptible(&priv->delta_msr_wait); - /* break takes precedence over parity, */ - /* which takes precedence over framing errors */ - if (line_status & UART_BREAK_ERROR) - tty_flag = TTY_BREAK; - else if (line_status & UART_PARITY_ERROR) - tty_flag = TTY_PARITY; - else if (line_status & UART_FRAME_ERROR) - tty_flag = TTY_FRAME; - dbg("%s - tty_flag = %d", __func__, tty_flag); - tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { - tty_buffer_request_room(tty, urb->actual_length + 1); - /* overrun is special, not associated with a char */ - if (line_status & UART_OVERRUN_ERROR) - tty_insert_flip_char(tty, 0, TTY_OVERRUN); - for (i = 0; i < urb->actual_length; ++i) - if (!usb_serial_handle_sysrq_char(port, data[i])) - tty_insert_flip_char(tty, data[i], tty_flag); - tty_flip_buffer_push(tty); + pl2303_push_data(tty, port, urb, line_status); } tty_kref_put(tty); /* Schedule the next read _if_ we are still open */ diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 1d7a22e3a9f..12aac7d2462 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -122,3 +122,7 @@ /* Hewlett-Packard LD220-HP POS Pole Display */ #define HP_VENDOR_ID 0x03f0 #define HP_LD220_PRODUCT_ID 0x3524 + +/* Cressi Edy (diving computer) PC interface */ +#define CRESSI_VENDOR_ID 0x04b8 +#define CRESSI_EDY_PRODUCT_ID 0x0521 diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 032f7aeb40a..f48d05e0acc 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -181,35 +181,50 @@ static const struct sierra_iface_info direct_ip_interface_blacklist = { }; static struct usb_device_id id_table [] = { + { USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */ + { USB_DEVICE(0x03F0, 0x1B1D) }, /* HP ev2200 a.k.a MC5720 */ + { USB_DEVICE(0x03F0, 0x1E1D) }, /* HP hs2300 a.k.a MC8775 */ + { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */ { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */ { USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */ - { USB_DEVICE(0x03f0, 0x1b1d) }, /* HP ev2200 a.k.a MC5720 */ { USB_DEVICE(0x1199, 0x0020) }, /* Sierra Wireless MC5725 */ - { USB_DEVICE(0x1199, 0x0024) }, /* Sierra Wireless MC5727 */ { USB_DEVICE(0x1199, 0x0220) }, /* Sierra Wireless MC5725 */ + { USB_DEVICE(0x1199, 0x0022) }, /* Sierra Wireless EM5725 */ + { USB_DEVICE(0x1199, 0x0024) }, /* Sierra Wireless MC5727 */ + { USB_DEVICE(0x1199, 0x0224) }, /* Sierra Wireless MC5727 */ { USB_DEVICE(0x1199, 0x0019) }, /* Sierra Wireless AirCard 595 */ { USB_DEVICE(0x1199, 0x0021) }, /* Sierra Wireless AirCard 597E */ + { USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */ { USB_DEVICE(0x1199, 0x0120) }, /* Sierra Wireless USB Dongle 595U */ - /* Sierra Wireless C597 */ + /* Sierra Wireless C597 */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0023, 0xFF, 0xFF, 0xFF) }, - /* Sierra Wireless Device */ + /* Sierra Wireless T598 */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x0025, 0xFF, 0xFF, 0xFF) }, - { USB_DEVICE(0x1199, 0x0026) }, /* Sierra Wireless Device */ - { USB_DEVICE(0x1199, 0x0027) }, /* Sierra Wireless Device */ - { USB_DEVICE(0x1199, 0x0028) }, /* Sierra Wireless Device */ + { USB_DEVICE(0x1199, 0x0026) }, /* Sierra Wireless T11 */ + { USB_DEVICE(0x1199, 0x0027) }, /* Sierra Wireless AC402 */ + { USB_DEVICE(0x1199, 0x0028) }, /* Sierra Wireless MC5728 */ + { USB_DEVICE(0x1199, 0x0029) }, /* Sierra Wireless Device */ { USB_DEVICE(0x1199, 0x6802) }, /* Sierra Wireless MC8755 */ - { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ { USB_DEVICE(0x1199, 0x6803) }, /* Sierra Wireless MC8765 */ + { USB_DEVICE(0x1199, 0x6804) }, /* Sierra Wireless MC8755 */ + { USB_DEVICE(0x1199, 0x6805) }, /* Sierra Wireless MC8765 */ + { USB_DEVICE(0x1199, 0x6808) }, /* Sierra Wireless MC8755 */ + { USB_DEVICE(0x1199, 0x6809) }, /* Sierra Wireless MC8765 */ { USB_DEVICE(0x1199, 0x6812) }, /* Sierra Wireless MC8775 & AC 875U */ - { USB_DEVICE(0x1199, 0x6813) }, /* Sierra Wireless MC8775 (Lenovo) */ + { USB_DEVICE(0x1199, 0x6813) }, /* Sierra Wireless MC8775 */ { USB_DEVICE(0x1199, 0x6815) }, /* Sierra Wireless MC8775 */ - { USB_DEVICE(0x03f0, 0x1e1d) }, /* HP hs2300 a.k.a MC8775 */ + { USB_DEVICE(0x1199, 0x6816) }, /* Sierra Wireless MC8775 */ { USB_DEVICE(0x1199, 0x6820) }, /* Sierra Wireless AirCard 875 */ { USB_DEVICE(0x1199, 0x6821) }, /* Sierra Wireless AirCard 875U */ + { USB_DEVICE(0x1199, 0x6822) }, /* Sierra Wireless AirCard 875E */ { USB_DEVICE(0x1199, 0x6832) }, /* Sierra Wireless MC8780 */ { USB_DEVICE(0x1199, 0x6833) }, /* Sierra Wireless MC8781 */ + { USB_DEVICE(0x1199, 0x6834) }, /* Sierra Wireless MC8780 */ + { USB_DEVICE(0x1199, 0x6835) }, /* Sierra Wireless MC8781 */ + { USB_DEVICE(0x1199, 0x6838) }, /* Sierra Wireless MC8780 */ + { USB_DEVICE(0x1199, 0x6839) }, /* Sierra Wireless MC8781 */ { USB_DEVICE(0x1199, 0x683A) }, /* Sierra Wireless MC8785 */ { USB_DEVICE(0x1199, 0x683B) }, /* Sierra Wireless MC8785 Composite */ /* Sierra Wireless MC8790, MC8791, MC8792 Composite */ @@ -227,16 +242,13 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x1199, 0x685A) }, /* Sierra Wireless AirCard 885 E */ /* Sierra Wireless C885 */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6880, 0xFF, 0xFF, 0xFF)}, - /* Sierra Wireless Device */ + /* Sierra Wireless C888, Air Card 501, USB 303, USB 304 */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6890, 0xFF, 0xFF, 0xFF)}, - /* Sierra Wireless Device */ + /* Sierra Wireless C22/C33 */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6891, 0xFF, 0xFF, 0xFF)}, - /* Sierra Wireless Device */ + /* Sierra Wireless HSPA Non-Composite Device */ { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x6892, 0xFF, 0xFF, 0xFF)}, - - { USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless AirCard 580 */ - { USB_DEVICE(0x0F3D, 0x0112) }, /* Airprime/Sierra PC 5220 */ - + { USB_DEVICE(0x1199, 0x6893) }, /* Sierra Wireless Device */ { USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */ .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist }, @@ -814,7 +826,7 @@ static int sierra_startup(struct usb_serial *serial) return 0; } -static void sierra_disconnect(struct usb_serial *serial) +static void sierra_release(struct usb_serial *serial) { int i; struct usb_serial_port *port; @@ -830,7 +842,6 @@ static void sierra_disconnect(struct usb_serial *serial) if (!portdata) continue; kfree(portdata); - usb_set_serial_port_data(port, NULL); } } @@ -853,7 +864,7 @@ static struct usb_serial_driver sierra_device = { .tiocmget = sierra_tiocmget, .tiocmset = sierra_tiocmset, .attach = sierra_startup, - .disconnect = sierra_disconnect, + .release = sierra_release, .read_int_callback = sierra_instat_callback, }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 991d8232e37..14971a92699 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -191,7 +191,6 @@ static struct usb_device_id ti_id_table_5052[5+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, - { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, }; static struct usb_device_id ti_id_table_combined[14+2*TI_EXTRA_VID_PID_COUNT+1] = { @@ -1658,7 +1657,7 @@ static int ti_do_download(struct usb_device *dev, int pipe, u8 cs = 0; int done; struct ti_firmware_header *header; - int status; + int status = 0; int len; for (pos = sizeof(struct ti_firmware_header); pos < size; pos++) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index a84216464ca..bd7581b3a48 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -21,6 +21,7 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> +#include <linux/smp_lock.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> @@ -220,7 +221,8 @@ static int serial_open (struct tty_struct *tty, struct file *filp) tty->driver_data = port; tty_port_tty_set(&port->port, tty); - if (port->port.count == 1) { + /* If the console is attached, the device is already open */ + if (port->port.count == 1 && !port->console) { /* lock this module before we call it * this may fail, which means we must bail out, diff --git a/drivers/usb/storage/option_ms.c b/drivers/usb/storage/option_ms.c index d41cc0a970f..773a5cd38c5 100644 --- a/drivers/usb/storage/option_ms.c +++ b/drivers/usb/storage/option_ms.c @@ -118,6 +118,9 @@ static int option_inquiry(struct us_data *us) result = memcmp(buffer+8, "Option", 6); + if (result != 0) + result = memcmp(buffer+8, "ZCOPTION", 8); + /* Read the CSW */ usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, |