From 316541bf70281b6c3b501b442b6994c1839776ad Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 11 Jul 2011 22:59:36 +0200 Subject: USB: Remove test for NULL that'll never happen in usb_disconnect() In drivers/usb/core/hub.c::usb_disconnect(), 'udev' will never be NULL, so remove the test and printing of debug message. Signed-off-by: Jesper Juhl Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a428aa0..99fff6b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1636,11 +1636,6 @@ void usb_disconnect(struct usb_device **pdev) int i; struct usb_hcd *hcd = bus_to_hcd(udev->bus); - if (!udev) { - pr_debug ("%s nodev\n", __func__); - return; - } - /* mark the device as inactive, so any further urb submissions for * this device (and any of its children) will fail immediately. * this quiesces everything except pending urbs. -- cgit v0.10.2 From f02fe890ece7d695a5744b20525d45312382e6e4 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Tue, 26 Jul 2011 09:56:07 -0500 Subject: usb_storage: Don't freeze in usb-stor-scan Scanning cannot be run during suspend or hibernation, but if usb-stor-scan freezes another thread waiting on scanning to complete may fail to freeze. However, if usb-stor-scan is left freezable without ever actually freezing then the freezer will wait on it to exit, and threads waiting for scanning to finish will no longer be blocked. One problem with this approach is that usb-stor-scan has a delay to wait for devices to settle (which is currently the only point where it can freeze). To work around this we can request that the freezer send a fake signal when freezing, then use interruptible sleep to wake the thread early when freezing happens. To make this happen, the following changes are made to usb-stor-scan: * Use set_freezable_with_signal() instead of set_freezable() to request a fake signal when freezing * Use wait_event_interruptible_timeout() instead of wait_event_freezable_timeout() to avoid freezing Signed-off-by: Seth Forshee Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 0ca0958..c325e69 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -831,12 +831,22 @@ static int usb_stor_scan_thread(void * __us) dev_dbg(dev, "device found\n"); - set_freezable(); - /* Wait for the timeout to expire or for a disconnect */ + set_freezable_with_signal(); + /* + * Wait for the timeout to expire or for a disconnect + * + * We can't freeze in this thread or we risk causing khubd to + * fail to freeze, but we can't be non-freezable either. Nor can + * khubd freeze while waiting for scanning to complete as it may + * hold the device lock, causing a hang when suspending devices. + * So we request a fake signal when freezing and use + * interruptible sleep to kick us out of our wait early when + * freezing happens. + */ if (delay_use > 0) { dev_dbg(dev, "waiting for device to settle " "before scanning\n"); - wait_event_freezable_timeout(us->delay_wait, + wait_event_interruptible_timeout(us->delay_wait, test_bit(US_FLIDX_DONT_SCAN, &us->dflags), delay_use * HZ); } -- cgit v0.10.2 From 53e77df25f64567ee1f55e7d76b8843689c79d9e Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 13 Jul 2011 13:54:48 +0200 Subject: USB: ipw: convert to usb-wwan framework From 2d487c10136f76cf3705881d34868e8592839cfe Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 12 Jul 2011 15:36:51 +0200 Subject: [PATCH] USB: ipw: convert to usb-wwan framework This patch allows the ipw driver to use the multibuffer infrastructure of usb-wwan. This improves speed. Signed-off-by: Oliver Neukum Tested-by: Michal Hybner Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index ca77e88..5170799 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -47,6 +47,7 @@ #include #include #include +#include "usb-wwan.h" /* * Version Information @@ -185,7 +186,7 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port) /*--2: Start reading from the device */ dbg("%s: setting up bulk read callback", __func__); - usb_serial_generic_open(tty, port); + usb_wwan_open(tty, port); /*--3: Tell the modem to open the floodgates on the rx bulk channel */ dbg("%s:asking modem for RxRead (RXBULK_ON)", __func__); @@ -219,6 +220,29 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port) return 0; } +/* fake probe - only to allocate data structures */ +static int ipw_probe(struct usb_serial *serial, const struct usb_device_id *id) +{ + struct usb_wwan_intf_private *data; + + data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); + if (!data) + return -ENOMEM; + + spin_lock_init(&data->susp_lock); + usb_set_serial_data(serial, data); + return 0; +} + +static void ipw_release(struct usb_serial *serial) +{ + struct usb_wwan_intf_private *data = usb_get_serial_data(serial); + + usb_wwan_release(serial); + usb_set_serial_data(serial, NULL); + kfree(data); +} + static void ipw_dtr_rts(struct usb_serial_port *port, int on) { struct usb_device *dev = port->serial->dev; @@ -285,7 +309,7 @@ static void ipw_close(struct usb_serial_port *port) dev_err(&port->dev, "Disabling bulk RxRead failed (error = %d)\n", result); - usb_serial_generic_close(port); + usb_wwan_close(port); } static struct usb_serial_driver ipw_device = { @@ -297,9 +321,14 @@ static struct usb_serial_driver ipw_device = { .usb_driver = &usb_ipw_driver, .id_table = usb_ipw_ids, .num_ports = 1, + .disconnect = usb_wwan_disconnect, .open = ipw_open, .close = ipw_close, + .probe = ipw_probe, + .attach = usb_wwan_startup, + .release = ipw_release, .dtr_rts = ipw_dtr_rts, + .write = usb_wwan_write, }; -- cgit v0.10.2 From ea835863950dce6f9c2bddc97d8fc67e1d01a2b9 Mon Sep 17 00:00:00 2001 From: Zack Parsons Date: Fri, 29 Jul 2011 00:17:57 -0700 Subject: usb: misc: usbled: fixed coding style issues Fixed multiple coding style issues Signed-off-by: Zack Parsons Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c index 1616ad1..43f84e5 100644 --- a/drivers/usb/misc/usbled.c +++ b/drivers/usb/misc/usbled.c @@ -33,10 +33,10 @@ static const struct usb_device_id id_table[] = { .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER }, { }, }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); struct usb_led { - struct usb_device * udev; + struct usb_device *udev; unsigned char blue; unsigned char red; unsigned char green; @@ -113,14 +113,16 @@ static void change_color(struct usb_led *led) } #define show_set(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ +static ssize_t show_##value(struct device *dev, struct device_attribute *attr,\ + char *buf) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ struct usb_led *led = usb_get_intfdata(intf); \ \ return sprintf(buf, "%d\n", led->value); \ } \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +static ssize_t set_##value(struct device *dev, struct device_attribute *attr,\ + const char *buf, size_t count) \ { \ struct usb_interface *intf = to_usb_interface(dev); \ struct usb_led *led = usb_get_intfdata(intf); \ @@ -135,7 +137,8 @@ show_set(blue); show_set(red); show_set(green); -static int led_probe(struct usb_interface *interface, const struct usb_device_id *id) +static int led_probe(struct usb_interface *interface, + const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(interface); struct usb_led *dev = NULL; @@ -150,7 +153,7 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id dev->udev = usb_get_dev(udev); dev->type = id->driver_info; - usb_set_intfdata (interface, dev); + usb_set_intfdata(interface, dev); retval = device_create_file(&interface->dev, &dev_attr_blue); if (retval) @@ -194,7 +197,7 @@ error: device_remove_file(&interface->dev, &dev_attr_blue); device_remove_file(&interface->dev, &dev_attr_red); device_remove_file(&interface->dev, &dev_attr_green); - usb_set_intfdata (interface, NULL); + usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); kfree(dev); error_mem: @@ -205,14 +208,14 @@ static void led_disconnect(struct usb_interface *interface) { struct usb_led *dev; - dev = usb_get_intfdata (interface); + dev = usb_get_intfdata(interface); device_remove_file(&interface->dev, &dev_attr_blue); device_remove_file(&interface->dev, &dev_attr_red); device_remove_file(&interface->dev, &dev_attr_green); /* first remove the files, then set the pointer to NULL */ - usb_set_intfdata (interface, NULL); + usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); @@ -243,8 +246,8 @@ static void __exit usb_led_exit(void) usb_deregister(&led_driver); } -module_init (usb_led_init); -module_exit (usb_led_exit); +module_init(usb_led_init); +module_exit(usb_led_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); -- cgit v0.10.2 From 507a3ea7f20b0964e459f0ed5125717e8b46d159 Mon Sep 17 00:00:00 2001 From: Zack Parsons Date: Thu, 28 Jul 2011 18:58:30 -0700 Subject: usb: misc: usblcd: fixed coding style issues Fixed multiple coding style issues Signed-off-by: Zack Parsons Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 5164815..6ac1069 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #define DRIVER_VERSION "USBLCD Driver Version 1.05" @@ -34,22 +34,29 @@ static const struct usb_device_id id_table[] = { { .idVendor = 0x10D2, .match_flags = USB_DEVICE_ID_MATCH_VENDOR, }, { }, }; -MODULE_DEVICE_TABLE (usb, id_table); +MODULE_DEVICE_TABLE(usb, id_table); static DEFINE_MUTEX(open_disc_mutex); struct usb_lcd { - struct usb_device * udev; /* init: probe_lcd */ - struct usb_interface * interface; /* the interface for this device */ - unsigned char * bulk_in_buffer; /* the buffer to receive data */ - size_t bulk_in_size; /* the size of the receive buffer */ - __u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */ - __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ + struct usb_device *udev; /* init: probe_lcd */ + struct usb_interface *interface; /* the interface for + this device */ + unsigned char *bulk_in_buffer; /* the buffer to receive + data */ + size_t bulk_in_size; /* the size of the + receive buffer */ + __u8 bulk_in_endpointAddr; /* the address of the + bulk in endpoint */ + __u8 bulk_out_endpointAddr; /* the address of the + bulk out endpoint */ struct kref kref; - struct semaphore limit_sem; /* to stop writes at full throttle from - * using up all RAM */ - struct usb_anchor submitted; /* URBs to wait for before suspend */ + struct semaphore limit_sem; /* to stop writes at + full throttle from + using up all RAM */ + struct usb_anchor submitted; /* URBs to wait for + before suspend */ }; #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) @@ -63,8 +70,8 @@ static void lcd_delete(struct kref *kref) struct usb_lcd *dev = to_lcd_dev(kref); usb_put_dev(dev->udev); - kfree (dev->bulk_in_buffer); - kfree (dev); + kfree(dev->bulk_in_buffer); + kfree(dev); } @@ -80,7 +87,7 @@ static int lcd_open(struct inode *inode, struct file *file) interface = usb_find_interface(&lcd_driver, subminor); if (!interface) { mutex_unlock(&lcd_mutex); - err ("USBLCD: %s - error, can't find device for minor %d", + err("USBLCD: %s - error, can't find device for minor %d", __func__, subminor); return -ENODEV; } @@ -126,7 +133,8 @@ static int lcd_release(struct inode *inode, struct file *file) return 0; } -static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, loff_t *ppos) +static ssize_t lcd_read(struct file *file, char __user * buffer, + size_t count, loff_t *ppos) { struct usb_lcd *dev; int retval = 0; @@ -135,8 +143,9 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, l dev = file->private_data; /* do a blocking bulk read to get data from the device */ - retval = usb_bulk_msg(dev->udev, - usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr), + retval = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpointAddr), dev->bulk_in_buffer, min(dev->bulk_in_size, count), &bytes_read, 10000); @@ -161,23 +170,23 @@ static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) dev = file->private_data; if (dev == NULL) return -ENODEV; - + switch (cmd) { case IOCTL_GET_HARD_VERSION: mutex_lock(&lcd_mutex); bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice); - sprintf(buf,"%1d%1d.%1d%1d", + sprintf(buf, "%1d%1d.%1d%1d", (bcdDevice & 0xF000)>>12, (bcdDevice & 0xF00)>>8, (bcdDevice & 0xF0)>>4, (bcdDevice & 0xF)); mutex_unlock(&lcd_mutex); - if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0) + if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0) return -EFAULT; break; case IOCTL_GET_DRV_VERSION: - sprintf(buf,DRIVER_VERSION); - if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0) + sprintf(buf, DRIVER_VERSION); + if (copy_to_user((void __user *)arg, buf, strlen(buf)) != 0) return -EFAULT; break; default: @@ -199,7 +208,7 @@ static void lcd_write_bulk_callback(struct urb *urb) if (status && !(status == -ENOENT || status == -ECONNRESET || - status == -ESHUTDOWN)) { + status == -ESHUTDOWN)) { dbg("USBLCD: %s - nonzero write bulk status received: %d", __func__, status); } @@ -210,15 +219,16 @@ static void lcd_write_bulk_callback(struct urb *urb) up(&dev->limit_sem); } -static ssize_t lcd_write(struct file *file, const char __user * user_buffer, size_t count, loff_t *ppos) +static ssize_t lcd_write(struct file *file, const char __user * user_buffer, + size_t count, loff_t *ppos) { struct usb_lcd *dev; - int retval = 0, r; + int retval = 0, r; struct urb *urb = NULL; char *buf = NULL; - + dev = file->private_data; - + /* verify that we actually have some data to write */ if (count == 0) goto exit; @@ -233,34 +243,38 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, siz retval = -ENOMEM; goto err_no_buf; } - - buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, &urb->transfer_dma); + + buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL, + &urb->transfer_dma); if (!buf) { retval = -ENOMEM; goto error; } - + if (copy_from_user(buf, user_buffer, count)) { retval = -EFAULT; goto error; } - + /* initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, - usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), + usb_sndbulkpipe(dev->udev, + dev->bulk_out_endpointAddr), buf, count, lcd_write_bulk_callback, dev); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_anchor_urb(urb, &dev->submitted); - + /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); if (retval) { - err("USBLCD: %s - failed submitting write urb, error %d", __func__, retval); + err("USBLCD: %s - failed submitting write urb, error %d", + __func__, retval); goto error_unanchor; } - - /* release our reference to this urb, the USB core will eventually free it entirely */ + + /* release our reference to this urb, + the USB core will eventually free it entirely */ usb_free_urb(urb); exit: @@ -276,13 +290,13 @@ err_no_buf: } static const struct file_operations lcd_fops = { - .owner = THIS_MODULE, - .read = lcd_read, - .write = lcd_write, - .open = lcd_open, + .owner = THIS_MODULE, + .read = lcd_read, + .write = lcd_write, + .open = lcd_open, .unlocked_ioctl = lcd_ioctl, - .release = lcd_release, - .llseek = noop_llseek, + .release = lcd_release, + .llseek = noop_llseek, }; /* @@ -290,12 +304,13 @@ static const struct file_operations lcd_fops = { * and to have the device registered with the driver core */ static struct usb_class_driver lcd_class = { - .name = "lcd%d", - .fops = &lcd_fops, - .minor_base = USBLCD_MINOR, + .name = "lcd%d", + .fops = &lcd_fops, + .minor_base = USBLCD_MINOR, }; -static int lcd_probe(struct usb_interface *interface, const struct usb_device_id *id) +static int lcd_probe(struct usb_interface *interface, + const struct usb_device_id *id) { struct usb_lcd *dev = NULL; struct usb_host_interface *iface_desc; @@ -322,7 +337,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id retval = -ENODEV; goto error; } - + /* set up the endpoint information */ /* use only the first bulk-in and bulk-out endpoints */ iface_desc = interface->cur_altsetting; @@ -369,7 +384,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id dev_info(&interface->dev, "USBLCD Version %1d%1d.%1d%1d found " "at address %d\n", (i & 0xF000)>>12, (i & 0xF00)>>8, - (i & 0xF0)>>4,(i & 0xF), dev->udev->devnum); + (i & 0xF0)>>4, (i & 0xF), dev->udev->devnum); /* let the user know what node this device is now attached to */ dev_info(&interface->dev, "USB LCD device now attached to USBLCD-%d\n", @@ -401,7 +416,7 @@ static int lcd_suspend(struct usb_interface *intf, pm_message_t message) return 0; } -static int lcd_resume (struct usb_interface *intf) +static int lcd_resume(struct usb_interface *intf) { return 0; } @@ -409,16 +424,16 @@ static int lcd_resume (struct usb_interface *intf) static void lcd_disconnect(struct usb_interface *interface) { struct usb_lcd *dev; - int minor = interface->minor; + int minor = interface->minor; mutex_lock(&open_disc_mutex); - dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); mutex_unlock(&open_disc_mutex); - /* give back our minor */ - usb_deregister_dev(interface, &lcd_class); - + /* give back our minor */ + usb_deregister_dev(interface, &lcd_class); + /* decrement our usage count */ kref_put(&dev->kref, lcd_delete); @@ -438,7 +453,7 @@ static struct usb_driver lcd_driver = { static int __init usb_lcd_init(void) { int result; - + result = usb_register(&lcd_driver); if (result) err("usb_register failed. Error number %d", result); -- cgit v0.10.2 From e25e0eb209bf945db554512430ee128e6bb7b291 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Sun, 21 Aug 2011 08:29:24 +0200 Subject: usb/isp1760: Move some code (prepare for next patch) Move the few lines of code in isp1760_enable_interrupts() and isp1760_init_maps() into isp1760_run(). This makes the following patch easier. Signed-off-by: Arvid Brodin Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 840beda..a46ccf9 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -514,23 +514,18 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) return priv_init(hcd); } -static void isp1760_init_maps(struct usb_hcd *hcd) +static int isp1760_run(struct usb_hcd *hcd) { - /*set last maps, for iso its only 1, else 32 tds bitmap*/ - reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000); - reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); - reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); + int retval; + u32 temp; + u32 command; + u32 chipid; - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff); - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff); - reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff); + hcd->uses_new_polling = 1; - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, - ATL_BUF_FILL | INT_BUF_FILL); -} + hcd->state = HC_STATE_RUNNING; -static void isp1760_enable_interrupts(struct usb_hcd *hcd) -{ + /* Set PTD interrupt AND & OR maps */ reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0); reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff); reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0); @@ -538,19 +533,7 @@ static void isp1760_enable_interrupts(struct usb_hcd *hcd) reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0); reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff); /* step 23 passed */ -} -static int isp1760_run(struct usb_hcd *hcd) -{ - int retval; - u32 temp; - u32 command; - u32 chipid; - - hcd->uses_new_polling = 1; - - hcd->state = HC_STATE_RUNNING; - isp1760_enable_interrupts(hcd); temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL); reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN); @@ -581,8 +564,16 @@ static int isp1760_run(struct usb_hcd *hcd) chipid & 0xffff, chipid >> 16); /* PTD Register Init Part 2, Step 28 */ - /* enable INTs */ - isp1760_init_maps(hcd); + + /* Setup registers controlling PTD checking */ + reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000); + reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); + reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, + ATL_BUF_FILL | INT_BUF_FILL); /* GRR this is run-once init(), being done every time the HC starts. * So long as they're part of class devices, we can't do it init() -- cgit v0.10.2 From 0ba7905e030a409fd0bb92cb965e915c0cb01030 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Sun, 21 Aug 2011 08:29:25 +0200 Subject: usb/isp1760: Move isp1760_run within file (prepare for next patch) Like the previous patch, this patch has been split from the next one for clarity. Signed-off-by: Arvid Brodin Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index a46ccf9..e399e23 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -514,74 +514,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) return priv_init(hcd); } -static int isp1760_run(struct usb_hcd *hcd) -{ - int retval; - u32 temp; - u32 command; - u32 chipid; - - hcd->uses_new_polling = 1; - - hcd->state = HC_STATE_RUNNING; - - /* Set PTD interrupt AND & OR maps */ - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff); - reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff); - /* step 23 passed */ - - temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL); - reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN); - - command = reg_read32(hcd->regs, HC_USBCMD); - command &= ~(CMD_LRESET|CMD_RESET); - command |= CMD_RUN; - reg_write32(hcd->regs, HC_USBCMD, command); - - retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000); - if (retval) - return retval; - - /* - * XXX - * Spec says to write FLAG_CF as last config action, priv code grabs - * the semaphore while doing so. - */ - down_write(&ehci_cf_port_reset_rwsem); - reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF); - - retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000); - up_write(&ehci_cf_port_reset_rwsem); - if (retval) - return retval; - - chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); - dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", - chipid & 0xffff, chipid >> 16); - - /* PTD Register Init Part 2, Step 28 */ - - /* Setup registers controlling PTD checking */ - reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000); - reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); - reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff); - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff); - reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff); - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, - ATL_BUF_FILL | INT_BUF_FILL); - - /* GRR this is run-once init(), being done every time the HC starts. - * So long as they're part of class devices, we can't do it init() - * since the class device isn't created that early. - */ - return 0; -} - static u32 base_to_chip(u32 base) { return ((base - 0x400) >> 3); @@ -1326,6 +1258,74 @@ leave: return irqret; } +static int isp1760_run(struct usb_hcd *hcd) +{ + int retval; + u32 temp; + u32 command; + u32 chipid; + + hcd->uses_new_polling = 1; + + hcd->state = HC_STATE_RUNNING; + + /* Set PTD interrupt AND & OR maps */ + reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0); + reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff); + reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0); + reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff); + reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0); + reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff); + /* step 23 passed */ + + temp = reg_read32(hcd->regs, HC_HW_MODE_CTRL); + reg_write32(hcd->regs, HC_HW_MODE_CTRL, temp | HW_GLOBAL_INTR_EN); + + command = reg_read32(hcd->regs, HC_USBCMD); + command &= ~(CMD_LRESET|CMD_RESET); + command |= CMD_RUN; + reg_write32(hcd->regs, HC_USBCMD, command); + + retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000); + if (retval) + return retval; + + /* + * XXX + * Spec says to write FLAG_CF as last config action, priv code grabs + * the semaphore while doing so. + */ + down_write(&ehci_cf_port_reset_rwsem); + reg_write32(hcd->regs, HC_CONFIGFLAG, FLAG_CF); + + retval = handshake(hcd, HC_CONFIGFLAG, FLAG_CF, FLAG_CF, 250 * 1000); + up_write(&ehci_cf_port_reset_rwsem); + if (retval) + return retval; + + chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); + dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", + chipid & 0xffff, chipid >> 16); + + /* PTD Register Init Part 2, Step 28 */ + + /* Setup registers controlling PTD checking */ + reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000); + reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); + reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, + ATL_BUF_FILL | INT_BUF_FILL); + + /* GRR this is run-once init(), being done every time the HC starts. + * So long as they're part of class devices, we can't do it init() + * since the class device isn't created that early. + */ + return 0; +} + static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len) { qtd->data_buffer = databuffer; -- cgit v0.10.2 From 6d50c60e6d169b04b0cf0b267eb14ab0839f9540 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Sun, 21 Aug 2011 08:29:26 +0200 Subject: usb/isp1760: Use polling instead of SOF interrupts to fix Errata 2 Errata 2 for the isp1760 explains that the chip sometimes does not issue interrupts when an ATL (bulk or control) transfer is completed. There are several issues with the current work-around (SOF interrupts) for this: 1) It seems the chip sometimes does not even set the done bit for a completed transfer, in which case SOF interrupts does not solve the problem since we still check the done map to find out which transfer descriptors to handle. 2) The above point seems to happen only when ATL and SOF interrupts are enabled at the same time. However, disabling ATL interrupts increases the latency between transfer completion and handling. This is very noticeable in the testusb suite, which take several minutes more to run with ATL interrupts disabled. This patch removes the code to switch on SOF interrupts, and instead use a kernel timer to periodically check for "old" descriptors that have their VALID and ACTIVE flags unset, indicating completion, thus avoiding the dependency on the chip's done map (and SOF interrupts) to find transfers affected by this HW bug. [bigeasy@linutronix: 80 lines limit] Signed-off-by: Arvid Brodin Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index e399e23..14c9238 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -39,7 +40,6 @@ struct isp1760_hcd { int int_done_map; struct memory_chunk memory_pool[BLOCKS]; struct list_head controlqhs, bulkqhs, interruptqhs; - int active_ptds; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 @@ -489,10 +489,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? "analog" : "digital"); - /* This is weird: at the first plug-in of a device there seems to be - one packet queued that never gets returned? */ - priv->active_ptds = -1; - /* ATL reset */ reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET); mdelay(10); @@ -741,8 +737,8 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, qh->slot = slot; qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since interrupt routine may preempt and expects this value. */ + slots[slot].timestamp = jiffies; ptd_write(hcd->regs, ptd_offset, slot, ptd); - priv->active_ptds++; /* Make sure done map has not triggered from some unlinked transfer */ if (ptd_offset == ATL_PTD_OFFSET) { @@ -1091,11 +1087,9 @@ static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd, return PTD_STATE_QTD_DONE; } -static irqreturn_t isp1760_irq(struct usb_hcd *hcd) +static void handle_done_ptds(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 imask; - irqreturn_t irqret = IRQ_NONE; struct ptd ptd; struct isp1760_qh *qh; int slot; @@ -1104,27 +1098,14 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) u32 ptd_offset; struct isp1760_qtd *qtd; int modified; - static int last_active_ptds; - int int_skip_map, atl_skip_map; - - spin_lock(&priv->lock); - - if (!(hcd->state & HC_STATE_RUNNING)) - goto leave; - - imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); - if (unlikely(!imask)) - goto leave; - reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ + int skip_map; - int_skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); - atl_skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); - priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); - priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); - priv->int_done_map &= ~int_skip_map; - priv->atl_done_map &= ~atl_skip_map; + skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); + priv->int_done_map &= ~skip_map; + skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + priv->atl_done_map &= ~skip_map; - modified = priv->int_done_map | priv->atl_done_map; + modified = priv->int_done_map || priv->atl_done_map; while (priv->int_done_map || priv->atl_done_map) { if (priv->int_done_map) { @@ -1163,7 +1144,6 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) slots[slot].qtd = NULL; qh = slots[slot].qh; slots[slot].qh = NULL; - priv->active_ptds--; qh->slot = -1; WARN_ON(qtd->status != QTD_XFER_STARTED); @@ -1234,22 +1214,28 @@ static irqreturn_t isp1760_irq(struct usb_hcd *hcd) if (modified) schedule_ptds(hcd); +} - /* ISP1760 Errata 2 explains that interrupts may be missed (or not - happen?) if two USB devices are running simultaneously. Perhaps - this happens when a PTD is finished during interrupt handling; - enable SOF interrupts if PTDs are still scheduled when exiting this - interrupt handler, just to be safe. */ +static irqreturn_t isp1760_irq(struct usb_hcd *hcd) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + u32 imask; + irqreturn_t irqret = IRQ_NONE; - if (priv->active_ptds != last_active_ptds) { - if (priv->active_ptds > 0) - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_SOT_MASK); - else - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_MASK); - last_active_ptds = priv->active_ptds; - } + spin_lock(&priv->lock); + + if (!(hcd->state & HC_STATE_RUNNING)) + goto leave; + + imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); + if (unlikely(!imask)) + goto leave; + reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ + + priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); + priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); + + handle_done_ptds(hcd); irqret = IRQ_HANDLED; leave: @@ -1258,6 +1244,63 @@ leave: return irqret; } +/* + * Workaround for problem described in chip errata 2: + * + * Sometimes interrupts are not generated when ATL (not INT?) completion occurs. + * One solution suggested in the errata is to use SOF interrupts _instead_of_ + * ATL done interrupts (the "instead of" might be important since it seems + * enabling ATL interrupts also causes the chip to sometimes - rarely - "forget" + * to set the PTD's done bit in addition to not generating an interrupt!). + * + * So if we use SOF + ATL interrupts, we sometimes get stale PTDs since their + * done bit is not being set. This is bad - it blocks the endpoint until reboot. + * + * If we use SOF interrupts only, we get latency between ptd completion and the + * actual handling. This is very noticeable in testusb runs which takes several + * minutes longer without ATL interrupts. + * + * A better solution is to run the code below every SLOT_CHECK_PERIOD ms. If it + * finds active ATL slots which are older than SLOT_TIMEOUT ms, it checks the + * slot's ACTIVE and VALID bits. If these are not set, the ptd is considered + * completed and its done map bit is set. + * + * The values of SLOT_TIMEOUT and SLOT_CHECK_PERIOD have been arbitrarily chosen + * not to cause too much lag when this HW bug occurs, while still hopefully + * ensuring that the check does not falsely trigger. + */ +#define SLOT_TIMEOUT 180 +#define SLOT_CHECK_PERIOD 200 +static struct timer_list errata2_timer; + +void errata2_function(unsigned long data) +{ + struct usb_hcd *hcd = (struct usb_hcd *) data; + struct isp1760_hcd *priv = hcd_to_priv(hcd); + int slot; + struct ptd ptd; + unsigned long spinflags; + + spin_lock_irqsave(&priv->lock, spinflags); + + for (slot = 0; slot < 32; slot++) + if ((priv->atl_slots[slot].qh || priv->atl_slots[slot].qtd) && + time_after(jiffies + SLOT_TIMEOUT * HZ / 1000, + priv->atl_slots[slot].timestamp)) { + ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); + if (!FROM_DW0_VALID(ptd.dw0) && + !FROM_DW3_ACTIVE(ptd.dw3)) + priv->atl_done_map |= 1 << slot; + } + + handle_done_ptds(hcd); + + spin_unlock_irqrestore(&priv->lock, spinflags); + + errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000; + add_timer(&errata2_timer); +} + static int isp1760_run(struct usb_hcd *hcd) { int retval; @@ -1303,6 +1346,12 @@ static int isp1760_run(struct usb_hcd *hcd) if (retval) return retval; + init_timer(&errata2_timer); + errata2_timer.function = errata2_function; + errata2_timer.data = (unsigned long) hcd; + errata2_timer.expires = jiffies + SLOT_CHECK_PERIOD * HZ / 1000; + add_timer(&errata2_timer); + chipid = reg_read32(hcd->regs, HC_CHIP_ID_REG); dev_info(hcd->self.controller, "USB ISP %04x HW rev. %d started\n", chipid & 0xffff, chipid >> 16); @@ -1561,7 +1610,6 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb, } qh->slot = -1; - priv->active_ptds--; } static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, @@ -2012,6 +2060,8 @@ static void isp1760_stop(struct usb_hcd *hcd) struct isp1760_hcd *priv = hcd_to_priv(hcd); u32 temp; + del_timer(&errata2_timer); + isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1, NULL, 0); mdelay(20); diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 014a7df..fda0f2d 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -73,7 +73,6 @@ void deinit_kmem_cache(void); #define HC_EOT_INT (1 << 3) #define HC_SOT_INT (1 << 1) #define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT) -#define INTERRUPT_ENABLE_SOT_MASK (HC_SOT_INT) #define HC_ISO_IRQ_MASK_OR_REG 0x318 #define HC_INT_IRQ_MASK_OR_REG 0x31C @@ -107,6 +106,7 @@ struct ptd { struct slotinfo { struct isp1760_qh *qh; struct isp1760_qtd *qtd; + unsigned long timestamp; }; @@ -188,6 +188,7 @@ struct memory_chunk { #define DW3_BABBLE_BIT (1 << 29) #define DW3_HALT_BIT (1 << 30) #define DW3_ACTIVE_BIT (1 << 31) +#define FROM_DW3_ACTIVE(x) (((x) >> 31) & 0x01) #define INT_UNDERRUN (1 << 2) #define INT_BABBLE (1 << 1) -- cgit v0.10.2 From 38679b720014d89b192c38d4c604f5a718b9788d Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Sun, 21 Aug 2011 08:29:27 +0200 Subject: usb/isp1760: Fix missing endpoint unlink when no mem during enqueue ... and some small code style fixes. Signed-off-by: Arvid Brodin Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 14c9238..1eb69e0 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -777,10 +777,7 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, if (qtd->status < QTD_XFER_COMPLETE) break; - if (list_is_last(&qtd->qtd_list, &qh->qtd_list)) - last_qtd = 1; - else - last_qtd = qtd->urb != qtd_next->urb; + last_qtd = last_qtd_of_urb(qtd, qh); if ((!last_qtd) && (qtd->status == QTD_RETIRE)) qtd_next->status = QTD_RETIRE; @@ -821,7 +818,7 @@ static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, urb_listitem = kmem_cache_zalloc(urb_listitem_cachep, GFP_ATOMIC); if (unlikely(!urb_listitem)) - break; + break; /* Try again on next call */ urb_listitem->urb = qtd->urb; list_add_tail(&urb_listitem->urb_list, urb_list); } @@ -1543,7 +1540,6 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, packetize_urb(hcd, urb, &new_qtds, mem_flags); if (list_empty(&new_qtds)) return -ENOMEM; - urb->hcpriv = NULL; /* Used to signal unlink to interrupt handler */ retval = 0; spin_lock_irqsave(&priv->lock, spinflags); @@ -1571,6 +1567,7 @@ static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, qh = qh_alloc(GFP_ATOMIC); if (!qh) { retval = -ENOMEM; + usb_hcd_unlink_urb_from_ep(hcd, urb); goto out; } list_add_tail(&qh->qh_list, ep_queue); -- cgit v0.10.2 From 6477acc0814fc7aadfe3d723d2596fb4eb6d1732 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Sun, 21 Aug 2011 08:29:28 +0200 Subject: usb/isp1760: Fix problems that trigger WARNING at line 1136. 1) A bug in the usage of time_after() in errata2_function(). 2) Clear done_maps just prior to starting a new transfer in start_bus_transfer(), instead of just after, when done_map bits might have been validly set by the started transfer. Signed-off-by: Arvid Brodin Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 1eb69e0..11367b4 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -732,28 +732,29 @@ static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, WARN_ON(slots[slot].qh); WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC); - slots[slot].qtd = qtd; - slots[slot].qh = qh; - qh->slot = slot; - qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since - interrupt routine may preempt and expects this value. */ - slots[slot].timestamp = jiffies; - ptd_write(hcd->regs, ptd_offset, slot, ptd); - /* Make sure done map has not triggered from some unlinked transfer */ if (ptd_offset == ATL_PTD_OFFSET) { priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); - priv->atl_done_map &= ~(1 << qh->slot); + priv->atl_done_map &= ~(1 << slot); + } else { + priv->int_done_map |= reg_read32(hcd->regs, + HC_INT_PTD_DONEMAP_REG); + priv->int_done_map &= ~(1 << slot); + } + qh->slot = slot; + qtd->status = QTD_XFER_STARTED; + slots[slot].timestamp = jiffies; + slots[slot].qtd = qtd; + slots[slot].qh = qh; + ptd_write(hcd->regs, ptd_offset, slot, ptd); + + if (ptd_offset == ATL_PTD_OFFSET) { skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); skip_map &= ~(1 << qh->slot); reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); } else { - priv->int_done_map |= reg_read32(hcd->regs, - HC_INT_PTD_DONEMAP_REG); - priv->int_done_map &= ~(1 << qh->slot); - skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); skip_map &= ~(1 << qh->slot); reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); @@ -1266,7 +1267,7 @@ leave: * not to cause too much lag when this HW bug occurs, while still hopefully * ensuring that the check does not falsely trigger. */ -#define SLOT_TIMEOUT 180 +#define SLOT_TIMEOUT 300 #define SLOT_CHECK_PERIOD 200 static struct timer_list errata2_timer; @@ -1281,16 +1282,17 @@ void errata2_function(unsigned long data) spin_lock_irqsave(&priv->lock, spinflags); for (slot = 0; slot < 32; slot++) - if ((priv->atl_slots[slot].qh || priv->atl_slots[slot].qtd) && - time_after(jiffies + SLOT_TIMEOUT * HZ / 1000, - priv->atl_slots[slot].timestamp)) { + if (priv->atl_slots[slot].qh && time_after(jiffies, + priv->atl_slots[slot].timestamp + + SLOT_TIMEOUT * HZ / 1000)) { ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); if (!FROM_DW0_VALID(ptd.dw0) && !FROM_DW3_ACTIVE(ptd.dw3)) priv->atl_done_map |= 1 << slot; } - handle_done_ptds(hcd); + if (priv->atl_done_map) + handle_done_ptds(hcd); spin_unlock_irqrestore(&priv->lock, spinflags); -- cgit v0.10.2 From 7a01f496c5218d98ea4542f74fccb60c23b6185c Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 27 Jul 2011 08:57:25 +0200 Subject: usb: isp1362-hcd: Quieten printks These messages just clutter the log and provide no useful information to the user, so make them pr_debug(). Signed-off-by: Tobias Klauser Acked-by: Mike Frysinger Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 9c37dad..21efca9 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2358,7 +2358,7 @@ static int isp1362_hc_reset(struct usb_hcd *hcd) unsigned long flags; int clkrdy = 0; - pr_info("%s:\n", __func__); + pr_debug("%s:\n", __func__); if (isp1362_hcd->board && isp1362_hcd->board->reset) { isp1362_hcd->board->reset(hcd->self.controller, 1); @@ -2395,7 +2395,7 @@ static void isp1362_hc_stop(struct usb_hcd *hcd) unsigned long flags; u32 tmp; - pr_info("%s:\n", __func__); + pr_debug("%s:\n", __func__); del_timer_sync(&hcd->rh_timer); @@ -2523,7 +2523,7 @@ static int isp1362_hc_start(struct usb_hcd *hcd) u16 chipid; unsigned long flags; - pr_info("%s:\n", __func__); + pr_debug("%s:\n", __func__); spin_lock_irqsave(&isp1362_hcd->lock, flags); chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID); -- cgit v0.10.2 From 3abd7f68b28dcf6394c71c998376fc7bc92342c4 Mon Sep 17 00:00:00 2001 From: Tanmay Upadhyay Date: Wed, 20 Jul 2011 10:00:58 +0530 Subject: USB: pxa168: Add onchip USB host controller support - Add EHCI Host controller driver - Add wrapper that creates resources for host controller driver v2 - Call clk_put() after clk_disable() in probe function Signed-off-by: Tanmay Upadhyay Signed-off-by: Greg Kroah-Hartman diff --git a/arch/arm/mach-mmp/include/mach/pxa168.h b/arch/arm/mach-mmp/include/mach/pxa168.h index 7f00584..7fb568d 100644 --- a/arch/arm/mach-mmp/include/mach/pxa168.h +++ b/arch/arm/mach-mmp/include/mach/pxa168.h @@ -35,6 +35,13 @@ extern struct pxa_device_desc pxa168_device_fb; extern struct pxa_device_desc pxa168_device_keypad; extern struct pxa_device_desc pxa168_device_eth; +struct pxa168_usb_pdata { + /* If NULL, default phy init routine for PXA168 would be called */ + int (*phy_init)(void __iomem *usb_phy_reg_base); +}; +/* pdata can be NULL */ +int __init pxa168_add_usb_host(struct pxa168_usb_pdata *pdata); + static inline int pxa168_add_uart(int id) { struct pxa_device_desc *d = NULL; diff --git a/arch/arm/mach-mmp/pxa168.c b/arch/arm/mach-mmp/pxa168.c index 0156f53..b2b280c 100644 --- a/arch/arm/mach-mmp/pxa168.c +++ b/arch/arm/mach-mmp/pxa168.c @@ -25,6 +25,9 @@ #include #include #include +#include +#include +#include #include "common.h" #include "clock.h" @@ -83,6 +86,7 @@ static APBC_CLK(keypad, PXA168_KPC, 0, 32000); static APMU_CLK(nand, NAND, 0x19b, 156000000); static APMU_CLK(lcd, LCD, 0x7f, 312000000); static APMU_CLK(eth, ETH, 0x09, 0); +static APMU_CLK(usb, USB, 0x12, 0); /* device and clock bindings */ static struct clk_lookup pxa168_clkregs[] = { @@ -104,6 +108,7 @@ static struct clk_lookup pxa168_clkregs[] = { INIT_CLKREG(&clk_lcd, "pxa168-fb", NULL), INIT_CLKREG(&clk_keypad, "pxa27x-keypad", NULL), INIT_CLKREG(&clk_eth, "pxa168-eth", "MFUCLK"), + INIT_CLKREG(&clk_usb, "pxa168-ehci", "PXA168-USBCLK"), }; static int __init pxa168_init(void) @@ -169,3 +174,44 @@ PXA168_DEVICE(ssp5, "pxa168-ssp", 4, SSP5, 0xd4021000, 0x40, 60, 61); PXA168_DEVICE(fb, "pxa168-fb", -1, LCD, 0xd420b000, 0x1c8); PXA168_DEVICE(keypad, "pxa27x-keypad", -1, KEYPAD, 0xd4012000, 0x4c); PXA168_DEVICE(eth, "pxa168-eth", -1, MFU, 0xc0800000, 0x0fff); + +struct resource pxa168_usb_host_resources[] = { + /* USB Host conroller register base */ + [0] = { + .start = 0xd4209000, + .end = 0xd4209000 + 0x200, + .flags = IORESOURCE_MEM, + .name = "pxa168-usb-host", + }, + /* USB PHY register base */ + [1] = { + .start = 0xd4206000, + .end = 0xd4206000 + 0xff, + .flags = IORESOURCE_MEM, + .name = "pxa168-usb-phy", + }, + [2] = { + .start = IRQ_PXA168_USB2, + .end = IRQ_PXA168_USB2, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 pxa168_usb_host_dmamask = DMA_BIT_MASK(32); +struct platform_device pxa168_device_usb_host = { + .name = "pxa168-ehci", + .id = -1, + .dev = { + .dma_mask = &pxa168_usb_host_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, + + .num_resources = ARRAY_SIZE(pxa168_usb_host_resources), + .resource = pxa168_usb_host_resources, +}; + +int __init pxa168_add_usb_host(struct pxa168_usb_pdata *pdata) +{ + pxa168_device_usb_host.dev.platform_data = pdata; + return platform_device_register(&pxa168_device_usb_host); +} diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 48f1781..7e7f42b 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -69,6 +69,7 @@ config USB_ARCH_HAS_EHCI default y if ARCH_MSM default y if MICROBLAZE default y if SPARC_LEON + default y if ARCH_MMP default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ab085f1..5791fe9 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -578,3 +578,10 @@ config USB_OCTEON_OHCI config USB_OCTEON2_COMMON bool default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI + +config USB_PXA168_EHCI + bool "Marvell PXA168 on-chip EHCI HCD support" + depends on USB_EHCI_HCD && ARCH_MMP + help + Enable support for Marvell PXA168 SoC's on-chip EHCI + host controller diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f72ae0b..750f60f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1291,6 +1291,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_grlib_driver #endif +#ifdef CONFIG_USB_PXA168_EHCI +#include "ehci-pxa168.c" +#define PLATFORM_DRIVER ehci_pxa168_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-pxa168.c b/drivers/usb/host/ehci-pxa168.c new file mode 100644 index 0000000..ac0c16e --- /dev/null +++ b/drivers/usb/host/ehci-pxa168.c @@ -0,0 +1,363 @@ +/* + * drivers/usb/host/ehci-pxa168.c + * + * Tanmay Upadhyay + * + * Based on drivers/usb/host/ehci-orion.c + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#define USB_PHY_CTRL_REG 0x4 +#define USB_PHY_PLL_REG 0x8 +#define USB_PHY_TX_REG 0xc + +#define FBDIV_SHIFT 4 + +#define ICP_SHIFT 12 +#define ICP_15 2 +#define ICP_20 3 +#define ICP_25 4 + +#define KVCO_SHIFT 15 + +#define PLLCALI12_SHIFT 25 +#define CALI12_VDD 0 +#define CALI12_09 1 +#define CALI12_10 2 +#define CALI12_11 3 + +#define PLLVDD12_SHIFT 27 +#define VDD12_VDD 0 +#define VDD12_10 1 +#define VDD12_11 2 +#define VDD12_12 3 + +#define PLLVDD18_SHIFT 29 +#define VDD18_19 0 +#define VDD18_20 1 +#define VDD18_21 2 +#define VDD18_22 3 + + +#define PLL_READY (1 << 23) +#define VCOCAL_START (1 << 21) +#define REG_RCAL_START (1 << 12) + +struct pxa168_usb_drv_data { + struct ehci_hcd ehci; + struct clk *pxa168_usb_clk; + struct resource *usb_phy_res; + void __iomem *usb_phy_reg_base; +}; + +static int ehci_pxa168_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + ehci_reset(ehci); + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* + * data structure init + */ + retval = ehci_init(hcd); + if (retval) + return retval; + + hcd->has_tt = 1; + + ehci_port_power(ehci, 0); + + return retval; +} + +static const struct hc_driver ehci_pxa168_hc_driver = { + .description = hcd_name, + .product_desc = "Marvell PXA168 EHCI", + .hcd_priv_size = sizeof(struct pxa168_usb_drv_data), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_pxa168_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int pxa168_usb_phy_init(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *usb_phy_reg_base; + struct pxa168_usb_pdata *pdata; + struct pxa168_usb_drv_data *drv_data; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + unsigned long reg_val; + int pll_retry_cont = 10000, err = 0; + + drv_data = (struct pxa168_usb_drv_data *)hcd->hcd_priv; + pdata = (struct pxa168_usb_pdata *)pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no PHY register addr. Check %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + if (!request_mem_region(res->start, resource_size(res), + ehci_pxa168_hc_driver.description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + return -EBUSY; + } + + usb_phy_reg_base = ioremap(res->start, resource_size(res)); + if (usb_phy_reg_base == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + err = -EFAULT; + goto err1; + } + drv_data->usb_phy_reg_base = usb_phy_reg_base; + drv_data->usb_phy_res = res; + + /* If someone wants to init USB phy in board specific way */ + if (pdata && pdata->phy_init) + return pdata->phy_init(usb_phy_reg_base); + + /* Power up the PHY and PLL */ + writel(readl(usb_phy_reg_base + USB_PHY_CTRL_REG) | 0x3, + usb_phy_reg_base + USB_PHY_CTRL_REG); + + /* Configure PHY PLL */ + reg_val = readl(usb_phy_reg_base + USB_PHY_PLL_REG) & ~(0x7e03ffff); + reg_val |= (VDD18_22 << PLLVDD18_SHIFT | VDD12_12 << PLLVDD12_SHIFT | + CALI12_11 << PLLCALI12_SHIFT | 3 << KVCO_SHIFT | + ICP_15 << ICP_SHIFT | 0xee << FBDIV_SHIFT | 0xb); + writel(reg_val, usb_phy_reg_base + USB_PHY_PLL_REG); + + /* Make sure PHY PLL is ready */ + while (!(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & PLL_READY)) { + if (!(pll_retry_cont--)) { + dev_dbg(&pdev->dev, "USB PHY PLL not ready\n"); + err = -EIO; + goto err2; + } + } + + /* Toggle VCOCAL_START bit of U2PLL for PLL calibration */ + udelay(200); + writel(readl(usb_phy_reg_base + USB_PHY_PLL_REG) | VCOCAL_START, + usb_phy_reg_base + USB_PHY_PLL_REG); + udelay(40); + writel(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & ~VCOCAL_START, + usb_phy_reg_base + USB_PHY_PLL_REG); + + /* Toggle REG_RCAL_START bit of U2PTX for impedance calibration */ + udelay(400); + writel(readl(usb_phy_reg_base + USB_PHY_TX_REG) | REG_RCAL_START, + usb_phy_reg_base + USB_PHY_TX_REG); + udelay(40); + writel(readl(usb_phy_reg_base + USB_PHY_TX_REG) & ~REG_RCAL_START, + usb_phy_reg_base + USB_PHY_TX_REG); + + /* Make sure PHY PLL is ready again */ + pll_retry_cont = 0; + while (!(readl(usb_phy_reg_base + USB_PHY_PLL_REG) & PLL_READY)) { + if (!(pll_retry_cont--)) { + dev_dbg(&pdev->dev, "USB PHY PLL not ready\n"); + err = -EIO; + goto err2; + } + } + + return 0; +err2: + iounmap(usb_phy_reg_base); +err1: + release_mem_region(res->start, resource_size(res)); + return err; +} + +static int __devinit ehci_pxa168_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct pxa168_usb_drv_data *drv_data; + void __iomem *regs; + int irq, err = 0; + + if (usb_disabled()) + return -ENODEV; + + pr_debug("Initializing pxa168-SoC USB Host Controller\n"); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, + "Found HC with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + err = -ENODEV; + goto err1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no register addr. Check %s setup!\n", + dev_name(&pdev->dev)); + err = -ENODEV; + goto err1; + } + + if (!request_mem_region(res->start, resource_size(res), + ehci_pxa168_hc_driver.description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + err = -EBUSY; + goto err1; + } + + regs = ioremap(res->start, resource_size(res)); + if (regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + err = -EFAULT; + goto err2; + } + + hcd = usb_create_hcd(&ehci_pxa168_hc_driver, + &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + err = -ENOMEM; + goto err3; + } + + drv_data = (struct pxa168_usb_drv_data *)hcd->hcd_priv; + + /* Enable USB clock */ + drv_data->pxa168_usb_clk = clk_get(&pdev->dev, "PXA168-USBCLK"); + if (IS_ERR(drv_data->pxa168_usb_clk)) { + dev_err(&pdev->dev, "Couldn't get USB clock\n"); + err = PTR_ERR(drv_data->pxa168_usb_clk); + goto err4; + } + clk_enable(drv_data->pxa168_usb_clk); + + err = pxa168_usb_phy_init(pdev); + if (err) { + dev_err(&pdev->dev, "USB PHY initialization failed\n"); + goto err5; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + hcd->regs = regs; + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + hcd->has_tt = 1; + ehci->sbrn = 0x20; + + err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + if (err) + goto err5; + + return 0; + +err5: + clk_disable(drv_data->pxa168_usb_clk); + clk_put(drv_data->pxa168_usb_clk); +err4: + usb_put_hcd(hcd); +err3: + iounmap(regs); +err2: + release_mem_region(res->start, resource_size(res)); +err1: + dev_err(&pdev->dev, "init %s fail, %d\n", + dev_name(&pdev->dev), err); + + return err; +} + +static int __exit ehci_pxa168_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct pxa168_usb_drv_data *drv_data = + (struct pxa168_usb_drv_data *)hcd->hcd_priv; + + usb_remove_hcd(hcd); + + /* Power down PHY & PLL */ + writel(readl(drv_data->usb_phy_reg_base + USB_PHY_CTRL_REG) & (~0x3), + drv_data->usb_phy_reg_base + USB_PHY_CTRL_REG); + + clk_disable(drv_data->pxa168_usb_clk); + clk_put(drv_data->pxa168_usb_clk); + + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + iounmap(drv_data->usb_phy_reg_base); + release_mem_region(drv_data->usb_phy_res->start, + resource_size(drv_data->usb_phy_res)); + + usb_put_hcd(hcd); + + return 0; +} + +MODULE_ALIAS("platform:pxa168-ehci"); + +static struct platform_driver ehci_pxa168_driver = { + .probe = ehci_pxa168_drv_probe, + .remove = __exit_p(ehci_pxa168_drv_remove), + .shutdown = usb_hcd_platform_shutdown, + .driver.name = "pxa168-ehci", +}; -- cgit v0.10.2 From 23106343db66171c94ae486d2035478ec575b228 Mon Sep 17 00:00:00 2001 From: Jayachandran C Date: Fri, 5 Aug 2011 01:28:22 +0530 Subject: usb: OHCI/EHCI support for Netlogic XLS processor. Add supprt for on-chip USB controller for Netlogic XLS MIPS64 SoC processor family. Changes are: - update ehci-hcd.c and ohci-hcd.c to add XLS hcds - add ehci-xls.c: EHCI support for Netlogic XLS. - add ohci-xls.c: OHCI support for Netlogic XLS. Signed-off-by: Jayachandran C Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 750f60f..2af3e2a 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1296,6 +1296,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_pxa168_driver #endif +#ifdef CONFIG_NLM_XLR +#include "ehci-xls.c" +#define PLATFORM_DRIVER ehci_xls_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-xls.c b/drivers/usb/host/ehci-xls.c new file mode 100644 index 0000000..fe74bd6 --- /dev/null +++ b/drivers/usb/host/ehci-xls.c @@ -0,0 +1,161 @@ +/* + * EHCI HCD for Netlogic XLS processors. + * + * (C) Copyright 2011 Netlogic Microsystems Inc. + * + * Based on various ehci-*.c drivers + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include + +static int ehci_xls_setup(struct usb_hcd *hcd) +{ + int retval; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci_reset(ehci); + + return retval; +} + +int ehci_xls_probe_internal(const struct hc_driver *driver, + struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct resource *res; + int retval, irq; + + /* Get our IRQ from an earlier registered Platform Resource */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + + /* Get our Memory Handle */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Error: MMIO Handle %s setup!\n", + dev_name(&pdev->dev)); + return -ENODEV; + } + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto err1; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + retval = -EBUSY; + goto err2; + } + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + + if (hcd->regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto err3; + } + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval != 0) + goto err4; + return retval; + +err4: + iounmap(hcd->regs); +err3: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err2: + usb_put_hcd(hcd); +err1: + dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), + retval); + return retval; +} + +static struct hc_driver ehci_xls_hc_driver = { + .description = hcd_name, + .product_desc = "XLS EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + .irq = ehci_irq, + .flags = HCD_USB2 | HCD_MEMORY, + .reset = ehci_xls_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + .get_frame_number = ehci_get_frame, + + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int ehci_xls_probe(struct platform_device *pdev) +{ + if (usb_disabled()) + return -ENODEV; + + return ehci_xls_probe_internal(&ehci_xls_hc_driver, pdev); +} + +static int ehci_xls_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + return 0; +} + +MODULE_ALIAS("ehci-xls"); + +static struct platform_driver ehci_xls_driver = { + .probe = ehci_xls_probe, + .remove = ehci_xls_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "ehci-xls", + }, +}; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f9cf3f0..34efd47 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1114,6 +1114,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_hcd_ath79_driver #endif +#ifdef CONFIG_NLM_XLR +#include "ohci-xls.c" +#define PLATFORM_DRIVER ohci_xls_driver +#endif + #if !defined(PCI_DRIVER) && \ !defined(PLATFORM_DRIVER) && \ !defined(OMAP1_PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ohci-xls.c b/drivers/usb/host/ohci-xls.c new file mode 100644 index 0000000..a3a9c6f --- /dev/null +++ b/drivers/usb/host/ohci-xls.c @@ -0,0 +1,151 @@ +/* + * OHCI HCD for Netlogic XLS processors. + * + * (C) Copyright 2011 Netlogic Microsystems Inc. + * + * Based on ohci-au1xxx.c, and other Linux OHCI drivers. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include +#include + +static int ohci_xls_probe_internal(const struct hc_driver *driver, + struct platform_device *dev) +{ + struct resource *res; + struct usb_hcd *hcd; + int retval, irq; + + /* Get our IRQ from an earlier registered Platform Resource */ + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "Found HC with no IRQ\n"); + return -ENODEV; + } + + /* Get our Memory Handle */ + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&dev->dev, "MMIO Handle incorrect!\n"); + return -ENODEV; + } + + hcd = usb_create_hcd(driver, &dev->dev, "XLS"); + if (!hcd) { + retval = -ENOMEM; + goto err1; + } + hcd->rsrc_start = res->start; + hcd->rsrc_len = res->end - res->start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&dev->dev, "Controller already in use\n"); + retval = -EBUSY; + goto err2; + } + + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(&dev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto err3; + } + + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (retval != 0) + goto err4; + return retval; + +err4: + iounmap(hcd->regs); +err3: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err2: + usb_put_hcd(hcd); +err1: + dev_err(&dev->dev, "init fail, %d\n", retval); + return retval; +} + +static int ohci_xls_reset(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + ohci_hcd_init(ohci); + return ohci_init(ohci); +} + +static int __devinit ohci_xls_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci; + int ret; + + ohci = hcd_to_ohci(hcd); + ret = ohci_run(ohci); + if (ret < 0) { + err("can't start %s", hcd->self.bus_name); + ohci_stop(hcd); + return ret; + } + return 0; +} + +static struct hc_driver ohci_xls_hc_driver = { + .description = hcd_name, + .product_desc = "XLS OHCI Host Controller", + .hcd_priv_size = sizeof(struct ohci_hcd), + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, + .reset = ohci_xls_reset, + .start = ohci_xls_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + .get_frame_number = ohci_get_frame, + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +static int ohci_xls_probe(struct platform_device *dev) +{ + int ret; + + pr_debug("In ohci_xls_probe"); + if (usb_disabled()) + return -ENODEV; + ret = ohci_xls_probe_internal(&ohci_xls_hc_driver, dev); + return ret; +} + +static int ohci_xls_remove(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + return 0; +} + +static struct platform_driver ohci_xls_driver = { + .probe = ohci_xls_probe, + .remove = ohci_xls_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "ohci-xls-0", + .owner = THIS_MODULE, + }, +}; -- cgit v0.10.2 From 939f325f4a0fb7eb189268a4593e427d3a075514 Mon Sep 17 00:00:00 2001 From: "kuninori.morimoto.gx@renesas.com" Date: Mon, 25 Jul 2011 00:39:30 -0700 Subject: usb: add usb_endpoint_maxp() macro Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 0fd3fbd..e809dea 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -34,6 +34,7 @@ #define __LINUX_USB_CH9_H #include /* __u8 etc */ +#include /* le16_to_cpu */ /*-------------------------------------------------------------------------*/ @@ -570,6 +571,17 @@ static inline int usb_endpoint_is_isoc_out( return usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd); } +/** + * usb_endpoint_maxp - get endpoint's max packet size + * @epd: endpoint to be checked + * + * Returns @epd's max packet + */ +static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd) +{ + return le16_to_cpu(epd->wMaxPacketSize); +} + /*-------------------------------------------------------------------------*/ /* USB_DT_SS_ENDPOINT_COMP: SuperSpeed Endpoint Companion descriptor */ -- cgit v0.10.2 From dfd8c81fd1c09c740140a2334669994d5c6edcaa Mon Sep 17 00:00:00 2001 From: "kuninori.morimoto.gx@renesas.com" Date: Mon, 25 Jul 2011 00:39:51 -0700 Subject: usb: renesas_usbhs: use usb_endpoint_maxp() Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 1b14cae..3bbbbb4 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -392,7 +392,7 @@ static u16 usbhsp_setup_pipemaxp(struct usbhs_pipe *pipe, /* host should set DEVSEL */ /* reutn MXPS */ - return PIPE_MAXP_MASK & le16_to_cpu(desc->wMaxPacketSize); + return PIPE_MAXP_MASK & usb_endpoint_maxp(desc); } static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, -- cgit v0.10.2 From e8799906045302776b35b66b16495c575db3b69c Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 18 Aug 2011 16:31:30 -0400 Subject: USB: EHCI: remove usages of hcd->state This patch (as1483) improves the ehci-hcd driver family by getting rid of the reliance on the hcd->state variable. It has no clear owner and it isn't protected by the usual HCD locks. In its place, the patch adds a new, private ehci->rh_state field to record the state of the root hub. Along the way, the patch removes a couple of lines containing redundant assignments to the state variable. Also, the QUIESCING state simply gets changed to the RUNNING state, because the driver doesn't make any distinction between them. Signed-off-by: Alan Stern Acked-by: Jingoo Han Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 42ae574..4363fea 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -293,7 +293,7 @@ static int ehci_hcd_au1xxx_drv_resume(struct device *dev) /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; return 0; } diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 40a844c..9952505d 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -697,6 +697,19 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) } #undef DBG_SCHED_LIMIT +static const char *rh_state_string(struct ehci_hcd *ehci) +{ + switch (ehci->rh_state) { + case EHCI_RH_HALTED: + return "halted"; + case EHCI_RH_SUSPENDED: + return "suspended"; + case EHCI_RH_RUNNING: + return "running"; + } + return "?"; +} + static ssize_t fill_registers_buffer(struct debug_buffer *buf) { struct usb_hcd *hcd; @@ -730,11 +743,11 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) temp = scnprintf (next, size, "bus %s, device %s\n" "%s\n" - "EHCI %x.%02x, hcd state %d\n", + "EHCI %x.%02x, rh state %s\n", hcd->self.controller->bus->name, dev_name(hcd->self.controller), hcd->product_desc, - i >> 8, i & 0x0ff, hcd->state); + i >> 8, i & 0x0ff, rh_state_string(ehci)); size -= temp; next += temp; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 34a3140..3bf9f16 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -392,7 +392,7 @@ static int ehci_fsl_mpc512x_drv_suspend(struct device *dev) dev_dbg(dev, "suspending...\n"); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; dev->power.power_state = PMSG_SUSPEND; /* ignore non-host interrupts */ @@ -481,7 +481,7 @@ static int ehci_fsl_mpc512x_drv_resume(struct device *dev) ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - hcd->state = HC_STATE_RUNNING; + ehci->rh_state = EHCI_RH_RUNNING; dev->power.power_state = PMSG_ON; tmp = ehci_readl(ehci, &ehci->regs->command); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2af3e2a..8696489 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -238,7 +238,7 @@ static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr, error = handshake(ehci, ptr, mask, done, usec); if (error) { ehci_halt(ehci); - ehci_to_hcd(ehci)->state = HC_STATE_HALT; + ehci->rh_state = EHCI_RH_HALTED; ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n", ptr, mask, done, error); } @@ -278,7 +278,7 @@ static int ehci_reset (struct ehci_hcd *ehci) command |= CMD_RESET; dbg_cmd (ehci, "reset", command); ehci_writel(ehci, command, &ehci->regs->command); - ehci_to_hcd(ehci)->state = HC_STATE_HALT; + ehci->rh_state = EHCI_RH_HALTED; ehci->next_statechange = jiffies; retval = handshake (ehci, &ehci->regs->command, CMD_RESET, 0, 250 * 1000); @@ -307,7 +307,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci) u32 temp; #ifdef DEBUG - if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) BUG (); #endif @@ -356,7 +356,7 @@ static void ehci_iaa_watchdog(unsigned long param) */ if (ehci->reclaim && !timer_pending(&ehci->iaa_watchdog) - && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + && ehci->rh_state == EHCI_RH_RUNNING) { u32 cmd, status; /* If we get here, IAA is *REALLY* late. It's barely @@ -496,7 +496,7 @@ static void ehci_work (struct ehci_hcd *ehci) * misplace IRQs, and should let us run completely without IRQs. * such lossage has been observed on both VT6202 and VT8235. */ - if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && + if (ehci->rh_state == EHCI_RH_RUNNING && (ehci->async->qh_next.ptr != NULL || ehci->periodic_sched != 0)) timer_action (ehci, TIMER_IO_WATCHDOG); @@ -516,7 +516,7 @@ static void ehci_stop (struct usb_hcd *hcd) del_timer_sync(&ehci->iaa_watchdog); spin_lock_irq(&ehci->lock); - if (HC_IS_RUNNING (hcd->state)) + if (ehci->rh_state == EHCI_RH_RUNNING) ehci_quiesce (ehci); ehci_silence_controller(ehci); @@ -741,7 +741,7 @@ static int ehci_run (struct usb_hcd *hcd) * be started before the port switching actions could complete. */ down_write(&ehci_cf_port_reset_rwsem); - hcd->state = HC_STATE_RUNNING; + ehci->rh_state = EHCI_RH_RUNNING; ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ msleep(5); @@ -788,7 +788,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* Shared IRQ? */ masked_status = status & INTR_MASK; - if (!masked_status || unlikely(hcd->state == HC_STATE_HALT)) { + if (!masked_status || unlikely(ehci->rh_state == EHCI_RH_HALTED)) { spin_unlock(&ehci->lock); return IRQ_NONE; } @@ -952,7 +952,7 @@ static int ehci_urb_enqueue ( static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) { /* failfast */ - if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state) && ehci->reclaim) + if (ehci->rh_state != EHCI_RH_RUNNING && ehci->reclaim) end_unlink_async(ehci); /* If the QH isn't linked then there's nothing we can do @@ -1079,7 +1079,7 @@ rescan: goto idle_timeout; } - if (!HC_IS_RUNNING (hcd->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) qh->qh_state = QH_STATE_IDLE; switch (qh->qh_state) { case QH_STATE_LINKED: diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index e051b30..c6104c4 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -236,10 +236,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) } /* stop schedules, clean any completed work */ - if (HC_IS_RUNNING(hcd->state)) { + if (ehci->rh_state == EHCI_RH_RUNNING) ehci_quiesce (ehci); - hcd->state = HC_STATE_QUIESCING; - } ehci->command = ehci_readl(ehci, &ehci->regs->command); ehci_work(ehci); @@ -313,7 +311,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) /* turn off now-idle HC */ ehci_halt (ehci); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; if (ehci->reclaim) end_unlink_async(ehci); @@ -382,6 +380,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd) /* restore CMD_RUN, framelist size, and irq threshold */ ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci->rh_state = EHCI_RH_RUNNING; /* Some controller/firmware combinations need a delay during which * they set up the port statuses. See Bugzilla #8190. */ @@ -452,7 +451,6 @@ static int ehci_bus_resume (struct usb_hcd *hcd) } ehci->next_statechange = jiffies + msecs_to_jiffies(5); - hcd->state = HC_STATE_RUNNING; /* Now we can safely re-enable irqs */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); @@ -564,7 +562,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) u32 ppcd = 0; /* if !USB_SUSPEND, root hub timers won't get shut down ... */ - if (!HC_IS_RUNNING(hcd->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) return 0; /* init status to no-changes */ diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 1102ce6..8311de7 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -439,7 +439,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; return 0; } #endif diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 0917e3a..6ce0b3a 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -153,7 +153,7 @@ static void ehci_clear_tt_buffer_complete(struct usb_hcd *hcd, 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)) + && ehci->rh_state == EHCI_RH_RUNNING) qh_link_async(ehci, qh); spin_unlock_irqrestore(&ehci->lock, flags); } @@ -425,7 +425,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) /* stop scanning when we reach qtds the hc is using */ } else if (likely (!stopped - && HC_IS_RUNNING (ehci_to_hcd(ehci)->state))) { + && ehci->rh_state == EHCI_RH_RUNNING)) { break; /* scan the whole queue for unlinks whenever it stops */ @@ -433,7 +433,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) stopped = 1; /* cancel everything if we halt, suspend, etc */ - if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) + if (ehci->rh_state != EHCI_RH_RUNNING) last_status = -ESHUTDOWN; /* this qtd is active; skip it unless a previous qtd @@ -977,9 +977,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* in case a clear of CMD_ASE didn't take yet */ (void)handshake(ehci, &ehci->regs->status, STS_ASS, 0, 150); - cmd |= CMD_ASE | CMD_RUN; + cmd |= CMD_ASE; ehci_writel(ehci, cmd, &ehci->regs->command); - ehci_to_hcd(ehci)->state = HC_STATE_RUNNING; /* posted write need not be known to HC yet ... */ } } @@ -1168,14 +1167,13 @@ static void end_unlink_async (struct ehci_hcd *ehci) qh_completions (ehci, qh); - if (!list_empty (&qh->qtd_list) - && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) + if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) { qh_link_async (ehci, qh); - else { + } else { /* it's not free to turn the async schedule on/off; leave it * active but idle for a while once it empties. */ - if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state) + if (ehci->rh_state == EHCI_RH_RUNNING && ehci->async->qh_next.qh == NULL) timer_action (ehci, TIMER_ASYNC_OFF); } @@ -1211,7 +1209,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) /* stop async schedule right now? */ if (unlikely (qh == ehci->async)) { /* can't get here without STS_ASS set */ - if (ehci_to_hcd(ehci)->state != HC_STATE_HALT + if (ehci->rh_state != EHCI_RH_HALTED && !ehci->reclaim) { /* ... and CMD_IAAD clear */ ehci_writel(ehci, cmd & ~CMD_ASE, @@ -1237,7 +1235,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) wmb (); /* If the controller isn't running, we don't have to wait for it */ - if (unlikely(!HC_IS_RUNNING(ehci_to_hcd(ehci)->state))) { + if (unlikely(ehci->rh_state != EHCI_RH_RUNNING)) { /* if (unlikely (qh->reclaim != 0)) * this will recurse, probably not much */ @@ -1260,7 +1258,7 @@ static void scan_async (struct ehci_hcd *ehci) enum ehci_timer_action action = TIMER_IO_WATCHDOG; timer_action_done (ehci, TIMER_ASYNC_SHRINK); - stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state); + stopped = (ehci->rh_state != EHCI_RH_RUNNING); ehci->qh_scan_next = ehci->async->qh_next.qh; while (ehci->qh_scan_next) { diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index b3958b3..318e0c6 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -269,7 +269,7 @@ static int s5p_ehci_resume(struct device *dev) /* here we "know" root ports should always stay powered */ ehci_port_power(ehci, 1); - hcd->state = HC_STATE_SUSPENDED; + ehci->rh_state = EHCI_RH_SUSPENDED; return 0; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 2abf854..488151b 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -479,7 +479,6 @@ static int enable_periodic (struct ehci_hcd *ehci) cmd = ehci_readl(ehci, &ehci->regs->command) | CMD_PSE; ehci_writel(ehci, cmd, &ehci->regs->command); /* posted write ... PSS happens later */ - ehci_to_hcd(ehci)->state = HC_STATE_RUNNING; /* make sure ehci_work scans these */ ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index) @@ -677,7 +676,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) /* reschedule QH iff another request is queued */ if (!list_empty(&qh->qtd_list) && - HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + ehci->rh_state == EHCI_RH_RUNNING) { rc = qh_schedule(ehci, qh); /* An error here likely indicates handshake failure @@ -2275,7 +2274,7 @@ scan_periodic (struct ehci_hcd *ehci) * Touches as few pages as possible: cache-friendly. */ now_uframe = ehci->next_uframe; - if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + if (ehci->rh_state == EHCI_RH_RUNNING) { clock = ehci_readl(ehci, &ehci->regs->frame_index); clock_frame = (clock >> 3) & (ehci->periodic_size - 1); } else { @@ -2310,7 +2309,7 @@ restart: union ehci_shadow temp; int live; - live = HC_IS_RUNNING (ehci_to_hcd(ehci)->state); + live = (ehci->rh_state == EHCI_RH_RUNNING); switch (hc32_to_cpu(ehci, type)) { case Q_TYPE_QH: /* handle any completions */ @@ -2435,7 +2434,7 @@ restart: * We can't advance our scan without collecting the ISO * transfers that are still pending in this frame. */ - if (incomplete && HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { + if (incomplete && ehci->rh_state == EHCI_RH_RUNNING) { ehci->next_uframe = now_uframe; break; } @@ -2451,7 +2450,7 @@ restart: if (now_uframe == clock) { unsigned now; - if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) + if (ehci->rh_state != EHCI_RH_RUNNING || ehci->periodic_sched == 0) break; ehci->next_uframe = now_uframe; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index cc7d337..c161d97 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -62,6 +62,12 @@ struct ehci_stats { #define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */ +enum ehci_rh_state { + EHCI_RH_HALTED, + EHCI_RH_SUSPENDED, + EHCI_RH_RUNNING +}; + struct ehci_hcd { /* one per controller */ /* glue to PCI and HCD framework */ struct ehci_caps __iomem *caps; @@ -70,6 +76,7 @@ struct ehci_hcd { /* one per controller */ __u32 hcs_params; /* cached register copy */ spinlock_t lock; + enum ehci_rh_state rh_state; /* async schedule support */ struct ehci_qh *async; -- cgit v0.10.2 From da6819dbffb34861b79d91c05c4eeb37a3792433 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 19 Aug 2011 18:10:56 +0300 Subject: usb: ch9: add function defines from ch9, USB 3.0 spec not to confuse with Table 9-7 in USB 2.0 spec Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index e809dea..1ded281 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -144,6 +144,11 @@ #define USB_INTRF_FUNC_SUSPEND 0 /* function suspend */ #define USB_INTR_FUNC_SUSPEND_OPT_MASK 0xFF00 +/* + * Suspend Options, Table 9-7 USB 3.0 spec + */ +#define USB_INTRF_FUNC_SUSPEND_LP (1 << (8 + 0)) +#define USB_INTRF_FUNC_SUSPEND_RW (1 << (8 + 1)) #define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */ -- cgit v0.10.2 From 500fdf8becb9c8d51970c7ac6a4fa308a5481ebe Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 19 Aug 2011 18:10:57 +0300 Subject: usb: gadget: introduce gadget_is_dwc3() ... to check whether we're running on DesignWare USB3 DRD Controller. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index f3a83cd..a8855d0 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -31,6 +31,7 @@ #define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name)) #define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name)) #define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name)) +#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name)) #define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name)) #define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name)) #define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name)) @@ -115,6 +116,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x30; else if (gadget_is_net2272(gadget)) return 0x31; + else if (gadget_is_dwc3(gadget)) + return 0x32; return -ENOENT; } -- cgit v0.10.2 From 72246da40f3719af3bfd104a2365b32537c27d83 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 19 Aug 2011 18:10:58 +0300 Subject: usb: Introduce DesignWare USB3 DRD Driver The DesignWare USB3 is a highly configurable IP Core which can be instantiated as Dual-Role Device (DRD), Peripheral Only and Host Only (XHCI) configurations. Several other parameters can be configured like amount of FIFO space, amount of TX and RX endpoints, amount of Host Interrupters, etc. The current driver has been validated with a virtual model of version 1.73a of that core and with an FPGA burned with version 1.83a of the DRD core. We have support for PCIe bus, which is used on FPGA prototyping, and for the OMAP5, more adaptation (or glue) layers can be easily added and the driver is half prepared to handle any possible configuration the HW engineer has chosen considering we have the information on one of the GHWPARAMS registers to do runtime checking of certain features. More runtime checks can, and should, be added in order to make this driver even more flexible with regards to number of endpoints, FIFO sizes, transfer types, etc. While this supports only the device side, for now, we will add support for Host side (xHCI - see the updated series Sebastian has sent [1]) and OTG after we have it all stabilized. [1] http://marc.info/?l=linux-usb&m=131341992020339&w=2 Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/usb/dwc3.txt b/Documentation/usb/dwc3.txt new file mode 100644 index 0000000..2f65853 --- /dev/null +++ b/Documentation/usb/dwc3.txt @@ -0,0 +1,53 @@ + + TODO +~~~~~~ +Please pick something while reading :) + +- Implement streaming support for BULK endpoints + Tatyana's patch "usb: Add streams support to the gadget framework" + introduces streaming support for the gadget driver. + Every usb_request has new field called stream_id which holds its id. + Every usb_ep has a field num_supported_strms which describes the max + number of streams supported (for this ep). + UAS is AFAIK the only gadget with streaming support. + +- Convert interrupt handler to per-ep-thread-irq + + As it turns out some DWC3-commands ~1ms to complete. Currently we spin + until the command completes which is bad. + + Implementation idea: + - dwc core implements a demultiplexing irq chip for interrupts per + endpoint. The interrupt numbers are allocated during probe and belong + to the device. If MSI provides per-endpoint interrupt this dummy + interrupt chip can be replaced with "real" interrupts. + - interrupts are requested / allocated on usb_ep_enable() and removed on + usb_ep_disable(). Worst case are 32 interrupts, the lower limit is two + for ep0/1. + - dwc3_send_gadget_ep_cmd() will sleep in wait_for_completion_timeout() + until the command completes. + - the interrupt handler is split into the following pieces: + - primary handler of the device + goes through every event and calls generic_handle_irq() for event + it. On return from generic_handle_irq() in acknowledges the event + counter so interrupt goes away (eventually). + + - threaded handler of the device + none + + - primary handler of the EP-interrupt + reads the event and tries to process it. Everything that requries + sleeping is handed over to the Thread. The event is saved in an + per-endpoint data-structure. + We probably have to pay attention not to process events once we + handed something to thread so we don't process event X prio Y + where X > Y. + + - threaded handler of the EP-interrupt + handles the remaining EP work which might sleep such as waiting + for command completion. + + Latency: + There should be no increase in latency since the interrupt-thread has a + high priority and will be run before an average task in user land + (except the user changed priorities). diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 7e7f42b..2651852 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -111,6 +111,8 @@ config USB source "drivers/usb/core/Kconfig" +source "drivers/usb/dwc3/Kconfig" + source "drivers/usb/mon/Kconfig" source "drivers/usb/wusbcore/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 30ddf8d..969b0a5 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -6,6 +6,8 @@ obj-$(CONFIG_USB) += core/ +obj-$(CONFIG_USB_DWC3) += dwc3/ + obj-$(CONFIG_USB_MON) += mon/ obj-$(CONFIG_PCI) += host/ diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig new file mode 100644 index 0000000..3c1d67d --- /dev/null +++ b/drivers/usb/dwc3/Kconfig @@ -0,0 +1,25 @@ +config USB_DWC3 + tristate "DesignWare USB3 DRD Core Support" + depends on (USB || USB_GADGET) + select USB_OTG_UTILS + help + Say Y or M here if your system has a Dual Role SuperSpeed + USB controller based on the DesignWare USB3 IP Core. + + If you choose to build this driver is a dynamically linked + module, the module will be called dwc3.ko. + +if USB_DWC3 + +config USB_DWC3_DEBUG + bool "Enable Debugging Messages" + help + Say Y here to enable debugging messages on DWC3 Driver. + +config USB_DWC3_VERBOSE + bool "Enable Verbose Debugging Messages" + depends on USB_DWC3_DEBUG + help + Say Y here to enable verbose debugging messages on DWC3 Driver. + +endif diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile new file mode 100644 index 0000000..593d1db --- /dev/null +++ b/drivers/usb/dwc3/Makefile @@ -0,0 +1,36 @@ +ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG +ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG + +obj-$(CONFIG_USB_DWC3) += dwc3.o + +dwc3-y := core.o + +ifneq ($(CONFIG_USB_GADGET_DWC3),) + dwc3-y += gadget.o ep0.o +endif + +ifneq ($(CONFIG_DEBUG_FS),) + dwc3-y += debugfs.o +endif + +## +# Platform-specific glue layers go here +# +# NOTICE: Make sure your glue layer doesn't depend on anything +# which is arch-specific and that it compiles on all situations. +# +# We want to keep this requirement in order to be able to compile +# the entire driver (with all its glue layers) on several architectures +# and make sure it compiles fine. This will also help with allmodconfig +# and allyesconfig builds. +# +# The only exception is the PCI glue layer, but that's only because +# PCI doesn't provide nops if CONFIG_PCI isn't enabled. +## + +obj-$(CONFIG_USB_DWC3) += dwc3-omap.o + +ifneq ($(CONFIG_PCI),) + obj-$(CONFIG_USB_DWC3) += dwc3-pci.o +endif + diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c new file mode 100644 index 0000000..443e4fb --- /dev/null +++ b/drivers/usb/dwc3/core.c @@ -0,0 +1,467 @@ +/** + * core.c - DesignWare USB3 DRD Controller Core file + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "core.h" +#include "gadget.h" +#include "io.h" + +#include "debug.h" + +/** + * dwc3_core_soft_reset - Issues core soft reset and PHY reset + * @dwc: pointer to our context structure + */ +static void dwc3_core_soft_reset(struct dwc3 *dwc) +{ + u32 reg; + + /* Before Resetting PHY, put Core in Reset */ + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg |= DWC3_GCTL_CORESOFTRESET; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + /* Assert USB3 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + /* Assert USB2 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + mdelay(100); + + /* Clear USB3 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + + /* Clear USB2 PHY reset */ + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + + /* After PHYs are stable we can take Core out of reset state */ + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg &= ~DWC3_GCTL_CORESOFTRESET; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +/** + * dwc3_free_one_event_buffer - Frees one event buffer + * @dwc: Pointer to our controller context structure + * @evt: Pointer to event buffer to be freed + */ +static void dwc3_free_one_event_buffer(struct dwc3 *dwc, + struct dwc3_event_buffer *evt) +{ + dma_free_coherent(dwc->dev, evt->length, evt->buf, evt->dma); + kfree(evt); +} + +/** + * dwc3_alloc_one_event_buffer - Allocated one event buffer structure + * @dwc: Pointer to our controller context structure + * @length: size of the event buffer + * + * Returns a pointer to the allocated event buffer structure on succes + * otherwise ERR_PTR(errno). + */ +static struct dwc3_event_buffer *__devinit +dwc3_alloc_one_event_buffer(struct dwc3 *dwc, unsigned length) +{ + struct dwc3_event_buffer *evt; + + evt = kzalloc(sizeof(*evt), GFP_KERNEL); + if (!evt) + return ERR_PTR(-ENOMEM); + + evt->dwc = dwc; + evt->length = length; + evt->buf = dma_alloc_coherent(dwc->dev, length, + &evt->dma, GFP_KERNEL); + if (!evt->buf) { + kfree(evt); + return ERR_PTR(-ENOMEM); + } + + return evt; +} + +/** + * dwc3_free_event_buffers - frees all allocated event buffers + * @dwc: Pointer to our controller context structure + */ +static void dwc3_free_event_buffers(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int i; + + for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) { + evt = dwc->ev_buffs[i]; + if (evt) { + dwc3_free_one_event_buffer(dwc, evt); + dwc->ev_buffs[i] = NULL; + } + } +} + +/** + * dwc3_alloc_event_buffers - Allocates @num event buffers of size @length + * @dwc: Pointer to out controller context structure + * @num: number of event buffers to allocate + * @length: size of event buffer + * + * Returns 0 on success otherwise negative errno. In error the case, dwc + * may contain some buffers allocated but not all which were requested. + */ +static int __devinit dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned num, + unsigned length) +{ + int i; + + for (i = 0; i < num; i++) { + struct dwc3_event_buffer *evt; + + evt = dwc3_alloc_one_event_buffer(dwc, length); + if (IS_ERR(evt)) { + dev_err(dwc->dev, "can't allocate event buffer\n"); + return PTR_ERR(evt); + } + dwc->ev_buffs[i] = evt; + } + + return 0; +} + +/** + * dwc3_event_buffers_setup - setup our allocated event buffers + * @dwc: Pointer to out controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int __devinit dwc3_event_buffers_setup(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int n; + + for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) { + evt = dwc->ev_buffs[n]; + dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n", + evt->buf, (unsigned long long) evt->dma, + evt->length); + + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), + lower_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), + upper_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), + evt->length & 0xffff); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); + } + + return 0; +} + +static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) +{ + struct dwc3_event_buffer *evt; + int n; + + for (n = 0; n < DWC3_EVENT_BUFFERS_NUM; n++) { + evt = dwc->ev_buffs[n]; + dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), 0); + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); + } +} + +/** + * dwc3_core_init - Low-level initialization of DWC3 Core + * @dwc: Pointer to our controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +static int __devinit dwc3_core_init(struct dwc3 *dwc) +{ + unsigned long timeout; + u32 reg; + int ret; + + dwc3_core_soft_reset(dwc); + + /* issue device SoftReset too */ + timeout = jiffies + msecs_to_jiffies(500); + dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + break; + + if (time_after(jiffies, timeout)) { + dev_err(dwc->dev, "Reset Timed Out\n"); + ret = -ETIMEDOUT; + goto err0; + } + + cpu_relax(); + } while (true); + + reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + /* This should read as U3 followed by revision number */ + if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + ret = -ENODEV; + goto err0; + } + + dwc->revision = reg & DWC3_GSNPSREV_MASK; + + ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_NUM, + DWC3_EVENT_BUFFERS_SIZE); + if (ret) { + dev_err(dwc->dev, "failed to allocate event buffers\n"); + ret = -ENOMEM; + goto err1; + } + + ret = dwc3_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, "failed to setup event buffers\n"); + goto err1; + } + + return 0; + +err1: + dwc3_free_event_buffers(dwc); + +err0: + return ret; +} + +static void dwc3_core_exit(struct dwc3 *dwc) +{ + dwc3_event_buffers_cleanup(dwc); + dwc3_free_event_buffers(dwc); +} + +#define DWC3_ALIGN_MASK (16 - 1) + +static int __devinit dwc3_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + struct resource *res; + struct dwc3 *dwc; + void __iomem *regs; + unsigned int features = id->driver_data; + int ret = -ENOMEM; + int irq; + void *mem; + + mem = kzalloc(sizeof(*dwc) + DWC3_ALIGN_MASK, GFP_KERNEL); + if (!mem) { + dev_err(&pdev->dev, "not enough memory\n"); + goto err0; + } + dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1); + dwc->mem = mem; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "missing resource\n"); + goto err1; + } + + res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!res) { + dev_err(&pdev->dev, "can't request mem region\n"); + goto err1; + } + + regs = ioremap(res->start, resource_size(res)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + goto err2; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "missing IRQ\n"); + goto err3; + } + + spin_lock_init(&dwc->lock); + platform_set_drvdata(pdev, dwc); + + dwc->regs = regs; + dwc->regs_size = resource_size(res); + dwc->dev = &pdev->dev; + dwc->irq = irq; + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + pm_runtime_forbid(&pdev->dev); + + ret = dwc3_core_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialize core\n"); + goto err3; + } + + if (features & DWC3_HAS_PERIPHERAL) { + ret = dwc3_gadget_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialized gadget\n"); + goto err4; + } + } + + ret = dwc3_debugfs_init(dwc); + if (ret) { + dev_err(&pdev->dev, "failed to initialize debugfs\n"); + goto err5; + } + + pm_runtime_allow(&pdev->dev); + + return 0; + +err5: + if (features & DWC3_HAS_PERIPHERAL) + dwc3_gadget_exit(dwc); + +err4: + dwc3_core_exit(dwc); + +err3: + iounmap(regs); + +err2: + release_mem_region(res->start, resource_size(res)); + +err1: + kfree(dwc->mem); + +err0: + return ret; +} + +static int __devexit dwc3_remove(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + struct dwc3 *dwc = platform_get_drvdata(pdev); + struct resource *res; + unsigned int features = id->driver_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + dwc3_debugfs_exit(dwc); + + if (features & DWC3_HAS_PERIPHERAL) + dwc3_gadget_exit(dwc); + + dwc3_core_exit(dwc); + release_mem_region(res->start, resource_size(res)); + iounmap(dwc->regs); + kfree(dwc->mem); + + return 0; +} + +static const struct platform_device_id dwc3_id_table[] __devinitconst = { + { + .name = "dwc3-omap", + .driver_data = (DWC3_HAS_PERIPHERAL + | DWC3_HAS_XHCI + | DWC3_HAS_OTG), + }, + { + .name = "dwc3-pci", + .driver_data = DWC3_HAS_PERIPHERAL, + }, + { }, /* Terminating Entry */ +}; +MODULE_DEVICE_TABLE(platform, dwc3_id_table); + +static struct platform_driver dwc3_driver = { + .probe = dwc3_probe, + .remove = __devexit_p(dwc3_remove), + .driver = { + .name = "dwc3", + }, + .id_table = dwc3_id_table, +}; + +MODULE_AUTHOR("Felipe Balbi "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("DesignWare USB3 DRD Controller Driver"); + +static int __devinit dwc3_init(void) +{ + return platform_driver_register(&dwc3_driver); +} +module_init(dwc3_init); + +static void __exit dwc3_exit(void) +{ + platform_driver_unregister(&dwc3_driver); +} +module_exit(dwc3_exit); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h new file mode 100644 index 0000000..83b2960 --- /dev/null +++ b/drivers/usb/dwc3/core.h @@ -0,0 +1,709 @@ +/** + * core.h - DesignWare USB3 DRD Core Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DRIVERS_USB_DWC3_CORE_H +#define __DRIVERS_USB_DWC3_CORE_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Global constants */ +#define DWC3_ENDPOINTS_NUM 32 + +#define DWC3_EVENT_BUFFERS_NUM 2 +#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE +#define DWC3_EVENT_TYPE_MASK 0xfe + +#define DWC3_EVENT_TYPE_DEV 0 +#define DWC3_EVENT_TYPE_CARKIT 3 +#define DWC3_EVENT_TYPE_I2C 4 + +#define DWC3_DEVICE_EVENT_DISCONNECT 0 +#define DWC3_DEVICE_EVENT_RESET 1 +#define DWC3_DEVICE_EVENT_CONNECT_DONE 2 +#define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE 3 +#define DWC3_DEVICE_EVENT_WAKEUP 4 +#define DWC3_DEVICE_EVENT_EOPF 6 +#define DWC3_DEVICE_EVENT_SOF 7 +#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9 +#define DWC3_DEVICE_EVENT_CMD_CMPL 10 +#define DWC3_DEVICE_EVENT_OVERFLOW 11 + +#define DWC3_GEVNTCOUNT_MASK 0xfffc +#define DWC3_GSNPSID_MASK 0xffff0000 +#define DWC3_GSNPSREV_MASK 0xffff + +/* Global Registers */ +#define DWC3_GSBUSCFG0 0xc100 +#define DWC3_GSBUSCFG1 0xc104 +#define DWC3_GTXTHRCFG 0xc108 +#define DWC3_GRXTHRCFG 0xc10c +#define DWC3_GCTL 0xc110 +#define DWC3_GEVTEN 0xc114 +#define DWC3_GSTS 0xc118 +#define DWC3_GSNPSID 0xc120 +#define DWC3_GGPIO 0xc124 +#define DWC3_GUID 0xc128 +#define DWC3_GUCTL 0xc12c +#define DWC3_GBUSERRADDR0 0xc130 +#define DWC3_GBUSERRADDR1 0xc134 +#define DWC3_GPRTBIMAP0 0xc138 +#define DWC3_GPRTBIMAP1 0xc13c +#define DWC3_GHWPARAMS0 0xc140 +#define DWC3_GHWPARAMS1 0xc144 +#define DWC3_GHWPARAMS2 0xc148 +#define DWC3_GHWPARAMS3 0xc14c +#define DWC3_GHWPARAMS4 0xc150 +#define DWC3_GHWPARAMS5 0xc154 +#define DWC3_GHWPARAMS6 0xc158 +#define DWC3_GHWPARAMS7 0xc15c +#define DWC3_GDBGFIFOSPACE 0xc160 +#define DWC3_GDBGLTSSM 0xc164 +#define DWC3_GPRTBIMAP_HS0 0xc180 +#define DWC3_GPRTBIMAP_HS1 0xc184 +#define DWC3_GPRTBIMAP_FS0 0xc188 +#define DWC3_GPRTBIMAP_FS1 0xc18c + +#define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04)) +#define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04)) + +#define DWC3_GUSB2PHYACC(n) (0xc280 + (n * 0x04)) + +#define DWC3_GUSB3PIPECTL(n) (0xc2c0 + (n * 0x04)) + +#define DWC3_GTXFIFOSIZ(n) (0xc300 + (n * 0x04)) +#define DWC3_GRXFIFOSIZ(n) (0xc380 + (n * 0x04)) + +#define DWC3_GEVNTADRLO(n) (0xc400 + (n * 0x10)) +#define DWC3_GEVNTADRHI(n) (0xc404 + (n * 0x10)) +#define DWC3_GEVNTSIZ(n) (0xc408 + (n * 0x10)) +#define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) + +#define DWC3_GHWPARAMS8 0xc600 + +/* Device Registers */ +#define DWC3_DCFG 0xc700 +#define DWC3_DCTL 0xc704 +#define DWC3_DEVTEN 0xc708 +#define DWC3_DSTS 0xc70c +#define DWC3_DGCMDPAR 0xc710 +#define DWC3_DGCMD 0xc714 +#define DWC3_DALEPENA 0xc720 +#define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10)) +#define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10)) +#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10)) +#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10)) + +/* OTG Registers */ +#define DWC3_OCFG 0xcc00 +#define DWC3_OCTL 0xcc04 +#define DWC3_OEVTEN 0xcc08 +#define DWC3_OSTS 0xcc0C + +/* Bit fields */ + +/* Global Configuration Register */ +#define DWC3_GCTL_PWRDNSCALE(n) (n << 19) +#define DWC3_GCTL_U2RSTECN 16 +#define DWC3_GCTL_RAMCLKSEL(x) ((x & DWC3_GCTL_CLK_MASK) << 6) +#define DWC3_GCTL_CLK_BUS (0) +#define DWC3_GCTL_CLK_PIPE (1) +#define DWC3_GCTL_CLK_PIPEHALF (2) +#define DWC3_GCTL_CLK_MASK (3) + +#define DWC3_GCTL_PRTCAPDIR(n) (n << 12) +#define DWC3_GCTL_PRTCAP_HOST 1 +#define DWC3_GCTL_PRTCAP_DEVICE 2 +#define DWC3_GCTL_PRTCAP_OTG 3 + +#define DWC3_GCTL_CORESOFTRESET (1 << 11) +#define DWC3_GCTL_DISSCRAMBLE (1 << 3) + +/* Global USB2 PHY Configuration Register */ +#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) +#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) + +/* Global USB3 PIPE Control Register */ +#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) +#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) + +/* Device Configuration Register */ +#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) +#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) + +#define DWC3_DCFG_SPEED_MASK (7 << 0) +#define DWC3_DCFG_SUPERSPEED (4 << 0) +#define DWC3_DCFG_HIGHSPEED (0 << 0) +#define DWC3_DCFG_FULLSPEED2 (1 << 0) +#define DWC3_DCFG_LOWSPEED (2 << 0) +#define DWC3_DCFG_FULLSPEED1 (3 << 0) + +/* Device Control Register */ +#define DWC3_DCTL_RUN_STOP (1 << 31) +#define DWC3_DCTL_CSFTRST (1 << 30) +#define DWC3_DCTL_LSFTRST (1 << 29) + +#define DWC3_DCTL_HIRD_THRES_MASK (0x1f << 24) +#define DWC3_DCTL_HIRD_THRES(n) (((n) & DWC3_DCTL_HIRD_THRES_MASK) >> 24) + +#define DWC3_DCTL_APPL1RES (1 << 23) + +#define DWC3_DCTL_INITU2ENA (1 << 12) +#define DWC3_DCTL_ACCEPTU2ENA (1 << 11) +#define DWC3_DCTL_INITU1ENA (1 << 10) +#define DWC3_DCTL_ACCEPTU1ENA (1 << 9) +#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1) + +#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5) +#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK) + +#define DWC3_DCTL_ULSTCHNG_NO_ACTION (DWC3_DCTL_ULSTCHNGREQ(0)) +#define DWC3_DCTL_ULSTCHNG_SS_DISABLED (DWC3_DCTL_ULSTCHNGREQ(4)) +#define DWC3_DCTL_ULSTCHNG_RX_DETECT (DWC3_DCTL_ULSTCHNGREQ(5)) +#define DWC3_DCTL_ULSTCHNG_SS_INACTIVE (DWC3_DCTL_ULSTCHNGREQ(6)) +#define DWC3_DCTL_ULSTCHNG_RECOVERY (DWC3_DCTL_ULSTCHNGREQ(8)) +#define DWC3_DCTL_ULSTCHNG_COMPLIANCE (DWC3_DCTL_ULSTCHNGREQ(10)) +#define DWC3_DCTL_ULSTCHNG_LOOPBACK (DWC3_DCTL_ULSTCHNGREQ(11)) + +/* Device Event Enable Register */ +#define DWC3_DEVTEN_VNDRDEVTSTRCVEDEN (1 << 12) +#define DWC3_DEVTEN_EVNTOVERFLOWEN (1 << 11) +#define DWC3_DEVTEN_CMDCMPLTEN (1 << 10) +#define DWC3_DEVTEN_ERRTICERREN (1 << 9) +#define DWC3_DEVTEN_SOFEN (1 << 7) +#define DWC3_DEVTEN_EOPFEN (1 << 6) +#define DWC3_DEVTEN_WKUPEVTEN (1 << 4) +#define DWC3_DEVTEN_ULSTCNGEN (1 << 3) +#define DWC3_DEVTEN_CONNECTDONEEN (1 << 2) +#define DWC3_DEVTEN_USBRSTEN (1 << 1) +#define DWC3_DEVTEN_DISCONNEVTEN (1 << 0) + +/* Device Status Register */ +#define DWC3_DSTS_PWRUPREQ (1 << 24) +#define DWC3_DSTS_COREIDLE (1 << 23) +#define DWC3_DSTS_DEVCTRLHLT (1 << 22) + +#define DWC3_DSTS_USBLNKST_MASK (0x0f << 18) +#define DWC3_DSTS_USBLNKST(n) (((n) & DWC3_DSTS_USBLNKST_MASK) >> 18) + +#define DWC3_DSTS_RXFIFOEMPTY (1 << 17) + +#define DWC3_DSTS_SOFFN_MASK (0x3ff << 3) +#define DWC3_DSTS_SOFFN(n) (((n) & DWC3_DSTS_SOFFN_MASK) >> 3) + +#define DWC3_DSTS_CONNECTSPD (7 << 0) + +#define DWC3_DSTS_SUPERSPEED (4 << 0) +#define DWC3_DSTS_HIGHSPEED (0 << 0) +#define DWC3_DSTS_FULLSPEED2 (1 << 0) +#define DWC3_DSTS_LOWSPEED (2 << 0) +#define DWC3_DSTS_FULLSPEED1 (3 << 0) + +/* Device Generic Command Register */ +#define DWC3_DGCMD_SET_LMP 0x01 +#define DWC3_DGCMD_SET_PERIODIC_PAR 0x02 +#define DWC3_DGCMD_XMIT_FUNCTION 0x03 +#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09 +#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a +#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c +#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10 + +/* Device Endpoint Command Register */ +#define DWC3_DEPCMD_PARAM_SHIFT 16 +#define DWC3_DEPCMD_PARAM(x) (x << DWC3_DEPCMD_PARAM_SHIFT) +#define DWC3_DEPCMD_GET_RSC_IDX(x) ((x >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f) +#define DWC3_DEPCMD_STATUS_MASK (0x0f << 12) +#define DWC3_DEPCMD_STATUS(x) ((x & DWC3_DEPCMD_STATUS_MASK) >> 12) +#define DWC3_DEPCMD_HIPRI_FORCERM (1 << 11) +#define DWC3_DEPCMD_CMDACT (1 << 10) +#define DWC3_DEPCMD_CMDIOC (1 << 8) + +#define DWC3_DEPCMD_DEPSTARTCFG (0x09 << 0) +#define DWC3_DEPCMD_ENDTRANSFER (0x08 << 0) +#define DWC3_DEPCMD_UPDATETRANSFER (0x07 << 0) +#define DWC3_DEPCMD_STARTTRANSFER (0x06 << 0) +#define DWC3_DEPCMD_CLEARSTALL (0x05 << 0) +#define DWC3_DEPCMD_SETSTALL (0x04 << 0) +#define DWC3_DEPCMD_GETSEQNUMBER (0x03 << 0) +#define DWC3_DEPCMD_SETTRANSFRESOURCE (0x02 << 0) +#define DWC3_DEPCMD_SETEPCONFIG (0x01 << 0) + +/* The EP number goes 0..31 so ep0 is always out and ep1 is always in */ +#define DWC3_DALEPENA_EP(n) (1 << n) + +#define DWC3_DEPCMD_TYPE_CONTROL 0 +#define DWC3_DEPCMD_TYPE_ISOC 1 +#define DWC3_DEPCMD_TYPE_BULK 2 +#define DWC3_DEPCMD_TYPE_INTR 3 + +/* Structures */ + +struct dwc3_trb_hw; + +/** + * struct dwc3_event_buffer - Software event buffer representation + * @list: a list of event buffers + * @buf: _THE_ buffer + * @length: size of this buffer + * @dma: dma_addr_t + * @dwc: pointer to DWC controller + */ +struct dwc3_event_buffer { + void *buf; + unsigned length; + unsigned int lpos; + + dma_addr_t dma; + + struct dwc3 *dwc; +}; + +#define DWC3_EP_FLAG_STALLED (1 << 0) +#define DWC3_EP_FLAG_WEDGED (1 << 1) + +#define DWC3_EP_DIRECTION_TX true +#define DWC3_EP_DIRECTION_RX false + +#define DWC3_TRB_NUM 32 +#define DWC3_TRB_MASK (DWC3_TRB_NUM - 1) + +/** + * struct dwc3_ep - device side endpoint representation + * @endpoint: usb endpoint + * @request_list: list of requests for this endpoint + * @req_queued: list of requests on this ep which have TRBs setup + * @trb_pool: array of transaction buffers + * @trb_pool_dma: dma address of @trb_pool + * @free_slot: next slot which is going to be used + * @busy_slot: first slot which is owned by HW + * @desc: usb_endpoint_descriptor pointer + * @dwc: pointer to DWC controller + * @flags: endpoint flags (wedged, stalled, ...) + * @current_trb: index of current used trb + * @number: endpoint number (1 - 15) + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK + * @res_trans_idx: Resource transfer index + * @interval: the intervall on which the ISOC transfer is started + * @name: a human readable name e.g. ep1out-bulk + * @direction: true for TX, false for RX + */ +struct dwc3_ep { + struct usb_ep endpoint; + struct list_head request_list; + struct list_head req_queued; + + struct dwc3_trb_hw *trb_pool; + dma_addr_t trb_pool_dma; + u32 free_slot; + u32 busy_slot; + const struct usb_endpoint_descriptor *desc; + struct dwc3 *dwc; + + unsigned flags; +#define DWC3_EP_ENABLED (1 << 0) +#define DWC3_EP_STALL (1 << 1) +#define DWC3_EP_WEDGE (1 << 2) +#define DWC3_EP_BUSY (1 << 4) +#define DWC3_EP_PENDING_REQUEST (1 << 5) +#define DWC3_EP_WILL_SHUTDOWN (1 << 6) + + unsigned current_trb; + + u8 number; + u8 type; + u8 res_trans_idx; + u32 interval; + + char name[20]; + + unsigned direction:1; +}; + +enum dwc3_phy { + DWC3_PHY_UNKNOWN = 0, + DWC3_PHY_USB3, + DWC3_PHY_USB2, +}; + +enum dwc3_ep0_state { + EP0_UNCONNECTED = 0, + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_IN_WAIT_GADGET, + EP0_OUT_WAIT_GADGET, + EP0_IN_WAIT_NRDY, + EP0_OUT_WAIT_NRDY, + EP0_IN_STATUS_PHASE, + EP0_OUT_STATUS_PHASE, + EP0_STALL, +}; + +enum dwc3_link_state { + /* In SuperSpeed */ + DWC3_LINK_STATE_U0 = 0x00, /* in HS, means ON */ + DWC3_LINK_STATE_U1 = 0x01, + DWC3_LINK_STATE_U2 = 0x02, /* in HS, means SLEEP */ + DWC3_LINK_STATE_U3 = 0x03, /* in HS, means SUSPEND */ + DWC3_LINK_STATE_SS_DIS = 0x04, + DWC3_LINK_STATE_RX_DET = 0x05, /* in HS, means Early Suspend */ + DWC3_LINK_STATE_SS_INACT = 0x06, + DWC3_LINK_STATE_POLL = 0x07, + DWC3_LINK_STATE_RECOV = 0x08, + DWC3_LINK_STATE_HRESET = 0x09, + DWC3_LINK_STATE_CMPLY = 0x0a, + DWC3_LINK_STATE_LPBK = 0x0b, + DWC3_LINK_STATE_MASK = 0x0f, +}; + +enum dwc3_device_state { + DWC3_DEFAULT_STATE, + DWC3_ADDRESS_STATE, + DWC3_CONFIGURED_STATE, +}; + +/** + * struct dwc3_trb - transfer request block + * @bpl: lower 32bit of the buffer + * @bph: higher 32bit of the buffer + * @length: buffer size (up to 16mb - 1) + * @pcm1: packet count m1 + * @trbsts: trb status + * 0 = ok + * 1 = missed isoc + * 2 = setup pending + * @hwo: hardware owner of descriptor + * @lst: last trb + * @chn: chain buffers + * @csp: continue on short packets (only supported on isoc eps) + * @trbctl: trb control + * 1 = normal + * 2 = control-setup + * 3 = control-status-2 + * 4 = control-status-3 + * 5 = control-data (first trb of data stage) + * 6 = isochronous-first (first trb of service interval) + * 7 = isochronous + * 8 = link trb + * others = reserved + * @isp_imi: interrupt on short packet / interrupt on missed isoc + * @ioc: interrupt on complete + * @sid_sofn: Stream ID / SOF Number + */ +struct dwc3_trb { + u64 bplh; + + union { + struct { + u32 length:24; + u32 pcm1:2; + u32 reserved27_26:2; + u32 trbsts:4; +#define DWC3_TRB_STS_OKAY 0 +#define DWC3_TRB_STS_MISSED_ISOC 1 +#define DWC3_TRB_STS_SETUP_PENDING 2 + }; + u32 len_pcm; + }; + + union { + struct { + u32 hwo:1; + u32 lst:1; + u32 chn:1; + u32 csp:1; + u32 trbctl:6; + u32 isp_imi:1; + u32 ioc:1; + u32 reserved13_12:2; + u32 sid_sofn:16; + u32 reserved31_30:2; + }; + u32 control; + }; +} __packed; + +/** + * struct dwc3_trb_hw - transfer request block (hw format) + * @bpl: DW0-3 + * @bph: DW4-7 + * @size: DW8-B + * @trl: DWC-F + */ +struct dwc3_trb_hw { + __le32 bpl; + __le32 bph; + __le32 size; + __le32 ctrl; +} __packed; + +static inline void dwc3_trb_to_hw(struct dwc3_trb *nat, struct dwc3_trb_hw *hw) +{ + hw->bpl = cpu_to_le32(lower_32_bits(nat->bplh)); + hw->bph = cpu_to_le32(upper_32_bits(nat->bplh)); + hw->size = cpu_to_le32p(&nat->len_pcm); + /* HWO is written last */ + hw->ctrl = cpu_to_le32p(&nat->control); +} + +static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) +{ + u64 bplh; + + bplh = le32_to_cpup(&hw->bpl); + bplh |= (u64) le32_to_cpup(&hw->bph) << 32; + nat->bplh = bplh; + + nat->len_pcm = le32_to_cpup(&hw->size); + nat->control = le32_to_cpup(&hw->ctrl); +} + +/** + * struct dwc3 - representation of our controller + * ctrl_req: usb control request which is used for ep0 + * ep0_trb: trb which is used for the ctrl_req + * setup_buf: used while precessing STD USB requests + * ctrl_req_addr: dma address of ctrl_req + * ep0_trb: dma address of ep0_trb + * ep0_usb_req: dummy req used while handling STD USB requests + * setup_buf_addr: dma address of setup_buf + * @lock: for synchronizing + * @dev: pointer to our struct device + * @event_buffer_list: a list of event buffers + * @gadget: device side representation of the peripheral controller + * @gadget_driver: pointer to the gadget driver + * @regs: base address for our registers + * @regs_size: address space size + * @irq: IRQ number + * @revision: revision register contents + * @is_selfpowered: true when we are selfpowered + * @three_stage_setup: set if we perform a three phase setup + * @ep0_status_pending: ep0 status response without a req is pending + * @ep0state: state of endpoint zero + * @link_state: link state + * @speed: device speed (super, high, full, low) + * @mem: points to start of memory which is used for this struct. + * @root: debugfs root folder pointer + */ +struct dwc3 { + struct usb_ctrlrequest *ctrl_req; + struct dwc3_trb_hw *ep0_trb; + u8 *setup_buf; + dma_addr_t ctrl_req_addr; + dma_addr_t ep0_trb_addr; + dma_addr_t setup_buf_addr; + struct usb_request ep0_usb_req; + /* device lock */ + spinlock_t lock; + struct device *dev; + + struct dwc3_event_buffer *ev_buffs[DWC3_EVENT_BUFFERS_NUM]; + struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM]; + + struct usb_gadget gadget; + struct usb_gadget_driver *gadget_driver; + + void __iomem *regs; + size_t regs_size; + + int irq; + + u32 revision; + +#define DWC3_REVISION_173A 0x5533173a +#define DWC3_REVISION_175A 0x5533175a +#define DWC3_REVISION_180A 0x5533180a +#define DWC3_REVISION_183A 0x5533183a +#define DWC3_REVISION_185A 0x5533185a +#define DWC3_REVISION_188A 0x5533188a +#define DWC3_REVISION_190A 0x5533190a + + unsigned is_selfpowered:1; + unsigned three_stage_setup:1; + unsigned ep0_status_pending:1; + + enum dwc3_ep0_state ep0state; + enum dwc3_link_state link_state; + enum dwc3_device_state dev_state; + + u8 speed; + void *mem; + + struct dentry *root; +}; + +/* -------------------------------------------------------------------------- */ + +#define DWC3_TRBSTS_OK 0 +#define DWC3_TRBSTS_MISSED_ISOC 1 +#define DWC3_TRBSTS_SETUP_PENDING 2 + +#define DWC3_TRBCTL_NORMAL 1 +#define DWC3_TRBCTL_CONTROL_SETUP 2 +#define DWC3_TRBCTL_CONTROL_STATUS2 3 +#define DWC3_TRBCTL_CONTROL_STATUS3 4 +#define DWC3_TRBCTL_CONTROL_DATA 5 +#define DWC3_TRBCTL_ISOCHRONOUS_FIRST 6 +#define DWC3_TRBCTL_ISOCHRONOUS 7 +#define DWC3_TRBCTL_LINK_TRB 8 + +/* -------------------------------------------------------------------------- */ + +struct dwc3_event_type { + u32 is_devspec:1; + u32 type:6; + u32 reserved8_31:25; +} __packed; + +#define DWC3_DEPEVT_XFERCOMPLETE 0x01 +#define DWC3_DEPEVT_XFERINPROGRESS 0x02 +#define DWC3_DEPEVT_XFERNOTREADY 0x03 +#define DWC3_DEPEVT_RXTXFIFOEVT 0x04 +#define DWC3_DEPEVT_STREAMEVT 0x06 +#define DWC3_DEPEVT_EPCMDCMPLT 0x07 + +/** + * struct dwc3_event_depvt - Device Endpoint Events + * @one_bit: indicates this is an endpoint event (not used) + * @endpoint_number: number of the endpoint + * @endpoint_event: The event we have: + * 0x00 - Reserved + * 0x01 - XferComplete + * 0x02 - XferInProgress + * 0x03 - XferNotReady + * 0x04 - RxTxFifoEvt (IN->Underrun, OUT->Overrun) + * 0x05 - Reserved + * 0x06 - StreamEvt + * 0x07 - EPCmdCmplt + * @reserved11_10: Reserved, don't use. + * @status: Indicates the status of the event. Refer to databook for + * more information. + * @parameters: Parameters of the current event. Refer to databook for + * more information. + */ +struct dwc3_event_depevt { + u32 one_bit:1; + u32 endpoint_number:5; + u32 endpoint_event:4; + u32 reserved11_10:2; + u32 status:4; +#define DEPEVT_STATUS_BUSERR (1 << 0) +#define DEPEVT_STATUS_SHORT (1 << 1) +#define DEPEVT_STATUS_IOC (1 << 2) +#define DEPEVT_STATUS_LST (1 << 3) + u32 parameters:16; +} __packed; + +/** + * struct dwc3_event_devt - Device Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's a device event. Should read as 0x00 + * @type: indicates the type of device event. + * 0 - DisconnEvt + * 1 - USBRst + * 2 - ConnectDone + * 3 - ULStChng + * 4 - WkUpEvt + * 5 - Reserved + * 6 - EOPF + * 7 - SOF + * 8 - Reserved + * 9 - ErrticErr + * 10 - CmdCmplt + * 11 - EvntOverflow + * 12 - VndrDevTstRcved + * @reserved15_12: Reserved, not used + * @event_info: Information about this event + * @reserved31_24: Reserved, not used + */ +struct dwc3_event_devt { + u32 one_bit:1; + u32 device_event:7; + u32 type:4; + u32 reserved15_12:4; + u32 event_info:8; + u32 reserved31_24:8; +} __packed; + +/** + * struct dwc3_event_gevt - Other Core Events + * @one_bit: indicates this is a non-endpoint event (not used) + * @device_event: indicates it's (0x03) Carkit or (0x04) I2C event. + * @phy_port_number: self-explanatory + * @reserved31_12: Reserved, not used. + */ +struct dwc3_event_gevt { + u32 one_bit:1; + u32 device_event:7; + u32 phy_port_number:4; + u32 reserved31_12:20; +} __packed; + +/** + * union dwc3_event - representation of Event Buffer contents + * @raw: raw 32-bit event + * @type: the type of the event + * @depevt: Device Endpoint Event + * @devt: Device Event + * @gevt: Global Event + */ +union dwc3_event { + u32 raw; + struct dwc3_event_type type; + struct dwc3_event_depevt depevt; + struct dwc3_event_devt devt; + struct dwc3_event_gevt gevt; +}; + +/* + * DWC3 Features to be used as Driver Data + */ + +#define DWC3_HAS_PERIPHERAL BIT(0) +#define DWC3_HAS_XHCI BIT(1) +#define DWC3_HAS_OTG BIT(3) + +#endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h new file mode 100644 index 0000000..ee3ba73 --- /dev/null +++ b/drivers/usb/dwc3/debug.h @@ -0,0 +1,51 @@ +/** + * debug.h - DesignWare USB3 DRD Controller Debug Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core.h" + +#ifdef CONFIG_DEBUG_FS +extern int dwc3_debugfs_init(struct dwc3 *); +extern void dwc3_debugfs_exit(struct dwc3 *); +#else +static inline int dwc3_debugfs_init(struct dwc3 *d) +{ return 0; } +static inline void dwc3_debugfs_exit(struct dwc3 *d) +{ } +#endif + diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c new file mode 100644 index 0000000..432df53 --- /dev/null +++ b/drivers/usb/dwc3/debugfs.c @@ -0,0 +1,534 @@ +/** + * debugfs.c - DesignWare USB3 DRD Controller DebugFS file + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "core.h" +#include "gadget.h" +#include "io.h" + +struct dwc3_register { + const char *name; + u32 offset; +}; + +#define dump_register(nm) \ +{ \ + .name = __stringify(nm), \ + .offset = DWC3_ ##nm, \ +} + +static const struct dwc3_register dwc3_regs[] = { + dump_register(GSBUSCFG0), + dump_register(GSBUSCFG1), + dump_register(GTXTHRCFG), + dump_register(GRXTHRCFG), + dump_register(GCTL), + dump_register(GEVTEN), + dump_register(GSTS), + dump_register(GSNPSID), + dump_register(GGPIO), + dump_register(GUID), + dump_register(GUCTL), + dump_register(GBUSERRADDR0), + dump_register(GBUSERRADDR1), + dump_register(GPRTBIMAP0), + dump_register(GPRTBIMAP1), + dump_register(GHWPARAMS0), + dump_register(GHWPARAMS1), + dump_register(GHWPARAMS2), + dump_register(GHWPARAMS3), + dump_register(GHWPARAMS4), + dump_register(GHWPARAMS5), + dump_register(GHWPARAMS6), + dump_register(GHWPARAMS7), + dump_register(GDBGFIFOSPACE), + dump_register(GDBGLTSSM), + dump_register(GPRTBIMAP_HS0), + dump_register(GPRTBIMAP_HS1), + dump_register(GPRTBIMAP_FS0), + dump_register(GPRTBIMAP_FS1), + + dump_register(GUSB2PHYCFG(0)), + dump_register(GUSB2PHYCFG(1)), + dump_register(GUSB2PHYCFG(2)), + dump_register(GUSB2PHYCFG(3)), + dump_register(GUSB2PHYCFG(4)), + dump_register(GUSB2PHYCFG(5)), + dump_register(GUSB2PHYCFG(6)), + dump_register(GUSB2PHYCFG(7)), + dump_register(GUSB2PHYCFG(8)), + dump_register(GUSB2PHYCFG(9)), + dump_register(GUSB2PHYCFG(10)), + dump_register(GUSB2PHYCFG(11)), + dump_register(GUSB2PHYCFG(12)), + dump_register(GUSB2PHYCFG(13)), + dump_register(GUSB2PHYCFG(14)), + dump_register(GUSB2PHYCFG(15)), + + dump_register(GUSB2I2CCTL(0)), + dump_register(GUSB2I2CCTL(1)), + dump_register(GUSB2I2CCTL(2)), + dump_register(GUSB2I2CCTL(3)), + dump_register(GUSB2I2CCTL(4)), + dump_register(GUSB2I2CCTL(5)), + dump_register(GUSB2I2CCTL(6)), + dump_register(GUSB2I2CCTL(7)), + dump_register(GUSB2I2CCTL(8)), + dump_register(GUSB2I2CCTL(9)), + dump_register(GUSB2I2CCTL(10)), + dump_register(GUSB2I2CCTL(11)), + dump_register(GUSB2I2CCTL(12)), + dump_register(GUSB2I2CCTL(13)), + dump_register(GUSB2I2CCTL(14)), + dump_register(GUSB2I2CCTL(15)), + + dump_register(GUSB2PHYACC(0)), + dump_register(GUSB2PHYACC(1)), + dump_register(GUSB2PHYACC(2)), + dump_register(GUSB2PHYACC(3)), + dump_register(GUSB2PHYACC(4)), + dump_register(GUSB2PHYACC(5)), + dump_register(GUSB2PHYACC(6)), + dump_register(GUSB2PHYACC(7)), + dump_register(GUSB2PHYACC(8)), + dump_register(GUSB2PHYACC(9)), + dump_register(GUSB2PHYACC(10)), + dump_register(GUSB2PHYACC(11)), + dump_register(GUSB2PHYACC(12)), + dump_register(GUSB2PHYACC(13)), + dump_register(GUSB2PHYACC(14)), + dump_register(GUSB2PHYACC(15)), + + dump_register(GUSB3PIPECTL(0)), + dump_register(GUSB3PIPECTL(1)), + dump_register(GUSB3PIPECTL(2)), + dump_register(GUSB3PIPECTL(3)), + dump_register(GUSB3PIPECTL(4)), + dump_register(GUSB3PIPECTL(5)), + dump_register(GUSB3PIPECTL(6)), + dump_register(GUSB3PIPECTL(7)), + dump_register(GUSB3PIPECTL(8)), + dump_register(GUSB3PIPECTL(9)), + dump_register(GUSB3PIPECTL(10)), + dump_register(GUSB3PIPECTL(11)), + dump_register(GUSB3PIPECTL(12)), + dump_register(GUSB3PIPECTL(13)), + dump_register(GUSB3PIPECTL(14)), + dump_register(GUSB3PIPECTL(15)), + + dump_register(GTXFIFOSIZ(0)), + dump_register(GTXFIFOSIZ(1)), + dump_register(GTXFIFOSIZ(2)), + dump_register(GTXFIFOSIZ(3)), + dump_register(GTXFIFOSIZ(4)), + dump_register(GTXFIFOSIZ(5)), + dump_register(GTXFIFOSIZ(6)), + dump_register(GTXFIFOSIZ(7)), + dump_register(GTXFIFOSIZ(8)), + dump_register(GTXFIFOSIZ(9)), + dump_register(GTXFIFOSIZ(10)), + dump_register(GTXFIFOSIZ(11)), + dump_register(GTXFIFOSIZ(12)), + dump_register(GTXFIFOSIZ(13)), + dump_register(GTXFIFOSIZ(14)), + dump_register(GTXFIFOSIZ(15)), + dump_register(GTXFIFOSIZ(16)), + dump_register(GTXFIFOSIZ(17)), + dump_register(GTXFIFOSIZ(18)), + dump_register(GTXFIFOSIZ(19)), + dump_register(GTXFIFOSIZ(20)), + dump_register(GTXFIFOSIZ(21)), + dump_register(GTXFIFOSIZ(22)), + dump_register(GTXFIFOSIZ(23)), + dump_register(GTXFIFOSIZ(24)), + dump_register(GTXFIFOSIZ(25)), + dump_register(GTXFIFOSIZ(26)), + dump_register(GTXFIFOSIZ(27)), + dump_register(GTXFIFOSIZ(28)), + dump_register(GTXFIFOSIZ(29)), + dump_register(GTXFIFOSIZ(30)), + dump_register(GTXFIFOSIZ(31)), + + dump_register(GRXFIFOSIZ(0)), + dump_register(GRXFIFOSIZ(1)), + dump_register(GRXFIFOSIZ(2)), + dump_register(GRXFIFOSIZ(3)), + dump_register(GRXFIFOSIZ(4)), + dump_register(GRXFIFOSIZ(5)), + dump_register(GRXFIFOSIZ(6)), + dump_register(GRXFIFOSIZ(7)), + dump_register(GRXFIFOSIZ(8)), + dump_register(GRXFIFOSIZ(9)), + dump_register(GRXFIFOSIZ(10)), + dump_register(GRXFIFOSIZ(11)), + dump_register(GRXFIFOSIZ(12)), + dump_register(GRXFIFOSIZ(13)), + dump_register(GRXFIFOSIZ(14)), + dump_register(GRXFIFOSIZ(15)), + dump_register(GRXFIFOSIZ(16)), + dump_register(GRXFIFOSIZ(17)), + dump_register(GRXFIFOSIZ(18)), + dump_register(GRXFIFOSIZ(19)), + dump_register(GRXFIFOSIZ(20)), + dump_register(GRXFIFOSIZ(21)), + dump_register(GRXFIFOSIZ(22)), + dump_register(GRXFIFOSIZ(23)), + dump_register(GRXFIFOSIZ(24)), + dump_register(GRXFIFOSIZ(25)), + dump_register(GRXFIFOSIZ(26)), + dump_register(GRXFIFOSIZ(27)), + dump_register(GRXFIFOSIZ(28)), + dump_register(GRXFIFOSIZ(29)), + dump_register(GRXFIFOSIZ(30)), + dump_register(GRXFIFOSIZ(31)), + + dump_register(GEVNTADRLO(0)), + dump_register(GEVNTADRHI(0)), + dump_register(GEVNTSIZ(0)), + dump_register(GEVNTCOUNT(0)), + + dump_register(GHWPARAMS8), + dump_register(DCFG), + dump_register(DCTL), + dump_register(DEVTEN), + dump_register(DSTS), + dump_register(DGCMDPAR), + dump_register(DGCMD), + dump_register(DALEPENA), + + dump_register(DEPCMDPAR2(0)), + dump_register(DEPCMDPAR2(1)), + dump_register(DEPCMDPAR2(2)), + dump_register(DEPCMDPAR2(3)), + dump_register(DEPCMDPAR2(4)), + dump_register(DEPCMDPAR2(5)), + dump_register(DEPCMDPAR2(6)), + dump_register(DEPCMDPAR2(7)), + dump_register(DEPCMDPAR2(8)), + dump_register(DEPCMDPAR2(9)), + dump_register(DEPCMDPAR2(10)), + dump_register(DEPCMDPAR2(11)), + dump_register(DEPCMDPAR2(12)), + dump_register(DEPCMDPAR2(13)), + dump_register(DEPCMDPAR2(14)), + dump_register(DEPCMDPAR2(15)), + dump_register(DEPCMDPAR2(16)), + dump_register(DEPCMDPAR2(17)), + dump_register(DEPCMDPAR2(18)), + dump_register(DEPCMDPAR2(19)), + dump_register(DEPCMDPAR2(20)), + dump_register(DEPCMDPAR2(21)), + dump_register(DEPCMDPAR2(22)), + dump_register(DEPCMDPAR2(23)), + dump_register(DEPCMDPAR2(24)), + dump_register(DEPCMDPAR2(25)), + dump_register(DEPCMDPAR2(26)), + dump_register(DEPCMDPAR2(27)), + dump_register(DEPCMDPAR2(28)), + dump_register(DEPCMDPAR2(29)), + dump_register(DEPCMDPAR2(30)), + dump_register(DEPCMDPAR2(31)), + + dump_register(DEPCMDPAR1(0)), + dump_register(DEPCMDPAR1(1)), + dump_register(DEPCMDPAR1(2)), + dump_register(DEPCMDPAR1(3)), + dump_register(DEPCMDPAR1(4)), + dump_register(DEPCMDPAR1(5)), + dump_register(DEPCMDPAR1(6)), + dump_register(DEPCMDPAR1(7)), + dump_register(DEPCMDPAR1(8)), + dump_register(DEPCMDPAR1(9)), + dump_register(DEPCMDPAR1(10)), + dump_register(DEPCMDPAR1(11)), + dump_register(DEPCMDPAR1(12)), + dump_register(DEPCMDPAR1(13)), + dump_register(DEPCMDPAR1(14)), + dump_register(DEPCMDPAR1(15)), + dump_register(DEPCMDPAR1(16)), + dump_register(DEPCMDPAR1(17)), + dump_register(DEPCMDPAR1(18)), + dump_register(DEPCMDPAR1(19)), + dump_register(DEPCMDPAR1(20)), + dump_register(DEPCMDPAR1(21)), + dump_register(DEPCMDPAR1(22)), + dump_register(DEPCMDPAR1(23)), + dump_register(DEPCMDPAR1(24)), + dump_register(DEPCMDPAR1(25)), + dump_register(DEPCMDPAR1(26)), + dump_register(DEPCMDPAR1(27)), + dump_register(DEPCMDPAR1(28)), + dump_register(DEPCMDPAR1(29)), + dump_register(DEPCMDPAR1(30)), + dump_register(DEPCMDPAR1(31)), + + dump_register(DEPCMDPAR0(0)), + dump_register(DEPCMDPAR0(1)), + dump_register(DEPCMDPAR0(2)), + dump_register(DEPCMDPAR0(3)), + dump_register(DEPCMDPAR0(4)), + dump_register(DEPCMDPAR0(5)), + dump_register(DEPCMDPAR0(6)), + dump_register(DEPCMDPAR0(7)), + dump_register(DEPCMDPAR0(8)), + dump_register(DEPCMDPAR0(9)), + dump_register(DEPCMDPAR0(10)), + dump_register(DEPCMDPAR0(11)), + dump_register(DEPCMDPAR0(12)), + dump_register(DEPCMDPAR0(13)), + dump_register(DEPCMDPAR0(14)), + dump_register(DEPCMDPAR0(15)), + dump_register(DEPCMDPAR0(16)), + dump_register(DEPCMDPAR0(17)), + dump_register(DEPCMDPAR0(18)), + dump_register(DEPCMDPAR0(19)), + dump_register(DEPCMDPAR0(20)), + dump_register(DEPCMDPAR0(21)), + dump_register(DEPCMDPAR0(22)), + dump_register(DEPCMDPAR0(23)), + dump_register(DEPCMDPAR0(24)), + dump_register(DEPCMDPAR0(25)), + dump_register(DEPCMDPAR0(26)), + dump_register(DEPCMDPAR0(27)), + dump_register(DEPCMDPAR0(28)), + dump_register(DEPCMDPAR0(29)), + dump_register(DEPCMDPAR0(30)), + dump_register(DEPCMDPAR0(31)), + + dump_register(DEPCMD(0)), + dump_register(DEPCMD(1)), + dump_register(DEPCMD(2)), + dump_register(DEPCMD(3)), + dump_register(DEPCMD(4)), + dump_register(DEPCMD(5)), + dump_register(DEPCMD(6)), + dump_register(DEPCMD(7)), + dump_register(DEPCMD(8)), + dump_register(DEPCMD(9)), + dump_register(DEPCMD(10)), + dump_register(DEPCMD(11)), + dump_register(DEPCMD(12)), + dump_register(DEPCMD(13)), + dump_register(DEPCMD(14)), + dump_register(DEPCMD(15)), + dump_register(DEPCMD(16)), + dump_register(DEPCMD(17)), + dump_register(DEPCMD(18)), + dump_register(DEPCMD(19)), + dump_register(DEPCMD(20)), + dump_register(DEPCMD(21)), + dump_register(DEPCMD(22)), + dump_register(DEPCMD(23)), + dump_register(DEPCMD(24)), + dump_register(DEPCMD(25)), + dump_register(DEPCMD(26)), + dump_register(DEPCMD(27)), + dump_register(DEPCMD(28)), + dump_register(DEPCMD(29)), + dump_register(DEPCMD(30)), + dump_register(DEPCMD(31)), + + dump_register(OCFG), + dump_register(OCTL), + dump_register(OEVTEN), + dump_register(OSTS), +}; + +static int dwc3_regdump_show(struct seq_file *s, void *unused) +{ + struct dwc3 *dwc = s->private; + int i; + + seq_printf(s, "DesignWare USB3 Core Register Dump\n"); + + for (i = 0; i < ARRAY_SIZE(dwc3_regs); i++) { + seq_printf(s, "%-20s : %08x\n", dwc3_regs[i].name, + dwc3_readl(dwc->regs, dwc3_regs[i].offset)); + } + + return 0; +} + +static int dwc3_regdump_open(struct inode *inode, struct file *file) +{ + return single_open(file, dwc3_regdump_show, inode->i_private); +} + +static const struct file_operations dwc3_regdump_fops = { + .open = dwc3_regdump_open, + .read = seq_read, + .release = single_release, +}; + + +static int dwc3_send_testmode_cmd(struct dwc3 *dwc, int mode) +{ + u32 timeout = 250; + + dwc3_writel(dwc->regs, DWC3_DGCMDPAR, mode); + dwc3_writel(dwc->regs, DWC3_DGCMD, DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK | + DWC3_DEPCMD_CMDACT); + do { + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DGCMD); + if (!(reg & DWC3_DEPCMD_CMDACT)) + return 0; + timeout--; + if (!timeout) + return -ETIMEDOUT; + mdelay(1); + } while (1); +} + +static struct dwc3_trb_hw trb_0 __aligned(16); +static struct dwc3_trb_hw trb_1 __aligned(16); + +#define BUF_SIZE 4096 +static int dwc3_testmode_open(struct inode *inode, struct file *file) +{ + struct dwc3 *dwc = inode->i_private; + struct dwc3_gadget_ep_cmd_params par0; + struct dwc3_gadget_ep_cmd_params par1; + struct dwc3_trb trb; + int ret; + u8 *buf0; + u8 *buf1; + + buf0 = kmalloc(BUF_SIZE, GFP_KERNEL); + if (!buf0) + return -ENOMEM; + buf1 = kmalloc(BUF_SIZE, GFP_KERNEL); + if (!buf1) + return -ENOMEM; + + memset(buf0, 0xaa, BUF_SIZE); + memset(buf1, 0x33, BUF_SIZE); + + memset(&trb, 0, sizeof(trb)); + memset(&par0, 0, sizeof(par0)); + memset(&par1, 0, sizeof(par1)); + + trb.lst = 1; + trb.trbctl = DWC3_TRBCTL_NORMAL; + trb.length = BUF_SIZE; + trb.hwo = 1; + + trb.bplh = virt_to_phys(buf0); + dwc3_trb_to_hw(&trb, &trb_0); + + trb.bplh = virt_to_phys(buf1); + dwc3_trb_to_hw(&trb, &trb_1); + + par0.param0.depstrtxfer.transfer_desc_addr_high = + upper_32_bits(virt_to_phys(&trb_0)); + par0.param1.depstrtxfer.transfer_desc_addr_low = + lower_32_bits(virt_to_phys(&trb_0)); + + par1.param0.depstrtxfer.transfer_desc_addr_high = + upper_32_bits(virt_to_phys(&trb_1)); + par1.param1.depstrtxfer.transfer_desc_addr_low = + lower_32_bits(virt_to_phys(&trb_1)); + + dwc3_send_testmode_cmd(dwc, 1); + + ret = dwc3_send_gadget_ep_cmd(dwc, 0, DWC3_DEPCMD_STARTTRANSFER, &par0); + ret = dwc3_send_gadget_ep_cmd(dwc, 1, DWC3_DEPCMD_STARTTRANSFER, &par1); + + dwc3_send_testmode_cmd(dwc, 0); + return -EBUSY; +} + +static const struct file_operations dwc3_testmode_fops = { + .open = dwc3_testmode_open, + .read = seq_read, + .release = single_release, +}; + +int __devinit dwc3_debugfs_init(struct dwc3 *dwc) +{ + struct dentry *root; + struct dentry *file; + int ret; + + root = debugfs_create_dir(dev_name(dwc->dev), NULL); + if (IS_ERR(root)){ + ret = PTR_ERR(root); + goto err0; + } + + dwc->root = root; + + file = debugfs_create_file("regdump", S_IRUGO, root, dwc, + &dwc3_regdump_fops); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto err1; + } + file = debugfs_create_file("testmode", S_IRUGO, root, dwc, + &dwc3_testmode_fops); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto err1; + } + + return 0; + +err1: + debugfs_remove_recursive(root); + +err0: + return ret; +} + +void __devexit dwc3_debugfs_exit(struct dwc3 *dwc) +{ + debugfs_remove_recursive(dwc->root); + dwc->root = NULL; +} diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c new file mode 100644 index 0000000..08fffe6 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -0,0 +1,410 @@ +/** + * dwc3-omap.c - OMAP Specific Glue layer + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "io.h" + +/* + * All these registers belong to OMAP's Wrapper around the + * DesignWare USB3 Core. + */ + +#define USBOTGSS_REVISION 0x0000 +#define USBOTGSS_SYSCONFIG 0x0010 +#define USBOTGSS_IRQ_EOI 0x0020 +#define USBOTGSS_IRQSTATUS_RAW_0 0x0024 +#define USBOTGSS_IRQSTATUS_0 0x0028 +#define USBOTGSS_IRQENABLE_SET_0 0x002c +#define USBOTGSS_IRQENABLE_CLR_0 0x0030 +#define USBOTGSS_IRQSTATUS_RAW_1 0x0034 +#define USBOTGSS_IRQSTATUS_1 0x0038 +#define USBOTGSS_IRQENABLE_SET_1 0x003c +#define USBOTGSS_IRQENABLE_CLR_1 0x0040 +#define USBOTGSS_UTMI_OTG_CTRL 0x0080 +#define USBOTGSS_UTMI_OTG_STATUS 0x0084 +#define USBOTGSS_MMRAM_OFFSET 0x0100 +#define USBOTGSS_FLADJ 0x0104 +#define USBOTGSS_DEBUG_CFG 0x0108 +#define USBOTGSS_DEBUG_DATA 0x010c + +/* SYSCONFIG REGISTER */ +#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16) +#define USBOTGSS_SYSCONFIG_STANDBYMODE(x) ((x) << 4) +#define USBOTGSS_SYSCONFIG_IDLEMODE(x) ((x) << 2) + +/* IRQ_EOI REGISTER */ +#define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0) + +/* IRQS0 BITS */ +#define USBOTGSS_IRQO_COREIRQ_ST (1 << 0) + +/* IRQ1 BITS */ +#define USBOTGSS_IRQ1_DMADISABLECLR (1 << 17) +#define USBOTGSS_IRQ1_OEVT (1 << 16) +#define USBOTGSS_IRQ1_DRVVBUS_RISE (1 << 13) +#define USBOTGSS_IRQ1_CHRGVBUS_RISE (1 << 12) +#define USBOTGSS_IRQ1_DISCHRGVBUS_RISE (1 << 11) +#define USBOTGSS_IRQ1_IDPULLUP_RISE (1 << 8) +#define USBOTGSS_IRQ1_DRVVBUS_FALL (1 << 5) +#define USBOTGSS_IRQ1_CHRGVBUS_FALL (1 << 4) +#define USBOTGSS_IRQ1_DISCHRGVBUS_FALL (1 << 3) +#define USBOTGSS_IRQ1_IDPULLUP_FALL (1 << 0) + +/* UTMI_OTG_CTRL REGISTER */ +#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS (1 << 5) +#define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS (1 << 4) +#define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS (1 << 3) +#define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP (1 << 0) + +/* UTMI_OTG_STATUS REGISTER */ +#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE (1 << 31) +#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT (1 << 9) +#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8) +#define USBOTGSS_UTMI_OTG_STATUS_IDDIG (1 << 4) +#define USBOTGSS_UTMI_OTG_STATUS_SESSEND (1 << 3) +#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID (1 << 2) +#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1) + +struct dwc3_omap { + /* device lock */ + spinlock_t lock; + + struct platform_device *dwc3; + struct device *dev; + + int irq; + void __iomem *base; + + void *context; + u32 resource_size; + + u32 dma_status:1; +}; + +#ifdef CONFIG_PM +static int dwc3_omap_suspend(struct device *dev) +{ + struct dwc3_omap *omap = dev_get_drvdata(dev); + + memcpy_fromio(omap->context, omap->base, omap->resource_size); + + return 0; +} + +static int dwc3_omap_resume(struct device *dev) +{ + struct dwc3_omap *omap = dev_get_drvdata(dev); + + memcpy_toio(omap->base, omap->context, omap->resource_size); + + return 0; +} + +static int dwc3_omap_idle(struct device *dev) +{ + struct dwc3_omap *omap = dev_get_drvdata(dev); + u32 reg; + + /* stop DMA Engine */ + reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG); + reg &= ~(USBOTGSS_SYSCONFIG_DMADISABLE); + dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg); + + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(dwc3_omap_pm_ops, dwc3_omap_suspend, + dwc3_omap_resume, dwc3_omap_idle); + +#define DEV_PM_OPS (&dwc3_omap_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + +static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) +{ + struct dwc3_omap *omap = _omap; + u32 reg; + u32 ctrl; + + spin_lock(&omap->lock); + + reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_1); + ctrl = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_CTRL); + + if (reg & USBOTGSS_IRQ1_DMADISABLECLR) { + dev_dbg(omap->base, "DMA Disable was Cleared\n"); + omap->dma_status = false; + } + + if (reg & USBOTGSS_IRQ1_OEVT) + dev_dbg(omap->base, "OTG Event\n"); + + if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE) { + dev_dbg(omap->base, "DRVVBUS Rise\n"); + ctrl |= USBOTGSS_UTMI_OTG_CTRL_DRVVBUS; + } + + if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE) { + dev_dbg(omap->base, "CHRGVBUS Rise\n"); + ctrl |= USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS; + } + + if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE) { + dev_dbg(omap->base, "DISCHRGVBUS Rise\n"); + ctrl |= USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS; + } + + if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE) { + dev_dbg(omap->base, "IDPULLUP Rise\n"); + ctrl |= USBOTGSS_UTMI_OTG_CTRL_IDPULLUP; + } + + if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL) { + dev_dbg(omap->base, "DRVVBUS Fall\n"); + ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_DRVVBUS; + } + + if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL) { + dev_dbg(omap->base, "CHRGVBUS Fall\n"); + ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS; + } + + if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL) { + dev_dbg(omap->base, "DISCHRGVBUS Fall\n"); + ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS; + } + + if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL) { + dev_dbg(omap->base, "IDPULLUP Fall\n"); + ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_IDPULLUP; + } + + dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_CTRL, ctrl); + + spin_unlock(&omap->lock); + + return IRQ_HANDLED; +} + +static int __devinit dwc3_omap_probe(struct platform_device *pdev) +{ + struct platform_device *dwc3; + struct dwc3_omap *omap; + struct resource *res; + + int ret = -ENOMEM; + int irq; + + u32 reg; + + void __iomem *base; + void *context; + + omap = kzalloc(sizeof(*omap), GFP_KERNEL); + if (!omap) { + dev_err(&pdev->dev, "not enough memory\n"); + goto err0; + } + + platform_set_drvdata(pdev, omap); + + irq = platform_get_irq(pdev, 1); + if (irq < 0) { + dev_err(&pdev->dev, "missing IRQ resource\n"); + ret = -EINVAL; + goto err1; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, "missing memory base resource\n"); + ret = -EINVAL; + goto err1; + } + + base = ioremap_nocache(res->start, resource_size(res)); + if (!base) { + dev_err(&pdev->dev, "ioremap failed\n"); + goto err1; + } + + dwc3 = platform_device_alloc("dwc3-omap", -1); + if (!dwc3) { + dev_err(&pdev->dev, "couldn't allocate dwc3 device\n"); + goto err2; + } + + context = kzalloc(resource_size(res), GFP_KERNEL); + if (!context) { + dev_err(&pdev->dev, "couldn't allocate dwc3 context memory\n"); + goto err3; + } + + spin_lock_init(&omap->lock); + dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask); + + dwc3->dev.parent = &pdev->dev; + dwc3->dev.dma_mask = pdev->dev.dma_mask; + dwc3->dev.dma_parms = pdev->dev.dma_parms; + omap->resource_size = resource_size(res); + omap->context = context; + omap->dev = &pdev->dev; + omap->irq = irq; + omap->base = base; + omap->dwc3 = dwc3; + + /* check the DMA Status */ + reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG); + omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE); + + ret = request_irq(omap->irq, dwc3_omap_interrupt, 0, + "dwc3-wrapper", omap); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ #%d --> %d\n", + omap->irq, ret); + goto err4; + } + + /* enable all IRQs */ + dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, 0x01); + + reg = (USBOTGSS_IRQ1_DMADISABLECLR | + USBOTGSS_IRQ1_OEVT | + USBOTGSS_IRQ1_DRVVBUS_RISE | + USBOTGSS_IRQ1_CHRGVBUS_RISE | + USBOTGSS_IRQ1_DISCHRGVBUS_RISE | + USBOTGSS_IRQ1_IDPULLUP_RISE | + USBOTGSS_IRQ1_DRVVBUS_FALL | + USBOTGSS_IRQ1_CHRGVBUS_FALL | + USBOTGSS_IRQ1_DISCHRGVBUS_FALL | + USBOTGSS_IRQ1_IDPULLUP_FALL); + + dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg); + + ret = platform_device_add_resources(dwc3, pdev->resource, + pdev->num_resources); + if (ret) { + dev_err(&pdev->dev, "couldn't add resources to dwc3 device\n"); + goto err5; + } + + ret = platform_device_add(dwc3); + if (ret) { + dev_err(&pdev->dev, "failed to register dwc3 device\n"); + goto err5; + } + + return 0; + +err5: + free_irq(omap->irq, omap); + +err4: + kfree(omap->context); + +err3: + platform_device_put(dwc3); + +err2: + iounmap(base); + +err1: + kfree(omap); + +err0: + return ret; +} + +static int __devexit dwc3_omap_remove(struct platform_device *pdev) +{ + struct dwc3_omap *omap = platform_get_drvdata(pdev); + + platform_device_unregister(omap->dwc3); + + free_irq(omap->irq, omap); + iounmap(omap->base); + + kfree(omap->context); + kfree(omap); + + return 0; +} + +static const struct of_device_id of_dwc3_matach[] = { + { + "ti,dwc3", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_dwc3_matach); + +static struct platform_driver dwc3_omap_driver = { + .probe = dwc3_omap_probe, + .remove = __devexit_p(dwc3_omap_remove), + .driver = { + .name = "omap-dwc3", + .pm = DEV_PM_OPS, + .of_match_table = of_dwc3_matach, + }, +}; + +MODULE_AUTHOR("Felipe Balbi "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer"); + +static int __devinit dwc3_omap_init(void) +{ + return platform_driver_register(&dwc3_omap_driver); +} +module_init(dwc3_omap_init); + +static void __exit dwc3_omap_exit(void) +{ + platform_driver_unregister(&dwc3_omap_driver); +} +module_exit(dwc3_omap_exit); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c new file mode 100644 index 0000000..2578595 --- /dev/null +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -0,0 +1,219 @@ +/** + * dwc3-pci.c - PCI Specific glue layer + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +/* FIXME define these in */ +#define PCI_VENDOR_ID_SYNOPSYS 0x16c3 +#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd + +#define DWC3_PCI_DEVS_POSSIBLE 32 + +struct dwc3_pci { + struct device *dev; + struct platform_device *dwc3; +}; + +static DECLARE_BITMAP(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE); + +static int dwc3_pci_get_device_id(struct dwc3_pci *glue) +{ + int id; + +again: + id = find_first_zero_bit(dwc3_pci_devs, DWC3_PCI_DEVS_POSSIBLE); + if (id < DWC3_PCI_DEVS_POSSIBLE) { + int old; + + old = test_and_set_bit(id, dwc3_pci_devs); + if (old) + goto again; + } else { + dev_err(glue->dev, "no space for new device\n"); + id = -ENOMEM; + } + + return 0; +} + +static void dwc3_pci_put_device_id(struct dwc3_pci *glue, int id) +{ + int ret; + + if (id < 0) + return; + + ret = test_bit(id, dwc3_pci_devs); + WARN(!ret, "Device: %s\nID %d not in use\n", + dev_driver_string(glue->dev), id); + clear_bit(id, dwc3_pci_devs); +} + +static int __devinit dwc3_pci_probe(struct pci_dev *pci, + const struct pci_device_id *id) +{ + struct resource res[2]; + struct platform_device *dwc3; + struct dwc3_pci *glue; + int ret = -ENOMEM; + int devid; + + glue = kzalloc(sizeof(*glue), GFP_KERNEL); + if (!glue) { + dev_err(&pci->dev, "not enough memory\n"); + goto err0; + } + + glue->dev = &pci->dev; + + ret = pci_enable_device(pci); + if (ret) { + dev_err(&pci->dev, "failed to enable pci device\n"); + goto err1; + } + + pci_set_power_state(pci, PCI_D0); + pci_set_master(pci); + + devid = dwc3_pci_get_device_id(glue); + if (devid < 0) + goto err2; + + dwc3 = platform_device_alloc("dwc3-pci", devid); + if (!dwc3) { + dev_err(&pci->dev, "couldn't allocate dwc3 device\n"); + goto err3; + } + + memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res)); + + res[0].start = pci_resource_start(pci, 0); + res[0].end = pci_resource_end(pci, 0); + res[0].name = "dwc_usb3"; + res[0].flags = IORESOURCE_MEM; + + res[1].start = pci->irq; + res[1].name = "dwc_usb3"; + res[1].flags = IORESOURCE_IRQ; + + ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res)); + if (ret) { + dev_err(&pci->dev, "couldn't add resources to dwc3 device\n"); + goto err4; + } + + pci_set_drvdata(pci, glue); + + dma_set_coherent_mask(&dwc3->dev, pci->dev.coherent_dma_mask); + + dwc3->dev.dma_mask = pci->dev.dma_mask; + dwc3->dev.dma_parms = pci->dev.dma_parms; + dwc3->dev.parent = &pci->dev; + glue->dwc3 = dwc3; + + ret = platform_device_add(dwc3); + if (ret) { + dev_err(&pci->dev, "failed to register dwc3 device\n"); + goto err4; + } + + return 0; + +err4: + pci_set_drvdata(pci, NULL); + platform_device_put(dwc3); + +err3: + dwc3_pci_put_device_id(glue, devid); + +err2: + pci_disable_device(pci); + +err1: + kfree(pci); + +err0: + return ret; +} + +static void __devexit dwc3_pci_remove(struct pci_dev *pci) +{ + struct dwc3_pci *glue = pci_get_drvdata(pci); + + dwc3_pci_put_device_id(glue, glue->dwc3->id); + platform_device_unregister(glue->dwc3); + pci_set_drvdata(pci, NULL); + pci_disable_device(pci); + kfree(glue); +} + +static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = { + { + PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, + PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), + }, + { } /* Terminating Entry */ +}; +MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); + +static struct pci_driver dwc3_pci_driver = { + .name = "pci-dwc3", + .id_table = dwc3_pci_id_table, + .probe = dwc3_pci_probe, + .remove = __devexit_p(dwc3_pci_remove), +}; + +MODULE_AUTHOR("Felipe Balbi "); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("DesignWare USB3 PCI Glue Layer"); + +static int __devinit dwc3_pci_init(void) +{ + return pci_register_driver(&dwc3_pci_driver); +} +module_init(dwc3_pci_init); + +static void __exit dwc3_pci_exit(void) +{ + pci_unregister_driver(&dwc3_pci_driver); +} +module_exit(dwc3_pci_exit); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c new file mode 100644 index 0000000..4698fe0 --- /dev/null +++ b/drivers/usb/dwc3/ep0.c @@ -0,0 +1,782 @@ +/** + * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "core.h" +#include "gadget.h" +#include "io.h" + +static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, + const struct dwc3_event_depevt *event); + +static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) +{ + switch (state) { + case EP0_UNCONNECTED: + return "Unconnected"; + case EP0_IDLE: + return "Idle"; + case EP0_IN_DATA_PHASE: + return "IN Data Phase"; + case EP0_OUT_DATA_PHASE: + return "OUT Data Phase"; + case EP0_IN_WAIT_GADGET: + return "IN Wait Gadget"; + case EP0_OUT_WAIT_GADGET: + return "OUT Wait Gadget"; + case EP0_IN_WAIT_NRDY: + return "IN Wait NRDY"; + case EP0_OUT_WAIT_NRDY: + return "OUT Wait NRDY"; + case EP0_IN_STATUS_PHASE: + return "IN Status Phase"; + case EP0_OUT_STATUS_PHASE: + return "OUT Status Phase"; + case EP0_STALL: + return "Stall"; + default: + return "UNKNOWN"; + } +} + +static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, + u32 len) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_trb_hw *trb_hw; + struct dwc3_trb trb; + struct dwc3_ep *dep; + + int ret; + + dep = dwc->eps[epnum]; + + trb_hw = dwc->ep0_trb; + memset(&trb, 0, sizeof(trb)); + + switch (dwc->ep0state) { + case EP0_IDLE: + trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP; + break; + + case EP0_IN_WAIT_NRDY: + case EP0_OUT_WAIT_NRDY: + case EP0_IN_STATUS_PHASE: + case EP0_OUT_STATUS_PHASE: + if (dwc->three_stage_setup) + trb.trbctl = DWC3_TRBCTL_CONTROL_STATUS3; + else + trb.trbctl = DWC3_TRBCTL_CONTROL_STATUS2; + + if (dwc->ep0state == EP0_IN_WAIT_NRDY) + dwc->ep0state = EP0_IN_STATUS_PHASE; + else if (dwc->ep0state == EP0_OUT_WAIT_NRDY) + dwc->ep0state = EP0_OUT_STATUS_PHASE; + break; + + case EP0_IN_WAIT_GADGET: + dwc->ep0state = EP0_IN_WAIT_NRDY; + return 0; + break; + + case EP0_OUT_WAIT_GADGET: + dwc->ep0state = EP0_OUT_WAIT_NRDY; + return 0; + + break; + + case EP0_IN_DATA_PHASE: + case EP0_OUT_DATA_PHASE: + trb.trbctl = DWC3_TRBCTL_CONTROL_DATA; + break; + + default: + dev_err(dwc->dev, "%s() can't in state %d\n", __func__, + dwc->ep0state); + return -EINVAL; + } + + trb.bplh = buf_dma; + trb.length = len; + + trb.hwo = 1; + trb.lst = 1; + trb.ioc = 1; + trb.isp_imi = 1; + + dwc3_trb_to_hw(&trb, trb_hw); + + memset(¶ms, 0, sizeof(params)); + params.param0.depstrtxfer.transfer_desc_addr_high = + upper_32_bits(dwc->ep0_trb_addr); + params.param1.depstrtxfer.transfer_desc_addr_low = + lower_32_bits(dwc->ep0_trb_addr); + + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_STARTTRANSFER, ¶ms); + if (ret < 0) { + dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); + return ret; + } + + dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, + dep->number); + + return 0; +} + +static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, + struct dwc3_request *req) +{ + struct dwc3 *dwc = dep->dwc; + int ret; + + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->direction = dep->direction; + req->epnum = dep->number; + + list_add_tail(&req->list, &dep->request_list); + dwc3_map_buffer_to_dma(req); + + ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma, + req->request.length); + if (ret < 0) { + list_del(&req->list); + dwc3_unmap_buffer_from_dma(req); + } + + return ret; +} + +int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; + + switch (dwc->ep0state) { + case EP0_IN_DATA_PHASE: + case EP0_IN_WAIT_GADGET: + case EP0_IN_WAIT_NRDY: + case EP0_IN_STATUS_PHASE: + dep = dwc->eps[1]; + break; + + case EP0_OUT_DATA_PHASE: + case EP0_OUT_WAIT_GADGET: + case EP0_OUT_WAIT_NRDY: + case EP0_OUT_STATUS_PHASE: + dep = dwc->eps[0]; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&dwc->lock, flags); + if (!dep->desc) { + dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", + request, dep->name); + ret = -ESHUTDOWN; + goto out; + } + + /* we share one TRB for ep0/1 */ + if (!list_empty(&dwc->eps[0]->request_list) || + !list_empty(&dwc->eps[1]->request_list) || + dwc->ep0_status_pending) { + ret = -EBUSY; + goto out; + } + + dev_vdbg(dwc->dev, "queueing request %p to %s length %d, state '%s'\n", + request, dep->name, request->length, + dwc3_ep0_state_string(dwc->ep0state)); + + ret = __dwc3_gadget_ep0_queue(dep, req); + +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) +{ + /* stall is always issued on EP0 */ + __dwc3_gadget_ep_set_halt(dwc->eps[0], 1); + dwc->eps[0]->flags &= ~DWC3_EP_STALL; + dwc->ep0state = EP0_IDLE; + dwc3_ep0_out_start(dwc); +} + +void dwc3_ep0_out_start(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + int ret; + + dep = dwc->eps[0]; + + ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8); + WARN_ON(ret < 0); +} + +/* + * Send a zero length packet for the status phase of the control transfer + */ +static void dwc3_ep0_do_setup_status(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep; + int ret; + u32 epnum; + + epnum = event->endpoint_number; + dep = dwc->eps[epnum]; + + if (epnum) + dwc->ep0state = EP0_IN_STATUS_PHASE; + else + dwc->ep0state = EP0_OUT_STATUS_PHASE; + + /* + * Not sure Why I need a buffer for a zero transfer. Maybe the + * HW reacts strange on a NULL pointer + */ + ret = dwc3_ep0_start_trans(dwc, epnum, dwc->ctrl_req_addr, 0); + if (ret) { + dev_dbg(dwc->dev, "failed to start transfer, stalling\n"); + dwc3_ep0_stall_and_restart(dwc); + } +} + +static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) +{ + struct dwc3_ep *dep; + u32 windex = le16_to_cpu(wIndex_le); + u32 epnum; + + epnum = (windex & USB_ENDPOINT_NUMBER_MASK) << 1; + if ((windex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + epnum |= 1; + + dep = dwc->eps[epnum]; + if (dep->flags & DWC3_EP_ENABLED) + return dep; + + return NULL; +} + +static void dwc3_ep0_send_status_response(struct dwc3 *dwc) +{ + u32 epnum; + + if (dwc->ep0state == EP0_IN_DATA_PHASE) + epnum = 1; + else + epnum = 0; + + dwc3_ep0_start_trans(dwc, epnum, dwc->ctrl_req_addr, + dwc->ep0_usb_req.length); + dwc->ep0_status_pending = 1; +} + +/* + * ch 9.4.5 + */ +static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + struct dwc3_ep *dep; + u32 recip; + u16 usb_status = 0; + __le16 *response_pkt; + + recip = ctrl->bRequestType & USB_RECIP_MASK; + switch (recip) { + case USB_RECIP_DEVICE: + /* + * We are self-powered. U1/U2/LTM will be set later + * once we handle this states. RemoteWakeup is 0 on SS + */ + usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED; + break; + + case USB_RECIP_INTERFACE: + /* + * Function Remote Wake Capable D0 + * Function Remote Wakeup D1 + */ + break; + + case USB_RECIP_ENDPOINT: + dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); + if (!dep) + return -EINVAL; + + if (dep->flags & DWC3_EP_STALL) + usb_status = 1 << USB_ENDPOINT_HALT; + break; + default: + return -EINVAL; + }; + + response_pkt = (__le16 *) dwc->setup_buf; + *response_pkt = cpu_to_le16(usb_status); + dwc->ep0_usb_req.length = sizeof(*response_pkt); + dwc3_ep0_send_status_response(dwc); + + return 0; +} + +static int dwc3_ep0_handle_feature(struct dwc3 *dwc, + struct usb_ctrlrequest *ctrl, int set) +{ + struct dwc3_ep *dep; + u32 recip; + u32 wValue; + u32 wIndex; + u32 reg; + int ret; + u32 mode; + + wValue = le16_to_cpu(ctrl->wValue); + wIndex = le16_to_cpu(ctrl->wIndex); + recip = ctrl->bRequestType & USB_RECIP_MASK; + switch (recip) { + case USB_RECIP_DEVICE: + + /* + * 9.4.1 says only only for SS, in AddressState only for + * default control pipe + */ + switch (wValue) { + case USB_DEVICE_U1_ENABLE: + case USB_DEVICE_U2_ENABLE: + case USB_DEVICE_LTM_ENABLE: + if (dwc->dev_state != DWC3_CONFIGURED_STATE) + return -EINVAL; + if (dwc->speed != DWC3_DSTS_SUPERSPEED) + return -EINVAL; + } + + /* XXX add U[12] & LTM */ + switch (wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + break; + case USB_DEVICE_U1_ENABLE: + break; + case USB_DEVICE_U2_ENABLE: + break; + case USB_DEVICE_LTM_ENABLE: + break; + + case USB_DEVICE_TEST_MODE: + if ((wIndex & 0xff) != 0) + return -EINVAL; + if (!set) + return -EINVAL; + + mode = wIndex >> 8; + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_TSTCTRL_MASK; + + switch (mode) { + case TEST_J: + case TEST_K: + case TEST_SE0_NAK: + case TEST_PACKET: + case TEST_FORCE_EN: + reg |= mode << 1; + break; + default: + return -EINVAL; + } + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + break; + default: + return -EINVAL; + } + break; + + case USB_RECIP_INTERFACE: + switch (wValue) { + case USB_INTRF_FUNC_SUSPEND: + if (wIndex & USB_INTRF_FUNC_SUSPEND_LP) + /* XXX enable Low power suspend */ + ; + if (wIndex & USB_INTRF_FUNC_SUSPEND_RW) + /* XXX enable remote wakeup */ + ; + break; + default: + return -EINVAL; + } + break; + + case USB_RECIP_ENDPOINT: + switch (wValue) { + case USB_ENDPOINT_HALT: + + dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); + if (!dep) + return -EINVAL; + ret = __dwc3_gadget_ep_set_halt(dep, set); + if (ret) + return -EINVAL; + break; + default: + return -EINVAL; + } + break; + + default: + return -EINVAL; + }; + + dwc->ep0state = EP0_IN_WAIT_NRDY; + + return 0; +} + +static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + int ret = 0; + u32 addr; + u32 reg; + + addr = le16_to_cpu(ctrl->wValue); + if (addr > 127) + return -EINVAL; + + switch (dwc->dev_state) { + case DWC3_DEFAULT_STATE: + case DWC3_ADDRESS_STATE: + /* + * Not sure if we should program DevAddr now or later + */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_DEVADDR_MASK); + reg |= DWC3_DCFG_DEVADDR(addr); + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + if (addr) + dwc->dev_state = DWC3_ADDRESS_STATE; + else + dwc->dev_state = DWC3_DEFAULT_STATE; + break; + + case DWC3_CONFIGURED_STATE: + ret = -EINVAL; + break; + } + dwc->ep0state = EP0_IN_WAIT_NRDY; + return ret; +} + +static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + int ret; + + spin_unlock(&dwc->lock); + ret = dwc->gadget_driver->setup(&dwc->gadget, ctrl); + spin_lock(&dwc->lock); + return ret; +} + +static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + u32 cfg; + int ret; + + cfg = le16_to_cpu(ctrl->wValue); + + switch (dwc->dev_state) { + case DWC3_DEFAULT_STATE: + return -EINVAL; + break; + + case DWC3_ADDRESS_STATE: + ret = dwc3_ep0_delegate_req(dwc, ctrl); + /* if the cfg matches and the cfg is non zero */ + if (!ret && cfg) + dwc->dev_state = DWC3_CONFIGURED_STATE; + break; + + case DWC3_CONFIGURED_STATE: + ret = dwc3_ep0_delegate_req(dwc, ctrl); + if (!cfg) + dwc->dev_state = DWC3_ADDRESS_STATE; + break; + } + return 0; +} + +static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) +{ + int ret; + + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + dev_vdbg(dwc->dev, "USB_REQ_GET_STATUS\n"); + ret = dwc3_ep0_handle_status(dwc, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + dev_vdbg(dwc->dev, "USB_REQ_CLEAR_FEATURE\n"); + ret = dwc3_ep0_handle_feature(dwc, ctrl, 0); + break; + case USB_REQ_SET_FEATURE: + dev_vdbg(dwc->dev, "USB_REQ_SET_FEATURE\n"); + ret = dwc3_ep0_handle_feature(dwc, ctrl, 1); + break; + case USB_REQ_SET_ADDRESS: + dev_vdbg(dwc->dev, "USB_REQ_SET_ADDRESS\n"); + ret = dwc3_ep0_set_address(dwc, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + dev_vdbg(dwc->dev, "USB_REQ_SET_CONFIGURATION\n"); + ret = dwc3_ep0_set_config(dwc, ctrl); + break; + default: + dev_vdbg(dwc->dev, "Forwarding to gadget driver\n"); + ret = dwc3_ep0_delegate_req(dwc, ctrl); + break; + }; + + return ret; +} + +static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct usb_ctrlrequest *ctrl = dwc->ctrl_req; + int ret; + u32 len; + + if (!dwc->gadget_driver) + goto err; + + len = le16_to_cpu(ctrl->wLength); + if (!len) { + dwc->ep0state = EP0_IN_WAIT_GADGET; + dwc->three_stage_setup = 0; + } else { + dwc->three_stage_setup = 1; + if (ctrl->bRequestType & USB_DIR_IN) + dwc->ep0state = EP0_IN_DATA_PHASE; + else + dwc->ep0state = EP0_OUT_DATA_PHASE; + } + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + ret = dwc3_ep0_std_request(dwc, ctrl); + else + ret = dwc3_ep0_delegate_req(dwc, ctrl); + + if (ret >= 0) + return; + +err: + dwc3_ep0_stall_and_restart(dwc); +} + +static void dwc3_ep0_complete_data(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_request *r = NULL; + struct usb_request *ur; + struct dwc3_trb trb; + struct dwc3_ep *dep; + u32 transfered; + u8 epnum; + + epnum = event->endpoint_number; + dep = dwc->eps[epnum]; + + if (!dwc->ep0_status_pending) { + r = next_request(&dep->request_list); + ur = &r->request; + } else { + ur = &dwc->ep0_usb_req; + dwc->ep0_status_pending = 0; + } + + dwc3_trb_to_nat(dwc->ep0_trb, &trb); + + transfered = ur->length - trb.length; + ur->actual += transfered; + + if ((epnum & 1) && ur->actual < ur->length) { + /* for some reason we did not get everything out */ + + dwc3_ep0_stall_and_restart(dwc); + dwc3_gadget_giveback(dep, r, -ECONNRESET); + } else { + /* + * handle the case where we have to send a zero packet. This + * seems to be case when req.length > maxpacket. Could it be? + */ + /* The transfer is complete, wait for HOST */ + if (epnum & 1) + dwc->ep0state = EP0_IN_WAIT_NRDY; + else + dwc->ep0state = EP0_OUT_WAIT_NRDY; + + if (r) + dwc3_gadget_giveback(dep, r, 0); + } +} + +static void dwc3_ep0_complete_req(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_request *r; + struct dwc3_ep *dep; + u8 epnum; + + epnum = event->endpoint_number; + dep = dwc->eps[epnum]; + + if (!list_empty(&dep->request_list)) { + r = next_request(&dep->request_list); + + dwc3_gadget_giveback(dep, r, 0); + } + + dwc->ep0state = EP0_IDLE; + dwc3_ep0_out_start(dwc); +} + +static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + switch (dwc->ep0state) { + case EP0_IDLE: + dwc3_ep0_inspect_setup(dwc, event); + break; + + case EP0_IN_DATA_PHASE: + case EP0_OUT_DATA_PHASE: + dwc3_ep0_complete_data(dwc, event); + break; + + case EP0_IN_STATUS_PHASE: + case EP0_OUT_STATUS_PHASE: + dwc3_ep0_complete_req(dwc, event); + break; + + case EP0_IN_WAIT_NRDY: + case EP0_OUT_WAIT_NRDY: + case EP0_IN_WAIT_GADGET: + case EP0_OUT_WAIT_GADGET: + case EP0_UNCONNECTED: + case EP0_STALL: + break; + } +} + +static void dwc3_ep0_xfernotready(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + switch (dwc->ep0state) { + case EP0_IN_WAIT_GADGET: + dwc->ep0state = EP0_IN_WAIT_NRDY; + break; + case EP0_OUT_WAIT_GADGET: + dwc->ep0state = EP0_OUT_WAIT_NRDY; + break; + + case EP0_IN_WAIT_NRDY: + case EP0_OUT_WAIT_NRDY: + dwc3_ep0_do_setup_status(dwc, event); + break; + + case EP0_IDLE: + case EP0_IN_STATUS_PHASE: + case EP0_OUT_STATUS_PHASE: + case EP0_IN_DATA_PHASE: + case EP0_OUT_DATA_PHASE: + case EP0_UNCONNECTED: + case EP0_STALL: + break; + } +} + +void dwc3_ep0_interrupt(struct dwc3 *dwc, + const const struct dwc3_event_depevt *event) +{ + u8 epnum = event->endpoint_number; + + dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n", + dwc3_ep_event_string(event->endpoint_event), + epnum, (epnum & 1) ? "in" : "out", + dwc3_ep0_state_string(dwc->ep0state)); + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERCOMPLETE: + dwc3_ep0_xfer_complete(dwc, event); + break; + + case DWC3_DEPEVT_XFERNOTREADY: + dwc3_ep0_xfernotready(dwc, event); + break; + + case DWC3_DEPEVT_XFERINPROGRESS: + case DWC3_DEPEVT_RXTXFIFOEVT: + case DWC3_DEPEVT_STREAMEVT: + case DWC3_DEPEVT_EPCMDCMPLT: + break; + } +} diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c new file mode 100644 index 0000000..de5f0af --- /dev/null +++ b/drivers/usb/dwc3/gadget.c @@ -0,0 +1,2063 @@ +/** + * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "core.h" +#include "gadget.h" +#include "io.h" + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +void dwc3_map_buffer_to_dma(struct dwc3_request *req) +{ + struct dwc3 *dwc = req->dep->dwc; + + if (req->request.dma == DMA_ADDR_INVALID) { + req->request.dma = dma_map_single(dwc->dev, req->request.buf, + req->request.length, req->direction + ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->mapped = true; + } else { + dma_sync_single_for_device(dwc->dev, req->request.dma, + req->request.length, req->direction + ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->mapped = false; + } +} + +void dwc3_unmap_buffer_from_dma(struct dwc3_request *req) +{ + struct dwc3 *dwc = req->dep->dwc; + + if (req->mapped) { + dma_unmap_single(dwc->dev, req->request.dma, + req->request.length, req->direction + ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->mapped = 0; + } else { + dma_sync_single_for_cpu(dwc->dev, req->request.dma, + req->request.length, req->direction + ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + } +} + +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status) +{ + struct dwc3 *dwc = dep->dwc; + + if (req->queued) { + dep->busy_slot++; + /* + * Skip LINK TRB. We can't use req->trb and check for + * DWC3_TRBCTL_LINK_TRB because it points the TRB we just + * completed (not the LINK TRB). + */ + if (((dep->busy_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->desc)) + dep->busy_slot++; + } + list_del(&req->list); + + if (req->request.status == -EINPROGRESS) + req->request.status = status; + + dwc3_unmap_buffer_from_dma(req); + + dev_dbg(dwc->dev, "request %p from %s completed %d/%d ===> %d\n", + req, dep->name, req->request.actual, + req->request.length, status); + + spin_unlock(&dwc->lock); + req->request.complete(&req->dep->endpoint, &req->request); + spin_lock(&dwc->lock); +} + +static const char *dwc3_gadget_ep_cmd_string(u8 cmd) +{ + switch (cmd) { + case DWC3_DEPCMD_DEPSTARTCFG: + return "Start New Configuration"; + case DWC3_DEPCMD_ENDTRANSFER: + return "End Transfer"; + case DWC3_DEPCMD_UPDATETRANSFER: + return "Update Transfer"; + case DWC3_DEPCMD_STARTTRANSFER: + return "Start Transfer"; + case DWC3_DEPCMD_CLEARSTALL: + return "Clear Stall"; + case DWC3_DEPCMD_SETSTALL: + return "Set Stall"; + case DWC3_DEPCMD_GETSEQNUMBER: + return "Get Data Sequence Number"; + case DWC3_DEPCMD_SETTRANSFRESOURCE: + return "Set Endpoint Transfer Resource"; + case DWC3_DEPCMD_SETEPCONFIG: + return "Set Endpoint Configuration"; + default: + return "UNKNOWN command"; + } +} + +int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +{ + struct dwc3_ep *dep = dwc->eps[ep]; + unsigned long timeout = 500; + u32 reg; + + dev_vdbg(dwc->dev, "%s: cmd '%s' params %08x %08x %08x\n", + dep->name, + dwc3_gadget_ep_cmd_string(cmd), params->param0.raw, + params->param1.raw, params->param2.raw); + + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0.raw); + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1.raw); + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2.raw); + + dwc3_writel(dwc->regs, DWC3_DEPCMD(ep), cmd | DWC3_DEPCMD_CMDACT); + do { + reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep)); + if (!(reg & DWC3_DEPCMD_CMDACT)) { + dev_vdbg(dwc->dev, "CMD Compl Status %d DEPCMD %04x\n", + ((reg & 0xf000) >> 12), reg); + return 0; + } + + /* + * XXX Figure out a sane timeout here. 500ms is way too much. + * We can't sleep here, because it is also called from + * interrupt context. + */ + timeout--; + if (!timeout) + return -ETIMEDOUT; + + mdelay(1); + } while (1); +} + +static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, + struct dwc3_trb_hw *trb) +{ + u32 offset = trb - dep->trb_pool; + + return dep->trb_pool_dma + offset; +} + +static int dwc3_alloc_trb_pool(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + + if (dep->trb_pool) + return 0; + + if (dep->number == 0 || dep->number == 1) + return 0; + + dep->trb_pool = dma_alloc_coherent(dwc->dev, + sizeof(struct dwc3_trb) * DWC3_TRB_NUM, + &dep->trb_pool_dma, GFP_KERNEL); + if (!dep->trb_pool) { + dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n", + dep->name); + return -ENOMEM; + } + + return 0; +} + +static void dwc3_free_trb_pool(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + + dma_free_coherent(dwc->dev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM, + dep->trb_pool, dep->trb_pool_dma); + + dep->trb_pool = NULL; + dep->trb_pool_dma = 0; +} + +static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + + memset(¶ms, 0x00, sizeof(params)); + + if (dep->number != 1) { + cmd = DWC3_DEPCMD_DEPSTARTCFG; + /* XferRscIdx == 0 for ep0 and 2 for the remaining */ + if (dep->number > 1) + cmd |= DWC3_DEPCMD_PARAM(2); + + return dwc3_send_gadget_ep_cmd(dwc, 0, cmd, ¶ms); + } + + return 0; +} + +static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc3_gadget_ep_cmd_params params; + + memset(¶ms, 0x00, sizeof(params)); + + params.param0.depcfg.ep_type = usb_endpoint_type(desc); + params.param0.depcfg.max_packet_size = + le16_to_cpu(desc->wMaxPacketSize); + + params.param1.depcfg.xfer_complete_enable = true; + params.param1.depcfg.xfer_not_ready_enable = true; + + if (usb_endpoint_xfer_isoc(desc)) + params.param1.depcfg.xfer_in_progress_enable = true; + + /* + * We are doing 1:1 mapping for endpoints, meaning + * Physical Endpoints 2 maps to Logical Endpoint 2 and + * so on. We consider the direction bit as part of the physical + * endpoint number. So USB endpoint 0x81 is 0x03. + */ + params.param1.depcfg.ep_number = dep->number; + + /* + * We must use the lower 16 TX FIFOs even though + * HW might have more + */ + if (dep->direction) + params.param0.depcfg.fifo_number = dep->number >> 1; + + if (desc->bInterval) { + params.param1.depcfg.binterval_m1 = desc->bInterval - 1; + dep->interval = 1 << (desc->bInterval - 1); + } + + return dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETEPCONFIG, ¶ms); +} + +static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) +{ + struct dwc3_gadget_ep_cmd_params params; + + memset(¶ms, 0x00, sizeof(params)); + + params.param0.depxfercfg.number_xfer_resources = 1; + + return dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETTRANSFRESOURCE, ¶ms); +} + +/** + * __dwc3_gadget_ep_enable - Initializes a HW endpoint + * @dep: endpoint to be initialized + * @desc: USB Endpoint Descriptor + * + * Caller should take care of locking + */ +static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc3 *dwc = dep->dwc; + u32 reg; + int ret = -ENOMEM; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + ret = dwc3_gadget_start_config(dwc, dep); + if (ret) + return ret; + } + + ret = dwc3_gadget_set_ep_config(dwc, dep, desc); + if (ret) + return ret; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + struct dwc3_trb_hw *trb_st_hw; + struct dwc3_trb_hw *trb_link_hw; + struct dwc3_trb trb_link; + + ret = dwc3_gadget_set_xfer_resource(dwc, dep); + if (ret) + return ret; + + dep->desc = desc; + dep->type = usb_endpoint_type(desc); + dep->flags |= DWC3_EP_ENABLED; + + reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg |= DWC3_DALEPENA_EP(dep->number); + dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + + if (!usb_endpoint_xfer_isoc(desc)) + return 0; + + memset(&trb_link, 0, sizeof(trb_link)); + + /* Link TRB for ISOC. The HWO but is never reset */ + trb_st_hw = &dep->trb_pool[0]; + + trb_link.bplh = dwc3_trb_dma_offset(dep, trb_st_hw); + trb_link.trbctl = DWC3_TRBCTL_LINK_TRB; + trb_link.hwo = true; + + trb_link_hw = &dep->trb_pool[DWC3_TRB_NUM - 1]; + dwc3_trb_to_hw(&trb_link, trb_link_hw); + } + + return 0; +} + +static void dwc3_gadget_nuke_reqs(struct dwc3_ep *dep, const int status) +{ + struct dwc3_request *req; + + while (!list_empty(&dep->request_list)) { + req = next_request(&dep->request_list); + + dwc3_gadget_giveback(dep, req, status); + } + /* nuke queued TRBs as well on command complete */ + dep->flags |= DWC3_EP_WILL_SHUTDOWN; +} + +/** + * __dwc3_gadget_ep_disable - Disables a HW endpoint + * @dep: the endpoint to disable + * + * Caller should take care of locking + */ +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum); +static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) +{ + struct dwc3 *dwc = dep->dwc; + u32 reg; + + dep->flags &= ~DWC3_EP_ENABLED; + dwc3_stop_active_transfer(dwc, dep->number); + dwc3_gadget_nuke_reqs(dep, -ESHUTDOWN); + + reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); + reg &= ~DWC3_DALEPENA_EP(dep->number); + dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + + dep->desc = NULL; + dep->type = 0; + + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_ep0_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EINVAL; +} + +static int dwc3_gadget_ep0_disable(struct usb_ep *ep) +{ + return -EINVAL; +} + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; + + if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { + pr_debug("dwc3: invalid parameters\n"); + return -EINVAL; + } + + if (!desc->wMaxPacketSize) { + pr_debug("dwc3: missing wMaxPacketSize\n"); + return -EINVAL; + } + + dep = to_dwc3_ep(ep); + dwc = dep->dwc; + + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_CONTROL: + strncat(dep->name, "-control", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_ISOC: + strncat(dep->name, "-isoc", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_BULK: + strncat(dep->name, "-bulk", sizeof(dep->name)); + break; + case USB_ENDPOINT_XFER_INT: + strncat(dep->name, "-int", sizeof(dep->name)); + break; + default: + dev_err(dwc->dev, "invalid endpoint transfer type\n"); + } + + if (dep->flags & DWC3_EP_ENABLED) { + dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n", + dep->name); + return 0; + } + + dev_vdbg(dwc->dev, "Enabling %s\n", dep->name); + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_enable(dep, desc); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_disable(struct usb_ep *ep) +{ + struct dwc3_ep *dep; + struct dwc3 *dwc; + unsigned long flags; + int ret; + + if (!ep) { + pr_debug("dwc3: invalid parameters\n"); + return -EINVAL; + } + + dep = to_dwc3_ep(ep); + dwc = dep->dwc; + + if (!(dep->flags & DWC3_EP_ENABLED)) { + dev_WARN_ONCE(dwc->dev, true, "%s is already disabled\n", + dep->name); + return 0; + } + + snprintf(dep->name, sizeof(dep->name), "ep%d%s", + dep->number >> 1, + (dep->number & 1) ? "in" : "out"); + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_disable(dep); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct dwc3_request *req; + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) { + dev_err(dwc->dev, "not enough memory\n"); + return NULL; + } + + req->epnum = dep->number; + req->dep = dep; + req->request.dma = DMA_ADDR_INVALID; + + return &req->request; +} + +static void dwc3_gadget_ep_free_request(struct usb_ep *ep, + struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + + kfree(req); +} + +/* + * dwc3_prepare_trbs - setup TRBs from requests + * @dep: endpoint for which requests are being prepared + * @starting: true if the endpoint is idle and no requests are queued. + * + * The functions goes through the requests list and setups TRBs for the + * transfers. The functions returns once there are not more TRBs available or + * it run out of requests. + */ +static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, + bool starting) +{ + struct dwc3_request *req, *n, *ret = NULL; + struct dwc3_trb_hw *trb_hw; + struct dwc3_trb trb; + u32 trbs_left; + + BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); + + /* the first request must not be queued */ + trbs_left = (dep->busy_slot - dep->free_slot) & DWC3_TRB_MASK; + /* + * if busy & slot are equal than it is either full or empty. If we are + * starting to proceed requests then we are empty. Otherwise we ar + * full and don't do anything + */ + if (!trbs_left) { + if (!starting) + return NULL; + trbs_left = DWC3_TRB_NUM; + /* + * In case we start from scratch, we queue the ISOC requests + * starting from slot 1. This is done because we use ring + * buffer and have no LST bit to stop us. Instead, we place + * IOC bit TRB_NUM/4. We try to avoid to having an interrupt + * after the first request so we start at slot 1 and have + * 7 requests proceed before we hit the first IOC. + * Other transfer types don't use the ring buffer and are + * processed from the first TRB until the last one. Since we + * don't wrap around we have to start at the beginning. + */ + if (usb_endpoint_xfer_isoc(dep->desc)) { + dep->busy_slot = 1; + dep->free_slot = 1; + } else { + dep->busy_slot = 0; + dep->free_slot = 0; + } + } + + /* The last TRB is a link TRB, not used for xfer */ + if ((trbs_left <= 1) && usb_endpoint_xfer_isoc(dep->desc)) + return NULL; + + list_for_each_entry_safe(req, n, &dep->request_list, list) { + unsigned int last_one = 0; + unsigned int cur_slot; + + trb_hw = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK]; + cur_slot = dep->free_slot; + dep->free_slot++; + + /* Skip the LINK-TRB on ISOC */ + if (((cur_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) && + usb_endpoint_xfer_isoc(dep->desc)) + continue; + + dwc3_gadget_move_request_queued(req); + memset(&trb, 0, sizeof(trb)); + trbs_left--; + + /* Is our TRB pool empty? */ + if (!trbs_left) + last_one = 1; + /* Is this the last request? */ + if (list_empty(&dep->request_list)) + last_one = 1; + + /* + * FIXME we shouldn't need to set LST bit always but we are + * facing some weird problem with the Hardware where it doesn't + * complete even though it has been previously started. + * + * While we're debugging the problem, as a workaround to + * multiple TRBs handling, use only one TRB at a time. + */ + last_one = 1; + + req->trb = trb_hw; + if (!ret) + ret = req; + + trb.bplh = req->request.dma; + + if (usb_endpoint_xfer_isoc(dep->desc)) { + trb.isp_imi = true; + trb.csp = true; + } else { + trb.lst = last_one; + } + + switch (usb_endpoint_type(dep->desc)) { + case USB_ENDPOINT_XFER_CONTROL: + trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP; + break; + + case USB_ENDPOINT_XFER_ISOC: + trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS; + + /* IOC every DWC3_TRB_NUM / 4 so we can refill */ + if (!(cur_slot % (DWC3_TRB_NUM / 4))) + trb.ioc = last_one; + break; + + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + trb.trbctl = DWC3_TRBCTL_NORMAL; + break; + default: + /* + * This is only possible with faulty memory because we + * checked it already :) + */ + BUG(); + } + + trb.length = req->request.length; + trb.hwo = true; + + dwc3_trb_to_hw(&trb, trb_hw); + req->trb_dma = dwc3_trb_dma_offset(dep, trb_hw); + + if (last_one) + break; + } + + return ret; +} + +static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, + int start_new) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_request *req; + struct dwc3 *dwc = dep->dwc; + int ret; + u32 cmd; + + if (start_new && (dep->flags & DWC3_EP_BUSY)) { + dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name); + return -EBUSY; + } + dep->flags &= ~DWC3_EP_PENDING_REQUEST; + + /* + * If we are getting here after a short-out-packet we don't enqueue any + * new requests as we try to set the IOC bit only on the last request. + */ + if (start_new) { + if (list_empty(&dep->req_queued)) + dwc3_prepare_trbs(dep, start_new); + + /* req points to the first request which will be sent */ + req = next_request(&dep->req_queued); + } else { + /* + * req points to the first request where HWO changed + * from 0 to 1 + */ + req = dwc3_prepare_trbs(dep, start_new); + } + if (!req) { + dep->flags |= DWC3_EP_PENDING_REQUEST; + return 0; + } + + memset(¶ms, 0, sizeof(params)); + params.param0.depstrtxfer.transfer_desc_addr_high = + upper_32_bits(req->trb_dma); + params.param1.depstrtxfer.transfer_desc_addr_low = + lower_32_bits(req->trb_dma); + + if (start_new) + cmd = DWC3_DEPCMD_STARTTRANSFER; + else + cmd = DWC3_DEPCMD_UPDATETRANSFER; + + cmd |= DWC3_DEPCMD_PARAM(cmd_param); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + if (ret < 0) { + dev_dbg(dwc->dev, "failed to send STARTTRANSFER command\n"); + + /* + * FIXME we need to iterate over the list of requests + * here and stop, unmap, free and del each of the linked + * requests instead of we do now. + */ + dwc3_unmap_buffer_from_dma(req); + list_del(&req->list); + return ret; + } + + dep->flags |= DWC3_EP_BUSY; + dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, + dep->number); + if (!dep->res_trans_idx) + printk_once(KERN_ERR "%s() res_trans_idx is invalid\n", __func__); + return 0; +} + +static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) +{ + req->request.actual = 0; + req->request.status = -EINPROGRESS; + req->direction = dep->direction; + req->epnum = dep->number; + + /* + * We only add to our list of requests now and + * start consuming the list once we get XferNotReady + * IRQ. + * + * That way, we avoid doing anything that we don't need + * to do now and defer it until the point we receive a + * particular token from the Host side. + * + * This will also avoid Host cancelling URBs due to too + * many NACKs. + */ + dwc3_map_buffer_to_dma(req); + list_add_tail(&req->list, &dep->request_list); + + /* + * There is one special case: XferNotReady with + * empty list of requests. We need to kick the + * transfer here in that situation, otherwise + * we will be NAKing forever. + * + * If we get XferNotReady before gadget driver + * has a chance to queue a request, we will ACK + * the IRQ but won't be able to receive the data + * until the next request is queued. The following + * code is handling exactly that. + */ + if (dep->flags & DWC3_EP_PENDING_REQUEST) { + int ret; + int start_trans; + + start_trans = 1; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && + dep->flags & DWC3_EP_BUSY) + start_trans = 0; + + ret = __dwc3_gadget_kick_transfer(dep, 0, start_trans); + if (ret && ret != -EBUSY) { + struct dwc3 *dwc = dep->dwc; + + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + } + }; + + return 0; +} + +static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; + + if (!dep->desc) { + dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", + request, ep->name); + return -ESHUTDOWN; + } + + dev_vdbg(dwc->dev, "queing request %p to %s length %d\n", + request, ep->name, request->length); + + spin_lock_irqsave(&dwc->lock, flags); + ret = __dwc3_gadget_ep_queue(dep, req); + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_dequeue(struct usb_ep *ep, + struct usb_request *request) +{ + struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_request *r = NULL; + + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&dwc->lock, flags); + + list_for_each_entry(r, &dep->request_list, list) { + if (r == req) + break; + } + + if (r != req) { + list_for_each_entry(r, &dep->req_queued, list) { + if (r == req) + break; + } + if (r == req) { + /* wait until it is processed */ + dwc3_stop_active_transfer(dwc, dep->number); + goto out0; + } + dev_err(dwc->dev, "request %p was not queued to %s\n", + request, ep->name); + ret = -EINVAL; + goto out0; + } + + /* giveback the request */ + dwc3_gadget_giveback(dep, req, -ECONNRESET); + +out0: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3 *dwc = dep->dwc; + int ret; + + memset(¶ms, 0x00, sizeof(params)); + + if (value) { + if (dep->number == 0 || dep->number == 1) + dwc->ep0state = EP0_STALL; + + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_SETSTALL, ¶ms); + if (ret) + dev_err(dwc->dev, "failed to %s STALL on %s\n", + value ? "set" : "clear", + dep->name); + else + dep->flags |= DWC3_EP_STALL; + } else { + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_CLEARSTALL, ¶ms); + if (ret) + dev_err(dwc->dev, "failed to %s STALL on %s\n", + value ? "set" : "clear", + dep->name); + else + dep->flags &= ~DWC3_EP_STALL; + } + return ret; +} + +static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + unsigned long flags; + + int ret; + + spin_lock_irqsave(&dwc->lock, flags); + + if (usb_endpoint_xfer_isoc(dep->desc)) { + dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name); + ret = -EINVAL; + goto out; + } + + ret = __dwc3_gadget_ep_set_halt(dep, value); +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + + dep->flags |= DWC3_EP_WEDGE; + + return usb_ep_set_halt(ep); +} + +/* -------------------------------------------------------------------------- */ + +static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, +}; + +static const struct usb_ep_ops dwc3_gadget_ep0_ops = { + .enable = dwc3_gadget_ep0_enable, + .disable = dwc3_gadget_ep0_disable, + .alloc_request = dwc3_gadget_ep_alloc_request, + .free_request = dwc3_gadget_ep_free_request, + .queue = dwc3_gadget_ep0_queue, + .dequeue = dwc3_gadget_ep_dequeue, + .set_halt = dwc3_gadget_ep_set_halt, + .set_wedge = dwc3_gadget_ep_set_wedge, +}; + +static const struct usb_ep_ops dwc3_gadget_ep_ops = { + .enable = dwc3_gadget_ep_enable, + .disable = dwc3_gadget_ep_disable, + .alloc_request = dwc3_gadget_ep_alloc_request, + .free_request = dwc3_gadget_ep_free_request, + .queue = dwc3_gadget_ep_queue, + .dequeue = dwc3_gadget_ep_dequeue, + .set_halt = dwc3_gadget_ep_set_halt, + .set_wedge = dwc3_gadget_ep_set_wedge, +}; + +/* -------------------------------------------------------------------------- */ + +static int dwc3_gadget_get_frame(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + return DWC3_DSTS_SOFFN(reg); +} + +static int dwc3_gadget_wakeup(struct usb_gadget *g) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + unsigned long timeout; + unsigned long flags; + + u32 reg; + + int ret = 0; + + u8 link_state; + u8 speed; + + spin_lock_irqsave(&dwc->lock, flags); + + /* + * According to the Databook Remote wakeup request should + * be issued only when the device is in early suspend state. + * + * We can check that via USB Link State bits in DSTS register. + */ + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + speed = reg & DWC3_DSTS_CONNECTSPD; + if (speed == DWC3_DSTS_SUPERSPEED) { + dev_dbg(dwc->dev, "no wakeup on SuperSpeed\n"); + ret = -EINVAL; + goto out; + } + + link_state = DWC3_DSTS_USBLNKST(reg); + + switch (link_state) { + case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */ + case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */ + break; + default: + dev_dbg(dwc->dev, "can't wakeup from link state %d\n", + link_state); + ret = -EINVAL; + goto out; + } + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + + /* + * Switch link state to Recovery. In HS/FS/LS this means + * RemoteWakeup Request + */ + reg |= DWC3_DCTL_ULSTCHNG_RECOVERY; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + /* wait for at least 2000us */ + usleep_range(2000, 2500); + + /* write zeroes to Link Change Request */ + reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + /* pool until Link State change to ON */ + timeout = jiffies + msecs_to_jiffies(100); + + while (!(time_after(jiffies, timeout))) { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + + /* in HS, means ON */ + if (DWC3_DSTS_USBLNKST(reg) == DWC3_LINK_STATE_U0) + break; + } + + if (DWC3_DSTS_USBLNKST(reg) != DWC3_LINK_STATE_U0) { + dev_err(dwc->dev, "failed to send remote wakeup\n"); + ret = -EINVAL; + } + +out: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, + int is_selfpowered) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + + dwc->is_selfpowered = !!is_selfpowered; + + return 0; +} + +static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) +{ + u32 reg; + unsigned long timeout = 500; + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (is_on) + reg |= DWC3_DCTL_RUN_STOP; + else + reg &= ~DWC3_DCTL_RUN_STOP; + + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + do { + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + if (is_on) { + if (!(reg & DWC3_DSTS_DEVCTRLHLT)) + break; + } else { + if (reg & DWC3_DSTS_DEVCTRLHLT) + break; + } + /* + * XXX reduce the 500ms delay + */ + timeout--; + if (!timeout) + break; + mdelay(1); + } while (1); + + dev_vdbg(dwc->dev, "gadget %s data soft-%s\n", + dwc->gadget_driver + ? dwc->gadget_driver->function : "no-function", + is_on ? "connect" : "disconnect"); +} + +static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + is_on = !!is_on; + + spin_lock_irqsave(&dwc->lock, flags); + dwc3_gadget_run_stop(dwc, is_on); + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} + +static int dwc3_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + struct dwc3_ep *dep; + unsigned long flags; + int ret = 0; + u32 reg; + + spin_lock_irqsave(&dwc->lock, flags); + + if (dwc->gadget_driver) { + dev_err(dwc->dev, "%s is already bound to %s\n", + dwc->gadget.name, + dwc->gadget_driver->driver.name); + ret = -EBUSY; + goto err0; + } + + dwc->gadget_driver = driver; + dwc->gadget.dev.driver = &driver->driver; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + + /* + * REVISIT: power down scale might be different + * depending on PHY used, need to pass that via platform_data + */ + reg |= DWC3_GCTL_PWRDNSCALE(0x61a) + | DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE); + reg &= ~DWC3_GCTL_DISSCRAMBLE; + + /* + * WORKAROUND: DWC3 revisions <1.90a have a bug + * when The device fails to connect at SuperSpeed + * and falls back to high-speed mode which causes + * the device to enter in a Connect/Disconnect loop + */ + if (dwc->revision < DWC3_REVISION_190A) + reg |= DWC3_GCTL_U2RSTECN; + + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_SPEED_MASK); + reg |= DWC3_DCFG_SUPERSPEED; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + /* Start with SuperSpeed Default */ + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + goto err0; + } + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + goto err1; + } + + /* begin to receive SETUP packets */ + dwc->ep0state = EP0_IDLE; + dwc3_ep0_out_start(dwc); + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; + +err1: + __dwc3_gadget_ep_disable(dwc->eps[0]); + +err0: + spin_unlock_irqrestore(&dwc->lock, flags); + + return ret; +} + +static int dwc3_gadget_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + + spin_lock_irqsave(&dwc->lock, flags); + + __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_ep_disable(dwc->eps[1]); + + dwc->gadget_driver = NULL; + dwc->gadget.dev.driver = NULL; + + spin_unlock_irqrestore(&dwc->lock, flags); + + return 0; +} +static const struct usb_gadget_ops dwc3_gadget_ops = { + .get_frame = dwc3_gadget_get_frame, + .wakeup = dwc3_gadget_wakeup, + .set_selfpowered = dwc3_gadget_set_selfpowered, + .pullup = dwc3_gadget_pullup, + .udc_start = dwc3_gadget_start, + .udc_stop = dwc3_gadget_stop, +}; + +/* -------------------------------------------------------------------------- */ + +static int __devinit dwc3_gadget_init_endpoints(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + u8 epnum; + + INIT_LIST_HEAD(&dwc->gadget.ep_list); + + for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + dep = kzalloc(sizeof(*dep), GFP_KERNEL); + if (!dep) { + dev_err(dwc->dev, "can't allocate endpoint %d\n", + epnum); + return -ENOMEM; + } + + dep->dwc = dwc; + dep->number = epnum; + dwc->eps[epnum] = dep; + + snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1, + (epnum & 1) ? "in" : "out"); + dep->endpoint.name = dep->name; + dep->direction = (epnum & 1); + + if (epnum == 0 || epnum == 1) { + dep->endpoint.maxpacket = 512; + dep->endpoint.ops = &dwc3_gadget_ep0_ops; + if (!epnum) + dwc->gadget.ep0 = &dep->endpoint; + } else { + int ret; + + dep->endpoint.maxpacket = 1024; + dep->endpoint.ops = &dwc3_gadget_ep_ops; + list_add_tail(&dep->endpoint.ep_list, + &dwc->gadget.ep_list); + + ret = dwc3_alloc_trb_pool(dep); + if (ret) { + dev_err(dwc->dev, "%s: failed to allocate TRB pool\n", dep->name); + return ret; + } + } + INIT_LIST_HEAD(&dep->request_list); + INIT_LIST_HEAD(&dep->req_queued); + } + + return 0; +} + +static void dwc3_gadget_free_endpoints(struct dwc3 *dwc) +{ + struct dwc3_ep *dep; + u8 epnum; + + for (epnum = 0; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + dep = dwc->eps[epnum]; + dwc3_free_trb_pool(dep); + + if (epnum != 0 && epnum != 1) + list_del(&dep->endpoint.ep_list); + + kfree(dep); + } +} + +static void dwc3_gadget_release(struct device *dev) +{ + dev_dbg(dev, "%s\n", __func__); +} + +/* -------------------------------------------------------------------------- */ +static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, + const struct dwc3_event_depevt *event, int status) +{ + struct dwc3_request *req; + struct dwc3_trb trb; + unsigned int count; + unsigned int s_pkt = 0; + + do { + req = next_request(&dep->req_queued); + if (!req) + break; + + dwc3_trb_to_nat(req->trb, &trb); + + if (trb.hwo) { + dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n", + dep->name, req->trb); + continue; + } + count = trb.length; + + if (dep->direction) { + if (count) { + dev_err(dwc->dev, "incomplete IN transfer %s\n", + dep->name); + status = -ECONNRESET; + } + } else { + if (count && (event->status & DEPEVT_STATUS_SHORT)) + s_pkt = 1; + } + + /* + * We assume here we will always receive the entire data block + * which we should receive. Meaning, if we program RX to + * receive 4K but we receive only 2K, we assume that's all we + * should receive and we simply bounce the request back to the + * gadget driver for further processing. + */ + req->request.actual += req->request.length - count; + dwc3_gadget_giveback(dep, req, status); + if (s_pkt) + break; + if ((event->status & DEPEVT_STATUS_LST) && trb.lst) + break; + if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc) + break; + } while (1); + + if ((event->status & DEPEVT_STATUS_IOC) && trb.ioc) + return 0; + return 1; +} + +static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, + struct dwc3_ep *dep, const struct dwc3_event_depevt *event, + int start_new) +{ + unsigned status = 0; + int clean_busy; + + if (event->status & DEPEVT_STATUS_BUSERR) + status = -ECONNRESET; + + clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); + if (clean_busy) + dep->flags &= ~DWC3_EP_BUSY; +} + +static void dwc3_gadget_start_isoc(struct dwc3 *dwc, + struct dwc3_ep *dep, const struct dwc3_event_depevt *event) +{ + u32 uf; + + if (list_empty(&dep->request_list)) { + dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n", + dep->name); + return; + } + + if (event->parameters) { + u32 mask; + + mask = ~(dep->interval - 1); + uf = event->parameters & mask; + /* 4 micro frames in the future */ + uf += dep->interval * 4; + } else { + uf = 0; + } + + __dwc3_gadget_kick_transfer(dep, uf, 1); +} + +static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + struct dwc3 *dwc = dep->dwc; + struct dwc3_event_depevt mod_ev = *event; + + /* + * We were asked to remove one requests. It is possible that this + * request and a few other were started together and have the same + * transfer index. Since we stopped the complete endpoint we don't + * know how many requests were already completed (and not yet) + * reported and how could be done (later). We purge them all until + * the end of the list. + */ + mod_ev.status = DEPEVT_STATUS_LST; + dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN); + dep->flags &= ~DWC3_EP_BUSY; + /* pending requets are ignored and are queued on XferNotReady */ + + if (dep->flags & DWC3_EP_WILL_SHUTDOWN) { + while (!list_empty(&dep->req_queued)) { + struct dwc3_request *req; + + req = next_request(&dep->req_queued); + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); + } + dep->flags &= DWC3_EP_WILL_SHUTDOWN; + } +} + +static void dwc3_ep_cmd_compl(struct dwc3_ep *dep, + const struct dwc3_event_depevt *event) +{ + u32 param = event->parameters; + u32 cmd_type = (param >> 8) & ((1 << 5) - 1); + + switch (cmd_type) { + case DWC3_DEPCMD_ENDTRANSFER: + dwc3_process_ep_cmd_complete(dep, event); + break; + case DWC3_DEPCMD_STARTTRANSFER: + dep->res_trans_idx = param & 0x7f; + break; + default: + printk(KERN_ERR "%s() unknown /unexpected type: %d\n", + __func__, cmd_type); + break; + }; +} + +static void dwc3_endpoint_interrupt(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep; + u8 epnum = event->endpoint_number; + + dep = dwc->eps[epnum]; + + dev_vdbg(dwc->dev, "%s: %s\n", dep->name, + dwc3_ep_event_string(event->endpoint_event)); + + if (epnum == 0 || epnum == 1) { + dwc3_ep0_interrupt(dwc, event); + return; + } + + switch (event->endpoint_event) { + case DWC3_DEPEVT_XFERCOMPLETE: + if (usb_endpoint_xfer_isoc(dep->desc)) { + dev_dbg(dwc->dev, "%s is an Isochronous endpoint\n", + dep->name); + return; + } + + dwc3_endpoint_transfer_complete(dwc, dep, event, 1); + break; + case DWC3_DEPEVT_XFERINPROGRESS: + if (!usb_endpoint_xfer_isoc(dep->desc)) { + dev_dbg(dwc->dev, "%s is not an Isochronous endpoint\n", + dep->name); + return; + } + + dwc3_endpoint_transfer_complete(dwc, dep, event, 0); + break; + case DWC3_DEPEVT_XFERNOTREADY: + if (usb_endpoint_xfer_isoc(dep->desc)) { + dwc3_gadget_start_isoc(dwc, dep, event); + } else { + int ret; + + dev_vdbg(dwc->dev, "%s: reason %s\n", + dep->name, event->status + ? "Transfer Active" + : "Transfer Not Active"); + + ret = __dwc3_gadget_kick_transfer(dep, 0, 1); + if (!ret || ret == -EBUSY) + return; + + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + } + + break; + case DWC3_DEPEVT_RXTXFIFOEVT: + dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name); + break; + case DWC3_DEPEVT_STREAMEVT: + dev_dbg(dwc->dev, "%s Stream Event\n", dep->name); + break; + case DWC3_DEPEVT_EPCMDCMPLT: + dwc3_ep_cmd_compl(dep, event); + break; + } +} + +static void dwc3_disconnect_gadget(struct dwc3 *dwc) +{ + if (dwc->gadget_driver && dwc->gadget_driver->disconnect) { + spin_unlock(&dwc->lock); + dwc->gadget_driver->disconnect(&dwc->gadget); + spin_lock(&dwc->lock); + } +} + +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) +{ + struct dwc3_ep *dep; + struct dwc3_gadget_ep_cmd_params params; + u32 cmd; + int ret; + + dep = dwc->eps[epnum]; + + if (dep->res_trans_idx) { + cmd = DWC3_DEPCMD_ENDTRANSFER; + cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; + cmd |= DWC3_DEPCMD_PARAM(dep->res_trans_idx); + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + WARN_ON_ONCE(ret); + } +} + +static void dwc3_stop_active_transfers(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + struct dwc3_ep *dep; + + dep = dwc->eps[epnum]; + if (!(dep->flags & DWC3_EP_ENABLED)) + continue; + + __dwc3_gadget_ep_disable(dep); + } +} + +static void dwc3_clear_stall_all_ep(struct dwc3 *dwc) +{ + u32 epnum; + + for (epnum = 1; epnum < DWC3_ENDPOINTS_NUM; epnum++) { + struct dwc3_ep *dep; + struct dwc3_gadget_ep_cmd_params params; + int ret; + + dep = dwc->eps[epnum]; + + if (!(dep->flags & DWC3_EP_STALL)) + continue; + + dep->flags &= ~DWC3_EP_STALL; + + memset(¶ms, 0, sizeof(params)); + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, + DWC3_DEPCMD_CLEARSTALL, ¶ms); + WARN_ON_ONCE(ret); + } +} + +static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) +{ + dev_vdbg(dwc->dev, "%s\n", __func__); +#if 0 + XXX + U1/U2 is powersave optimization. Skip it for now. Anyway we need to + enable it before we can disable it. + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_INITU1ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + reg &= ~DWC3_DCTL_INITU2ENA; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); +#endif + + dwc3_stop_active_transfers(dwc); + dwc3_disconnect_gadget(dwc); + + dwc->gadget.speed = USB_SPEED_UNKNOWN; +} + +static void dwc3_gadget_usb3_phy_power(struct dwc3 *dwc, int on) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + + if (on) + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + else + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); +} + +static void dwc3_gadget_usb2_phy_power(struct dwc3 *dwc, int on) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + + if (on) + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + else + reg |= DWC3_GUSB2PHYCFG_SUSPHY; + + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); +} + +static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) +{ + u32 reg; + + dev_vdbg(dwc->dev, "%s\n", __func__); + + /* Enable PHYs */ + dwc3_gadget_usb2_phy_power(dwc, true); + dwc3_gadget_usb3_phy_power(dwc, true); + + if (dwc->gadget.speed != USB_SPEED_UNKNOWN) + dwc3_disconnect_gadget(dwc); + + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg &= ~DWC3_DCTL_TSTCTRL_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, reg); + + dwc3_stop_active_transfers(dwc); + dwc3_clear_stall_all_ep(dwc); + + /* Reset device address to zero */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_DEVADDR_MASK); + dwc3_writel(dwc->regs, DWC3_DCFG, reg); + + /* + * Wait for RxFifo to drain + * + * REVISIT probably shouldn't wait forever. + * In case Hardware ends up in a screwed up + * case, we error out, notify the user and, + * maybe, WARN() or BUG() but leave the rest + * of the kernel working fine. + * + * REVISIT the below is rather CPU intensive, + * maybe we should read and if it doesn't work + * sleep (not busy wait) for a few useconds. + * + * REVISIT why wait until the RXFIFO is empty anyway? + */ + while (!(dwc3_readl(dwc->regs, DWC3_DSTS) + & DWC3_DSTS_RXFIFOEMPTY)) + cpu_relax(); +} + +static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed) +{ + u32 reg; + u32 usb30_clock = DWC3_GCTL_CLK_BUS; + + /* + * We change the clock only at SS but I dunno why I would want to do + * this. Maybe it becomes part of the power saving plan. + */ + + if (speed != DWC3_DSTS_SUPERSPEED) + return; + + /* + * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed + * each time on Connect Done. + */ + if (!usb30_clock) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg |= DWC3_GCTL_RAMCLKSEL(usb30_clock); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); +} + +static void dwc3_gadget_disable_phy(struct dwc3 *dwc, u8 speed) +{ + switch (speed) { + case USB_SPEED_SUPER: + dwc3_gadget_usb2_phy_power(dwc, false); + break; + case USB_SPEED_HIGH: + case USB_SPEED_FULL: + case USB_SPEED_LOW: + dwc3_gadget_usb3_phy_power(dwc, false); + break; + } +} + +static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) +{ + struct dwc3_gadget_ep_cmd_params params; + struct dwc3_ep *dep; + int ret; + u32 reg; + u8 speed; + + dev_vdbg(dwc->dev, "%s\n", __func__); + + memset(¶ms, 0x00, sizeof(params)); + + dwc->ep0state = EP0_IDLE; + reg = dwc3_readl(dwc->regs, DWC3_DSTS); + speed = reg & DWC3_DSTS_CONNECTSPD; + dwc->speed = speed; + + dwc3_update_ram_clk_sel(dwc, speed); + + switch (speed) { + case DWC3_DCFG_SUPERSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); + dwc->gadget.ep0->maxpacket = 512; + dwc->gadget.speed = USB_SPEED_SUPER; + break; + case DWC3_DCFG_HIGHSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); + dwc->gadget.ep0->maxpacket = 64; + dwc->gadget.speed = USB_SPEED_HIGH; + break; + case DWC3_DCFG_FULLSPEED2: + case DWC3_DCFG_FULLSPEED1: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); + dwc->gadget.ep0->maxpacket = 64; + dwc->gadget.speed = USB_SPEED_FULL; + break; + case DWC3_DCFG_LOWSPEED: + dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); + dwc->gadget.ep0->maxpacket = 8; + dwc->gadget.speed = USB_SPEED_LOW; + break; + } + + /* Disable unneded PHY */ + dwc3_gadget_disable_phy(dwc, dwc->gadget.speed); + + dep = dwc->eps[0]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + return; + } + + dep = dwc->eps[1]; + ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc); + if (ret) { + dev_err(dwc->dev, "failed to enable %s\n", dep->name); + return; + } + + /* + * Configure PHY via GUSB3PIPECTLn if required. + * + * Update GTXFIFOSIZn + * + * In both cases reset values should be sufficient. + */ +} + +static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) +{ + dev_vdbg(dwc->dev, "%s\n", __func__); + + /* + * TODO take core out of low power mode when that's + * implemented. + */ + + dwc->gadget_driver->resume(&dwc->gadget); +} + +static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + dev_vdbg(dwc->dev, "%s\n", __func__); + + /* The fith bit says SuperSpeed yes or no. */ + dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK; +} + +static void dwc3_gadget_interrupt(struct dwc3 *dwc, + const struct dwc3_event_devt *event) +{ + switch (event->type) { + case DWC3_DEVICE_EVENT_DISCONNECT: + dwc3_gadget_disconnect_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_RESET: + dwc3_gadget_reset_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + dwc3_gadget_conndone_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_WAKEUP: + dwc3_gadget_wakeup_interrupt(dwc); + break; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); + break; + case DWC3_DEVICE_EVENT_EOPF: + dev_vdbg(dwc->dev, "End of Periodic Frame\n"); + break; + case DWC3_DEVICE_EVENT_SOF: + dev_vdbg(dwc->dev, "Start of Periodic Frame\n"); + break; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + dev_vdbg(dwc->dev, "Erratic Error\n"); + break; + case DWC3_DEVICE_EVENT_CMD_CMPL: + dev_vdbg(dwc->dev, "Command Complete\n"); + break; + case DWC3_DEVICE_EVENT_OVERFLOW: + dev_vdbg(dwc->dev, "Overflow\n"); + break; + default: + dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type); + } +} + +static void dwc3_process_event_entry(struct dwc3 *dwc, + const union dwc3_event *event) +{ + /* Endpoint IRQ, handle it and return early */ + if (event->type.is_devspec == 0) { + /* depevt */ + return dwc3_endpoint_interrupt(dwc, &event->depevt); + } + + switch (event->type.type) { + case DWC3_EVENT_TYPE_DEV: + dwc3_gadget_interrupt(dwc, &event->devt); + break; + /* REVISIT what to do with Carkit and I2C events ? */ + default: + dev_err(dwc->dev, "UNKNOWN IRQ type %d\n", event->raw); + } +} + +static irqreturn_t dwc3_process_event_buf(struct dwc3 *dwc, u32 buf) +{ + struct dwc3_event_buffer *evt; + int left; + u32 count; + + count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(buf)); + count &= DWC3_GEVNTCOUNT_MASK; + if (!count) + return IRQ_NONE; + + evt = dwc->ev_buffs[buf]; + left = count; + + while (left > 0) { + union dwc3_event event; + + memcpy(&event.raw, (evt->buf + evt->lpos), sizeof(event.raw)); + dwc3_process_event_entry(dwc, &event); + /* + * XXX we wrap around correctly to the next entry as almost all + * entries are 4 bytes in size. There is one entry which has 12 + * bytes which is a regular entry followed by 8 bytes data. ATM + * I don't know how things are organized if were get next to the + * a boundary so I worry about that once we try to handle that. + */ + evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE; + left -= 4; + + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(buf), 4); + } + + return IRQ_HANDLED; +} + +static irqreturn_t dwc3_interrupt(int irq, void *_dwc) +{ + struct dwc3 *dwc = _dwc; + int i; + irqreturn_t ret = IRQ_NONE; + + spin_lock(&dwc->lock); + + for (i = 0; i < DWC3_EVENT_BUFFERS_NUM; i++) { + irqreturn_t status; + + status = dwc3_process_event_buf(dwc, i); + if (status == IRQ_HANDLED) + ret = status; + } + + spin_unlock(&dwc->lock); + + return ret; +} + +/** + * dwc3_gadget_init - Initializes gadget related registers + * @dwc: Pointer to out controller context structure + * + * Returns 0 on success otherwise negative errno. + */ +int __devinit dwc3_gadget_init(struct dwc3 *dwc) +{ + u32 reg; + int ret; + int irq; + + dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + &dwc->ctrl_req_addr, GFP_KERNEL); + if (!dwc->ctrl_req) { + dev_err(dwc->dev, "failed to allocate ctrl request\n"); + ret = -ENOMEM; + goto err0; + } + + dwc->ep0_trb = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + &dwc->ep0_trb_addr, GFP_KERNEL); + if (!dwc->ep0_trb) { + dev_err(dwc->dev, "failed to allocate ep0 trb\n"); + ret = -ENOMEM; + goto err1; + } + + dwc->setup_buf = dma_alloc_coherent(dwc->dev, + sizeof(*dwc->setup_buf) * 2, + &dwc->setup_buf_addr, GFP_KERNEL); + if (!dwc->setup_buf) { + dev_err(dwc->dev, "failed to allocate setup buffer\n"); + ret = -ENOMEM; + goto err2; + } + + dev_set_name(&dwc->gadget.dev, "gadget"); + + dwc->gadget.ops = &dwc3_gadget_ops; + dwc->gadget.is_dualspeed = true; + dwc->gadget.speed = USB_SPEED_UNKNOWN; + dwc->gadget.dev.parent = dwc->dev; + + dma_set_coherent_mask(&dwc->gadget.dev, dwc->dev->coherent_dma_mask); + + dwc->gadget.dev.dma_parms = dwc->dev->dma_parms; + dwc->gadget.dev.dma_mask = dwc->dev->dma_mask; + dwc->gadget.dev.release = dwc3_gadget_release; + dwc->gadget.name = "dwc3-gadget"; + + /* + * REVISIT: Here we should clear all pending IRQs to be + * sure we're starting from a well known location. + */ + + ret = dwc3_gadget_init_endpoints(dwc); + if (ret) + goto err3; + + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + + ret = request_irq(irq, dwc3_interrupt, IRQF_SHARED, + "dwc3", dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + irq, ret); + goto err4; + } + + /* Enable all but Start and End of Frame IRQs */ + reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN | + DWC3_DEVTEN_EVNTOVERFLOWEN | + DWC3_DEVTEN_CMDCMPLTEN | + DWC3_DEVTEN_ERRTICERREN | + DWC3_DEVTEN_WKUPEVTEN | + DWC3_DEVTEN_ULSTCNGEN | + DWC3_DEVTEN_CONNECTDONEEN | + DWC3_DEVTEN_USBRSTEN | + DWC3_DEVTEN_DISCONNEVTEN); + dwc3_writel(dwc->regs, DWC3_DEVTEN, reg); + + ret = device_register(&dwc->gadget.dev); + if (ret) { + dev_err(dwc->dev, "failed to register gadget device\n"); + put_device(&dwc->gadget.dev); + goto err5; + } + + ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); + if (ret) { + dev_err(dwc->dev, "failed to register udc\n"); + goto err6; + } + + return 0; + +err6: + device_unregister(&dwc->gadget.dev); + +err5: + dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); + free_irq(irq, dwc); + +err4: + dwc3_gadget_free_endpoints(dwc); + +err3: + dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, + dwc->setup_buf, dwc->setup_buf_addr); + +err2: + dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + dwc->ep0_trb, dwc->ep0_trb_addr); + +err1: + dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + dwc->ctrl_req, dwc->ctrl_req_addr); + +err0: + return ret; +} + +void dwc3_gadget_exit(struct dwc3 *dwc) +{ + int irq; + int i; + + usb_del_gadget_udc(&dwc->gadget); + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + + dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); + free_irq(irq, dwc); + + for (i = 0; i < ARRAY_SIZE(dwc->eps); i++) + __dwc3_gadget_ep_disable(dwc->eps[i]); + + dwc3_gadget_free_endpoints(dwc); + + dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, + dwc->setup_buf, dwc->setup_buf_addr); + + dma_free_coherent(dwc->dev, sizeof(*dwc->ep0_trb), + dwc->ep0_trb, dwc->ep0_trb_addr); + + dma_free_coherent(dwc->dev, sizeof(*dwc->ctrl_req), + dwc->ctrl_req, dwc->ctrl_req_addr); + + device_unregister(&dwc->gadget.dev); +} diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h new file mode 100644 index 0000000..ccfc109 --- /dev/null +++ b/drivers/usb/dwc3/gadget.h @@ -0,0 +1,292 @@ +/** + * gadget.h - DesignWare USB3 DRD Gadget Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DRIVERS_USB_DWC3_GADGET_H +#define __DRIVERS_USB_DWC3_GADGET_H + +#include +#include +#include "io.h" + +struct dwc3; +#define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint)) +#define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) + +/** + * struct dwc3_gadget_ep_depcfg_param1 - DEPCMDPAR0 for DEPCFG command + * @interrupt_number: self-explanatory + * @reserved7_5: set to zero + * @xfer_complete_enable: event generated when transfer completed + * @xfer_in_progress_enable: event generated when transfer in progress + * @xfer_not_ready_enable: event generated when transfer not read + * @fifo_error_enable: generates events when FIFO Underrun (IN eps) + * or FIFO Overrun (OUT) eps + * @reserved_12: set to zero + * @stream_event_enable: event generated on stream + * @reserved14_15: set to zero + * @binterval_m1: bInterval minus 1 + * @stream_capable: this EP is capable of handling streams + * @ep_number: self-explanatory + * @bulk_based: Set to ‘1’ if this isochronous endpoint represents a bulk + * data stream that ignores the relationship of bus time to the + * intervals programmed in TRBs. + * @fifo_based: Set to ‘1’ if this isochronous endpoint represents a + * FIFO-based data stream where TRBs have fixed values and are never + * written back by the core. + */ +struct dwc3_gadget_ep_depcfg_param1 { + u32 interrupt_number:5; + u32 reserved7_5:3; /* set to zero */ + u32 xfer_complete_enable:1; + u32 xfer_in_progress_enable:1; + u32 xfer_not_ready_enable:1; + u32 fifo_error_enable:1; /* IN-underrun, OUT-overrun */ + u32 reserved12:1; /* set to zero */ + u32 stream_event_enable:1; + u32 reserved14_15:2; + u32 binterval_m1:8; /* bInterval minus 1 */ + u32 stream_capable:1; + u32 ep_number:5; + u32 bulk_based:1; + u32 fifo_based:1; +} __packed; + +/** + * struct dwc3_gadget_ep_depcfg_param0 - Parameter 0 for DEPCFG + * @reserved0: set to zero + * @ep_type: Endpoint Type (control, bulk, iso, interrupt) + * @max_packet_size: max packet size in bytes + * @reserved16_14: set to zero + * @fifo_number: self-explanatory + * @burst_size: burst size minus 1 + * @data_sequence_number: Must be 0 when an endpoint is initially configured + * May be non-zero when an endpoint is configured after a power transition + * that requires a save/restore. + * @ignore_sequence_number: Set to ‘1’ to avoid resetting the sequence + * number. This setting is used by software to modify the DEPEVTEN + * event enable bits without modifying other endpoint settings. + */ +struct dwc3_gadget_ep_depcfg_param0 { + u32 reserved0:1; + u32 ep_type:2; + u32 max_packet_size:11; + u32 reserved16_14:3; + u32 fifo_number:5; + u32 burst_size:4; + u32 data_sequence_number:5; + u32 ignore_sequence_number:1; +} __packed; + +/** + * struct dwc3_gadget_ep_depxfercfg_param0 - Parameter 0 of DEPXFERCFG + * @number_xfer_resources: Defines the number of Transfer Resources allocated + * to this endpoint. This field must be set to 1. + * @reserved16_31: set to zero; + */ +struct dwc3_gadget_ep_depxfercfg_param0 { + u32 number_xfer_resources:16; + u32 reserved16_31:16; +} __packed; + +/** + * struct dwc3_gadget_ep_depstrtxfer_param1 - Parameter 1 of DEPSTRTXFER + * @transfer_desc_addr_low: Indicates the lower 32 bits of the external + * memory's start address for the transfer descriptor. Because TRBs + * must be aligned to a 16-byte boundary, the lower 4 bits of this + * address must be 0. + */ +struct dwc3_gadget_ep_depstrtxfer_param1 { + u32 transfer_desc_addr_low; +} __packed; + +/** + * struct dwc3_gadget_ep_depstrtxfer_param1 - Parameter 1 of DEPSTRTXFER + * @transfer_desc_addr_high: Indicates the higher 32 bits of the external + * memory’s start address for the transfer descriptor. + */ +struct dwc3_gadget_ep_depstrtxfer_param0 { + u32 transfer_desc_addr_high; +} __packed; + +struct dwc3_gadget_ep_cmd_params { + union { + u32 raw; + } param2; + + union { + u32 raw; + struct dwc3_gadget_ep_depcfg_param1 depcfg; + struct dwc3_gadget_ep_depstrtxfer_param1 depstrtxfer; + } param1; + + union { + u32 raw; + struct dwc3_gadget_ep_depcfg_param0 depcfg; + struct dwc3_gadget_ep_depxfercfg_param0 depxfercfg; + struct dwc3_gadget_ep_depstrtxfer_param0 depstrtxfer; + } param0; +} __packed; + +/* -------------------------------------------------------------------------- */ + +struct dwc3_request { + struct usb_request request; + struct list_head list; + struct dwc3_ep *dep; + + u8 epnum; + struct dwc3_trb_hw *trb; + dma_addr_t trb_dma; + + unsigned direction:1; + unsigned mapped:1; + unsigned queued:1; +}; +#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request)) + +static inline struct dwc3_request *next_request(struct list_head *list) +{ + if (list_empty(list)) + return NULL; + + return list_first_entry(list, struct dwc3_request, list); +} + +static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req) +{ + struct dwc3_ep *dep = req->dep; + + req->queued = true; + list_move_tail(&req->list, &dep->req_queued); +} + +#if defined(CONFIG_USB_GADGET_DWC3) || defined(CONFIG_USB_GADGET_DWC3_MODULE) +int dwc3_gadget_init(struct dwc3 *dwc); +void dwc3_gadget_exit(struct dwc3 *dwc); +#else +static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } +static inline void dwc3_gadget_exit(struct dwc3 *dwc) { } +#endif + +void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, + int status); + +void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event); +void dwc3_ep0_out_start(struct dwc3 *dwc); +int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags); +int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value); +int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); +void dwc3_map_buffer_to_dma(struct dwc3_request *req); +void dwc3_unmap_buffer_from_dma(struct dwc3_request *req); + +/** + * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW + * @dwc: DesignWare USB3 Pointer + * @number: DWC endpoint number + * + * Caller should take care of locking + */ +static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number) +{ + u32 res_id; + + res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number)); + + return DWC3_DEPCMD_GET_RSC_IDX(res_id); +} + +/** + * dwc3_gadget_event_string - returns event name + * @event: the event code + */ +static inline const char *dwc3_gadget_event_string(u8 event) +{ + switch (event) { + case DWC3_DEVICE_EVENT_DISCONNECT: + return "Disconnect"; + case DWC3_DEVICE_EVENT_RESET: + return "Reset"; + case DWC3_DEVICE_EVENT_CONNECT_DONE: + return "Connection Done"; + case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: + return "Link Status Change"; + case DWC3_DEVICE_EVENT_WAKEUP: + return "WakeUp"; + case DWC3_DEVICE_EVENT_EOPF: + return "End-Of-Frame"; + case DWC3_DEVICE_EVENT_SOF: + return "Start-Of-Frame"; + case DWC3_DEVICE_EVENT_ERRATIC_ERROR: + return "Erratic Error"; + case DWC3_DEVICE_EVENT_CMD_CMPL: + return "Command Complete"; + case DWC3_DEVICE_EVENT_OVERFLOW: + return "Overflow"; + } + + return "UNKNOWN"; +} + +/** + * dwc3_ep_event_string - returns event name + * @event: then event code + */ +static inline const char *dwc3_ep_event_string(u8 event) +{ + switch (event) { + case DWC3_DEPEVT_XFERCOMPLETE: + return "Transfer Complete"; + case DWC3_DEPEVT_XFERINPROGRESS: + return "Transfer In-Progress"; + case DWC3_DEPEVT_XFERNOTREADY: + return "Transfer Not Ready"; + case DWC3_DEPEVT_RXTXFIFOEVT: + return "FIFO"; + case DWC3_DEPEVT_STREAMEVT: + return "Stream"; + case DWC3_DEPEVT_EPCMDCMPLT: + return "Endpoint Command Complete"; + } + + return "UNKNOWN"; +} + +#endif /* __DRIVERS_USB_DWC3_GADGET_H */ diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h new file mode 100644 index 0000000..0c4e2a9 --- /dev/null +++ b/drivers/usb/dwc3/io.h @@ -0,0 +1,55 @@ +/** + * io.h - DesignWare USB3 DRD IO Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DRIVERS_USB_DWC3_IO_H +#define __DRIVERS_USB_DWC3_IO_H + +#include + +static inline u32 dwc3_readl(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) +{ + writel(value, base + offset); +} + +#endif /* __DRIVERS_USB_DWC3_IO_H */ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5a084b9..bb799d5 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -303,6 +303,18 @@ config USB_PXA_U2O PXA9xx Processor series include a high speed USB2.0 device controller, which support high speed and full speed USB peripheral. +config USB_GADGET_DWC3 + tristate "DesignWare USB3.0 (DRD) Controller" + depends on USB_DWC3 + select USB_GADGET_DUALSPEED + select USB_GADGET_SUPERSPEED + help + DesignWare USB3.0 controller is a SuperSpeed USB3.0 Controller + which can be configured for peripheral-only, host-only, hub-only + and Dual-Role operation. This Controller was first integrated into + the OMAP5 series of processors. More information about the OMAP5 + version of this controller, refer to http://www.ti.com/omap5. + # # Controllers available in both integrated and discrete versions # -- cgit v0.10.2 From 94ab23dd254a1050870160bf9620cd115cb75028 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 19 Aug 2011 18:10:59 +0300 Subject: MAINTAINERS: add a maintainer for DesignWare USB3 driver I'll be focussed on this driver and can maintain it without any problem. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/MAINTAINERS b/MAINTAINERS index 069ee3b..18a36f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2139,6 +2139,14 @@ M: Matthew Garrett S: Maintained F: drivers/platform/x86/dell-wmi.c +DESIGNWARE USB3 DRD IP DRIVER +M: Felipe Balbi +L: linux-usb@vger.kernel.org +L: linux-omap@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git +S: Maintained +F: drivers/usb/dwc3/ + DEVICE NUMBER REGISTRY M: Torben Mathiasen W: http://lanana.org/docs/device-list/index.html -- cgit v0.10.2 From da4fc14c9955bbcafb9cdcc34f9772f3e9481bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Sun, 21 Aug 2011 14:31:17 +0200 Subject: s3c-hsudc: Fix possible nullpointer dereference during probe The usb-interrupt is requested before the endpoints are initalised. If an interrupt happens in the time between request_irq and the init of the endpoint-data (as seen on the Qisda ESx00 ebook-platforms), it is therefore possible for the interrupt handler to access endpoint- data before its creation resulting in a null-pointer dereference. This patch simply moves the irq request below the endpoint init. Signed-off-by: Heiko Stuebner Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 3fa717c..00056c2 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1269,19 +1269,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev) goto err_remap; } - ret = platform_get_irq(pdev, 0); - if (ret < 0) { - dev_err(dev, "unable to obtain IRQ number\n"); - goto err_irq; - } - hsudc->irq = ret; - - ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc); - if (ret < 0) { - dev_err(dev, "irq request failed\n"); - goto err_irq; - } - spin_lock_init(&hsudc->lock); device_initialize(&hsudc->gadget.dev); @@ -1299,6 +1286,19 @@ static int s3c_hsudc_probe(struct platform_device *pdev) s3c_hsudc_setup_ep(hsudc); + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "unable to obtain IRQ number\n"); + goto err_irq; + } + hsudc->irq = ret; + + ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc); + if (ret < 0) { + dev_err(dev, "irq request failed\n"); + goto err_irq; + } + hsudc->uclk = clk_get(&pdev->dev, "usb-device"); if (IS_ERR(hsudc->uclk)) { dev_err(dev, "failed to find usb-device clock source\n"); -- cgit v0.10.2 From 938fbe54f33e97c2911d0665ec613d7f0c967d9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Sun, 21 Aug 2011 14:32:14 +0200 Subject: s3c-hsudc: Add basic otg transceiver handling Makes it possible to use i.e. gpio-vbus to handle vbus events. Signed-off-by: Heiko Stuebner Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 00056c2..3e96cc5 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -137,6 +138,7 @@ struct s3c_hsudc { struct usb_gadget_driver *driver; struct device *dev; struct s3c24xx_hsudc_platdata *pd; + struct otg_transceiver *transceiver; spinlock_t lock; void __iomem *regs; struct resource *mem_rsrc; @@ -1171,6 +1173,22 @@ static int s3c_hsudc_start(struct usb_gadget_driver *driver, return ret; } + /* connect to bus through transceiver */ + if (hsudc->transceiver) { + ret = otg_set_peripheral(hsudc->transceiver, &hsudc->gadget); + if (ret) { + dev_err(hsudc->dev, "%s: can't bind to transceiver\n", + hsudc->gadget.name); + driver->unbind(&hsudc->gadget); + + device_del(&hsudc->gadget.dev); + + hsudc->driver = NULL; + hsudc->gadget.dev.driver = NULL; + return ret; + } + } + enable_irq(hsudc->irq); dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name); @@ -1201,6 +1219,9 @@ static int s3c_hsudc_stop(struct usb_gadget_driver *driver) s3c_hsudc_stop_activity(hsudc, driver); spin_unlock_irqrestore(&hsudc->lock, flags); + if (hsudc->transceiver) + (void) otg_set_peripheral(hsudc->transceiver, NULL); + driver->unbind(&hsudc->gadget); device_del(&hsudc->gadget.dev); disable_irq(hsudc->irq); @@ -1247,6 +1268,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev) hsudc->dev = dev; hsudc->pd = pdev->dev.platform_data; + hsudc->transceiver = otg_get_transceiver(); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "unable to obtain driver resource data\n"); -- cgit v0.10.2 From e18a99e28fe308f0028be8644418a1ac653a2a96 Mon Sep 17 00:00:00 2001 From: Wanlong Gao Date: Sun, 21 Aug 2011 21:45:53 +0800 Subject: USB: otg: remove unused function twl6030_set_phy_clk Remove the unused function twl6030_set_phy_clk of twl6030-usb.c. Signed-off-by: Wanlong Gao Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index b4d2c09..ed2b26c 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -137,22 +137,6 @@ static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address) return ret; } -/*-------------------------------------------------------------------------*/ -static int twl6030_set_phy_clk(struct otg_transceiver *x, int on) -{ - struct twl6030_usb *twl; - struct device *dev; - struct twl4030_usb_data *pdata; - - twl = xceiv_to_twl(x); - dev = twl->dev; - pdata = dev->platform_data; - - pdata->phy_set_clock(twl->dev, on); - - return 0; -} - static int twl6030_phy_init(struct otg_transceiver *x) { struct twl6030_usb *twl; -- cgit v0.10.2 From d80cba6c53621ecdd768035f2988dc99addf12c2 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 22 Aug 2011 16:00:35 +0200 Subject: drivers/usb/host/ohci-omap3.c: test the just-initialized value Test the just-initialized value rather than some other one. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r@ identifier x,y,f!={PTR_ERR,ERR_PTR,ERR_CAST}; statement S; @@ x = f(...); ( if (\(x == NULL\|IS_ERR(x)\)) S | *if (\(y == NULL\|IS_ERR(y)\)) { ... when != x return ...; } ) // Signed-off-by: Julia Lawall Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index 6048f2f..853ad8d 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -149,7 +149,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ohci"); - if (!ret) { + if (!res) { dev_err(dev, "UHH OHCI get resource failed\n"); return -ENOMEM; } -- cgit v0.10.2 From 74ad60292bf0108de9884c531d7ec3f65c206230 Mon Sep 17 00:00:00 2001 From: Arvid Brodin Date: Tue, 23 Aug 2011 01:25:30 +0200 Subject: usb/isp1760: Clear TT buffer on interrupted low & full speed transfers When a low or full speed urb in progress is unlinked (or some other error occurs), the buffer in the transaction translator (part of the hub) might end up in an inconsistent state. This can make all further low and full speed transactions fail, unless the buffer is cleared. The bug can be seen when running the usbtest unlink tests as "set altsetting to 0 failed, -110", and gets fixed by this patch. Signed-off-by: Arvid Brodin Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 11367b4..5b08bd7 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -114,6 +114,7 @@ struct isp1760_qh { u32 toggle; u32 ping; int slot; + int tt_buffer_dirty; /* See USB2.0 spec section 11.17.5 */ }; struct urb_listitem { @@ -845,6 +846,10 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) return; } + /* Make sure this endpoint's TT buffer is clean before queueing ptds */ + if (qh->tt_buffer_dirty) + return; + if (usb_pipeint(list_entry(qh->qtd_list.next, struct isp1760_qtd, qtd_list)->urb->pipe)) { ptd_offset = INT_PTD_OFFSET; @@ -1182,6 +1187,15 @@ static void handle_done_ptds(struct usb_hcd *hcd) case PTD_STATE_URB_RETIRE: qtd->status = QTD_RETIRE; + if ((qtd->urb->dev->speed != USB_SPEED_HIGH) && + (qtd->urb->status != -EPIPE) && + (qtd->urb->status != -EREMOTEIO)) { + qh->tt_buffer_dirty = 1; + if (usb_hub_clear_tt_buffer(qtd->urb)) + /* Clear failed; let's hope things work + anyway */ + qh->tt_buffer_dirty = 0; + } qtd = NULL; qh->toggle = 0; qh->ping = 0; @@ -1611,6 +1625,41 @@ static void kill_transfer(struct usb_hcd *hcd, struct urb *urb, qh->slot = -1; } +/* + * Retire the qtds beginning at 'qtd' and belonging all to the same urb, killing + * any active transfer belonging to the urb in the process. + */ +static void dequeue_urb_from_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh, + struct isp1760_qtd *qtd) +{ + struct urb *urb; + int urb_was_running; + + urb = qtd->urb; + urb_was_running = 0; + list_for_each_entry_from(qtd, &qh->qtd_list, qtd_list) { + if (qtd->urb != urb) + break; + + if (qtd->status >= QTD_XFER_STARTED) + urb_was_running = 1; + if (last_qtd_of_urb(qtd, qh) && + (qtd->status >= QTD_XFER_COMPLETE)) + urb_was_running = 0; + + if (qtd->status == QTD_XFER_STARTED) + kill_transfer(hcd, urb, qh); + qtd->status = QTD_RETIRE; + } + + if ((urb->dev->speed != USB_SPEED_HIGH) && urb_was_running) { + qh->tt_buffer_dirty = 1; + if (usb_hub_clear_tt_buffer(urb)) + /* Clear failed; let's hope things work anyway */ + qh->tt_buffer_dirty = 0; + } +} + static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { @@ -1633,9 +1682,8 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, list_for_each_entry(qtd, &qh->qtd_list, qtd_list) if (qtd->urb == urb) { - if (qtd->status == QTD_XFER_STARTED) - kill_transfer(hcd, urb, qh); - qtd->status = QTD_RETIRE; + dequeue_urb_from_qtd(hcd, qh, qtd); + break; } urb->status = status; @@ -1660,12 +1708,11 @@ static void isp1760_endpoint_disable(struct usb_hcd *hcd, if (!qh) goto out; - list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { - if (qtd->status == QTD_XFER_STARTED) - kill_transfer(hcd, qtd->urb, qh); - qtd->status = QTD_RETIRE; - qtd->urb->status = -ECONNRESET; - } + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) + if (qtd->status != QTD_RETIRE) { + dequeue_urb_from_qtd(hcd, qh, qtd); + qtd->urb->status = -ECONNRESET; + } ep->hcpriv = NULL; /* Cannot free qh here since it will be parsed by schedule_ptds() */ @@ -2088,6 +2135,23 @@ static void isp1760_shutdown(struct usb_hcd *hcd) reg_write32(hcd->regs, HC_USBCMD, command); } +static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + struct isp1760_qh *qh = ep->hcpriv; + unsigned long spinflags; + + if (!qh) + return; + + spin_lock_irqsave(&priv->lock, spinflags); + qh->tt_buffer_dirty = 0; + schedule_ptds(hcd); + spin_unlock_irqrestore(&priv->lock, spinflags); +} + + static const struct hc_driver isp1760_hc_driver = { .description = "isp1760-hcd", .product_desc = "NXP ISP1760 USB Host Controller", @@ -2104,6 +2168,7 @@ static const struct hc_driver isp1760_hc_driver = { .get_frame_number = isp1760_get_frame, .hub_status_data = isp1760_hub_status_data, .hub_control = isp1760_hub_control, + .clear_tt_buffer_complete = isp1760_clear_tt_buffer_complete, }; int __init init_kmem_once(void) -- cgit v0.10.2 From 39eb4ed556c145f35059c59144192d8ee32ceaa5 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Tue, 23 Aug 2011 15:23:46 +0200 Subject: usb: fhci-hcd: Allocate pram dynamically. MPC832x does not have enough MURAM to do fixed MURAM allocation. Change to dynamic allocation. Signed-off-by: Joakim Tjernlund Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 572ea53..484a74f 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -621,12 +621,15 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) goto err_pram; } - pram_addr = cpm_muram_alloc_fixed(iprop[2], FHCI_PRAM_SIZE); + pram_addr = cpm_muram_alloc(FHCI_PRAM_SIZE, 64); if (IS_ERR_VALUE(pram_addr)) { dev_err(dev, "failed to allocate usb pram\n"); ret = -ENOMEM; goto err_pram; } + + qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, QE_CR_SUBBLOCK_USB, + QE_CR_PROTOCOL_UNSPECIFIED, pram_addr); fhci->pram = cpm_muram_addr(pram_addr); /* GPIOs and pins */ -- cgit v0.10.2 From 7c79d094358326132693279bfb22e4f8454cc652 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 23 Aug 2011 10:44:54 +0200 Subject: USB: usbtest: use URB_ZERO_PACKET for BULK-OUT transfers Executing | testusb -a -c 1 -t 3 -v 421 -s 2048 does not complete on the gadget side. g_zero enqueues a 4096 bytes long buffer. The host sends 2048bytes which is a multiple of wMaxPacketSize (either 64 or 512 bytes). The host is done with sending data but the gadget waits for more. Since the protocol does not include transfer-length-field sending a terminating zero packet seems the only way out. Reviewed-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index bb10846..bd71500 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -359,8 +359,10 @@ static int simple_io( urb->context = &completion; while (retval == 0 && iterations-- > 0) { init_completion(&completion); - if (usb_pipeout(urb->pipe)) + if (usb_pipeout(urb->pipe)) { simple_fill_buf(urb); + urb->transfer_flags |= URB_ZERO_PACKET; + } retval = usb_submit_urb(urb, GFP_KERNEL); if (retval != 0) break; -- cgit v0.10.2 From 46a57283e86c68351377ac3349f1465aee938fbd Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 23 Aug 2011 15:08:54 +1000 Subject: usb: include module.h in the DesignWare USB3 DRD driver Fixes this build error: drivers/usb/dwc3/dwc3-pci.c: In function 'dwc3_pci_init': drivers/usb/dwc3/dwc3-pci.c:211:9: error: 'THIS_MODULE' undeclared (first use in this function) Signed-off-by: Stephen Rothwell Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 2578595..e3b77d2 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -38,6 +38,7 @@ */ #include +#include #include #include #include -- cgit v0.10.2 From 29cc88979a8818cd8c5019426e945aed118b400e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 23 Aug 2011 03:12:03 -0700 Subject: USB: use usb_endpoint_maxp() instead of le16_to_cpu() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now ${LINUX}/drivers/usb/* can use usb_endpoint_maxp(desc) to get maximum packet size instead of le16_to_cpu(desc->wMaxPacketSize). This patch fix it up Cc: Armin Fuerst Cc: Pavel Machek Cc: Johannes Erdfelt Cc: Vojtech Pavlik Cc: Oliver Neukum Cc: David Kubicek Cc: Johan Hovold Cc: Brad Hards Acked-by: Felipe Balbi Cc: Sebastian Andrzej Siewior Cc: Thomas Dahlmann Cc: David Brownell Cc: David Lopo Cc: Alan Stern Cc: Michal Nazarewicz Cc: Xie Xiaobo Cc: Li Yang Cc: Jiang Bo Cc: Yuan-hsin Chen Cc: Darius Augulis Cc: Xiaochen Shen Cc: Yoshihiro Shimoda Cc: OKI SEMICONDUCTOR, Cc: Robert Jarzmik Cc: Ben Dooks Cc: Thomas Abraham Cc: Herbert Pötzl Cc: Arnaud Patard Cc: Roman Weissgaerber Acked-by: Sarah Sharp Cc: Tony Olech Cc: Florian Floe Echtler Cc: Christian Lucht Cc: Juergen Stuber Cc: Georges Toth Cc: Bill Ryder Cc: Kuba Ober Cc: Inaky Perez-Gonzalez Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index dac7676..f69a185 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1058,11 +1058,11 @@ made_compressed_probe: goto alloc_fail; } - ctrlsize = le16_to_cpu(epctrl->wMaxPacketSize); - readsize = le16_to_cpu(epread->wMaxPacketSize) * + ctrlsize = usb_endpoint_maxp(epctrl); + readsize = usb_endpoint_maxp(epread) * (quirks == SINGLE_RX_URB ? 1 : 2); acm->combined_interfaces = combined_interfaces; - acm->writesize = le16_to_cpu(epwrite->wMaxPacketSize) * 20; + acm->writesize = usb_endpoint_maxp(epwrite) * 20; acm->control = control_interface; acm->data = data_interface; acm->minor = minor; diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 2b9ff51..1d26a71 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -682,7 +682,7 @@ next_desc: if (!ep || !usb_endpoint_is_int_in(ep)) goto err; - desc->wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize); + desc->wMaxPacketSize = usb_endpoint_maxp(ep); desc->bMaxPacketSize0 = udev->descriptor.bMaxPacketSize0; desc->orq = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 3f94ac3..12cf5e7 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -186,8 +186,7 @@ static int usbtmc_ioctl_abort_bulk_in(struct usbtmc_device_data *data) for (n = 0; n < current_setting->desc.bNumEndpoints; n++) if (current_setting->endpoint[n].desc.bEndpointAddress == data->bulk_in) - max_size = le16_to_cpu(current_setting->endpoint[n]. - desc.wMaxPacketSize); + max_size = usb_endpoint_maxp(¤t_setting->endpoint[n].desc); if (max_size == 0) { dev_err(dev, "Couldn't get wMaxPacketSize\n"); @@ -636,7 +635,7 @@ static int usbtmc_ioctl_clear(struct usbtmc_device_data *data) for (n = 0; n < current_setting->desc.bNumEndpoints; n++) { desc = ¤t_setting->endpoint[n].desc; if (desc->bEndpointAddress == data->bulk_in) - max_size = le16_to_cpu(desc->wMaxPacketSize); + max_size = usb_endpoint_maxp(desc); } if (max_size == 0) { diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 26678ca..9d5e07a 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -124,9 +124,9 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, if (usb_endpoint_xfer_isoc(&ep->desc)) max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) * - le16_to_cpu(ep->desc.wMaxPacketSize); + usb_endpoint_maxp(&ep->desc); else if (usb_endpoint_xfer_int(&ep->desc)) - max_tx = le16_to_cpu(ep->desc.wMaxPacketSize) * + max_tx = usb_endpoint_maxp(&ep->desc) * (desc->bMaxBurst + 1); else max_tx = 999999; @@ -241,7 +241,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, cfgno, inum, asnum, d->bEndpointAddress); endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT; endpoint->desc.bInterval = 1; - if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8) + if (usb_endpoint_maxp(&endpoint->desc) > 8) endpoint->desc.wMaxPacketSize = cpu_to_le16(8); } @@ -254,7 +254,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, && usb_endpoint_xfer_bulk(d)) { unsigned maxp; - maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize) & 0x07ff; + maxp = usb_endpoint_maxp(&endpoint->desc) & 0x07ff; if (maxp != 512) dev_warn(ddev, "config %d interface %d altsetting %d " "bulk endpoint 0x%X has invalid maxpacket %d\n", diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 0149c09..d956965 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -190,7 +190,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, dir = usb_endpoint_dir_in(desc) ? 'I' : 'O'; if (speed == USB_SPEED_HIGH) { - switch (le16_to_cpu(desc->wMaxPacketSize) & (0x03 << 11)) { + switch (usb_endpoint_maxp(desc) & (0x03 << 11)) { case 1 << 11: bandwidth = 2; break; case 2 << 11: @@ -240,7 +240,7 @@ static char *usb_dump_endpoint_descriptor(int speed, char *start, char *end, start += sprintf(start, format_endpt, desc->bEndpointAddress, dir, desc->bmAttributes, type, - (le16_to_cpu(desc->wMaxPacketSize) & 0x07ff) * + (usb_endpoint_maxp(desc) & 0x07ff) * bandwidth, interval, unit); return start; diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index df502a9..db7fe50 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -56,7 +56,7 @@ static ssize_t show_ep_wMaxPacketSize(struct device *dev, { struct ep_device *ep = to_ep_device(dev); return sprintf(buf, "%04x\n", - le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff); + usb_endpoint_maxp(ep->desc) & 0x07ff); } static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 99fff6b..338f91f 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3018,7 +3018,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, i = 512; else i = udev->descriptor.bMaxPacketSize0; - if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) { + if (usb_endpoint_maxp(&udev->ep0.desc) != i) { if (udev->speed == USB_SPEED_LOW || !(i == 8 || i == 16 || i == 32 || i == 64)) { dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i); diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index ae334b0..909625b 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -350,7 +350,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) dev->state < USB_STATE_CONFIGURED) return -ENODEV; - max = le16_to_cpu(ep->desc.wMaxPacketSize); + max = usb_endpoint_maxp(&ep->desc); if (max <= 0) { dev_dbg(&dev->dev, "bogus endpoint ep%d%s in %s (bad maxpacket %d)\n", diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index de5f0af..cebaef7 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -254,8 +254,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, memset(¶ms, 0x00, sizeof(params)); params.param0.depcfg.ep_type = usb_endpoint_type(desc); - params.param0.depcfg.max_packet_size = - le16_to_cpu(desc->wMaxPacketSize); + params.param0.depcfg.max_packet_size = usb_endpoint_maxp(desc); params.param1.depcfg.xfer_complete_enable = true; params.param1.depcfg.xfer_not_ready_enable = true; diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index 70f2b37..d65d839 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -354,7 +354,7 @@ udc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc) writel(tmp, &dev->ep[ep->num].regs->ctl); /* set max packet size */ - maxpacket = le16_to_cpu(desc->wMaxPacketSize); + maxpacket = usb_endpoint_maxp(desc); tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt); tmp = AMD_ADDBITS(tmp, maxpacket, UDC_EP_MAX_PKT_SIZE); ep->ep.maxpacket = maxpacket; diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index ddb118a..d01fa5b 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -487,7 +487,7 @@ static int at91_ep_enable(struct usb_ep *_ep, || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT - || (maxpacket = le16_to_cpu(desc->wMaxPacketSize)) == 0 + || (maxpacket = usb_endpoint_maxp(desc)) == 0 || maxpacket > ep->maxpacket) { DBG("bad ep or descriptor\n"); return -EINVAL; diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 5b1665e..722c468 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -527,7 +527,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep->ep.name, desc); - maxpacket = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff; + maxpacket = usb_endpoint_maxp(desc) & 0x7ff; if (((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != ep->index) || ep->index == 0 @@ -571,7 +571,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) * Bits 11:12 specify number of _additional_ * transactions per microframe. */ - nr_trans = ((le16_to_cpu(desc->wMaxPacketSize) >> 11) & 3) + 1; + nr_trans = ((usb_endpoint_maxp(desc) >> 11) & 3) + 1; if (nr_trans > 3) return -EINVAL; diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index 1265a85..83428f5 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -2101,7 +2101,7 @@ static int ep_enable(struct usb_ep *ep, mEp->num = usb_endpoint_num(desc); mEp->type = usb_endpoint_type(desc); - mEp->ep.maxpacket = __constant_le16_to_cpu(desc->wMaxPacketSize); + mEp->ep.maxpacket = usb_endpoint_maxp(desc); dbg_event(_usb_addr(mEp), "ENABLE", 0); diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index aef4741..8065464 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -164,7 +164,7 @@ int config_ep_by_speed(struct usb_gadget *g, ep_found: /* commit results */ - _ep->maxpacket = le16_to_cpu(chosen_desc->wMaxPacketSize); + _ep->maxpacket = usb_endpoint_maxp(chosen_desc); _ep->desc = chosen_desc; _ep->comp_desc = NULL; _ep->maxburst = 0; diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index e755a9d..7b06d39 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -439,7 +439,7 @@ dummy_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) * maximum packet size. * For SS devices the wMaxPacketSize is limited by 1024. */ - max = le16_to_cpu(desc->wMaxPacketSize) & 0x7ff; + max = usb_endpoint_maxp(desc) & 0x7ff; /* drivers must not request bad settings, since lower levels * (hardware or its drivers) may not check. some endpoints @@ -1277,7 +1277,7 @@ static int periodic_bytes (struct dummy *dum, struct dummy_ep *ep) int tmp; /* high bandwidth mode */ - tmp = le16_to_cpu(ep->desc->wMaxPacketSize); + tmp = usb_endpoint_maxp(ep->desc); tmp = (tmp >> 11) & 0x03; tmp *= 8 /* applies to entire frame */; limit += limit * tmp; diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 7a7e6b7..cdca7eb 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -158,7 +158,7 @@ ep_matches ( * where it's an output parameter representing the full speed limit. * the usb spec fixes high speed bulk maxpacket at 512 bytes. */ - max = 0x7ff & le16_to_cpu(desc->wMaxPacketSize); + max = 0x7ff & usb_endpoint_maxp(desc); switch (type) { case USB_ENDPOINT_XFER_INT: /* INT: limit 64 bytes full speed, 1024 high/super speed */ diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 5b93395..4ce3dec 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2401,8 +2401,7 @@ reset: goto reset; fsg->bulk_out->driver_data = common; fsg->bulk_out_enabled = 1; - common->bulk_out_maxpacket = - le16_to_cpu(fsg->bulk_out->desc->wMaxPacketSize); + common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc); clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); /* Allocate the requests */ diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 639e14a..39ece40 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -2801,7 +2801,7 @@ reset: if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) goto reset; fsg->bulk_out_enabled = 1; - fsg->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); + fsg->bulk_out_maxpacket = usb_endpoint_maxp(d); clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); if (transport_is_cbi()) { diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 3bf872e..2a03e4d 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -540,7 +540,7 @@ static int qe_ep_init(struct qe_udc *udc, int reval = 0; u16 max = 0; - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* check the max package size validate for this endpoint */ /* Refer to USB2.0 spec table 9-13, diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index de24a42..d699350 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -559,7 +559,7 @@ static int fsl_ep_enable(struct usb_ep *_ep, if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* Disable automatic zlp generation. Driver is responsible to indicate * explicitly through req->req.zero. This is needed to enable multi-td diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index 4ec888f..d9ee6c3 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -220,7 +220,7 @@ static int config_ep(struct fusb300_ep *ep, info.type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; info.dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0; - info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(desc); info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; if ((info.type == USB_ENDPOINT_XFER_INT) || diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index 692fd9b..bf08bfc 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -689,7 +689,7 @@ static int imx_ep_enable(struct usb_ep *usb_ep, return -EINVAL; } - if (imx_ep->fifosize < le16_to_cpu(desc->wMaxPacketSize)) { + if (imx_ep->fifosize < usb_endpoint_maxp(desc)) { D_ERR(imx_usb->dev, "<%s> bad %s maxpacket\n", __func__, usb_ep->name); return -ERANGE; diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index a06e2c2..5bf9942 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -283,7 +283,7 @@ static int langwell_ep_enable(struct usb_ep *_ep, if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* * disable HW zero length termination select diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 491f825..5e597c3 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -370,7 +370,7 @@ static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep, ep->pipectr = get_pipectr_addr(pipenum); ep->pipenum = pipenum; - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp(desc); m66592->pipenum2ep[pipenum] = ep; m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep; INIT_LIST_HEAD(&ep->queue); @@ -447,7 +447,7 @@ static int alloc_pipe_config(struct m66592_ep *ep, ep->type = info.type; info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(desc); info.interval = desc->bInterval; if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) info.dir_in = 1; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index ce1ac2b..263dec4 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -493,7 +493,7 @@ static int mv_ep_enable(struct usb_ep *_ep, return -ESHUTDOWN; direction = ep_dir(ep); - max = le16_to_cpu(desc->wMaxPacketSize); + max = usb_endpoint_maxp(desc); /* * disable HW zero length termination select diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index ab98ea9..6fef1c0 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -204,7 +204,7 @@ net2272_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; + max = usb_endpoint_maxp(desc) & 0x1fff; spin_lock_irqsave(&dev->lock, flags); _ep->maxpacket = max & 0x7fff; diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 3dd40b4..8d3673f 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -169,7 +169,7 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) return -EDOM; /* sanity check ep-e/ep-f since their fifos are small */ - max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff; + max = usb_endpoint_maxp (desc) & 0x1fff; if (ep->num > 4 && max > 64) return -ERANGE; @@ -1640,7 +1640,7 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf) default: val = "iso"; break; }; val; }), - le16_to_cpu (d->wMaxPacketSize) & 0x1fff, + usb_endpoint_maxp (d) & 0x1fff, ep->dma ? "dma" : "pio", ep->fifo_size ); } else /* ep0 should only have one transfer queued */ diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 740c7da..b7a7799 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -166,15 +166,14 @@ static int omap_ep_enable(struct usb_ep *_ep, if (!_ep || !desc || ep->desc || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress - || ep->maxpacket < le16_to_cpu - (desc->wMaxPacketSize)) { + || ep->maxpacket < usb_endpoint_maxp(desc)) { DBG("%s, bad ep or descriptor\n", __func__); return -EINVAL; } - maxp = le16_to_cpu (desc->wMaxPacketSize); + maxp = usb_endpoint_maxp(desc); if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK && maxp != ep->maxpacket) - || le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket + || usb_endpoint_maxp(desc) > ep->maxpacket || !desc->wMaxPacketSize) { DBG("%s, bad %s maxpacket\n", __func__, _ep->name); return -ERANGE; diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index f96615a..b69ae3e 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -947,7 +947,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep, else buff_size = UDC_EPOUT_BUFF_SIZE; pch_udc_ep_set_bufsz(ep, buff_size, ep->in); - pch_udc_ep_set_maxpkt(ep, le16_to_cpu(desc->wMaxPacketSize)); + pch_udc_ep_set_maxpkt(ep, usb_endpoint_maxp(desc)); pch_udc_ep_set_nak(ep); pch_udc_ep_fifo_flush(ep, ep->in); /* Configure the endpoint */ @@ -957,7 +957,7 @@ static void pch_udc_ep_enable(struct pch_udc_ep *ep, (cfg->cur_cfg << UDC_CSR_NE_CFG_SHIFT) | (cfg->cur_intf << UDC_CSR_NE_INTF_SHIFT) | (cfg->cur_alt << UDC_CSR_NE_ALT_SHIFT) | - le16_to_cpu(desc->wMaxPacketSize) << UDC_CSR_NE_MAX_PKT_SHIFT; + usb_endpoint_maxp(desc) << UDC_CSR_NE_MAX_PKT_SHIFT; if (ep->in) pch_udc_write_csr(ep->dev, val, UDC_EPIN_IDX(ep->num)); @@ -1466,7 +1466,7 @@ static int pch_udc_pcd_ep_enable(struct usb_ep *usbep, ep->desc = desc; ep->halted = 0; pch_udc_ep_enable(ep, &ep->dev->cfg_data, desc); - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp(desc); pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num)); spin_unlock_irqrestore(&dev->lock, iflags); return 0; diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index e4e59b4..7862465 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -232,8 +232,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || ep->bEndpointAddress != desc->bEndpointAddress - || ep->fifo_size < le16_to_cpu - (desc->wMaxPacketSize)) { + || ep->fifo_size < usb_endpoint_maxp (desc)) { DMSG("%s, bad ep or descriptor\n", __func__); return -EINVAL; } @@ -248,7 +247,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, /* hardware _could_ do smaller, but driver doesn't */ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK - && le16_to_cpu (desc->wMaxPacketSize) + && usb_endpoint_maxp (desc) != BULK_FIFO_SIZE) || !desc->wMaxPacketSize) { DMSG("%s, bad %s maxpacket\n", __func__, _ep->name); @@ -264,7 +263,7 @@ static int pxa25x_ep_enable (struct usb_ep *_ep, ep->desc = desc; ep->stopped = 0; ep->pio_irqs = 0; - ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp (desc); /* flush fifo (mostly for OUT buffers) */ pxa25x_ep_fifo_flush (_ep); @@ -401,7 +400,7 @@ write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req) { unsigned max; - max = le16_to_cpu(ep->desc->wMaxPacketSize); + max = usb_endpoint_maxp(ep->desc); do { unsigned count; int is_last, is_short; @@ -671,8 +670,7 @@ pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) * we can report per-packet status. that also helps with dma. */ if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC - && req->req.length > le16_to_cpu - (ep->desc->wMaxPacketSize))) + && req->req.length > usb_endpoint_maxp (ep->desc))) return -EMSGSIZE; DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", @@ -1105,7 +1103,7 @@ udc_seq_show(struct seq_file *m, void *_d) tmp = *dev->ep [i].reg_udccs; seq_printf(m, "%s max %d %s udccs %02x irqs %lu\n", - ep->ep.name, le16_to_cpu(desc->wMaxPacketSize), + ep->ep.name, usb_endpoint_maxp(desc), "pio", tmp, ep->pio_irqs); /* TODO translate all five groups of udccs bits! */ diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 85b68c7..d21455f 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -1439,7 +1439,7 @@ static int pxa_ep_enable(struct usb_ep *_ep, return -EINVAL; } - if (ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) { + if (ep->fifo_size < usb_endpoint_maxp(desc)) { ep_err(ep, "bad maxpacket\n"); return -ERANGE; } diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 50991e5..61d0c65 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -341,7 +341,7 @@ static void r8a66597_ep_setting(struct r8a66597 *r8a66597, ep->pipectr = get_pipectr_addr(pipenum); ep->pipenum = pipenum; - ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + ep->ep.maxpacket = usb_endpoint_maxp(desc); r8a66597->pipenum2ep[pipenum] = ep; r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK] = ep; @@ -420,7 +420,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, ep->type = info.type; info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(desc); info.interval = desc->bInterval; if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) info.dir_in = 1; diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 8bdee67..39b134d 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -2297,7 +2297,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, return -EINVAL; } - mps = le16_to_cpu(desc->wMaxPacketSize); + mps = usb_endpoint_maxp(desc); /* note, we handle this here instead of s3c_hsotg_set_ep_maxpacket */ diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 3e96cc5..25829b4 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -761,11 +761,11 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep, if (!_ep || !desc || hsep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT || hsep->bEndpointAddress != desc->bEndpointAddress - || ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize)) + || ep_maxpacket(hsep) < usb_endpoint_maxp(desc)) return -EINVAL; if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK - && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep)) + && usb_endpoint_maxp(desc) != ep_maxpacket(hsep)) || !desc->wMaxPacketSize) return -ERANGE; @@ -781,7 +781,7 @@ static int s3c_hsudc_ep_enable(struct usb_ep *_ep, hsep->stopped = hsep->wedge = 0; hsep->desc = desc; - hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + hsep->ep.maxpacket = usb_endpoint_maxp(desc); s3c_hsudc_set_halt(_ep, 0); __set_bit(ep_index(hsep), hsudc->regs + S3C_EIER); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 8d31848..2572854 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1082,7 +1082,7 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep, if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; - max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; + max = usb_endpoint_maxp(desc) & 0x1fff; local_irq_save (flags); _ep->maxpacket = max & 0x7ff; diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index dd24fc1..15dc51d 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -428,7 +428,7 @@ static struct ed *ed_get ( ed->type = usb_pipetype(pipe); info |= (ep->desc.bEndpointAddress & ~USB_DIR_IN) << 7; - info |= le16_to_cpu(ep->desc.wMaxPacketSize) << 16; + info |= usb_endpoint_maxp(&ep->desc) << 16; if (udev->speed == USB_SPEED_LOW) info |= ED_LOWSPEED; /* only control transfers store pids in tds */ @@ -444,7 +444,7 @@ static struct ed *ed_get ( ed->load = usb_calc_bus_time ( udev->speed, !is_out, ed->type == PIPE_ISOCHRONOUS, - le16_to_cpu(ep->desc.wMaxPacketSize)) + usb_endpoint_maxp(&ep->desc)) / 1000; } } diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 40a0d8b..a6f2564 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -959,7 +959,7 @@ static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb, info.pipenum = get_empty_pipenum(r8a66597, ep); info.address = get_urb_to_r8a66597_addr(r8a66597, urb); info.epnum = usb_endpoint_num(ep); - info.maxpacket = le16_to_cpu(ep->wMaxPacketSize); + info.maxpacket = usb_endpoint_maxp(ep); info.type = get_r8a66597_type(usb_endpoint_type(ep)); info.bufnum = get_bufnum(info.pipenum); info.buf_bsize = get_buf_bsize(info.pipenum); diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 84ed28b3..f6ca80e 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -280,7 +280,7 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, qh->load = usb_calc_bus_time(udev->speed, usb_endpoint_dir_in(&hep->desc), qh->type == USB_ENDPOINT_XFER_ISOC, - le16_to_cpu(hep->desc.wMaxPacketSize)) + usb_endpoint_maxp(&hep->desc)) / 1000 + 1; } else { /* Skeleton QH */ @@ -792,7 +792,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, { struct uhci_td *td; unsigned long destination, status; - int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); + int maxsze = usb_endpoint_maxp(&qh->hep->desc); int len = urb->transfer_buffer_length; dma_addr_t data = urb->transfer_dma; __hc32 *plink; @@ -918,7 +918,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, { struct uhci_td *td; unsigned long destination, status; - int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); + int maxsze = usb_endpoint_maxp(&qh->hep->desc); int len = urb->transfer_buffer_length; int this_sg_len; dma_addr_t data; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index d446886..d873a03 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1141,8 +1141,8 @@ static u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci, if (udev->speed == USB_SPEED_SUPER) return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); - max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize)); - max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) & 0x1800) >> 11; + max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)); + max_burst = (usb_endpoint_maxp(&ep->desc) & 0x1800) >> 11; /* A 0 in max burst means 1 transfer per ESIT */ return max_packet * (max_burst + 1); } @@ -1211,7 +1211,7 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, /* Set the max packet size and max burst */ switch (udev->speed) { case USB_SPEED_SUPER: - max_packet = le16_to_cpu(ep->desc.wMaxPacketSize); + max_packet = usb_endpoint_maxp(&ep->desc); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet)); /* dig out max burst from ep companion desc */ max_packet = ep->ss_ep_comp.bMaxBurst; @@ -1223,14 +1223,14 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, */ if (usb_endpoint_xfer_isoc(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) { - max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) + max_burst = (usb_endpoint_maxp(&ep->desc) & 0x1800) >> 11; ep_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(max_burst)); } /* Fall through */ case USB_SPEED_FULL: case USB_SPEED_LOW: - max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize)); + max_packet = GET_MAX_PACKET(usb_endpoint_maxp(&ep->desc)); ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet)); break; default: diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7113d16..bf0b52c 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2676,7 +2676,7 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len, * running_total. */ packets_transferred = (running_total + trb_buff_len) / - le16_to_cpu(urb->ep->desc.wMaxPacketSize); + usb_endpoint_maxp(&urb->ep->desc); return xhci_td_remainder(total_packet_count - packets_transferred); } @@ -2706,7 +2706,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, num_trbs = count_sg_trbs_needed(xhci, urb); num_sgs = urb->num_sgs; total_packet_count = roundup(urb->transfer_buffer_length, - le16_to_cpu(urb->ep->desc.wMaxPacketSize)); + usb_endpoint_maxp(&urb->ep->desc)); trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, @@ -2913,7 +2913,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, running_total = 0; total_packet_count = roundup(urb->transfer_buffer_length, - le16_to_cpu(urb->ep->desc.wMaxPacketSize)); + usb_endpoint_maxp(&urb->ep->desc)); /* How much data is in the first TRB? */ addr = (u64) urb->transfer_dma; trb_buff_len = TRB_MAX_BUFF_SIZE - @@ -3239,7 +3239,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td_remain_len = td_len; /* FIXME: Ignoring zero-length packets, can those happen? */ total_packet_count = roundup(td_len, - le16_to_cpu(urb->ep->desc.wMaxPacketSize)); + usb_endpoint_maxp(&urb->ep->desc)); burst_count = xhci_get_burst_count(xhci, urb->dev, urb, total_packet_count); residue = xhci_get_last_burst_packet_count(xhci, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1c4432d..f53596b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -987,7 +987,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, out_ctx = xhci->devs[slot_id]->out_ctx; ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); - max_packet_size = le16_to_cpu(urb->dev->ep0.desc.wMaxPacketSize); + max_packet_size = usb_endpoint_maxp(&urb->dev->ep0.desc); if (hw_max_packet_size != max_packet_size) { xhci_dbg(xhci, "Max Packet Size for ep 0 changed.\n"); xhci_dbg(xhci, "Max packet size in usb_device = %d\n", diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index a6afd15..fe85871 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -213,7 +213,7 @@ static void adu_interrupt_in_callback(struct urb *urb) if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) { if (dev->read_buffer_length < - (4 * le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize)) - + (4 * usb_endpoint_maxp(dev->interrupt_in_endpoint)) - (urb->actual_length)) { memcpy (dev->read_buffer_primary + dev->read_buffer_length, @@ -315,7 +315,7 @@ static int adu_open(struct inode *inode, struct file *file) usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), adu_interrupt_in_callback, dev, dev->interrupt_in_endpoint->bInterval); dev->read_urb_finished = 0; @@ -483,7 +483,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), adu_interrupt_in_callback, dev, dev->interrupt_in_endpoint->bInterval); @@ -536,7 +536,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), adu_interrupt_in_callback, dev, dev->interrupt_in_endpoint->bInterval); @@ -622,7 +622,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, dbg(4," %s : sending, count = %Zd", __func__, count); /* write the data into interrupt_out_buffer from userspace */ - buffer_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); bytes_to_write = count > buffer_size ? buffer_size : count; dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd", __func__, buffer_size, count, bytes_to_write); @@ -752,8 +752,8 @@ static int adu_probe(struct usb_interface *interface, goto error; } - in_end_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize); - out_end_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize); + in_end_size = usb_endpoint_maxp(dev->interrupt_in_endpoint); + out_end_size = usb_endpoint_maxp(dev->interrupt_out_endpoint); dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL); if (!dev->read_buffer_primary) { diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index 2f41089c..2dbe600 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -2777,7 +2777,7 @@ static int ftdi_elan_probe(struct usb_interface *interface, endpoint = &iface_desc->endpoint[i].desc; if (!ftdi->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); ftdi->bulk_in_size = buffer_size; ftdi->bulk_in_endpointAddr = endpoint->bEndpointAddress; ftdi->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index c6184b4..515b67f 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -359,7 +359,7 @@ static int idmouse_probe(struct usb_interface *interface, endpoint = &iface_desc->endpoint[0].desc; if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ - dev->orig_bi_size = le16_to_cpu(endpoint->wMaxPacketSize); + dev->orig_bi_size = usb_endpoint_maxp(endpoint); dev->bulk_in_size = 0x200; /* works _much_ faster */ dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index a2190b9..8145790 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -803,7 +803,7 @@ static int iowarrior_probe(struct usb_interface *interface, dev->int_out_endpoint = endpoint; } /* we have to check the report_size often, so remember it in the endianess suitable for our machine */ - dev->report_size = le16_to_cpu(dev->int_in_endpoint->wMaxPacketSize); + dev->report_size = usb_endpoint_maxp(dev->int_in_endpoint); if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) && (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56)) /* IOWarrior56 has wMaxPacketSize different from report size */ diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index cb40962..48c166f 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -721,7 +721,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * if (dev->interrupt_out_endpoint == NULL) dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n"); - dev->interrupt_in_endpoint_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize); + dev->interrupt_in_endpoint_size = usb_endpoint_maxp(dev->interrupt_in_endpoint); dev->ring_buffer = kmalloc(ring_buffer_size*(sizeof(size_t)+dev->interrupt_in_endpoint_size), GFP_KERNEL); if (!dev->ring_buffer) { dev_err(&intf->dev, "Couldn't allocate ring_buffer\n"); @@ -737,7 +737,7 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n"); goto error; } - dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize) : + dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? usb_endpoint_maxp(dev->interrupt_out_endpoint) : udev->descriptor.bMaxPacketSize0; dev->interrupt_out_buffer = kmalloc(write_buffer_size*dev->interrupt_out_endpoint_size, GFP_KERNEL); if (!dev->interrupt_out_buffer) { diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 6482c6e..a989356 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -409,7 +409,7 @@ static int tower_open (struct inode *inode, struct file *file) dev->udev, usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress), dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + usb_endpoint_maxp(dev->interrupt_in_endpoint), tower_interrupt_in_callback, dev, dev->interrupt_in_interval); @@ -928,7 +928,7 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device err("Couldn't allocate read_buffer"); goto error; } - dev->interrupt_in_buffer = kmalloc (le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL); + dev->interrupt_in_buffer = kmalloc (usb_endpoint_maxp(dev->interrupt_in_endpoint), GFP_KERNEL); if (!dev->interrupt_in_buffer) { err("Couldn't allocate interrupt_in_buffer"); goto error; diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 6ac1069..1871cdf 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -347,7 +347,7 @@ static int lcd_probe(struct usb_interface *interface, if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); dev->bulk_in_size = buffer_size; dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index bd71500..930962f 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1585,8 +1585,8 @@ static struct urb *iso_alloc_urb( if (bytes < 0 || !desc) return NULL; - maxp = 0x7ff & le16_to_cpu(desc->wMaxPacketSize); - maxp *= 1 + (0x3 & (le16_to_cpu(desc->wMaxPacketSize) >> 11)); + maxp = 0x7ff & usb_endpoint_maxp(desc); + maxp *= 1 + (0x3 & (usb_endpoint_maxp(desc) >> 11)); packets = DIV_ROUND_UP(bytes, maxp); urb = usb_alloc_urb(packets, GFP_KERNEL); @@ -1656,7 +1656,7 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, "... iso period %d %sframes, wMaxPacket %04x\n", 1 << (desc->bInterval - 1), (udev->speed == USB_SPEED_HIGH) ? "micro" : "", - le16_to_cpu(desc->wMaxPacketSize)); + usb_endpoint_maxp(desc)); for (i = 0; i < param->sglen; i++) { urbs[i] = iso_alloc_urb(udev, pipe, desc, diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 8c41a2e..44b331a 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1020,7 +1020,7 @@ static int musb_gadget_enable(struct usb_ep *ep, goto fail; /* REVISIT this rules out high bandwidth periodic transfers */ - tmp = le16_to_cpu(desc->wMaxPacketSize); + tmp = usb_endpoint_maxp(desc); if (tmp & ~0x07ff) { int ok; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 8b2473f..60ddba8 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1932,7 +1932,7 @@ static int musb_urb_enqueue( INIT_LIST_HEAD(&qh->ring); qh->is_ready = 1; - qh->maxpacket = le16_to_cpu(epd->wMaxPacketSize); + qh->maxpacket = usb_endpoint_maxp(epd); qh->type = usb_endpoint_type(epd); /* Bits 11 & 12 of wMaxPacketSize encode high bandwidth multiplier. diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 78a2cf9..8aee28b 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1480,7 +1480,7 @@ static void ftdi_set_max_packet_size(struct usb_serial_port *port) } /* set max packet size based on descriptor */ - priv->max_packet_size = le16_to_cpu(ep_desc->wMaxPacketSize); + priv->max_packet_size = usb_endpoint_maxp(ep_desc); dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size); } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index abf095b..2ee8075 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -3042,7 +3042,7 @@ static int edge_startup(struct usb_serial *serial) endpoint = &serial->interface->altsetting[0]. endpoint[i].desc; - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); if (!interrupt_in_found && (usb_endpoint_is_int_in(endpoint))) { /* we found a interrupt in endpoint */ @@ -3107,7 +3107,7 @@ static int edge_startup(struct usb_serial *serial) usb_rcvbulkpipe(dev, endpoint->bEndpointAddress), edge_serial->bulk_in_buffer, - le16_to_cpu(endpoint->wMaxPacketSize), + usb_endpoint_maxp(endpoint), edge_bulk_in_callback, edge_serial); bulk_in_found = true; diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 96423f3..c248a91 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -523,7 +523,7 @@ static int opticon_startup(struct usb_serial *serial) goto error; } - priv->buffer_size = le16_to_cpu(endpoint->wMaxPacketSize) * 2; + priv->buffer_size = usb_endpoint_maxp(endpoint) * 2; priv->bulk_in_buffer = kmalloc(priv->buffer_size, GFP_KERNEL); if (!priv->bulk_in_buffer) { dev_err(&priv->udev->dev, "out of memory\n"); diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index d9457bd..7096f79 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -226,7 +226,7 @@ static int symbol_startup(struct usb_serial *serial) goto error; } - priv->buffer_size = le16_to_cpu(endpoint->wMaxPacketSize) * 2; + priv->buffer_size = usb_endpoint_maxp(endpoint) * 2; priv->int_buffer = kmalloc(priv->buffer_size, GFP_KERNEL); if (!priv->int_buffer) { dev_err(&priv->udev->dev, "out of memory\n"); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 1c03130..cc274fd 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -912,7 +912,7 @@ int usb_serial_probe(struct usb_interface *interface, goto probe_error; } buffer_size = max_t(int, serial->type->bulk_in_size, - le16_to_cpu(endpoint->wMaxPacketSize)); + usb_endpoint_maxp(endpoint)); port->bulk_in_size = buffer_size; port->bulk_in_endpointAddress = endpoint->bEndpointAddress; port->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); @@ -942,7 +942,7 @@ int usb_serial_probe(struct usb_interface *interface, goto probe_error; buffer_size = serial->type->bulk_out_size; if (!buffer_size) - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); port->bulk_out_size = buffer_size; port->bulk_out_endpointAddress = endpoint->bEndpointAddress; port->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); @@ -990,7 +990,7 @@ int usb_serial_probe(struct usb_interface *interface, "No free urbs available\n"); goto probe_error; } - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); port->interrupt_in_endpointAddress = endpoint->bEndpointAddress; port->interrupt_in_buffer = kmalloc(buffer_size, @@ -1021,7 +1021,7 @@ int usb_serial_probe(struct usb_interface *interface, "No free urbs available\n"); goto probe_error; } - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); port->interrupt_out_size = buffer_size; port->interrupt_out_endpointAddress = endpoint->bEndpointAddress; diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index e24ce31..32d6fc9 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -555,7 +555,7 @@ static int skel_probe(struct usb_interface *interface, if (!dev->bulk_in_endpointAddr && usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ - buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); + buffer_size = usb_endpoint_maxp(endpoint); dev->bulk_in_size = buffer_size; dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; dev->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c index 59a748a..0d1863c 100644 --- a/drivers/usb/wusbcore/wa-hc.c +++ b/drivers/usb/wusbcore/wa-hc.c @@ -43,7 +43,7 @@ int wa_create(struct wahc *wa, struct usb_interface *iface) /* Fill up Data Transfer EP pointers */ wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc; wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc; - wa->xfer_result_size = le16_to_cpu(wa->dti_epd->wMaxPacketSize); + wa->xfer_result_size = usb_endpoint_maxp(wa->dti_epd); wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL); if (wa->xfer_result == NULL) goto error_xfer_result_alloc; diff --git a/include/linux/usb.h b/include/linux/usb.h index 73c7df4..c19f910 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1574,7 +1574,7 @@ usb_maxpacket(struct usb_device *udev, int pipe, int is_out) return 0; /* NOTE: only 0x07ff bits are for packet size... */ - return le16_to_cpu(ep->desc.wMaxPacketSize); + return usb_endpoint_maxp(&ep->desc); } /* ----------------------------------------------------------------------- */ -- cgit v0.10.2 From 096cb25d624289a054c9e4d0537c26c0a5eff487 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 23 Aug 2011 12:52:47 -0700 Subject: usb: fix dwc3 build when USB_GADGET_DWC3 is not enabled Fix build error when CONFIG_USB_GADGET_DWC3 is not enabled: ERROR: "dwc3_send_gadget_ep_cmd" [drivers/usb/dwc3/dwc3.ko] undefined! Signed-off-by: Randy Dunlap Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index ccfc109..4809ac8 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -202,6 +202,11 @@ void dwc3_gadget_exit(struct dwc3 *dwc); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) { return 0; } static inline void dwc3_gadget_exit(struct dwc3 *dwc) { } +static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, + unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +{ + return 0; +} #endif void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, -- cgit v0.10.2 From 5d7a1c8a81244e8ea16d9a87b4434557f9103e3c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 23 Aug 2011 11:22:37 -0700 Subject: usb: fix ipw.c build error, select USB_SERIAL_WWAN USB_SERIAL_IPW needs to select USB_SERIAL_WWAN to fix build errors: ipw.c:(.text+0x333841): undefined reference to `usb_wwan_close' ipw.c:(.text+0x3339e6): undefined reference to `usb_wwan_open' ipw.c:(.text+0x333bb9): undefined reference to `usb_wwan_release' drivers/built-in.o:(.data+0x29dac): undefined reference to `usb_wwan_startup' drivers/built-in.o:(.data+0x29db4): undefined reference to `usb_wwan_disconnect' drivers/built-in.o:(.data+0x29dd4): undefined reference to `usb_wwan_write' Signed-off-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index b71e309..677f577 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -252,6 +252,7 @@ config USB_SERIAL_GARMIN config USB_SERIAL_IPW tristate "USB IPWireless (3G UMTS TDD) Driver" + select USB_SERIAL_WWAN help Say Y here if you want to use a IPWireless USB modem such as the ones supplied by Axity3G/Sentech South Africa. -- cgit v0.10.2 From 2e5a08a3693e4c5bd251afc13246bc86fc9fafc3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 24 Aug 2011 08:41:11 +0200 Subject: USB gadget i.MX1: remove dependency on ARCH_MXC The ARCH_MX1 scheduled for removal. Instead, depend on ARCH_MXC and make clear in the Kconfig text that only i.MX1 has this hardware. Signed-off-by: Sascha Hauer Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index bb799d5..fe56379 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -255,12 +255,11 @@ config USB_S3C_HSOTG integrated into the S3C64XX series SoC. config USB_IMX - tristate "Freescale IMX USB Peripheral Controller" - depends on ARCH_MX1 + tristate "Freescale i.MX1 USB Peripheral Controller" + depends on ARCH_MXC help - Freescale's IMX series include an integrated full speed - USB 1.1 device controller. The controller in the IMX series - is register-compatible. + Freescale's i.MX1 includes an integrated full speed + USB 1.1 device controller. It has Six fixed-function endpoints, as well as endpoint zero (for control transfers). -- cgit v0.10.2 From 5de9ec4dc10e1a39b924fb165bcb3ddb6e8e6525 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 24 Aug 2011 08:41:10 +0200 Subject: USB host i.MX21: remove dependency on MACH_MX21 the MACH_MX* macros are scheduled for removal, so just depend on ARCH_MXC instead. The Kconfig text makes it clear on which SoC the driver runs on. Signed-off-by: Sascha Hauer Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 5791fe9..13e1f67 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -544,11 +544,11 @@ config USB_HWA_HCD will be called "hwa-hc". config USB_IMX21_HCD - tristate "iMX21 HCD support" - depends on USB && ARM && MACH_MX21 + tristate "i.MX21 HCD support" + depends on USB && ARM && ARCH_MXC help This driver enables support for the on-chip USB host in the - iMX21 processor. + i.MX21 processor. To compile this driver as a module, choose M here: the module will be called "imx21-hcd". -- cgit v0.10.2 From 065e60964e293227e4feb0c1f7e27e609316ed9a Mon Sep 17 00:00:00 2001 From: Adam Cozzette Date: Wed, 24 Aug 2011 12:22:37 -0600 Subject: ums_realtek: do not use stack memory for DMA This patch changes rts51x_read_mem, rts51x_write_mem, and rts51x_read_status to allocate temporary buffers with kmalloc. This way stack addresses are not used for DMA when these functions call rts51x_bulk_transport. Signed-off-by: Adam Cozzette Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 34adc4b..232167a 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -320,6 +320,11 @@ static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len) { int retval; u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmalloc(len, GFP_NOIO); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len); @@ -331,10 +336,14 @@ static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len) cmnd[5] = (u8) len; retval = rts51x_bulk_transport(us, 0, cmnd, 12, - data, len, DMA_FROM_DEVICE, NULL); - if (retval != USB_STOR_TRANSPORT_GOOD) + buf, len, DMA_FROM_DEVICE, NULL); + if (retval != USB_STOR_TRANSPORT_GOOD) { + kfree(buf); return -EIO; + } + memcpy(data, buf, len); + kfree(buf); return 0; } @@ -342,6 +351,12 @@ static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len) { int retval; u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmalloc(len, GFP_NOIO); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; + memcpy(buf, data, len); US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len); @@ -353,7 +368,8 @@ static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len) cmnd[5] = (u8) len; retval = rts51x_bulk_transport(us, 0, cmnd, 12, - data, len, DMA_TO_DEVICE, NULL); + buf, len, DMA_TO_DEVICE, NULL); + kfree(buf); if (retval != USB_STOR_TRANSPORT_GOOD) return -EIO; @@ -365,6 +381,11 @@ static int rts51x_read_status(struct us_data *us, { int retval; u8 cmnd[12] = { 0 }; + u8 *buf; + + buf = kmalloc(len, GFP_NOIO); + if (buf == NULL) + return USB_STOR_TRANSPORT_ERROR; US_DEBUGP("%s, lun = %d\n", __func__, lun); @@ -372,10 +393,14 @@ static int rts51x_read_status(struct us_data *us, cmnd[1] = 0x09; retval = rts51x_bulk_transport(us, lun, cmnd, 12, - status, len, DMA_FROM_DEVICE, actlen); - if (retval != USB_STOR_TRANSPORT_GOOD) + buf, len, DMA_FROM_DEVICE, actlen); + if (retval != USB_STOR_TRANSPORT_GOOD) { + kfree(buf); return -EIO; + } + memcpy(status, buf, len); + kfree(buf); return 0; } -- cgit v0.10.2 From 55a46269ccedec140e2487a2344e8a247d48b385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sroczy=C5=84ski?= Date: Thu, 25 Aug 2011 19:52:27 +0200 Subject: USB: pl2303: remove unreachable code Signed-off-by: Michal Sroczynski Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1d33260..0ae840f 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -360,9 +360,6 @@ static void pl2303_set_termios(struct tty_struct *tty, tmp >>= 2; buf[1] <<= 1; } - if (tmp > 256) { - tmp %= 256; - } buf[0] = tmp; } } -- cgit v0.10.2 From 0d2f4758530d1598087d1d2f3e75b4f7d17fc6c7 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 19 Aug 2011 19:59:12 +0200 Subject: usb: dwc3: gadget: fixing dequeue of TRBs A TRB which is dequeued seems to have its HWO bits set to 1. Therefore we ignore it if we dequeue it after the command is completed. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index cebaef7..56f0e46 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1309,11 +1309,17 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, dwc3_trb_to_nat(req->trb, &trb); - if (trb.hwo) { + if (trb.hwo && status != -ESHUTDOWN) + /* + * We continue despite the error. There is not much we + * can do. If we don't clean in up we loop for ever. If + * we skip the TRB than it gets overwritten reused after + * a while since we use them in a ring buffer. a BUG() + * would help. Lets hope that if this occures, someone + * fixes the root cause instead of looking away :) + */ dev_err(dwc->dev, "%s's TRB (%p) still owned by HW\n", dep->name, req->trb); - continue; - } count = trb.length; if (dep->direction) { -- cgit v0.10.2 From a1ae9be5fc137d7e911a77ef408273ff55a53a39 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Aug 2011 17:42:18 +0200 Subject: usb: dwc3: gadget: reset resource index to zero If we collected two requests together (i.e. only the last of them has LST=1) then we only have to stop transfer once: The clean-up code will cleanup everything until first TRB with the LST bit set. After XferComplete this index should be no longer valid since there is no transfer pending. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 56f0e46..a324957 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1366,8 +1366,10 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, status = -ECONNRESET; clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); - if (clean_busy) + if (clean_busy) { dep->flags &= ~DWC3_EP_BUSY; + dep->res_trans_idx = 0; + } } static void dwc3_gadget_start_isoc(struct dwc3 *dwc, @@ -1537,6 +1539,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) memset(¶ms, 0, sizeof(params)); ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); WARN_ON_ONCE(ret); + dep->res_trans_idx = 0; } } -- cgit v0.10.2 From 5a18999ebd880f6fc4264aa5687daad4c4013bce Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Aug 2011 17:42:19 +0200 Subject: usb: dwc3: gadget: use TRB type 6 for ISOC transfers Type 6 should be used for the first transfer during an interval. This is also what the reference driver is using. Type 7 seems to be for following or additional transfers within the same interval. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a324957..ebe6bbc 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -632,7 +632,7 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, break; case USB_ENDPOINT_XFER_ISOC: - trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS; + trb.trbctl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; /* IOC every DWC3_TRB_NUM / 4 so we can refill */ if (!(cur_slot % (DWC3_TRB_NUM / 4))) -- cgit v0.10.2 From 0156cf8603b86c949d02aa315684b4c677e66638 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 22 Aug 2011 18:29:13 +0200 Subject: usb: dwc3: gaget: clear DWC3_EP_WILL_SHUTDOWN bit Without this patch we won't clear that bit and instead will clear all other bits on our endpoint flag. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index ebe6bbc..2ee6714 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1423,7 +1423,7 @@ static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep, req = next_request(&dep->req_queued); dwc3_gadget_giveback(dep, req, -ESHUTDOWN); } - dep->flags &= DWC3_EP_WILL_SHUTDOWN; + dep->flags &= ~DWC3_EP_WILL_SHUTDOWN; } } -- cgit v0.10.2 From b4f28a98ea64b6cb009bb03810e8a8dd08767f46 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 26 Aug 2011 12:21:13 +0300 Subject: usb: dwc3: debugfs: add a kfree() on error to dwc3_testmode_open() We may as well fix this potential leak so we don't have to listen to the static checkers complain. Signed-off-by: Dan Carpenter Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 432df53..dd861c4 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -445,8 +445,10 @@ static int dwc3_testmode_open(struct inode *inode, struct file *file) if (!buf0) return -ENOMEM; buf1 = kmalloc(BUF_SIZE, GFP_KERNEL); - if (!buf1) + if (!buf1) { + kfree(buf0); return -ENOMEM; + } memset(buf0, 0xaa, BUF_SIZE); memset(buf1, 0x33, BUF_SIZE); -- cgit v0.10.2 From 91db07dcbf379c7633011e841fe047d8130a1416 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 27 Aug 2011 01:40:52 +0300 Subject: usb: dwc3: core: add missing @ for kerneldoc trivial patch, no functional changes Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 83b2960..17e9661 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -503,13 +503,13 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) /** * struct dwc3 - representation of our controller - * ctrl_req: usb control request which is used for ep0 - * ep0_trb: trb which is used for the ctrl_req - * setup_buf: used while precessing STD USB requests - * ctrl_req_addr: dma address of ctrl_req - * ep0_trb: dma address of ep0_trb - * ep0_usb_req: dummy req used while handling STD USB requests - * setup_buf_addr: dma address of setup_buf + * @ctrl_req: usb control request which is used for ep0 + * @ep0_trb: trb which is used for the ctrl_req + * @setup_buf: used while precessing STD USB requests + * @ctrl_req_addr: dma address of ctrl_req + * @ep0_trb: dma address of ep0_trb + * @ep0_usb_req: dummy req used while handling STD USB requests + * @setup_buf_addr: dma address of setup_buf * @lock: for synchronizing * @dev: pointer to our struct device * @event_buffer_list: a list of event buffers -- cgit v0.10.2 From c611ccb48ac9b95e35741b43d018a2f6ed74c0e6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 27 Aug 2011 02:30:33 +0300 Subject: usb: dwc3: ep0: fix 'transfered' typo trivial patch. No functional changes. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 4698fe0..a6fc5c3 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -639,7 +639,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, struct usb_request *ur; struct dwc3_trb trb; struct dwc3_ep *dep; - u32 transfered; + u32 transferred; u8 epnum; epnum = event->endpoint_number; @@ -655,8 +655,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, dwc3_trb_to_nat(dwc->ep0_trb, &trb); - transfered = ur->length - trb.length; - ur->actual += transfered; + transferred = ur->length - trb.length; + ur->actual += transferred; if ((epnum & 1) && ur->actual < ur->length) { /* for some reason we did not get everything out */ -- cgit v0.10.2 From f198ead21bcb7b03d7bb2cba7ba0f5ad615a3862 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 27 Aug 2011 15:10:09 +0300 Subject: usb: dwc3: gadget: set request dma to invalid when unmapping if we don't set DMA address to invalid when unmapping, we might fall in a situation where request buffer can't be mapped to DMA again. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2ee6714..4d232c3 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -83,6 +83,7 @@ void dwc3_unmap_buffer_from_dma(struct dwc3_request *req) req->request.length, req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE); req->mapped = 0; + req->request.dma = DMA_ADDR_INVALID; } else { dma_sync_single_for_cpu(dwc->dev, req->request.dma, req->request.length, req->direction -- cgit v0.10.2 From 164f6e141ed214bda30fb219d41ec3254bd90886 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 27 Aug 2011 20:29:58 +0300 Subject: usb: dwc3: gadget: improve command completion debug message the previous message had too little meaning. Make it more human readable and use the macro we already had for extracting the command completion status out of DEPCMDn register. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4d232c3..56ccd97 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -169,8 +169,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, do { reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep)); if (!(reg & DWC3_DEPCMD_CMDACT)) { - dev_vdbg(dwc->dev, "CMD Compl Status %d DEPCMD %04x\n", - ((reg & 0xf000) >> 12), reg); + dev_vdbg(dwc->dev, "Command Complete --> %d\n", + DWC3_DEPCMD_STATUS(reg)); return 0; } -- cgit v0.10.2 From dc137f01aca23ceb196945130a960e2a01b95890 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 27 Aug 2011 22:04:32 +0300 Subject: usb: dwc3: core: add defines for XferNotReady event on Control EPs The status field of the Transfer Not Read event is different on Control Endpoints. On this patch we are just adding the defines to be used on a later patch which will re-work the control endpoint handling. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 17e9661..08d8ff6 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -634,6 +634,12 @@ struct dwc3_event_depevt { #define DEPEVT_STATUS_SHORT (1 << 1) #define DEPEVT_STATUS_IOC (1 << 2) #define DEPEVT_STATUS_LST (1 << 3) + +/* Control-only Status */ +#define DEPEVT_STATUS_CONTROL_SETUP 0 +#define DEPEVT_STATUS_CONTROL_DATA 1 +#define DEPEVT_STATUS_CONTROL_STATUS 2 + u32 parameters:16; } __packed; -- cgit v0.10.2 From 5812b1c236774ea580b6af39411eb4f7297d7623 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 27 Aug 2011 22:07:53 +0300 Subject: usb: dwc3: add a bounce buffer for control endpoints This core cannot handle OUT transfers which aren't aligned to wMaxPacketSize, but that can happen at least on control endpoint with the USB Audio Class. This patch adds a bounce buffer to be used on the case of a non-aligned ep0out request is queued. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 08d8ff6..8688b5a 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -505,11 +505,13 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) * struct dwc3 - representation of our controller * @ctrl_req: usb control request which is used for ep0 * @ep0_trb: trb which is used for the ctrl_req + * @ep0_bounce: bounce buffer for ep0 * @setup_buf: used while precessing STD USB requests * @ctrl_req_addr: dma address of ctrl_req * @ep0_trb: dma address of ep0_trb * @ep0_usb_req: dummy req used while handling STD USB requests * @setup_buf_addr: dma address of setup_buf + * @ep0_bounce_addr: dma address of ep0_bounce * @lock: for synchronizing * @dev: pointer to our struct device * @event_buffer_list: a list of event buffers @@ -522,6 +524,7 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) * @is_selfpowered: true when we are selfpowered * @three_stage_setup: set if we perform a three phase setup * @ep0_status_pending: ep0 status response without a req is pending + * @ep0_bounced: true when we used bounce buffer * @ep0state: state of endpoint zero * @link_state: link state * @speed: device speed (super, high, full, low) @@ -531,10 +534,12 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) struct dwc3 { struct usb_ctrlrequest *ctrl_req; struct dwc3_trb_hw *ep0_trb; + void *ep0_bounce; u8 *setup_buf; dma_addr_t ctrl_req_addr; dma_addr_t ep0_trb_addr; dma_addr_t setup_buf_addr; + dma_addr_t ep0_bounce_addr; struct usb_request ep0_usb_req; /* device lock */ spinlock_t lock; @@ -564,6 +569,7 @@ struct dwc3 { unsigned is_selfpowered:1; unsigned three_stage_setup:1; unsigned ep0_status_pending:1; + unsigned ep0_bounced:1; enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 56ccd97..569473b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1957,6 +1957,14 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) goto err2; } + dwc->ep0_bounce = dma_alloc_coherent(dwc->dev, + 512, &dwc->ep0_bounce_addr, GFP_KERNEL); + if (!dwc->ep0_bounce) { + dev_err(dwc->dev, "failed to allocate ep0 bounce buffer\n"); + ret = -ENOMEM; + goto err3; + } + dev_set_name(&dwc->gadget.dev, "gadget"); dwc->gadget.ops = &dwc3_gadget_ops; @@ -1978,7 +1986,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) ret = dwc3_gadget_init_endpoints(dwc); if (ret) - goto err3; + goto err4; irq = platform_get_irq(to_platform_device(dwc->dev), 0); @@ -1987,7 +1995,7 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) if (ret) { dev_err(dwc->dev, "failed to request irq #%d --> %d\n", irq, ret); - goto err4; + goto err5; } /* Enable all but Start and End of Frame IRQs */ @@ -2006,27 +2014,31 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) if (ret) { dev_err(dwc->dev, "failed to register gadget device\n"); put_device(&dwc->gadget.dev); - goto err5; + goto err6; } ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget); if (ret) { dev_err(dwc->dev, "failed to register udc\n"); - goto err6; + goto err7; } return 0; -err6: +err7: device_unregister(&dwc->gadget.dev); -err5: +err6: dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00); free_irq(irq, dwc); -err4: +err5: dwc3_gadget_free_endpoints(dwc); +err4: + dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, + dwc->ep0_bounce_addr); + err3: dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, dwc->setup_buf, dwc->setup_buf_addr); @@ -2059,6 +2071,9 @@ void dwc3_gadget_exit(struct dwc3 *dwc) dwc3_gadget_free_endpoints(dwc); + dma_free_coherent(dwc->dev, 512, dwc->ep0_bounce, + dwc->ep0_bounce_addr); + dma_free_coherent(dwc->dev, sizeof(*dwc->setup_buf) * 2, dwc->setup_buf, dwc->setup_buf_addr); -- cgit v0.10.2 From a6829706ce0bae7e4623ea987a639d91a721eee2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 27 Aug 2011 22:18:09 +0300 Subject: usb: dwc3: ep0: add handling for unaligned OUT transfers In case we have transfers which aren't aligned to wMaxPacketSize, we need to be careful with how we start the transfer with the HW. OUT transfers _must_ be aligned with wMaxPacketSize and in order to guarantee that, we use a bounce buffer. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index a6fc5c3..f1e0a5e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -185,10 +185,29 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, req->epnum = dep->number; list_add_tail(&req->list, &dep->request_list); - dwc3_map_buffer_to_dma(req); + if (req->request.length == 0) { + ret = dwc3_ep0_start_trans(dwc, dep->number, + dwc->ctrl_req_addr, 0); + } else if ((req->request.length % dep->endpoint.maxpacket) + && (dep->number == 0)) { + dwc->ep0_bounced = true; + + WARN_ON(req->request.length > dep->endpoint.maxpacket); + + /* + * REVISIT in case request length is bigger than EP0 + * wMaxPacketSize, we will need two chained TRBs to handle + * the transfer. + */ + ret = dwc3_ep0_start_trans(dwc, dep->number, + dwc->ep0_bounce_addr, dep->endpoint.maxpacket); + } else { + dwc3_map_buffer_to_dma(req); + + ret = dwc3_ep0_start_trans(dwc, dep->number, + req->request.dma, req->request.length); + } - ret = dwc3_ep0_start_trans(dwc, dep->number, req->request.dma, - req->request.length); if (ret < 0) { list_del(&req->list); dwc3_unmap_buffer_from_dma(req); @@ -655,8 +674,16 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, dwc3_trb_to_nat(dwc->ep0_trb, &trb); - transferred = ur->length - trb.length; - ur->actual += transferred; + if (dwc->ep0_bounced) { + struct dwc3_ep *ep0 = dwc->eps[0]; + + transferred = min(ur->length, dep->endpoint.maxpacket - trb.length); + memcpy(ur->buf, dwc->ep0_bounce, transferred); + dwc->ep0_bounced = false; + } else { + transferred = ur->length - trb.length; + ur->actual += transferred; + } if ((epnum & 1) && ur->actual < ur->length) { /* for some reason we did not get everything out */ -- cgit v0.10.2 From 984f66a6f9b9c02d6cb077ac49ec9fe5445fb1ee Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 27 Aug 2011 22:26:00 +0300 Subject: usb: dwc3: core: add flag for EP0 direction Add a flag to keep track of ep0 direction. This flag will be used on a following patch. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 8688b5a..e743969 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -350,6 +350,9 @@ struct dwc3_ep { #define DWC3_EP_PENDING_REQUEST (1 << 5) #define DWC3_EP_WILL_SHUTDOWN (1 << 6) + /* This last one is specific to EP0 */ +#define DWC3_EP0_DIR_IN (1 << 31) + unsigned current_trb; u8 number; -- cgit v0.10.2 From c7fcdeb2627c46b7ec3f0bcb2054b10405f9a70e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 27 Aug 2011 22:28:36 +0300 Subject: usb: dwc3: ep0: simplify EP0 state machine The DesignWare USB3 core tells us which phase of a control transfer should be started, it also tells us which physical endpoint needs that transfer. With these two informations, we have all we need to simply EP0 handling quite a lot and get rid rid of the SW state machine tracking ep0 states. For achieving this perfectly, we needed to add support for situations where we get XferNotReady while endpoint is still busy and XferNotReady while gadget driver still hasn't queued a request. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e743969..01892b1 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -373,15 +373,9 @@ enum dwc3_phy { enum dwc3_ep0_state { EP0_UNCONNECTED = 0, - EP0_IDLE, - EP0_IN_DATA_PHASE, - EP0_OUT_DATA_PHASE, - EP0_IN_WAIT_GADGET, - EP0_OUT_WAIT_GADGET, - EP0_IN_WAIT_NRDY, - EP0_OUT_WAIT_NRDY, - EP0_IN_STATUS_PHASE, - EP0_OUT_STATUS_PHASE, + EP0_SETUP_PHASE, + EP0_DATA_PHASE, + EP0_STATUS_PHASE, EP0_STALL, }; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index f1e0a5e..82a40a1 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -62,24 +62,12 @@ static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) switch (state) { case EP0_UNCONNECTED: return "Unconnected"; - case EP0_IDLE: - return "Idle"; - case EP0_IN_DATA_PHASE: - return "IN Data Phase"; - case EP0_OUT_DATA_PHASE: - return "OUT Data Phase"; - case EP0_IN_WAIT_GADGET: - return "IN Wait Gadget"; - case EP0_OUT_WAIT_GADGET: - return "OUT Wait Gadget"; - case EP0_IN_WAIT_NRDY: - return "IN Wait NRDY"; - case EP0_OUT_WAIT_NRDY: - return "OUT Wait NRDY"; - case EP0_IN_STATUS_PHASE: - return "IN Status Phase"; - case EP0_OUT_STATUS_PHASE: - return "OUT Status Phase"; + case EP0_SETUP_PHASE: + return "Setup Phase"; + case EP0_DATA_PHASE: + return "Data Phase"; + case EP0_STATUS_PHASE: + return "Status Phase"; case EP0_STALL: return "Stall"; default: @@ -88,7 +76,7 @@ static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) } static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, - u32 len) + u32 len, u32 type) { struct dwc3_gadget_ep_cmd_params params; struct dwc3_trb_hw *trb_hw; @@ -98,52 +86,15 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, int ret; dep = dwc->eps[epnum]; + if (dep->flags & DWC3_EP_BUSY) { + dev_vdbg(dwc->dev, "%s: still busy\n", dep->name); + return 0; + } trb_hw = dwc->ep0_trb; memset(&trb, 0, sizeof(trb)); - switch (dwc->ep0state) { - case EP0_IDLE: - trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP; - break; - - case EP0_IN_WAIT_NRDY: - case EP0_OUT_WAIT_NRDY: - case EP0_IN_STATUS_PHASE: - case EP0_OUT_STATUS_PHASE: - if (dwc->three_stage_setup) - trb.trbctl = DWC3_TRBCTL_CONTROL_STATUS3; - else - trb.trbctl = DWC3_TRBCTL_CONTROL_STATUS2; - - if (dwc->ep0state == EP0_IN_WAIT_NRDY) - dwc->ep0state = EP0_IN_STATUS_PHASE; - else if (dwc->ep0state == EP0_OUT_WAIT_NRDY) - dwc->ep0state = EP0_OUT_STATUS_PHASE; - break; - - case EP0_IN_WAIT_GADGET: - dwc->ep0state = EP0_IN_WAIT_NRDY; - return 0; - break; - - case EP0_OUT_WAIT_GADGET: - dwc->ep0state = EP0_OUT_WAIT_NRDY; - return 0; - - break; - - case EP0_IN_DATA_PHASE: - case EP0_OUT_DATA_PHASE: - trb.trbctl = DWC3_TRBCTL_CONTROL_DATA; - break; - - default: - dev_err(dwc->dev, "%s() can't in state %d\n", __func__, - dwc->ep0state); - return -EINVAL; - } - + trb.trbctl = type; trb.bplh = buf_dma; trb.length = len; @@ -167,6 +118,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, return ret; } + dep->flags |= DWC3_EP_BUSY; dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, dep->number); @@ -176,41 +128,46 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep, struct dwc3_request *req) { - struct dwc3 *dwc = dep->dwc; - int ret; + int ret = 0; req->request.actual = 0; req->request.status = -EINPROGRESS; - req->direction = dep->direction; req->epnum = dep->number; list_add_tail(&req->list, &dep->request_list); - if (req->request.length == 0) { - ret = dwc3_ep0_start_trans(dwc, dep->number, - dwc->ctrl_req_addr, 0); - } else if ((req->request.length % dep->endpoint.maxpacket) - && (dep->number == 0)) { - dwc->ep0_bounced = true; - - WARN_ON(req->request.length > dep->endpoint.maxpacket); - /* - * REVISIT in case request length is bigger than EP0 - * wMaxPacketSize, we will need two chained TRBs to handle - * the transfer. - */ - ret = dwc3_ep0_start_trans(dwc, dep->number, - dwc->ep0_bounce_addr, dep->endpoint.maxpacket); - } else { - dwc3_map_buffer_to_dma(req); - - ret = dwc3_ep0_start_trans(dwc, dep->number, - req->request.dma, req->request.length); - } + /* + * Gadget driver might not be quick enough to queue a request + * before we get a Transfer Not Ready event on this endpoint. + * + * In that case, we will set DWC3_EP_PENDING_REQUEST. When that + * flag is set, it's telling us that as soon as Gadget queues the + * required request, we should kick the transfer here because the + * IRQ we were waiting for is long gone. + */ + if (dep->flags & DWC3_EP_PENDING_REQUEST) { + struct dwc3 *dwc = dep->dwc; + unsigned direction; + u32 type; + + direction = !!(dep->flags & DWC3_EP0_DIR_IN); + + if (dwc->ep0state == EP0_STATUS_PHASE) { + type = dwc->three_stage_setup + ? DWC3_TRBCTL_CONTROL_STATUS3 + : DWC3_TRBCTL_CONTROL_STATUS2; + } else if (dwc->ep0state == EP0_DATA_PHASE) { + type = DWC3_TRBCTL_CONTROL_DATA; + } else { + /* should never happen */ + WARN_ON(1); + return 0; + } - if (ret < 0) { - list_del(&req->list); - dwc3_unmap_buffer_from_dma(req); + ret = dwc3_ep0_start_trans(dwc, direction, + req->request.dma, req->request.length, type); + dep->flags &= ~(DWC3_EP_PENDING_REQUEST | + DWC3_EP0_DIR_IN); } return ret; @@ -227,24 +184,6 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request, int ret; - switch (dwc->ep0state) { - case EP0_IN_DATA_PHASE: - case EP0_IN_WAIT_GADGET: - case EP0_IN_WAIT_NRDY: - case EP0_IN_STATUS_PHASE: - dep = dwc->eps[1]; - break; - - case EP0_OUT_DATA_PHASE: - case EP0_OUT_WAIT_GADGET: - case EP0_OUT_WAIT_NRDY: - case EP0_OUT_STATUS_PHASE: - dep = dwc->eps[0]; - break; - default: - return -EINVAL; - } - spin_lock_irqsave(&dwc->lock, flags); if (!dep->desc) { dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n", @@ -278,50 +217,19 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) /* stall is always issued on EP0 */ __dwc3_gadget_ep_set_halt(dwc->eps[0], 1); dwc->eps[0]->flags &= ~DWC3_EP_STALL; - dwc->ep0state = EP0_IDLE; + dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } void dwc3_ep0_out_start(struct dwc3 *dwc) { - struct dwc3_ep *dep; int ret; - dep = dwc->eps[0]; - - ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8); + ret = dwc3_ep0_start_trans(dwc, 0, dwc->ctrl_req_addr, 8, + DWC3_TRBCTL_CONTROL_SETUP); WARN_ON(ret < 0); } -/* - * Send a zero length packet for the status phase of the control transfer - */ -static void dwc3_ep0_do_setup_status(struct dwc3 *dwc, - const struct dwc3_event_depevt *event) -{ - struct dwc3_ep *dep; - int ret; - u32 epnum; - - epnum = event->endpoint_number; - dep = dwc->eps[epnum]; - - if (epnum) - dwc->ep0state = EP0_IN_STATUS_PHASE; - else - dwc->ep0state = EP0_OUT_STATUS_PHASE; - - /* - * Not sure Why I need a buffer for a zero transfer. Maybe the - * HW reacts strange on a NULL pointer - */ - ret = dwc3_ep0_start_trans(dwc, epnum, dwc->ctrl_req_addr, 0); - if (ret) { - dev_dbg(dwc->dev, "failed to start transfer, stalling\n"); - dwc3_ep0_stall_and_restart(dwc); - } -} - static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) { struct dwc3_ep *dep; @@ -341,15 +249,9 @@ static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) static void dwc3_ep0_send_status_response(struct dwc3 *dwc) { - u32 epnum; - - if (dwc->ep0state == EP0_IN_DATA_PHASE) - epnum = 1; - else - epnum = 0; - - dwc3_ep0_start_trans(dwc, epnum, dwc->ctrl_req_addr, - dwc->ep0_usb_req.length); + dwc3_ep0_start_trans(dwc, 1, dwc->ctrl_req_addr, + dwc->ep0_usb_req.length, + DWC3_TRBCTL_CONTROL_DATA); dwc->ep0_status_pending = 1; } @@ -505,8 +407,6 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, return -EINVAL; }; - dwc->ep0state = EP0_IN_WAIT_NRDY; - return 0; } @@ -541,7 +441,7 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) ret = -EINVAL; break; } - dwc->ep0state = EP0_IN_WAIT_NRDY; + return ret; } @@ -628,16 +528,10 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, goto err; len = le16_to_cpu(ctrl->wLength); - if (!len) { - dwc->ep0state = EP0_IN_WAIT_GADGET; + if (!len) dwc->three_stage_setup = 0; - } else { + else dwc->three_stage_setup = 1; - if (ctrl->bRequestType & USB_DIR_IN) - dwc->ep0state = EP0_IN_DATA_PHASE; - else - dwc->ep0state = EP0_OUT_DATA_PHASE; - } if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) ret = dwc3_ep0_std_request(dwc, ctrl); @@ -665,7 +559,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, dep = dwc->eps[epnum]; if (!dwc->ep0_status_pending) { - r = next_request(&dep->request_list); + r = next_request(&dwc->eps[0]->request_list); ur = &r->request; } else { ur = &dwc->ep0_usb_req; @@ -677,7 +571,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, if (dwc->ep0_bounced) { struct dwc3_ep *ep0 = dwc->eps[0]; - transferred = min(ur->length, dep->endpoint.maxpacket - trb.length); + transferred = min_t(u32, ur->length, + ep0->endpoint.maxpacket - trb.length); memcpy(ur->buf, dwc->ep0_bounce, transferred); dwc->ep0_bounced = false; } else { @@ -695,12 +590,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, * handle the case where we have to send a zero packet. This * seems to be case when req.length > maxpacket. Could it be? */ - /* The transfer is complete, wait for HOST */ - if (epnum & 1) - dwc->ep0state = EP0_IN_WAIT_NRDY; - else - dwc->ep0state = EP0_OUT_WAIT_NRDY; - if (r) dwc3_gadget_giveback(dep, r, 0); } @@ -711,10 +600,8 @@ static void dwc3_ep0_complete_req(struct dwc3 *dwc, { struct dwc3_request *r; struct dwc3_ep *dep; - u8 epnum; - epnum = event->endpoint_number; - dep = dwc->eps[epnum]; + dep = dwc->eps[0]; if (!list_empty(&dep->request_list)) { r = next_request(&dep->request_list); @@ -722,62 +609,130 @@ static void dwc3_ep0_complete_req(struct dwc3 *dwc, dwc3_gadget_giveback(dep, r, 0); } - dwc->ep0state = EP0_IDLE; + dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } static void dwc3_ep0_xfer_complete(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { + struct dwc3_ep *dep = dwc->eps[event->endpoint_number]; + + dep->flags &= ~DWC3_EP_BUSY; + switch (dwc->ep0state) { - case EP0_IDLE: + case EP0_SETUP_PHASE: + dev_vdbg(dwc->dev, "Inspecting Setup Bytes\n"); dwc3_ep0_inspect_setup(dwc, event); break; - case EP0_IN_DATA_PHASE: - case EP0_OUT_DATA_PHASE: + case EP0_DATA_PHASE: + dev_vdbg(dwc->dev, "Data Phase\n"); dwc3_ep0_complete_data(dwc, event); break; - case EP0_IN_STATUS_PHASE: - case EP0_OUT_STATUS_PHASE: + case EP0_STATUS_PHASE: + dev_vdbg(dwc->dev, "Status Phase\n"); dwc3_ep0_complete_req(dwc, event); break; + default: + WARN(true, "UNKNOWN ep0state %d\n", dwc->ep0state); + } +} - case EP0_IN_WAIT_NRDY: - case EP0_OUT_WAIT_NRDY: - case EP0_IN_WAIT_GADGET: - case EP0_OUT_WAIT_GADGET: - case EP0_UNCONNECTED: - case EP0_STALL: - break; +static void dwc3_ep0_do_control_setup(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + dwc->ep0state = EP0_SETUP_PHASE; + dwc3_ep0_out_start(dwc); +} + +static void dwc3_ep0_do_control_data(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + struct dwc3_ep *dep; + struct dwc3_request *req; + int ret; + + dep = dwc->eps[0]; + dwc->ep0state = EP0_DATA_PHASE; + + if (list_empty(&dep->request_list)) { + dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n"); + dep->flags |= DWC3_EP_PENDING_REQUEST; + + if (event->endpoint_number) + dep->flags |= DWC3_EP0_DIR_IN; + return; } + + req = next_request(&dep->request_list); + req->direction = !!event->endpoint_number; + + dwc->ep0state = EP0_DATA_PHASE; + if (req->request.length == 0) { + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + dwc->ctrl_req_addr, 0, + DWC3_TRBCTL_CONTROL_DATA); + } else if ((req->request.length % dep->endpoint.maxpacket) + && (event->endpoint_number == 0)) { + dwc3_map_buffer_to_dma(req); + + WARN_ON(req->request.length > dep->endpoint.maxpacket); + + dwc->ep0_bounced = true; + + /* + * REVISIT in case request length is bigger than EP0 + * wMaxPacketSize, we will need two chained TRBs to handle + * the transfer. + */ + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + dwc->ep0_bounce_addr, dep->endpoint.maxpacket, + DWC3_TRBCTL_CONTROL_DATA); + } else { + dwc3_map_buffer_to_dma(req); + + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + req->request.dma, req->request.length, + DWC3_TRBCTL_CONTROL_DATA); + } + + WARN_ON(ret < 0); } -static void dwc3_ep0_xfernotready(struct dwc3 *dwc, +static void dwc3_ep0_do_control_status(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { - switch (dwc->ep0state) { - case EP0_IN_WAIT_GADGET: - dwc->ep0state = EP0_IN_WAIT_NRDY; - break; - case EP0_OUT_WAIT_GADGET: - dwc->ep0state = EP0_OUT_WAIT_NRDY; - break; + u32 type; + int ret; - case EP0_IN_WAIT_NRDY: - case EP0_OUT_WAIT_NRDY: - dwc3_ep0_do_setup_status(dwc, event); - break; + dwc->ep0state = EP0_STATUS_PHASE; - case EP0_IDLE: - case EP0_IN_STATUS_PHASE: - case EP0_OUT_STATUS_PHASE: - case EP0_IN_DATA_PHASE: - case EP0_OUT_DATA_PHASE: - case EP0_UNCONNECTED: - case EP0_STALL: - break; + type = dwc->three_stage_setup ? DWC3_TRBCTL_CONTROL_STATUS3 + : DWC3_TRBCTL_CONTROL_STATUS2; + + ret = dwc3_ep0_start_trans(dwc, event->endpoint_number, + dwc->ctrl_req_addr, 0, type); + + WARN_ON(ret < 0); +} + +static void dwc3_ep0_xfernotready(struct dwc3 *dwc, + const struct dwc3_event_depevt *event) +{ + switch (event->status) { + case DEPEVT_STATUS_CONTROL_SETUP: + dev_vdbg(dwc->dev, "Control Setup\n"); + dwc3_ep0_do_control_setup(dwc, event); + break; + case DEPEVT_STATUS_CONTROL_DATA: + dev_vdbg(dwc->dev, "Control Data\n"); + dwc3_ep0_do_control_data(dwc, event); + break; + case DEPEVT_STATUS_CONTROL_STATUS: + dev_vdbg(dwc->dev, "Control Status\n"); + dwc3_ep0_do_control_status(dwc, event); } } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 569473b..b4f1aef 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1178,7 +1178,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, } /* begin to receive SETUP packets */ - dwc->ep0state = EP0_IDLE; + dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); spin_unlock_irqrestore(&dwc->lock, flags); @@ -1728,7 +1728,6 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) memset(¶ms, 0x00, sizeof(params)); - dwc->ep0state = EP0_IDLE; reg = dwc3_readl(dwc->regs, DWC3_DSTS); speed = reg & DWC3_DSTS_CONNECTSPD; dwc->speed = speed; -- cgit v0.10.2 From e7225315a89731d0f986c20165184f1dc9a9bf2b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Aug 2011 13:56:35 +0200 Subject: usb: dwc3: debugfs: remove test mode interface There are some issues around for enabling/disabling this mode and handling it. It does not work perfectly (yet). However we have a few gadgets tested successfuly so far. That means we are quite confident that we won't need this in near future. So I'm for removing it and bringing a working version back once there is a need for it. Thanks to Dan Carpenter who spotted the wrong memory handling here. [ balbi@ti.com : made it actually apply ] Cc: Dan Carpenter Cc: wharms@bfs.de Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index dd861c4..20d329f 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -406,93 +406,6 @@ static const struct file_operations dwc3_regdump_fops = { .release = single_release, }; - -static int dwc3_send_testmode_cmd(struct dwc3 *dwc, int mode) -{ - u32 timeout = 250; - - dwc3_writel(dwc->regs, DWC3_DGCMDPAR, mode); - dwc3_writel(dwc->regs, DWC3_DGCMD, DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK | - DWC3_DEPCMD_CMDACT); - do { - u32 reg; - - reg = dwc3_readl(dwc->regs, DWC3_DGCMD); - if (!(reg & DWC3_DEPCMD_CMDACT)) - return 0; - timeout--; - if (!timeout) - return -ETIMEDOUT; - mdelay(1); - } while (1); -} - -static struct dwc3_trb_hw trb_0 __aligned(16); -static struct dwc3_trb_hw trb_1 __aligned(16); - -#define BUF_SIZE 4096 -static int dwc3_testmode_open(struct inode *inode, struct file *file) -{ - struct dwc3 *dwc = inode->i_private; - struct dwc3_gadget_ep_cmd_params par0; - struct dwc3_gadget_ep_cmd_params par1; - struct dwc3_trb trb; - int ret; - u8 *buf0; - u8 *buf1; - - buf0 = kmalloc(BUF_SIZE, GFP_KERNEL); - if (!buf0) - return -ENOMEM; - buf1 = kmalloc(BUF_SIZE, GFP_KERNEL); - if (!buf1) { - kfree(buf0); - return -ENOMEM; - } - - memset(buf0, 0xaa, BUF_SIZE); - memset(buf1, 0x33, BUF_SIZE); - - memset(&trb, 0, sizeof(trb)); - memset(&par0, 0, sizeof(par0)); - memset(&par1, 0, sizeof(par1)); - - trb.lst = 1; - trb.trbctl = DWC3_TRBCTL_NORMAL; - trb.length = BUF_SIZE; - trb.hwo = 1; - - trb.bplh = virt_to_phys(buf0); - dwc3_trb_to_hw(&trb, &trb_0); - - trb.bplh = virt_to_phys(buf1); - dwc3_trb_to_hw(&trb, &trb_1); - - par0.param0.depstrtxfer.transfer_desc_addr_high = - upper_32_bits(virt_to_phys(&trb_0)); - par0.param1.depstrtxfer.transfer_desc_addr_low = - lower_32_bits(virt_to_phys(&trb_0)); - - par1.param0.depstrtxfer.transfer_desc_addr_high = - upper_32_bits(virt_to_phys(&trb_1)); - par1.param1.depstrtxfer.transfer_desc_addr_low = - lower_32_bits(virt_to_phys(&trb_1)); - - dwc3_send_testmode_cmd(dwc, 1); - - ret = dwc3_send_gadget_ep_cmd(dwc, 0, DWC3_DEPCMD_STARTTRANSFER, &par0); - ret = dwc3_send_gadget_ep_cmd(dwc, 1, DWC3_DEPCMD_STARTTRANSFER, &par1); - - dwc3_send_testmode_cmd(dwc, 0); - return -EBUSY; -} - -static const struct file_operations dwc3_testmode_fops = { - .open = dwc3_testmode_open, - .read = seq_read, - .release = single_release, -}; - int __devinit dwc3_debugfs_init(struct dwc3 *dwc) { struct dentry *root; @@ -513,13 +426,6 @@ int __devinit dwc3_debugfs_init(struct dwc3 *dwc) ret = PTR_ERR(file); goto err1; } - file = debugfs_create_file("testmode", S_IRUGO, root, dwc, - &dwc3_testmode_fops); - if (IS_ERR(file)) { - ret = PTR_ERR(file); - goto err1; - } - return 0; err1: -- cgit v0.10.2 From 7650bd74d3441907867276dc381b2ee0dc355c65 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Aug 2011 13:56:36 +0200 Subject: usb: dwc3: core: move the core check before soft reset We read the DWC3_GSNPSID register to make sure we got the correct register offset passed. One of the recent commits moved the soft reset before this so in case of the wrong offset we end up with "reset timed out". This patch moves the "id" check before the reset again. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 443e4fb..6aa0913 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -241,6 +241,15 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) u32 reg; int ret; + reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + /* This should read as U3 followed by revision number */ + if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) { + dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); + ret = -ENODEV; + goto err0; + } + dwc->revision = reg & DWC3_GSNPSREV_MASK; + dwc3_core_soft_reset(dwc); /* issue device SoftReset too */ @@ -260,16 +269,6 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) cpu_relax(); } while (true); - reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); - /* This should read as U3 followed by revision number */ - if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) { - dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); - ret = -ENODEV; - goto err0; - } - - dwc->revision = reg & DWC3_GSNPSREV_MASK; - ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_NUM, DWC3_EVENT_BUFFERS_SIZE); if (ret) { -- cgit v0.10.2 From 624407f96f134bcd3063eb0d404fc6d41323bef8 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Aug 2011 13:56:37 +0200 Subject: usb: dwc3: gadget: rework the dequeue on RESET & DISCONNECT - since a while we are disabling an endpoint and purging every requests on RESET and DISCONNECT which leads to a warning since the endpoint was disabled twice (once by the UDC, and second time by the gadget). I think UDC should nuke all requests because all those requests become invalid. It's gadget driver's responsability, though, to disable its used endpoints. This is done by merging dwc3_stop_active_transfer() and dwc3_gadget_nuke_reqs() into dwc3_remove_requests(). - dwc3_stop_active_transfer() is now no longer called unconditionaly. This has the advantage that it is always called to disable an active transfer which means if res_trans_idx 0 than something went wrong and it is an error condition because we can't clean up the requests. - Remove the DWC3_EP_WILL_SHUTDOWN which was introduced while introducing the command complete part for dequeue. All requests on req_queued list should be removed during the dwc3_cleanup_done_reqs() callback so there is no reason to go through the list again. We consider it an error condition if requests are still on this list since we never queue TRB without LST=1 (the last requests has always LST=1, there are no requests with LST=0 behind it). [ balbi@ti.com : reworked commit log a bit, made patch apply ] Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 01892b1..6c1ff7a 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -348,7 +348,6 @@ struct dwc3_ep { #define DWC3_EP_WEDGE (1 << 2) #define DWC3_EP_BUSY (1 << 4) #define DWC3_EP_PENDING_REQUEST (1 << 5) -#define DWC3_EP_WILL_SHUTDOWN (1 << 6) /* This last one is specific to EP0 */ #define DWC3_EP0_DIR_IN (1 << 31) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b4f1aef..7c014e9 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -359,34 +359,36 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return 0; } -static void dwc3_gadget_nuke_reqs(struct dwc3_ep *dep, const int status) +static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum); +static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_request *req; + if (!list_empty(&dep->req_queued)) + dwc3_stop_active_transfer(dwc, dep->number); + while (!list_empty(&dep->request_list)) { req = next_request(&dep->request_list); - dwc3_gadget_giveback(dep, req, status); + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); } - /* nuke queued TRBs as well on command complete */ - dep->flags |= DWC3_EP_WILL_SHUTDOWN; } /** * __dwc3_gadget_ep_disable - Disables a HW endpoint * @dep: the endpoint to disable * - * Caller should take care of locking + * This function also removes requests which are currently processed ny the + * hardware and those which are not yet scheduled. + * Caller should take care of locking. */ -static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum); static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) { struct dwc3 *dwc = dep->dwc; u32 reg; dep->flags &= ~DWC3_EP_ENABLED; - dwc3_stop_active_transfer(dwc, dep->number); - dwc3_gadget_nuke_reqs(dep, -ESHUTDOWN); + dwc3_remove_requests(dwc, dep); reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); reg &= ~DWC3_DALEPENA_EP(dep->number); @@ -1416,16 +1418,6 @@ static void dwc3_process_ep_cmd_complete(struct dwc3_ep *dep, dwc3_cleanup_done_reqs(dwc, dep, &mod_ev, -ESHUTDOWN); dep->flags &= ~DWC3_EP_BUSY; /* pending requets are ignored and are queued on XferNotReady */ - - if (dep->flags & DWC3_EP_WILL_SHUTDOWN) { - while (!list_empty(&dep->req_queued)) { - struct dwc3_request *req; - - req = next_request(&dep->req_queued); - dwc3_gadget_giveback(dep, req, -ESHUTDOWN); - } - dep->flags &= ~DWC3_EP_WILL_SHUTDOWN; - } } static void dwc3_ep_cmd_compl(struct dwc3_ep *dep, @@ -1533,6 +1525,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum) dep = dwc->eps[epnum]; + WARN_ON(!dep->res_trans_idx); if (dep->res_trans_idx) { cmd = DWC3_DEPCMD_ENDTRANSFER; cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC; @@ -1555,7 +1548,7 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc) if (!(dep->flags & DWC3_EP_ENABLED)) continue; - __dwc3_gadget_ep_disable(dep); + dwc3_remove_requests(dwc, dep); } } -- cgit v0.10.2 From 61d58242f634642de42d6a4098913b7254a65053 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 29 Aug 2011 16:46:38 +0200 Subject: usb: dwc3: gadget: replace mdelay with udelay in the busy loop There are two spots where we wait until the HW finishes processing a certain command. Initially we had a few problems and we used 500ms as a limit to be on a the safe side. Paul Zimmerman mentioned this is little too much. After a debugging session, we noticed that we hardly ever go over 20us and didn't pass 30usec so far. Using mdelay() seems way overloaded. Giving the current numbers 500usec as the upper limit is more than enough. Should it ever timeout then something is definitely wrong. While here, also replace the type with u32 since long does not really fit here. Cc: Paul Zimmerman Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7c014e9..b5d95f8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -153,7 +153,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) { struct dwc3_ep *dep = dwc->eps[ep]; - unsigned long timeout = 500; + u32 timeout = 500; u32 reg; dev_vdbg(dwc->dev, "%s: cmd '%s' params %08x %08x %08x\n", @@ -175,7 +175,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, } /* - * XXX Figure out a sane timeout here. 500ms is way too much. * We can't sleep here, because it is also called from * interrupt context. */ @@ -183,7 +182,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, if (!timeout) return -ETIMEDOUT; - mdelay(1); + udelay(1); } while (1); } @@ -1066,7 +1065,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) { u32 reg; - unsigned long timeout = 500; + u32 timeout = 500; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) @@ -1085,13 +1084,10 @@ static void dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on) if (reg & DWC3_DSTS_DEVCTRLHLT) break; } - /* - * XXX reduce the 500ms delay - */ timeout--; if (!timeout) break; - mdelay(1); + udelay(1); } while (1); dev_vdbg(dwc->dev, "gadget %s data soft-%s\n", -- cgit v0.10.2 From b673cf3002bfded05d68c8bad394ee68f5e8c515 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 31 Aug 2011 11:51:43 +0300 Subject: usb: dwc3: ep0: fix Get Status handling data was prepared on setup_buf but transfer was started on ctrl_req, fix it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 82a40a1..6745d14 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -249,7 +249,7 @@ static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le) static void dwc3_ep0_send_status_response(struct dwc3 *dwc) { - dwc3_ep0_start_trans(dwc, 1, dwc->ctrl_req_addr, + dwc3_ep0_start_trans(dwc, 1, dwc->setup_buf_addr, dwc->ep0_usb_req.length, DWC3_TRBCTL_CONTROL_DATA); dwc->ep0_status_pending = 1; -- cgit v0.10.2 From 76cb323f80ac32833109e2c089842be2b99d8d2b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 30 Aug 2011 15:54:53 +0300 Subject: usb: dwc3: ep0: clear all EP0 flags when we're going to issue Set Stall command, we should clear DWC3_EP_STALL flag, but also we should clear BUSY, HALTED and all others. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 6745d14..618a29e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -216,7 +216,7 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) { /* stall is always issued on EP0 */ __dwc3_gadget_ep_set_halt(dwc->eps[0], 1); - dwc->eps[0]->flags &= ~DWC3_EP_STALL; + dwc->eps[0]->flags = DWC3_EP_ENABLED; dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } -- cgit v0.10.2 From 0b7836a9eb32f626ffd3fe3045e8c618cb8ed965 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 30 Aug 2011 15:48:08 +0300 Subject: usb: dwc3: drop EP0_STALL state Whenever we issue a Set Stall command on EP0, the state machine will be restarted and Stall is cleared automatically, when core receives the next SETUP packet. There's no need to track that EP0_STALL state. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 6c1ff7a..72388d8 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -375,7 +375,6 @@ enum dwc3_ep0_state { EP0_SETUP_PHASE, EP0_DATA_PHASE, EP0_STATUS_PHASE, - EP0_STALL, }; enum dwc3_link_state { diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 618a29e..045c278 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -68,8 +68,6 @@ static const char *dwc3_ep0_state_string(enum dwc3_ep0_state state) return "Data Phase"; case EP0_STATUS_PHASE: return "Status Phase"; - case EP0_STALL: - return "Stall"; default: return "UNKNOWN"; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b5d95f8..0c934a1 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -872,8 +872,14 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) memset(¶ms, 0x00, sizeof(params)); if (value) { - if (dep->number == 0 || dep->number == 1) - dwc->ep0state = EP0_STALL; + if (dep->number == 0 || dep->number == 1) { + /* + * Whenever EP0 is stalled, we will restart + * the state machine, thus moving back to + * Setup Phase + */ + dwc->ep0state = EP0_SETUP_PHASE; + } ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_SETSTALL, ¶ms); -- cgit v0.10.2 From b53c772d16a9751554aabb05f95cef7b0b7fa2e9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 30 Aug 2011 15:50:40 +0300 Subject: usb: dwc3: core: add ep0_next_event field this field will hold the next expected event. In certain cases, host might fall into some error condition and ask from us the wrong Control phase. On such situations, we should stall and restart. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 72388d8..2e73d33 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -370,6 +370,14 @@ enum dwc3_phy { DWC3_PHY_USB2, }; +enum dwc3_ep0_next { + DWC3_EP0_UNKNOWN = 0, + DWC3_EP0_COMPLETE, + DWC3_EP0_NRDY_SETUP, + DWC3_EP0_NRDY_DATA, + DWC3_EP0_NRDY_STATUS, +}; + enum dwc3_ep0_state { EP0_UNCONNECTED = 0, EP0_SETUP_PHASE, @@ -520,6 +528,7 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) * @three_stage_setup: set if we perform a three phase setup * @ep0_status_pending: ep0 status response without a req is pending * @ep0_bounced: true when we used bounce buffer + * @ep0_next_event: hold the next expected event * @ep0state: state of endpoint zero * @link_state: link state * @speed: device speed (super, high, full, low) @@ -566,6 +575,7 @@ struct dwc3 { unsigned ep0_status_pending:1; unsigned ep0_bounced:1; + enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; enum dwc3_device_state dev_state; -- cgit v0.10.2 From 1ddcb218b5920fb20c2b3f029f0189568c2dc6e2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 30 Aug 2011 15:52:17 +0300 Subject: usb: dwc3: use ep0_next_event field Start tracking the next expected event and act on the error conditions as suggested by databook. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 045c278..48d3770 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -120,6 +120,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, dep->res_trans_idx = dwc3_gadget_ep_get_transfer_index(dwc, dep->number); + dwc->ep0_next_event = DWC3_EP0_COMPLETE; + return 0; } @@ -250,7 +252,6 @@ static void dwc3_ep0_send_status_response(struct dwc3 *dwc) dwc3_ep0_start_trans(dwc, 1, dwc->setup_buf_addr, dwc->ep0_usb_req.length, DWC3_TRBCTL_CONTROL_DATA); - dwc->ep0_status_pending = 1; } /* @@ -295,7 +296,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl response_pkt = (__le16 *) dwc->setup_buf; *response_pkt = cpu_to_le16(usb_status); dwc->ep0_usb_req.length = sizeof(*response_pkt); - dwc3_ep0_send_status_response(dwc); + dwc->ep0_status_pending = 1; return 0; } @@ -526,10 +527,13 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, goto err; len = le16_to_cpu(ctrl->wLength); - if (!len) + if (!len) { dwc->three_stage_setup = 0; - else + dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; + } else { dwc->three_stage_setup = 1; + dwc->ep0_next_event = DWC3_EP0_NRDY_DATA; + } if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) ret = dwc3_ep0_std_request(dwc, ctrl); @@ -556,6 +560,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, epnum = event->endpoint_number; dep = dwc->eps[epnum]; + dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; + if (!dwc->ep0_status_pending) { r = next_request(&dwc->eps[0]->request_list); ur = &r->request; @@ -655,6 +661,11 @@ static void dwc3_ep0_do_control_data(struct dwc3 *dwc, dep = dwc->eps[0]; dwc->ep0state = EP0_DATA_PHASE; + if (dwc->ep0_status_pending) { + dwc3_ep0_send_status_response(dwc); + return; + } + if (list_empty(&dep->request_list)) { dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n"); dep->flags |= DWC3_EP_PENDING_REQUEST; @@ -724,12 +735,33 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, dev_vdbg(dwc->dev, "Control Setup\n"); dwc3_ep0_do_control_setup(dwc, event); break; + case DEPEVT_STATUS_CONTROL_DATA: dev_vdbg(dwc->dev, "Control Data\n"); + + if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) { + dev_vdbg(dwc->dev, "Expected %d got %d\n", + DEPEVT_STATUS_CONTROL_DATA, + event->status); + + dwc3_ep0_stall_and_restart(dwc); + return; + } + dwc3_ep0_do_control_data(dwc, event); break; + case DEPEVT_STATUS_CONTROL_STATUS: dev_vdbg(dwc->dev, "Control Status\n"); + + if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) { + dev_vdbg(dwc->dev, "Expected %d got %d\n", + DEPEVT_STATUS_CONTROL_STATUS, + event->status); + + dwc3_ep0_stall_and_restart(dwc); + return; + } dwc3_ep0_do_control_status(dwc, event); } } -- cgit v0.10.2 From ccba3bca5ee34acec56dd3213d1fd8c8a6e541bc Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 1 Sep 2011 14:46:16 +0300 Subject: usb: dwc3: omap: fix dev_dbg() calls dev_dbg() macro expects a device pointer as argument, not a memory base address. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 08fffe6..424924d 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -177,50 +177,50 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) ctrl = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_CTRL); if (reg & USBOTGSS_IRQ1_DMADISABLECLR) { - dev_dbg(omap->base, "DMA Disable was Cleared\n"); + dev_dbg(omap->dev, "DMA Disable was Cleared\n"); omap->dma_status = false; } if (reg & USBOTGSS_IRQ1_OEVT) - dev_dbg(omap->base, "OTG Event\n"); + dev_dbg(omap->dev, "OTG Event\n"); if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE) { - dev_dbg(omap->base, "DRVVBUS Rise\n"); + dev_dbg(omap->dev, "DRVVBUS Rise\n"); ctrl |= USBOTGSS_UTMI_OTG_CTRL_DRVVBUS; } if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE) { - dev_dbg(omap->base, "CHRGVBUS Rise\n"); + dev_dbg(omap->dev, "CHRGVBUS Rise\n"); ctrl |= USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS; } if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE) { - dev_dbg(omap->base, "DISCHRGVBUS Rise\n"); + dev_dbg(omap->dev, "DISCHRGVBUS Rise\n"); ctrl |= USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS; } if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE) { - dev_dbg(omap->base, "IDPULLUP Rise\n"); + dev_dbg(omap->dev, "IDPULLUP Rise\n"); ctrl |= USBOTGSS_UTMI_OTG_CTRL_IDPULLUP; } if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL) { - dev_dbg(omap->base, "DRVVBUS Fall\n"); + dev_dbg(omap->dev, "DRVVBUS Fall\n"); ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_DRVVBUS; } if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL) { - dev_dbg(omap->base, "CHRGVBUS Fall\n"); + dev_dbg(omap->dev, "CHRGVBUS Fall\n"); ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS; } if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL) { - dev_dbg(omap->base, "DISCHRGVBUS Fall\n"); + dev_dbg(omap->dev, "DISCHRGVBUS Fall\n"); ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS; } if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL) { - dev_dbg(omap->base, "IDPULLUP Fall\n"); + dev_dbg(omap->dev, "IDPULLUP Fall\n"); ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_IDPULLUP; } -- cgit v0.10.2 From 324e5481401dbbadb6758c17480f50bad0996db9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 1 Sep 2011 14:52:52 +0300 Subject: usb: dwc3: omap: do not enable DMA Disable Clear IRQ Otherwise that IRQ will trigger forever. It's quite unnecessary. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 424924d..b47781c 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -313,8 +313,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) /* enable all IRQs */ dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, 0x01); - reg = (USBOTGSS_IRQ1_DMADISABLECLR | - USBOTGSS_IRQ1_OEVT | + reg = (USBOTGSS_IRQ1_OEVT | USBOTGSS_IRQ1_DRVVBUS_RISE | USBOTGSS_IRQ1_CHRGVBUS_RISE | USBOTGSS_IRQ1_DISCHRGVBUS_RISE | -- cgit v0.10.2 From df01c61e0675bddc8d0567d65f6174cdeb92c286 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 1 Sep 2011 18:22:01 +0300 Subject: usb: dwc3: omap: use the macro we already have trivial patch, no functional changes. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index b47781c..a4788ac 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -311,7 +311,8 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) } /* enable all IRQs */ - dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, 0x01); + reg = USBOTGSS_IRQO_COREIRQ_ST; + dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg); reg = (USBOTGSS_IRQ1_OEVT | USBOTGSS_IRQ1_DRVVBUS_RISE | -- cgit v0.10.2 From 29d8bc133f7bb3172201dc3f6540562cec195d13 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 1 Sep 2011 18:33:43 +0300 Subject: usb: dwc3: omap: drop DEV_PM_OPS for now We need to have actual HW in order to implement and test that part of the code anyway. Until then it's best to remove it. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index a4788ac..17d1822 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -125,46 +125,6 @@ struct dwc3_omap { u32 dma_status:1; }; -#ifdef CONFIG_PM -static int dwc3_omap_suspend(struct device *dev) -{ - struct dwc3_omap *omap = dev_get_drvdata(dev); - - memcpy_fromio(omap->context, omap->base, omap->resource_size); - - return 0; -} - -static int dwc3_omap_resume(struct device *dev) -{ - struct dwc3_omap *omap = dev_get_drvdata(dev); - - memcpy_toio(omap->base, omap->context, omap->resource_size); - - return 0; -} - -static int dwc3_omap_idle(struct device *dev) -{ - struct dwc3_omap *omap = dev_get_drvdata(dev); - u32 reg; - - /* stop DMA Engine */ - reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG); - reg &= ~(USBOTGSS_SYSCONFIG_DMADISABLE); - dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg); - - return 0; -} - -static UNIVERSAL_DEV_PM_OPS(dwc3_omap_pm_ops, dwc3_omap_suspend, - dwc3_omap_resume, dwc3_omap_idle); - -#define DEV_PM_OPS (&dwc3_omap_pm_ops) -#else -#define DEV_PM_OPS NULL -#endif - static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) { struct dwc3_omap *omap = _omap; @@ -388,7 +348,6 @@ static struct platform_driver dwc3_omap_driver = { .remove = __devexit_p(dwc3_omap_remove), .driver = { .name = "omap-dwc3", - .pm = DEV_PM_OPS, .of_match_table = of_dwc3_matach, }, }; -- cgit v0.10.2 From 9962444f592a53c08ce439b6dc362bba7ce5fd7e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 1 Sep 2011 22:26:25 +0300 Subject: usb: dwc3: omap: distinguish between SW and HW modes The OMAP wrapper allows us to either control internal OTG signals via SW or HW. Different boards might wish to use one or the other mode of operation. Let's have have that information passed via platform_data for now. After DT conversion is finished for OMAP, we can easily convert this to a DT attribute. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 17d1822..ddbf38a 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -193,6 +194,7 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) static int __devinit dwc3_omap_probe(struct platform_device *pdev) { + struct dwc3_omap_data *pdata = pdev->dev.platform_data; struct platform_device *dwc3; struct dwc3_omap *omap; struct resource *res; @@ -258,6 +260,26 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) omap->base = base; omap->dwc3 = dwc3; + reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); + + if (!pdata) { + dev_dbg(&pdev->dev, "missing platform data\n"); + } else { + switch (pdata->utmi_mode) { + case DWC3_OMAP_UTMI_MODE_SW: + reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + case DWC3_OMAP_UTMI_MODE_HW: + reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE; + break; + default: + dev_dbg(&pdev->dev, "UNKNOWN utmi mode %d\n", + pdata->utmi_mode); + } + } + + dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg); + /* check the DMA Status */ reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG); omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE); diff --git a/include/linux/platform_data/dwc3-omap.h b/include/linux/platform_data/dwc3-omap.h new file mode 100644 index 0000000..ada4012 --- /dev/null +++ b/include/linux/platform_data/dwc3-omap.h @@ -0,0 +1,47 @@ +/** + * dwc3-omap.h - OMAP Specific Glue layer, header. + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Author: Felipe Balbi + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2, as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +enum dwc3_omap_utmi_mode { + DWC3_OMAP_UTMI_MODE_UNKNOWN = 0, + DWC3_OMAP_UTMI_MODE_HW, + DWC3_OMAP_UTMI_MODE_SW, +}; + +struct dwc3_omap_data { + enum dwc3_omap_utmi_mode utmi_mode; +}; -- cgit v0.10.2 From a72e658bcdddead01b8a3580124debe60eb2aca8 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 5 Sep 2011 13:37:28 +0300 Subject: usb: dwc3: add module.h to dwc3-omap.c and core.c We need that header because of THIS_MODULE. Reported-by: Geert Uytterhoeven Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 6aa0913..64ba097 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -37,6 +37,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index ddbf38a..8a5d6ae 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -37,6 +37,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include -- cgit v0.10.2 From dd17a6b20cd998662dc869b415800a06856fcda6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 6 Sep 2011 10:57:41 +0300 Subject: usb: dwc3: omap: change IRQ name to dwc3-omap dwc3-wrapper can be used by any other wrapper, using dwc3-omap makes it clear that we're running on OMAP SoC. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 8a5d6ae..8b9a3d8 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -286,7 +286,7 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE); ret = request_irq(omap->irq, dwc3_omap_interrupt, 0, - "dwc3-wrapper", omap); + "dwc3-omap", omap); if (ret) { dev_err(&pdev->dev, "failed to request IRQ #%d --> %d\n", omap->irq, ret); -- cgit v0.10.2 From 42077b0a3328792974b232691f5d0eb9dd644768 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 6 Sep 2011 12:00:39 +0300 Subject: usb: dwc3: omap: fix IRQ handling In order to ACK the IRQ we must write back to the same register the bits we read. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 8b9a3d8..421b5db 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -131,12 +131,10 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) { struct dwc3_omap *omap = _omap; u32 reg; - u32 ctrl; spin_lock(&omap->lock); reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_1); - ctrl = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_CTRL); if (reg & USBOTGSS_IRQ1_DMADISABLECLR) { dev_dbg(omap->dev, "DMA Disable was Cleared\n"); @@ -146,47 +144,34 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) if (reg & USBOTGSS_IRQ1_OEVT) dev_dbg(omap->dev, "OTG Event\n"); - if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE) { + if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE) dev_dbg(omap->dev, "DRVVBUS Rise\n"); - ctrl |= USBOTGSS_UTMI_OTG_CTRL_DRVVBUS; - } - if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE) { + if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE) dev_dbg(omap->dev, "CHRGVBUS Rise\n"); - ctrl |= USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS; - } - if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE) { + if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE) dev_dbg(omap->dev, "DISCHRGVBUS Rise\n"); - ctrl |= USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS; - } - if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE) { + if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE) dev_dbg(omap->dev, "IDPULLUP Rise\n"); - ctrl |= USBOTGSS_UTMI_OTG_CTRL_IDPULLUP; - } - if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL) { + if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL) dev_dbg(omap->dev, "DRVVBUS Fall\n"); - ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_DRVVBUS; - } - if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL) { + if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL) dev_dbg(omap->dev, "CHRGVBUS Fall\n"); - ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS; - } - if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL) { + if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL) dev_dbg(omap->dev, "DISCHRGVBUS Fall\n"); - ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS; - } - if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL) { + if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL) dev_dbg(omap->dev, "IDPULLUP Fall\n"); - ctrl &= ~USBOTGSS_UTMI_OTG_CTRL_IDPULLUP; - } - dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_CTRL, ctrl); + dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg); + + reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_0); + dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg); spin_unlock(&omap->lock); -- cgit v0.10.2 From 78c58a53c9864447f2a46d4c06dd3c2616823ad2 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Wed, 31 Aug 2011 17:12:02 +0200 Subject: usb: dwc3: gadget: do not map/unmap ZLP transfers If the gadget drivers sends a ZLP we are trying to map this this request which does not work on all implementations. So we simply skip mapping it. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0c934a1..1de4d3f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -61,6 +61,11 @@ void dwc3_map_buffer_to_dma(struct dwc3_request *req) { struct dwc3 *dwc = req->dep->dwc; + if (req->request.length == 0) { + /* req->request.dma = dwc->setup_buf_addr; */ + return; + } + if (req->request.dma == DMA_ADDR_INVALID) { req->request.dma = dma_map_single(dwc->dev, req->request.buf, req->request.length, req->direction @@ -78,6 +83,11 @@ void dwc3_unmap_buffer_from_dma(struct dwc3_request *req) { struct dwc3 *dwc = req->dep->dwc; + if (req->request.length == 0) { + req->request.dma = DMA_ADDR_INVALID; + return; + } + if (req->mapped) { dma_unmap_single(dwc->dev, req->request.dma, req->request.length, req->direction -- cgit v0.10.2 From f4aadbe49e9b0cae2c5e889061e10b9a335fe791 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 8 Sep 2011 17:39:59 +0300 Subject: usb: dwc3: Fix definition of DWC3_GCTL_U2RSTECN that should be 1 << 16, not 16. Caused so many problems and we never caught it before. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 2e73d33..e149f57 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -147,7 +147,7 @@ /* Global Configuration Register */ #define DWC3_GCTL_PWRDNSCALE(n) (n << 19) -#define DWC3_GCTL_U2RSTECN 16 +#define DWC3_GCTL_U2RSTECN (1 << 16) #define DWC3_GCTL_RAMCLKSEL(x) ((x & DWC3_GCTL_CLK_MASK) << 6) #define DWC3_GCTL_CLK_BUS (0) #define DWC3_GCTL_CLK_PIPE (1) -- cgit v0.10.2 From f78d32e79ea3218da34e78ccca4eb4de14f2512d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 8 Sep 2011 17:41:00 +0300 Subject: usb: dwc3: define ScaleDown macro helper We must ensure that those bits aren't set as they should only be used in simulation. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e149f57..92673fd 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -160,6 +160,7 @@ #define DWC3_GCTL_PRTCAP_OTG 3 #define DWC3_GCTL_CORESOFTRESET (1 << 11) +#define DWC3_GCTL_SCALEDOWN(n) (n << 4) #define DWC3_GCTL_DISSCRAMBLE (1 << 3) /* Global USB2 PHY Configuration Register */ -- cgit v0.10.2 From 771f184ecebf34929a849eaa707aa463234254f7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 8 Sep 2011 17:42:11 +0300 Subject: usb: dwc3: gadget: fix GCTL programming ensure a few bits are cleared before enabling what we need. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1de4d3f..6c64c73 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1150,13 +1150,10 @@ static int dwc3_gadget_start(struct usb_gadget *g, reg = dwc3_readl(dwc->regs, DWC3_GCTL); - /* - * REVISIT: power down scale might be different - * depending on PHY used, need to pass that via platform_data - */ - reg |= DWC3_GCTL_PWRDNSCALE(0x61a) - | DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE); + reg &= ~DWC3_GCTL_SCALEDOWN(3); + reg &= ~DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG); reg &= ~DWC3_GCTL_DISSCRAMBLE; + reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE); /* * WORKAROUND: DWC3 revisions <1.90a have a bug -- cgit v0.10.2 From bb7ea2841e9c8669ac31e4262f585729bf779bff Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 8 Sep 2011 18:16:21 +0300 Subject: usb: dwc3: gadget: drop the useless dma_sync_single* calls if req->dma isn't DMA_ADDR_INVALID it means gadget driver mapped the request or allocated from coherent, so it's unnecessary to do anything. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 6c64c73..2ea2fc8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -71,11 +71,6 @@ void dwc3_map_buffer_to_dma(struct dwc3_request *req) req->request.length, req->direction ? DMA_TO_DEVICE : DMA_FROM_DEVICE); req->mapped = true; - } else { - dma_sync_single_for_device(dwc->dev, req->request.dma, - req->request.length, req->direction - ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - req->mapped = false; } } @@ -94,10 +89,6 @@ void dwc3_unmap_buffer_from_dma(struct dwc3_request *req) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); req->mapped = 0; req->request.dma = DMA_ADDR_INVALID; - } else { - dma_sync_single_for_cpu(dwc->dev, req->request.dma, - req->request.length, req->direction - ? DMA_TO_DEVICE : DMA_FROM_DEVICE); } } -- cgit v0.10.2 From d742220b357769fa0a764d238373b8667116cf64 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 8 Sep 2011 18:17:12 +0300 Subject: usb: dwc3: ep0: giveback requests on stall_and_restart if we don't, the list will be busy forever. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 48d3770..4cc72fd 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -214,9 +214,19 @@ out: static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) { + struct dwc3_ep *dep = dwc->eps[0]; + /* stall is always issued on EP0 */ __dwc3_gadget_ep_set_halt(dwc->eps[0], 1); dwc->eps[0]->flags = DWC3_EP_ENABLED; + + if (!list_empty(&dep->request_list)) { + struct dwc3_request *req; + + req = next_request(&dep->request_list); + dwc3_gadget_giveback(dep, req, -ECONNRESET); + } + dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } -- cgit v0.10.2 From 55f3fba6c822f05b02f06070efaadf0300b5f9f1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 8 Sep 2011 18:27:33 +0300 Subject: usb: dwc3: ep0: introduce ep0_expect_in flag This flag will tell us which direction we're expecting on the next (data or status) phase. It will help us catching errors of host going crazy and requesting data of the wrong direction. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 92673fd..07d2018 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -529,6 +529,7 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) * @three_stage_setup: set if we perform a three phase setup * @ep0_status_pending: ep0 status response without a req is pending * @ep0_bounced: true when we used bounce buffer + * @ep0_expect_in: true when we expect a DATA IN transfer * @ep0_next_event: hold the next expected event * @ep0state: state of endpoint zero * @link_state: link state @@ -575,6 +576,7 @@ struct dwc3 { unsigned three_stage_setup:1; unsigned ep0_status_pending:1; unsigned ep0_bounced:1; + unsigned ep0_expect_in:1; enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 4cc72fd..b66d969 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -545,6 +545,8 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, dwc->ep0_next_event = DWC3_EP0_NRDY_DATA; } + dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN); + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) ret = dwc3_ep0_std_request(dwc, ctrl); else @@ -758,6 +760,20 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, return; } + /* + * One of the possible error cases is when Host _does_ + * request for Data Phase, but it does so on the wrong + * direction. + * + * Here, we already know ep0_next_event is DATA (see above), + * so we only need to check for direction. + */ + if (dwc->ep0_expect_in != event->endpoint_number) { + dev_vdbg(dwc->dev, "Wrong direction for Data phase\n"); + dwc3_ep0_stall_and_restart(dwc); + return; + } + dwc3_ep0_do_control_data(dwc, event); break; -- cgit v0.10.2 From 4b5faa7aca64099d116db93d791b7a1f5c556c4a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 6 Sep 2011 10:56:51 +0300 Subject: usb: dwc3: omap: set idle and standby modes For now, let's disable IDLE and STANDBY transitions until we have a real HW to validate against. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 421b5db..72cc92b 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -76,8 +76,23 @@ /* SYSCONFIG REGISTER */ #define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16) #define USBOTGSS_SYSCONFIG_STANDBYMODE(x) ((x) << 4) + +#define USBOTGSS_STANDBYMODE_FORCE_STANDBY 0 +#define USBOTGSS_STANDBYMODE_NO_STANDBY 1 +#define USBOTGSS_STANDBYMODE_SMART_STANDBY 2 +#define USBOTGSS_STANDBYMODE_SMART_WAKEUP 3 + +#define USBOTGSS_STANDBYMODE_MASK (0x03 << 4) + #define USBOTGSS_SYSCONFIG_IDLEMODE(x) ((x) << 2) +#define USBOTGSS_IDLEMODE_FORCE_IDLE 0 +#define USBOTGSS_IDLEMODE_NO_IDLE 1 +#define USBOTGSS_IDLEMODE_SMART_IDLE 2 +#define USBOTGSS_IDLEMODE_SMART_WAKEUP 3 + +#define USBOTGSS_IDLEMODE_MASK (0x03 << 2) + /* IRQ_EOI REGISTER */ #define USBOTGSS_IRQ_EOI_LINE_NUMBER (1 << 0) @@ -270,6 +285,15 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev) reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG); omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE); + /* Set No-Idle and No-Standby */ + reg &= ~(USBOTGSS_STANDBYMODE_MASK + | USBOTGSS_IDLEMODE_MASK); + + reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY) + | USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE)); + + dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg); + ret = request_irq(omap->irq, dwc3_omap_interrupt, 0, "dwc3-omap", omap); if (ret) { -- cgit v0.10.2 From 019ac83252dc2b356cb0ca81c25a077ec90309e7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 8 Sep 2011 21:18:47 +0300 Subject: usb: dwc3: gadget: improve debug on link state change It's useful to know which states core is going through, as it might help us figure out misbehavior on specific link states. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2ea2fc8..859257a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1792,10 +1792,10 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc) static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, unsigned int evtinfo) { - dev_vdbg(dwc->dev, "%s\n", __func__); - /* The fith bit says SuperSpeed yes or no. */ dwc->link_state = evtinfo & DWC3_LINK_STATE_MASK; + + dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state); } static void dwc3_gadget_interrupt(struct dwc3 *dwc, -- cgit v0.10.2 From 3f565a363cee14d2ed281823196d455bfc7c4170 Mon Sep 17 00:00:00 2001 From: Peiyu Li Date: Wed, 17 Aug 2011 22:52:59 -0700 Subject: usb: gadget: storage: adapt logic block size to bound block devices Now the mass storage driver has fixed logic block size of 512 bytes. The mass storage gadget read/write bound devices only through VFS, so the bottom level devices actually are just RAW devices to the driver and connected PC. As a RAW, hosts can always format, read and write it right in 512 bytes logic block and don't care about the actual logic block size of devices bound to the gadget. But if we want to share the bound block device partition between target board and PC, in case the logic block size of the bound block device is 4KB, we execute the following steps: 1. connect a board with mass storage gadget to PC(the board has set one partition of on-board block device as file name of the mass storage) 2. PC format the mass storage to VFAT by default logic block size and read/write it 3. disconnect boards from PC 4. target board mount the partition as VFAT Step 4 will fail since kernel on target thinks the logic block size of the bound partition as 4KB. A typical error is "FAT: logical sector size too small for device (logical sector size = 512)" If we execute opposite steps: 1. format the partition to VFAT on target board and read/write this partition 2. connect the board to Windows PC as usb mass storage gadget, windows will think the disk is not formatted So the conclusion is that only as a gadget, the mass storage driver has no any problem. But being shared VFAT or other filesystem on PC and target board, it will fail. This patch adapts logic block size to bound block devices and fix the issue. Cc: Michal Nazarewicz Acked-by: Alan Stern Signed-off-by: Peiyu Li Signed-off-by: Xianglong Du Signed-off-by: Huayi Li Signed-off-by: Barry Song Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 4ce3dec..1cdb1fa 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -112,8 +112,7 @@ * is not loaded (an empty string as "filename" in the fsg_config * structure causes error). The CD-ROM emulation includes a single * data track and no audio tracks; hence there need be only one - * backing file per LUN. Note also that the CD-ROM block length is - * set to 512 rather than the more common value 2048. + * backing file per LUN. * * * MSF includes support for module parameters. If gadget using it @@ -771,7 +770,7 @@ static int do_read(struct fsg_common *common) curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; } - file_offset = ((loff_t) lba) << 9; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Carry out the file reads */ amount_left = common->data_size_from_cmnd; @@ -812,7 +811,8 @@ static int do_read(struct fsg_common *common) if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; bh->inreq->length = 0; bh->state = BUF_STATE_FULL; @@ -835,7 +835,7 @@ static int do_read(struct fsg_common *common) } else if (nread < amount) { LDBG(curlun, "partial file read: %d/%u\n", (int)nread, amount); - nread -= (nread & 511); /* Round down to a block */ + nread = round_down(nread, curlun->blksize); } file_offset += nread; amount_left -= nread; @@ -846,7 +846,8 @@ static int do_read(struct fsg_common *common) /* If an error occurred, report it and its position */ if (nread < amount) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -921,7 +922,7 @@ static int do_write(struct fsg_common *common) /* Carry out the file writes */ get_some_more = 1; - file_offset = usb_offset = ((loff_t) lba) << 9; + file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits; amount_left_to_req = common->data_size_from_cmnd; amount_left_to_write = common->data_size_from_cmnd; @@ -954,11 +955,12 @@ static int do_write(struct fsg_common *common) get_some_more = 0; curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = usb_offset >> 9; + curlun->sense_data_info = + usb_offset >> curlun->blkbits; curlun->info_valid = 1; continue; } - amount -= amount & 511; + amount = round_down(amount, curlun->blksize); if (amount == 0) { /* @@ -1002,7 +1004,8 @@ static int do_write(struct fsg_common *common) /* Did something go wrong with the transfer? */ if (bh->outreq->status != 0) { curlun->sense_data = SS_COMMUNICATION_FAILURE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1033,8 +1036,7 @@ static int do_write(struct fsg_common *common) } else if (nwritten < amount) { LDBG(curlun, "partial file write: %d/%u\n", (int)nwritten, amount); - nwritten -= (nwritten & 511); - /* Round down to a block */ + nwritten = round_down(nwritten, curlun->blksize); } file_offset += nwritten; amount_left_to_write -= nwritten; @@ -1043,7 +1045,8 @@ static int do_write(struct fsg_common *common) /* If an error occurred, report it and its position */ if (nwritten < amount) { curlun->sense_data = SS_WRITE_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1129,8 +1132,8 @@ static int do_verify(struct fsg_common *common) return -EIO; /* No default reply */ /* Prepare to carry out the file verify */ - amount_left = verification_length << 9; - file_offset = ((loff_t) lba) << 9; + amount_left = verification_length << curlun->blkbits; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Write out all the dirty buffers before invalidating them */ fsg_lun_fsync_sub(curlun); @@ -1157,7 +1160,8 @@ static int do_verify(struct fsg_common *common) if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1179,11 +1183,12 @@ static int do_verify(struct fsg_common *common) } else if (nread < amount) { LDBG(curlun, "partial file verify: %d/%u\n", (int)nread, amount); - nread -= nread & 511; /* Round down to a sector */ + nread = round_down(nread, curlun->blksize); } if (nread == 0) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = + file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1289,7 +1294,7 @@ static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); /* Max logical block */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */ return 8; } @@ -1527,7 +1532,7 @@ static int do_read_format_capacities(struct fsg_common *common, put_unaligned_be32(curlun->num_sectors, &buf[0]); /* Number of blocks */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */ buf[4] = 0x02; /* Current capacity */ return 12; } @@ -2022,7 +2027,8 @@ static int do_scsi_command(struct fsg_common *common) case READ_6: i = common->cmnd[4]; - common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << + common->curlun->blkbits; reply = check_command(common, 6, DATA_DIR_TO_HOST, (7<<1) | (1<<4), 1, "READ(6)"); @@ -2032,7 +2038,8 @@ static int do_scsi_command(struct fsg_common *common) case READ_10: common->data_size_from_cmnd = - get_unaligned_be16(&common->cmnd[7]) << 9; + get_unaligned_be16(&common->cmnd[7]) << + common->curlun->blkbits; reply = check_command(common, 10, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "READ(10)"); @@ -2042,7 +2049,8 @@ static int do_scsi_command(struct fsg_common *common) case READ_12: common->data_size_from_cmnd = - get_unaligned_be32(&common->cmnd[6]) << 9; + get_unaligned_be32(&common->cmnd[6]) << + common->curlun->blkbits; reply = check_command(common, 12, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "READ(12)"); @@ -2142,7 +2150,8 @@ static int do_scsi_command(struct fsg_common *common) case WRITE_6: i = common->cmnd[4]; - common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << + common->curlun->blkbits; reply = check_command(common, 6, DATA_DIR_FROM_HOST, (7<<1) | (1<<4), 1, "WRITE(6)"); @@ -2152,7 +2161,8 @@ static int do_scsi_command(struct fsg_common *common) case WRITE_10: common->data_size_from_cmnd = - get_unaligned_be16(&common->cmnd[7]) << 9; + get_unaligned_be16(&common->cmnd[7]) << + common->curlun->blkbits; reply = check_command(common, 10, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "WRITE(10)"); @@ -2162,7 +2172,8 @@ static int do_scsi_command(struct fsg_common *common) case WRITE_12: common->data_size_from_cmnd = - get_unaligned_be32(&common->cmnd[6]) << 9; + get_unaligned_be32(&common->cmnd[6]) << + common->curlun->blkbits; reply = check_command(common, 12, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "WRITE(12)"); diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 39ece40..59d9775 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -69,8 +69,7 @@ * each LUN would be settable independently as a disk drive or a CD-ROM * drive, but currently all LUNs have to be the same type. The CD-ROM * emulation includes a single data track and no audio tracks; hence there - * need be only one backing file per LUN. Note also that the CD-ROM block - * length is set to 512 rather than the more common value 2048. + * need be only one backing file per LUN. * * Requirements are modest; only a bulk-in and a bulk-out endpoint are * needed (an interrupt-out endpoint is also needed for CBI). The memory @@ -1158,7 +1157,7 @@ static int do_read(struct fsg_dev *fsg) curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; return -EINVAL; } - file_offset = ((loff_t) lba) << 9; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Carry out the file reads */ amount_left = fsg->data_size_from_cmnd; @@ -1196,7 +1195,7 @@ static int do_read(struct fsg_dev *fsg) if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; bh->inreq->length = 0; bh->state = BUF_STATE_FULL; @@ -1221,7 +1220,7 @@ static int do_read(struct fsg_dev *fsg) } else if (nread < amount) { LDBG(curlun, "partial file read: %d/%u\n", (int) nread, amount); - nread -= (nread & 511); // Round down to a block + nread = round_down(nread, curlun->blksize); } file_offset += nread; amount_left -= nread; @@ -1232,7 +1231,7 @@ static int do_read(struct fsg_dev *fsg) /* If an error occurred, report it and its position */ if (nread < amount) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1303,7 +1302,7 @@ static int do_write(struct fsg_dev *fsg) /* Carry out the file writes */ get_some_more = 1; - file_offset = usb_offset = ((loff_t) lba) << 9; + file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits; amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; while (amount_left_to_write > 0) { @@ -1333,11 +1332,11 @@ static int do_write(struct fsg_dev *fsg) get_some_more = 0; curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = usb_offset >> 9; + curlun->sense_data_info = usb_offset >> curlun->blkbits; curlun->info_valid = 1; continue; } - amount -= (amount & 511); + amount = round_down(amount, curlun->blksize); if (amount == 0) { /* Why were we were asked to transfer a @@ -1376,7 +1375,7 @@ static int do_write(struct fsg_dev *fsg) /* Did something go wrong with the transfer? */ if (bh->outreq->status != 0) { curlun->sense_data = SS_COMMUNICATION_FAILURE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1408,8 +1407,7 @@ static int do_write(struct fsg_dev *fsg) } else if (nwritten < amount) { LDBG(curlun, "partial file write: %d/%u\n", (int) nwritten, amount); - nwritten -= (nwritten & 511); - // Round down to a block + nwritten = round_down(nwritten, curlun->blksize); } file_offset += nwritten; amount_left_to_write -= nwritten; @@ -1418,7 +1416,7 @@ static int do_write(struct fsg_dev *fsg) /* If an error occurred, report it and its position */ if (nwritten < amount) { curlun->sense_data = SS_WRITE_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1500,8 +1498,8 @@ static int do_verify(struct fsg_dev *fsg) return -EIO; // No default reply /* Prepare to carry out the file verify */ - amount_left = verification_length << 9; - file_offset = ((loff_t) lba) << 9; + amount_left = verification_length << curlun->blkbits; + file_offset = ((loff_t) lba) << curlun->blkbits; /* Write out all the dirty buffers before invalidating them */ fsg_lun_fsync_sub(curlun); @@ -1527,7 +1525,7 @@ static int do_verify(struct fsg_dev *fsg) if (amount == 0) { curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1550,11 +1548,11 @@ static int do_verify(struct fsg_dev *fsg) } else if (nread < amount) { LDBG(curlun, "partial file verify: %d/%u\n", (int) nread, amount); - nread -= (nread & 511); // Round down to a sector + nread = round_down(nread, curlun->blksize); } if (nread == 0) { curlun->sense_data = SS_UNRECOVERED_READ_ERROR; - curlun->sense_data_info = file_offset >> 9; + curlun->sense_data_info = file_offset >> curlun->blkbits; curlun->info_valid = 1; break; } @@ -1668,7 +1666,7 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); /* Max logical block */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */ return 8; } @@ -1890,7 +1888,7 @@ static int do_read_format_capacities(struct fsg_dev *fsg, put_unaligned_be32(curlun->num_sectors, &buf[0]); /* Number of blocks */ - put_unaligned_be32(512, &buf[4]); /* Block length */ + put_unaligned_be32(curlun->blksize, &buf[4]); /* Block length */ buf[4] = 0x02; /* Current capacity */ return 12; } @@ -2415,7 +2413,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case READ_6: i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, (7<<1) | (1<<4), 1, "READ(6)")) == 0) @@ -2424,7 +2422,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case READ_10: fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->cmnd[7]) << 9; + get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "READ(10)")) == 0) @@ -2433,7 +2431,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case READ_12: fsg->data_size_from_cmnd = - get_unaligned_be32(&fsg->cmnd[6]) << 9; + get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "READ(12)")) == 0) @@ -2519,7 +2517,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case WRITE_6: i = fsg->cmnd[4]; - fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, (7<<1) | (1<<4), 1, "WRITE(6)")) == 0) @@ -2528,7 +2526,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case WRITE_10: fsg->data_size_from_cmnd = - get_unaligned_be16(&fsg->cmnd[7]) << 9; + get_unaligned_be16(&fsg->cmnd[7]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (3<<7), 1, "WRITE(10)")) == 0) @@ -2537,7 +2535,7 @@ static int do_scsi_command(struct fsg_dev *fsg) case WRITE_12: fsg->data_size_from_cmnd = - get_unaligned_be32(&fsg->cmnd[6]) << 9; + get_unaligned_be32(&fsg->cmnd[6]) << fsg->curlun->blkbits; if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, (1<<1) | (0xf<<2) | (0xf<<6), 1, "WRITE(12)")) == 0) diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index d3dd227..3ea70d8 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -247,6 +247,8 @@ struct fsg_lun { u32 sense_data_info; u32 unit_attention_data; + unsigned int blkbits; /* Bits of logical block size of bound block device */ + unsigned int blksize; /* logical block size of bound block device */ struct device dev; }; @@ -580,13 +582,24 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) rc = (int) size; goto out; } - num_sectors = size >> 9; /* File size in 512-byte blocks */ + + if (curlun->cdrom) { + curlun->blksize = 2048; + curlun->blkbits = 11; + } else if (inode->i_bdev) { + curlun->blksize = bdev_logical_block_size(inode->i_bdev); + curlun->blkbits = blksize_bits(curlun->blksize); + } else { + curlun->blksize = 512; + curlun->blkbits = 9; + } + + num_sectors = size >> curlun->blkbits; /* File size in logic-block-size blocks */ min_sectors = 1; if (curlun->cdrom) { - num_sectors &= ~3; /* Reduce to a multiple of 2048 */ - min_sectors = 300*4; /* Smallest track is 300 frames */ - if (num_sectors >= 256*60*75*4) { - num_sectors = (256*60*75 - 1) * 4; + min_sectors = 300; /* Smallest track is 300 frames */ + if (num_sectors >= 256*60*75) { + num_sectors = 256*60*75 - 1; LINFO(curlun, "file too big: %s\n", filename); LINFO(curlun, "using only first %d blocks\n", (int) num_sectors); -- cgit v0.10.2 From 04eee25b1d754a837360504b7af426d1f86ffeb7 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 18 Aug 2011 14:29:00 -0400 Subject: USB: gadget: storage: remove alignment assumption This patch (as1481) fixes a problem affecting g_file_storage and g_mass_storage when running at SuperSpeed. The two drivers currently assume that the bulk-out maxpacket size can evenly divide the SCSI block size, which is 512 bytes. But SuperSpeed bulk endpoints have a maxpacket size of 1024, so the assumption is no longer true. This patch removes that assumption from the drivers, by getting rid of a small optimization (they try to align VFS reads and writes on page cache boundaries). If a command's starting logical block address is 512 bytes below the end of a page, it's not okay to issue a USB command for just those 512 bytes when the maxpacket size is 1024 -- it would result in either babble (for an OUT transfer) or a short packet (for an IN transfer). Also, for backward compatibility, the test for writes extending beyond the end of the backing storage has to be changed. If the host tries to do this, we should accept the data that fits in the backing storage and ignore the rest. Because the storage's end may not align with a USB packet boundary, this means we may have to accept a USB OUT transfer that extends beyond the end of the storage and then write out only the part of the data that fits. Signed-off-by: Alan Stern Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 1cdb1fa..4306a83 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -744,7 +744,6 @@ static int do_read(struct fsg_common *common) u32 amount_left; loff_t file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nread; /* @@ -783,18 +782,10 @@ static int do_read(struct fsg_common *common) * Try to read the remaining amount. * But don't read more than the buffer size. * And don't try to read past the end of the file. - * Finally, if we're not at a page boundary, don't read past - * the next page. - * If this means reading 0 then we were asked to read past - * the end of file. */ amount = min(amount_left, FSG_BUFLEN); amount = min((loff_t)amount, curlun->file_length - file_offset); - partial_page = file_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, (unsigned int)PAGE_CACHE_SIZE - - partial_page); /* Wait for the next buffer to become available */ bh = common->next_buffhd_to_fill; @@ -840,6 +831,12 @@ static int do_read(struct fsg_common *common) file_offset += nread; amount_left -= nread; common->residue -= nread; + + /* + * Except at the end of the transfer, nread will be + * equal to the buffer size, which is divisible by the + * bulk-in maxpacket size. + */ bh->inreq->length = nread; bh->state = BUF_STATE_FULL; @@ -878,7 +875,6 @@ static int do_write(struct fsg_common *common) u32 amount_left_to_req, amount_left_to_write; loff_t usb_offset, file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nwritten; int rc; @@ -934,24 +930,13 @@ static int do_write(struct fsg_common *common) /* * Figure out how much we want to get: - * Try to get the remaining amount. - * But don't get more than the buffer size. - * And don't try to go past the end of the file. - * If we're not at a page boundary, - * don't go past the next page. - * If this means getting 0, then we were asked - * to write past the end of file. - * Finally, round down to a block boundary. + * Try to get the remaining amount, + * but not more than the buffer size. */ amount = min(amount_left_to_req, FSG_BUFLEN); - amount = min((loff_t)amount, - curlun->file_length - usb_offset); - partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, - (unsigned int)PAGE_CACHE_SIZE - partial_page); - - if (amount == 0) { + + /* Beyond the end of the backing file? */ + if (usb_offset >= curlun->file_length) { get_some_more = 0; curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; @@ -960,16 +945,6 @@ static int do_write(struct fsg_common *common) curlun->info_valid = 1; continue; } - amount = round_down(amount, curlun->blksize); - if (amount == 0) { - - /* - * Why were we were asked to transfer a - * partial block? - */ - get_some_more = 0; - continue; - } /* Get the next buffer */ usb_offset += amount; @@ -979,8 +954,9 @@ static int do_write(struct fsg_common *common) get_some_more = 0; /* - * amount is always divisible by 512, hence by - * the bulk-out maxpacket size + * Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. */ bh->outreq->length = amount; bh->bulk_out_intended_length = amount; @@ -1019,6 +995,11 @@ static int do_write(struct fsg_common *common) amount = curlun->file_length - file_offset; } + /* Don't write a partial block */ + amount = round_down(amount, curlun->blksize); + if (amount == 0) + goto empty_write; + /* Perform the write */ file_offset_tmp = file_offset; nwritten = vfs_write(curlun->filp, @@ -1051,6 +1032,7 @@ static int do_write(struct fsg_common *common) break; } + empty_write: /* Did the host decide to stop early? */ if (bh->outreq->actual != bh->outreq->length) { common->short_packet_received = 1; @@ -1151,8 +1133,6 @@ static int do_verify(struct fsg_common *common) * Try to read the remaining amount, but not more than * the buffer size. * And don't try to read past the end of the file. - * If this means reading 0 then we were asked to read - * past the end of file. */ amount = min(amount_left, FSG_BUFLEN); amount = min((loff_t)amount, @@ -1628,7 +1608,8 @@ static int throw_away_data(struct fsg_common *common) amount = min(common->usb_amount_left, FSG_BUFLEN); /* - * amount is always divisible by 512, hence by + * Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by * the bulk-out maxpacket size. */ bh->outreq->length = amount; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 59d9775..c6f96a2 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -1135,7 +1135,6 @@ static int do_read(struct fsg_dev *fsg) u32 amount_left; loff_t file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nread; /* Get the starting Logical Block Address and check that it's @@ -1170,17 +1169,10 @@ static int do_read(struct fsg_dev *fsg) * Try to read the remaining amount. * But don't read more than the buffer size. * And don't try to read past the end of the file. - * Finally, if we're not at a page boundary, don't read past - * the next page. - * If this means reading 0 then we were asked to read past - * the end of file. */ + */ amount = min((unsigned int) amount_left, mod_data.buflen); amount = min((loff_t) amount, curlun->file_length - file_offset); - partial_page = file_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - - partial_page); /* Wait for the next buffer to become available */ bh = fsg->next_buffhd_to_fill; @@ -1225,6 +1217,11 @@ static int do_read(struct fsg_dev *fsg) file_offset += nread; amount_left -= nread; fsg->residue -= nread; + + /* Except at the end of the transfer, nread will be + * equal to the buffer size, which is divisible by the + * bulk-in maxpacket size. + */ bh->inreq->length = nread; bh->state = BUF_STATE_FULL; @@ -1261,7 +1258,6 @@ static int do_write(struct fsg_dev *fsg) u32 amount_left_to_req, amount_left_to_write; loff_t usb_offset, file_offset, file_offset_tmp; unsigned int amount; - unsigned int partial_page; ssize_t nwritten; int rc; @@ -1312,23 +1308,13 @@ static int do_write(struct fsg_dev *fsg) if (bh->state == BUF_STATE_EMPTY && get_some_more) { /* Figure out how much we want to get: - * Try to get the remaining amount. - * But don't get more than the buffer size. - * And don't try to go past the end of the file. - * If we're not at a page boundary, - * don't go past the next page. - * If this means getting 0, then we were asked - * to write past the end of file. - * Finally, round down to a block boundary. */ + * Try to get the remaining amount, + * but not more than the buffer size. + */ amount = min(amount_left_to_req, mod_data.buflen); - amount = min((loff_t) amount, curlun->file_length - - usb_offset); - partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); - if (partial_page > 0) - amount = min(amount, - (unsigned int) PAGE_CACHE_SIZE - partial_page); - - if (amount == 0) { + + /* Beyond the end of the backing file? */ + if (usb_offset >= curlun->file_length) { get_some_more = 0; curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; @@ -1336,14 +1322,6 @@ static int do_write(struct fsg_dev *fsg) curlun->info_valid = 1; continue; } - amount = round_down(amount, curlun->blksize); - if (amount == 0) { - - /* Why were we were asked to transfer a - * partial block? */ - get_some_more = 0; - continue; - } /* Get the next buffer */ usb_offset += amount; @@ -1352,8 +1330,10 @@ static int do_write(struct fsg_dev *fsg) if (amount_left_to_req == 0) get_some_more = 0; - /* amount is always divisible by 512, hence by - * the bulk-out maxpacket size */ + /* Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. + */ bh->outreq->length = bh->bulk_out_intended_length = amount; bh->outreq->short_not_ok = 1; @@ -1389,6 +1369,11 @@ static int do_write(struct fsg_dev *fsg) amount = curlun->file_length - file_offset; } + /* Don't write a partial block */ + amount = round_down(amount, curlun->blksize); + if (amount == 0) + goto empty_write; + /* Perform the write */ file_offset_tmp = file_offset; nwritten = vfs_write(curlun->filp, @@ -1421,6 +1406,7 @@ static int do_write(struct fsg_dev *fsg) break; } + empty_write: /* Did the host decide to stop early? */ if (bh->outreq->actual != bh->outreq->length) { fsg->short_packet_received = 1; @@ -1517,8 +1503,7 @@ static int do_verify(struct fsg_dev *fsg) * Try to read the remaining amount, but not more than * the buffer size. * And don't try to read past the end of the file. - * If this means reading 0 then we were asked to read - * past the end of file. */ + */ amount = min((unsigned int) amount_left, mod_data.buflen); amount = min((loff_t) amount, curlun->file_length - file_offset); @@ -1981,8 +1966,10 @@ static int throw_away_data(struct fsg_dev *fsg) amount = min(fsg->usb_amount_left, (u32) mod_data.buflen); - /* amount is always divisible by 512, hence by - * the bulk-out maxpacket size */ + /* Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. + */ bh->outreq->length = bh->bulk_out_intended_length = amount; bh->outreq->short_not_ok = 1; -- cgit v0.10.2 From 6532c7fdb2c3a2ec1b949ecd2ff5375069c1639a Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Fri, 19 Aug 2011 21:21:27 +0200 Subject: usb: gadget: storage: make FSG_NUM_BUFFERS variable size FSG_NUM_BUFFERS is set to 2 as default. Usually 2 buffers are enough to establish a good buffering pipeline. The number may be increased in order to compensate a for bursty VFS behaviour. Here follows a description of system that may require more than 2 buffers. * CPU ondemand governor active * latency cost for wake up and/or frequency change * DMA for IO Use case description. * Data transfer from MMC via VFS to USB. * DMA shuffles data from MMC and to USB. * The CPU wakes up every now and then to pass data in and out from VFS, which cause the bursty VFS behaviour. Test set up * Running dd on the host reading from the mass storage device * cmdline: dd if=/dev/sdb of=/dev/null bs=4k count=$((256*100)) * Caches are dropped on the host and on the device before each run Measurements on a Snowball board with ondemand_governor active. FSG_NUM_BUFFERS 2 104857600 bytes (105 MB) copied, 5.62173 s, 18.7 MB/s 104857600 bytes (105 MB) copied, 5.61811 s, 18.7 MB/s 104857600 bytes (105 MB) copied, 5.57817 s, 18.8 MB/s FSG_NUM_BUFFERS 4 104857600 bytes (105 MB) copied, 5.26839 s, 19.9 MB/s 104857600 bytes (105 MB) copied, 5.2691 s, 19.9 MB/s 104857600 bytes (105 MB) copied, 5.2711 s, 19.9 MB/s There may not be one optimal number for all boards. This is why the number is added to Kconfig. If selecting USB_GADGET_DEBUG_FILES this value may be set by a module parameter as well. Signed-off-by: Per Forlin Acked-by: Michal Nazarewicz Acked-by: Alan Stern Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index fe56379..a60b472 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -96,6 +96,22 @@ config USB_GADGET_VBUS_DRAW This value will be used except for system-specific gadget drivers that have more specific information. +config USB_GADGET_STORAGE_NUM_BUFFERS + int "Number of storage pipeline buffers" + range 2 4 + default 2 + help + Usually 2 buffers are enough to establish a good buffering + pipeline. The number may be increased in order to compensate + for a bursty VFS behaviour. For instance there may be CPU wake up + latencies that makes the VFS to appear bursty in a system with + an CPU on-demand governor. Especially if DMA is doing IO to + offload the CPU. In this case the CPU will go into power + save often and spin up occasionally to move data within VFS. + If selecting USB_GADGET_DEBUG_FILES this value may be set by + a module parameter as well. + If unsure, say 2. + # # USB Peripheral Controller Support # diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 4306a83..7569414 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -362,7 +362,7 @@ struct fsg_common { struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; - struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; + struct fsg_buffhd *buffhds; int cmnd_size; u8 cmnd[MAX_COMMAND_SIZE]; @@ -2340,7 +2340,7 @@ reset: if (common->fsg) { fsg = common->fsg; - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; if (bh->inreq) { @@ -2397,7 +2397,7 @@ reset: clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); /* Allocate the requests */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &common->buffhds[i]; rc = alloc_request(common, fsg->bulk_in, &bh->inreq); @@ -2466,7 +2466,7 @@ static void handle_exception(struct fsg_common *common) /* Cancel all the pending transfers */ if (likely(common->fsg)) { - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &common->buffhds[i]; if (bh->inreq_busy) usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); @@ -2478,7 +2478,7 @@ static void handle_exception(struct fsg_common *common) /* Wait until everything is idle */ for (;;) { int num_active = 0; - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &common->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; } @@ -2501,7 +2501,7 @@ static void handle_exception(struct fsg_common *common) */ spin_lock_irq(&common->lock); - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &common->buffhds[i]; bh->state = BUF_STATE_EMPTY; } @@ -2710,6 +2710,10 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, int nluns, i, rc; char *pathbuf; + rc = fsg_num_buffers_validate(); + if (rc != 0) + return ERR_PTR(rc); + /* Find out how many LUNs there should be */ nluns = cfg->nluns; if (nluns < 1 || nluns > FSG_MAX_LUNS) { @@ -2728,6 +2732,14 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, common->free_storage_on_release = 0; } + common->buffhds = kcalloc(fsg_num_buffers, + sizeof *(common->buffhds), GFP_KERNEL); + if (!common->buffhds) { + if (common->free_storage_on_release) + kfree(common); + return ERR_PTR(-ENOMEM); + } + common->ops = cfg->ops; common->private_data = cfg->private_data; @@ -2805,7 +2817,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, /* Data buffers cyclic list */ bh = common->buffhds; - i = FSG_NUM_BUFFERS; + i = fsg_num_buffers; goto buffhds_first_it; do { bh->next = bh + 1; @@ -2931,12 +2943,13 @@ static void fsg_common_release(struct kref *ref) { struct fsg_buffhd *bh = common->buffhds; - unsigned i = FSG_NUM_BUFFERS; + unsigned i = fsg_num_buffers; do { kfree(bh->buf); } while (++bh, --i); } + kfree(common->buffhds); if (common->free_storage_on_release) kfree(common); } diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index c6f96a2..4b9797d 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -460,7 +460,6 @@ struct fsg_dev { struct fsg_buffhd *next_buffhd_to_fill; struct fsg_buffhd *next_buffhd_to_drain; - struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; int thread_wakeup_needed; struct completion thread_notifier; @@ -487,6 +486,8 @@ struct fsg_dev { unsigned int nluns; struct fsg_lun *luns; struct fsg_lun *curlun; + /* Must be the last entry */ + struct fsg_buffhd buffhds[]; }; typedef void (*fsg_routine_t)(struct fsg_dev *); @@ -2737,7 +2738,7 @@ static int do_set_interface(struct fsg_dev *fsg, int altsetting) reset: /* Deallocate the requests */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; if (bh->inreq) { @@ -2798,7 +2799,7 @@ reset: } /* Allocate the requests */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) @@ -2894,7 +2895,7 @@ static void handle_exception(struct fsg_dev *fsg) /* Cancel all the pending transfers */ if (fsg->intreq_busy) usb_ep_dequeue(fsg->intr_in, fsg->intreq); - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &fsg->buffhds[i]; if (bh->inreq_busy) usb_ep_dequeue(fsg->bulk_in, bh->inreq); @@ -2905,7 +2906,7 @@ static void handle_exception(struct fsg_dev *fsg) /* Wait until everything is idle */ for (;;) { num_active = fsg->intreq_busy; - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &fsg->buffhds[i]; num_active += bh->inreq_busy + bh->outreq_busy; } @@ -2927,7 +2928,7 @@ static void handle_exception(struct fsg_dev *fsg) * state, and the exception. Then invoke the handler. */ spin_lock_irq(&fsg->lock); - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { bh = &fsg->buffhds[i]; bh->state = BUF_STATE_EMPTY; } @@ -3157,7 +3158,7 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) } /* Free the data buffers */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) + for (i = 0; i < fsg_num_buffers; ++i) kfree(fsg->buffhds[i].buf); /* Free the request and buffer for endpoint 0 */ @@ -3445,7 +3446,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) req->complete = ep0_complete; /* Allocate the data buffers */ - for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + for (i = 0; i < fsg_num_buffers; ++i) { struct fsg_buffhd *bh = &fsg->buffhds[i]; /* Allocate for the bulk-in endpoint. We assume that @@ -3456,7 +3457,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) goto out; bh->next = bh + 1; } - fsg->buffhds[FSG_NUM_BUFFERS - 1].next = &fsg->buffhds[0]; + fsg->buffhds[fsg_num_buffers - 1].next = &fsg->buffhds[0]; /* This should reflect the actual gadget power source */ usb_gadget_set_selfpowered(gadget); @@ -3572,7 +3573,9 @@ static int __init fsg_alloc(void) { struct fsg_dev *fsg; - fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + fsg = kzalloc(sizeof *fsg + + fsg_num_buffers * sizeof *(fsg->buffhds), GFP_KERNEL); + if (!fsg) return -ENOMEM; spin_lock_init(&fsg->lock); @@ -3590,6 +3593,10 @@ static int __init fsg_init(void) int rc; struct fsg_dev *fsg; + rc = fsg_num_buffers_validate(); + if (rc != 0) + return rc; + if ((rc = fsg_alloc()) != 0) return rc; fsg = the_fsg; diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 3ea70d8..9fd3799 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -52,6 +52,12 @@ * characters rather then a pointer to void. */ +/* + * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers + * sets the number of pipeline buffers (length of the fsg_buffhd array). + * The valid range of num_buffers is: num >= 2 && num <= 4. + */ + #include #include @@ -264,8 +270,31 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev) #define EP0_BUFSIZE 256 #define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ -/* Number of buffers we will use. 2 is enough for double-buffering */ -#define FSG_NUM_BUFFERS 2 +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS; +module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO); +MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers"); + +#else + +/* + * Number of buffers we will use. + * 2 is usually enough for good buffering pipeline + */ +#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS + +#endif /* CONFIG_USB_DEBUG */ + +/* check if fsg_num_buffers is within a valid range */ +static inline int fsg_num_buffers_validate(void) +{ + if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4) + return 0; + pr_err("fsg_num_buffers %u is out of range (%d to %d)\n", + fsg_num_buffers, 2 ,4); + return -EINVAL; +} /* Default size of buffer length. */ #define FSG_BUFLEN ((u32)16384) -- cgit v0.10.2 From ef7f584ce7b40bcfc108477d2711270379ca8596 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 26 Aug 2011 12:48:15 +0300 Subject: usb: gadget: audio: actually support both speeds While testing g_audio with HighSpeed UDC on a FS Hub, we had no configurations to present to the host. That's because both speeds where mutually exclusive. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index a9a4ead..ddeaa45 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -681,17 +681,18 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) status = -ENOMEM; - /* supcard all relevant hardware speeds... we expect that when + /* copy descriptors, and track endpoint copies */ + f->descriptors = usb_copy_descriptors(f_audio_desc); + + /* + * support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ - - /* copy descriptors, and track endpoint copies */ if (gadget_is_dualspeed(c->cdev->gadget)) { c->highspeed = true; f->hs_descriptors = usb_copy_descriptors(f_audio_desc); - } else - f->descriptors = usb_copy_descriptors(f_audio_desc); + } return 0; -- cgit v0.10.2 From 7c5881d1625d844cdfcc027a970f0d034b982ba5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Aug 2011 11:54:08 +0300 Subject: usb: gadget: audio: queue wLength-sized requests On Audio class, the wLength field of the Setup packet, contains the data payload size of the following Data phase. Instead of harcoding values, use wLength. This also fixes a bug where Gadget driver had to receive 3 bytes, but it was queueing a ZLP. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index ddeaa45..ec7ffcd 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -460,7 +460,7 @@ static int audio_set_endpoint_req(struct usb_function *f, switch (ctrl->bRequest) { case UAC_SET_CUR: - value = 0; + value = len; break; case UAC_SET_MIN: @@ -499,7 +499,7 @@ static int audio_get_endpoint_req(struct usb_function *f, case UAC_GET_MIN: case UAC_GET_MAX: case UAC_GET_RES: - value = 3; + value = len; break; case UAC_GET_MEM: break; -- cgit v0.10.2 From b3c3dc22366b15350281b1c273adecd2b91e320f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sun, 10 Jul 2011 12:22:20 +0300 Subject: usb: musb: fix build breakage This patch fixes the compilation brekage which commits 208466dc ("usb: otg:OMAP4430: Powerdown the internal PHY when USB is disabled") and fb91cde4 ("usb: musb: OMAP4430: Power down the PHY during board init") introduced when building a OMAP2-only kernel. LD .tmp_vmlinux1 arch/arm/mach-omap2/built-in.o:(.data+0x7ce0): undefined reference to +`omap4430_phy_init' arch/arm/mach-omap2/built-in.o:(.data+0x7ce4): undefined reference to +`omap4430_phy_exit' arch/arm/mach-omap2/built-in.o:(.data+0x7ce8): undefined reference to +`omap4430_phy_power' arch/arm/mach-omap2/built-in.o:(.data+0x7cec): undefined reference to +`omap4430_phy_set_clk' arch/arm/mach-omap2/built-in.o:(.data+0x7cf0): undefined reference to +`omap4430_phy_suspend' make: *** [.tmp_vmlinux1] Error 1 Reported-by: Paul Walmsley Signed-off-by: Felipe Balbi diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index f343365..7317a2b 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -242,14 +242,11 @@ obj-$(CONFIG_MACH_IGEP0020) += board-igep0020.o \ obj-$(CONFIG_MACH_OMAP3_TOUCHBOOK) += board-omap3touchbook.o \ hsmmc.o obj-$(CONFIG_MACH_OMAP_4430SDP) += board-4430sdp.o \ - hsmmc.o \ - omap_phy_internal.o + hsmmc.o obj-$(CONFIG_MACH_OMAP4_PANDA) += board-omap4panda.o \ - hsmmc.o \ - omap_phy_internal.o + hsmmc.o -obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o \ - omap_phy_internal.o \ +obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o obj-$(CONFIG_MACH_CRANEBOARD) += board-am3517crane.o @@ -260,6 +257,8 @@ obj-$(CONFIG_MACH_TI8168EVM) += board-ti8168evm.o usbfs-$(CONFIG_ARCH_OMAP_OTG) := usb-fs.o obj-y += $(usbfs-m) $(usbfs-y) obj-y += usb-musb.o +obj-y += omap_phy_internal.o + obj-$(CONFIG_MACH_OMAP2_TUSB6010) += usb-tusb6010.o obj-y += usb-host.o -- cgit v0.10.2 From 0ae52d5458ddb14d5da63054f1d8269a13fe9054 Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Tue, 19 Jul 2011 22:11:58 -0700 Subject: usb: musb: Enable DMA mode1 RX for transfers without short packets This patch enables DMA mode1 for the RX path when we know there won't be any short packets. We check that by looking into the short_no_ok flag, if it's true we enable mode1, otherwise we use mode0 to transfer the data. This will result in a throughput performance gain of around 40% for USB mass-storage/mtp use cases. [ balbi@ti.com : updated commit log and code comments slightly ] Signed-off-by: Anand Gadiyar Signed-off-by: Moiz Sonasath Signed-off-by: Vikram Pandita Tested-by: Vikram Pandita Signed-off-by: Felipe Balbi diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 44b331a..82434dd 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -634,6 +634,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) u16 len; u16 csr = musb_readw(epio, MUSB_RXCSR); struct musb_hw_ep *hw_ep = &musb->endpoints[epnum]; + u8 use_mode_1; if (hw_ep->is_shared_fifo) musb_ep = &hw_ep->ep_in; @@ -683,6 +684,18 @@ static void rxstate(struct musb *musb, struct musb_request *req) if (csr & MUSB_RXCSR_RXPKTRDY) { len = musb_readw(epio, MUSB_RXCOUNT); + + /* + * Enable Mode 1 on RX transfers only when short_not_ok flag + * is set. Currently short_not_ok flag is set only from + * file_storage and f_mass_storage drivers + */ + + if (request->short_not_ok && len == musb_ep->packet_sz) + use_mode_1 = 1; + else + use_mode_1 = 0; + if (request->actual < request->length) { #ifdef CONFIG_USB_INVENTRA_DMA if (is_buffer_mapped(req)) { @@ -714,37 +727,41 @@ static void rxstate(struct musb *musb, struct musb_request *req) * then becomes usable as a runtime "use mode 1" hint... */ - csr |= MUSB_RXCSR_DMAENAB; -#ifdef USE_MODE1 - csr |= MUSB_RXCSR_AUTOCLEAR; - /* csr |= MUSB_RXCSR_DMAMODE; */ - - /* this special sequence (enabling and then - * disabling MUSB_RXCSR_DMAMODE) is required - * to get DMAReq to activate - */ - musb_writew(epio, MUSB_RXCSR, - csr | MUSB_RXCSR_DMAMODE); -#else - if (!musb_ep->hb_mult && - musb_ep->hw_ep->rx_double_buffered) + /* Experimental: Mode1 works with mass storage use cases */ + if (use_mode_1) { csr |= MUSB_RXCSR_AUTOCLEAR; -#endif - musb_writew(epio, MUSB_RXCSR, csr); + musb_writew(epio, MUSB_RXCSR, csr); + csr |= MUSB_RXCSR_DMAENAB; + musb_writew(epio, MUSB_RXCSR, csr); + + /* + * this special sequence (enabling and then + * disabling MUSB_RXCSR_DMAMODE) is required + * to get DMAReq to activate + */ + musb_writew(epio, MUSB_RXCSR, + csr | MUSB_RXCSR_DMAMODE); + musb_writew(epio, MUSB_RXCSR, csr); + + } else { + if (!musb_ep->hb_mult && + musb_ep->hw_ep->rx_double_buffered) + csr |= MUSB_RXCSR_AUTOCLEAR; + csr |= MUSB_RXCSR_DMAENAB; + musb_writew(epio, MUSB_RXCSR, csr); + } if (request->actual < request->length) { int transfer_size = 0; -#ifdef USE_MODE1 - transfer_size = min(request->length - request->actual, - channel->max_len); -#else - transfer_size = min(request->length - request->actual, - (unsigned)len); -#endif - if (transfer_size <= musb_ep->packet_sz) - musb_ep->dma->desired_mode = 0; - else + if (use_mode_1) { + transfer_size = min(request->length - request->actual, + channel->max_len); musb_ep->dma->desired_mode = 1; + } else { + transfer_size = min(request->length - request->actual, + (unsigned)len); + musb_ep->dma->desired_mode = 0; + } use_dma = c->channel_program( channel, -- cgit v0.10.2 From 2dc3753997f3c80ce8b950242ab9bb3fb936acfd Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Sep 2011 11:05:40 -0700 Subject: xhci: If no endpoints changed, don't issue BW command. Some alternate interface settings have no endpoints associated with them. This shows up in some USB webcams, particularly the Logitech HD 1080p, which uses the uvcvideo driver. If a driver switches between two alt settings with no endpoints, there is no need to issue a configure endpoint command, because there is no endpoint information to update. The only time a configure endpoint command with just the add slot flag set makes sense is when the driver is updating hub characteristics in the slot context. However, that code never calls xhci_check_bandwidth, so we should be safe not issuing a command if only the slot context add flag is set. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f647d91..fcc5821 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1888,6 +1888,12 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); ctrl_ctx->add_flags &= cpu_to_le32(~EP0_FLAG); ctrl_ctx->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); + + /* Don't issue the command if there's no endpoints to update. */ + if (ctrl_ctx->add_flags == cpu_to_le32(SLOT_FLAG) && + ctrl_ctx->drop_flags == 0) + return 0; + xhci_dbg(xhci, "New Input Control Context:\n"); slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); xhci_dbg_ctx(xhci, virt_dev->in_ctx, -- cgit v0.10.2 From fe30182c2553f491e5dc12074c8e52163d3bfbc7 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Sep 2011 11:05:41 -0700 Subject: xhci: Rename virt_dev->port to fake_port. The "port" field in xhci_virt_dev stores the port number associated with one of the two xHCI split roothubs, not the unique port number the xHCI hardware uses. Since we'll need to store the real hardware port number in future patches, rename this field to "fake_port". Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 1e96d1f..7a62b02 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -232,7 +232,7 @@ int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, continue; speed = xhci->devs[i]->udev->speed; if (((speed == USB_SPEED_SUPER) == (hcd->speed == HCD_USB3)) - && xhci->devs[i]->port == port) { + && xhci->devs[i]->fake_port == port) { slot_id = i; break; } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index d873a03..9afce47 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -921,9 +921,9 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud for (top_dev = udev; top_dev->parent && top_dev->parent->parent; top_dev = top_dev->parent) /* Found device below root hub */; - dev->port = top_dev->portnum; + dev->fake_port = top_dev->portnum; xhci_dbg(xhci, "Set root hub portnum to %d\n", port_num); - xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->port); + xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->fake_port); /* Is this a LS/FS device under an external HS hub? */ if (udev->tt && udev->tt->hub->parent) { diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index cae8e23..bfb3fab 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -798,7 +798,7 @@ struct xhci_virt_device { /* Status of the last command issued for this device */ u32 cmd_status; struct list_head cmd_list; - u8 port; + u8 fake_port; }; -- cgit v0.10.2 From 750645f8a6543ff4deb4d98a0052000a31fdd43f Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Sep 2011 11:05:43 -0700 Subject: xhci: Refactor endpoint limit checking. Move the code to check whether we've reached the host controller's limit on the number of endpoints out of the two conditional statements, to remove duplicate code. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index fcc5821..7da3b13 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1765,17 +1765,22 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, spin_lock_irqsave(&xhci->lock, flags); virt_dev = xhci->devs[udev->slot_id]; - if (command) { + + if (command) in_ctx = command->in_ctx; - if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) && - xhci_reserve_host_resources(xhci, in_ctx)) { - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_warn(xhci, "Not enough host resources, " - "active endpoint contexts = %u\n", - xhci->num_active_eps); - return -ENOMEM; - } + else + in_ctx = virt_dev->in_ctx; + if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) && + xhci_reserve_host_resources(xhci, in_ctx)) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_warn(xhci, "Not enough host resources, " + "active endpoint contexts = %u\n", + xhci->num_active_eps); + return -ENOMEM; + } + + if (command) { cmd_completion = command->completion; cmd_status = &command->status; command->command_trb = xhci->cmd_ring->enqueue; @@ -1789,15 +1794,6 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, list_add_tail(&command->cmd_list, &virt_dev->cmd_list); } else { - in_ctx = virt_dev->in_ctx; - if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK) && - xhci_reserve_host_resources(xhci, in_ctx)) { - spin_unlock_irqrestore(&xhci->lock, flags); - xhci_warn(xhci, "Not enough host resources, " - "active endpoint contexts = %u\n", - xhci->num_active_eps); - return -ENOMEM; - } cmd_completion = &virt_dev->cmd_completion; cmd_status = &virt_dev->cmd_status; } -- cgit v0.10.2 From 66381755442189bbeb15f1a51b1e0059327d84ed Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Sep 2011 11:05:45 -0700 Subject: xhci: Store the "real" root port number. Since the xHCI driver now has split USB2/USB3 roothubs, devices under each roothub can have duplicate "fake" port numbers. For the next set of patches, we need to keep track of the "real" port number that the xHCI host uses to index into the port status arrays. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 9afce47..1755c66 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -922,6 +922,7 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud top_dev = top_dev->parent) /* Found device below root hub */; dev->fake_port = top_dev->portnum; + dev->real_port = port_num; xhci_dbg(xhci, "Set root hub portnum to %d\n", port_num); xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->fake_port); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index bfb3fab..ec481700 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -799,6 +799,7 @@ struct xhci_virt_device { u32 cmd_status; struct list_head cmd_list; u8 fake_port; + u8 real_port; }; -- cgit v0.10.2 From 839c817ce67178ca3c7c7ad534c571bba1e69ebe Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Sep 2011 11:05:47 -0700 Subject: xhci: Store information about roothubs and TTs. For upcoming patches, we need to keep information about the bandwidth domains under the xHCI host. Each root port is a separate primary bandwidth domain, and each high speed hub's TT (and potentially each port on a multi-TT hub) is a secondary bandwidth domain. If the table were in text form, it would look a bit like this: EP Interval Sum of Number Largest Max Max Packet of Packets Packet Size Overhead 0 N mps overhead ... 15 N mps overhead Overhead is the maximum packet overhead (for bit stuffing, CRC, protocol overhead, etc) for all the endpoints in this interval. Devices with different speeds have different max packet overhead. For example, if there is a low speed and a full speed endpoint that both have an interval of 3, we would use the higher overhead (the low speed overhead). Interval 0 is a bit special, since we really just want to know the sum of the max ESIT payloads instead of the largest max packet size. That's stored in the interval0_esit_payload variable. For root ports, we also need to keep track of the number of active TTs. For each root port, and each TT under a root port, store some information about the bandwidth consumption. Dynamically allocate an array of root port bandwidth information for the number of root ports on the xHCI host. Each root port stores a list of TTs under the root port. A single TT hub only has one entry in the list, but a multi-TT hub will have an entry per port. When the USB core says that a USB device is a hub, create one or more entries in the root port TT list for the hub. When a device is deleted, and it is a hub, search through the root port TT list and delete all TT entries for the hub. Keep track of which TT entry is associated with a device under a TT. LS/FS devices attached directly to the root port will have usb_device->tt set to the roothub. Ignore that, and treat it like a primary bandwidth domain, since there isn't really a high speed bus between the roothub and the host. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 1755c66..5efb0af 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -687,7 +687,98 @@ static void xhci_init_endpoint_timer(struct xhci_hcd *xhci, ep->xhci = xhci; } -/* All the xhci_tds in the ring's TD list should be freed at this point */ +static void xhci_free_tt_info(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int slot_id) +{ + struct list_head *tt; + struct list_head *tt_list_head; + struct list_head *tt_next; + struct xhci_tt_bw_info *tt_info; + + /* If the device never made it past the Set Address stage, + * it may not have the real_port set correctly. + */ + if (virt_dev->real_port == 0 || + virt_dev->real_port > HCS_MAX_PORTS(xhci->hcs_params1)) { + xhci_dbg(xhci, "Bad real port.\n"); + return; + } + + tt_list_head = &(xhci->rh_bw[virt_dev->real_port - 1].tts); + if (list_empty(tt_list_head)) + return; + + list_for_each(tt, tt_list_head) { + tt_info = list_entry(tt, struct xhci_tt_bw_info, tt_list); + if (tt_info->slot_id == slot_id) + break; + } + /* Cautionary measure in case the hub was disconnected before we + * stored the TT information. + */ + if (tt_info->slot_id != slot_id) + return; + + tt_next = tt->next; + tt_info = list_entry(tt, struct xhci_tt_bw_info, + tt_list); + /* Multi-TT hubs will have more than one entry */ + do { + list_del(tt); + kfree(tt_info); + tt = tt_next; + if (list_empty(tt_list_head)) + break; + tt_next = tt->next; + tt_info = list_entry(tt, struct xhci_tt_bw_info, + tt_list); + } while (tt_info->slot_id == slot_id); +} + +int xhci_alloc_tt_info(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags) +{ + struct xhci_tt_bw_info *tt_info; + unsigned int num_ports; + int i, j; + + if (!tt->multi) + num_ports = 1; + else + num_ports = hdev->maxchild; + + for (i = 0; i < num_ports; i++, tt_info++) { + struct xhci_interval_bw_table *bw_table; + + tt_info = kzalloc(sizeof(*tt_info), mem_flags); + if (!tt_info) + goto free_tts; + INIT_LIST_HEAD(&tt_info->tt_list); + list_add(&tt_info->tt_list, + &xhci->rh_bw[virt_dev->real_port - 1].tts); + tt_info->slot_id = virt_dev->udev->slot_id; + if (tt->multi) + tt_info->ttport = i+1; + bw_table = &tt_info->bw_table; + for (j = 0; j < XHCI_MAX_INTERVAL; j++) + INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints); + } + return 0; + +free_tts: + xhci_free_tt_info(xhci, virt_dev, virt_dev->udev->slot_id); + return -ENOMEM; +} + + +/* All the xhci_tds in the ring's TD list should be freed at this point. + * Should be called with xhci->lock held if there is any chance the TT lists + * will be manipulated by the configure endpoint, allocate device, or update + * hub functions while this function is removing the TT entries from the list. + */ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) { struct xhci_virt_device *dev; @@ -709,6 +800,8 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) xhci_free_stream_info(xhci, dev->eps[i].stream_info); } + /* If this is a hub, free the TT(s) from the TT list */ + xhci_free_tt_info(xhci, dev, slot_id); if (dev->ring_cache) { for (i = 0; i < dev->num_rings_cached; i++) @@ -926,6 +1019,36 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud xhci_dbg(xhci, "Set root hub portnum to %d\n", port_num); xhci_dbg(xhci, "Set fake root hub portnum to %d\n", dev->fake_port); + /* Find the right bandwidth table that this device will be a part of. + * If this is a full speed device attached directly to a root port (or a + * decendent of one), it counts as a primary bandwidth domain, not a + * secondary bandwidth domain under a TT. An xhci_tt_info structure + * will never be created for the HS root hub. + */ + if (!udev->tt || !udev->tt->hub->parent) { + dev->bw_table = &xhci->rh_bw[port_num - 1].bw_table; + } else { + struct xhci_root_port_bw_info *rh_bw; + struct xhci_tt_bw_info *tt_bw; + + rh_bw = &xhci->rh_bw[port_num - 1]; + /* Find the right TT. */ + list_for_each_entry(tt_bw, &rh_bw->tts, tt_list) { + if (tt_bw->slot_id != udev->tt->hub->slot_id) + continue; + + if (!dev->udev->tt->multi || + (udev->tt->multi && + tt_bw->ttport == dev->udev->ttport)) { + dev->bw_table = &tt_bw->bw_table; + dev->tt_info = tt_bw; + break; + } + } + if (!dev->tt_info) + xhci_warn(xhci, "WARN: Didn't find a matching TT\n"); + } + /* Is this a LS/FS device under an external HS hub? */ if (udev->tt && udev->tt->hub->parent) { slot_ctx->tt_info = cpu_to_le32(udev->tt->hub->slot_id | @@ -1552,6 +1675,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) kfree(xhci->usb2_ports); kfree(xhci->usb3_ports); kfree(xhci->port_array); + kfree(xhci->rh_bw); xhci->page_size = 0; xhci->page_shift = 0; @@ -1822,6 +1946,12 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) if (!xhci->port_array) return -ENOMEM; + xhci->rh_bw = kzalloc(sizeof(*xhci->rh_bw)*num_ports, flags); + if (!xhci->rh_bw) + return -ENOMEM; + for (i = 0; i < num_ports; i++) + INIT_LIST_HEAD(&xhci->rh_bw[i].tts); + /* * For whatever reason, the first capability offset is from the * capability register base, not from the HCCPARAMS register. diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7da3b13..1657041 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2990,6 +2990,14 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, } spin_lock_irqsave(&xhci->lock, flags); + if (hdev->speed == USB_SPEED_HIGH && + xhci_alloc_tt_info(xhci, vdev, hdev, tt, GFP_ATOMIC)) { + xhci_dbg(xhci, "Could not allocate xHCI TT structure.\n"); + xhci_free_command(xhci, config_cmd); + spin_unlock_irqrestore(&xhci->lock, flags); + return -ENOMEM; + } + xhci_slot_copy(xhci, config_cmd->in_ctx, vdev->out_ctx); ctrl_ctx = xhci_get_input_control_ctx(xhci, config_cmd->in_ctx); ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ec481700..eee47c8 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -774,6 +774,26 @@ struct xhci_virt_ep { bool skip; }; +enum xhci_overhead_type { + LS_OVERHEAD_TYPE = 0, + FS_OVERHEAD_TYPE, + HS_OVERHEAD_TYPE, +}; + +struct xhci_interval_bw { + unsigned int num_packets; + /* How many endpoints of each speed are present. */ + unsigned int overhead[3]; +}; + +#define XHCI_MAX_INTERVAL 16 + +struct xhci_interval_bw_table { + unsigned int interval0_esit_payload; + struct xhci_interval_bw interval_bw[XHCI_MAX_INTERVAL]; +}; + + struct xhci_virt_device { struct usb_device *udev; /* @@ -800,6 +820,30 @@ struct xhci_virt_device { struct list_head cmd_list; u8 fake_port; u8 real_port; + struct xhci_interval_bw_table *bw_table; + struct xhci_tt_bw_info *tt_info; +}; + +/* + * For each roothub, keep track of the bandwidth information for each periodic + * interval. + * + * If a high speed hub is attached to the roothub, each TT associated with that + * hub is a separate bandwidth domain. The interval information for the + * endpoints on the devices under that TT will appear in the TT structure. + */ +struct xhci_root_port_bw_info { + struct list_head tts; + unsigned int num_active_tts; + struct xhci_interval_bw_table bw_table; +}; + +struct xhci_tt_bw_info { + struct list_head tt_list; + int slot_id; + int ttport; + struct xhci_interval_bw_table bw_table; + int active_eps; }; @@ -1268,6 +1312,8 @@ struct xhci_hcd { int slot_id; /* Internal mirror of the HW's dcbaa */ struct xhci_virt_device *devs[MAX_HC_SLOTS]; + /* For keeping track of bandwidth domains per roothub. */ + struct xhci_root_port_bw_info *rh_bw; /* DMA pools */ struct dma_pool *device_pool; @@ -1508,6 +1554,10 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd); irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd); int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev); void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_alloc_tt_info(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + struct usb_device *hdev, + struct usb_tt *tt, gfp_t mem_flags); int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, unsigned int num_streams, gfp_t mem_flags); -- cgit v0.10.2 From 9af5d71d8e1fc404ad2ac1b568dafa1a2f9b3be2 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Sep 2011 11:05:48 -0700 Subject: xhci: Store endpoint bandwidth information. In the upcoming patches, we'll use some stored endpoint information to make software keep track of the worst-case bandwidth schedule. We need to store several variables associated with each periodic endpoint: - the type of endpoint - Max Packet Size - Mult - Max ESIT payload - Max Burst Size (aka number of packets, stored in one-based form) - the endpoint interval (normalized to powers of 2 microframes) All this information is available to the hardware, and stored in its device output context. However, we need to ensure that the new information is stored before the xHCI driver drops the xhci->lock to wait on the Configure Endpoint command, so that another driver requesting a configuration or alt setting change will see the update. The Configure Endpoint command will never fail on the hardware that needs this software bandwidth checking (assuming the slot is enabled and the flags are set properly), so updating the endpoint info before the command completes should be fine. Until we add in the bandwidth checking code, just update the endpoint information after the Configure Endpoint command completes, and after a Reset Device command completes. Don't bother to clear the endpoint bandwidth info when a device is being freed, since the xhci_virt_ep is just going to be freed anyway. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 5efb0af..9451d94 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1410,6 +1410,69 @@ void xhci_endpoint_zero(struct xhci_hcd *xhci, */ } +void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info) +{ + bw_info->ep_interval = 0; + bw_info->mult = 0; + bw_info->num_packets = 0; + bw_info->max_packet_size = 0; + bw_info->type = 0; + bw_info->max_esit_payload = 0; +} + +void xhci_update_bw_info(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_input_control_ctx *ctrl_ctx, + struct xhci_virt_device *virt_dev) +{ + struct xhci_bw_info *bw_info; + struct xhci_ep_ctx *ep_ctx; + unsigned int ep_type; + int i; + + for (i = 1; i < 31; ++i) { + bw_info = &virt_dev->eps[i].bw_info; + + /* We can't tell what endpoint type is being dropped, but + * unconditionally clearing the bandwidth info for non-periodic + * endpoints should be harmless because the info will never be + * set in the first place. + */ + if (!EP_IS_ADDED(ctrl_ctx, i) && EP_IS_DROPPED(ctrl_ctx, i)) { + /* Dropped endpoint */ + xhci_clear_endpoint_bw_info(bw_info); + continue; + } + + if (EP_IS_ADDED(ctrl_ctx, i)) { + ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, i); + ep_type = CTX_TO_EP_TYPE(le32_to_cpu(ep_ctx->ep_info2)); + + /* Ignore non-periodic endpoints */ + if (ep_type != ISOC_OUT_EP && ep_type != INT_OUT_EP && + ep_type != ISOC_IN_EP && + ep_type != INT_IN_EP) + continue; + + /* Added or changed endpoint */ + bw_info->ep_interval = CTX_TO_EP_INTERVAL( + le32_to_cpu(ep_ctx->ep_info)); + bw_info->mult = CTX_TO_EP_MULT( + le32_to_cpu(ep_ctx->ep_info)); + /* Number of packets is zero-based in the input context, + * but we want one-based for the interval table. + */ + bw_info->num_packets = CTX_TO_MAX_BURST( + le32_to_cpu(ep_ctx->ep_info2)) + 1; + bw_info->max_packet_size = MAX_PACKET_DECODED( + le32_to_cpu(ep_ctx->ep_info2)); + bw_info->type = ep_type; + bw_info->max_esit_payload = CTX_TO_MAX_ESIT_PAYLOAD( + le32_to_cpu(ep_ctx->tx_info)); + } + } +} + /* Copy output xhci_ep_ctx to the input xhci_ep_ctx copy. * Useful when you want to change one particular aspect of the endpoint and then * issue a configure endpoint command. diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1657041..8279146 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1912,6 +1912,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); } + xhci_update_bw_info(xhci, virt_dev->in_ctx, ctrl_ctx, virt_dev); xhci_zero_in_ctx(xhci, virt_dev); /* * Install any rings for completely new endpoints or changed endpoints, @@ -2668,6 +2669,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); last_freed_endpoint = i; } + xhci_clear_endpoint_bw_info(&virt_dev->eps[i].bw_info); } xhci_dbg(xhci, "Output context after successful reset device cmd:\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index eee47c8..af15b90 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -611,11 +611,13 @@ struct xhci_ep_ctx { #define EP_STATE_ERROR 4 /* Mult - Max number of burtst within an interval, in EP companion desc. */ #define EP_MULT(p) (((p) & 0x3) << 8) +#define CTX_TO_EP_MULT(p) (((p) >> 8) & 0x3) /* bits 10:14 are Max Primary Streams */ /* bit 15 is Linear Stream Array */ /* Interval - period between requests to an endpoint - 125u increments. */ #define EP_INTERVAL(p) (((p) & 0xff) << 16) #define EP_INTERVAL_TO_UFRAMES(p) (1 << (((p) >> 16) & 0xff)) +#define CTX_TO_EP_INTERVAL(p) (((p) >> 16) & 0xff) #define EP_MAXPSTREAMS_MASK (0x1f << 10) #define EP_MAXPSTREAMS(p) (((p) << 10) & EP_MAXPSTREAMS_MASK) /* Endpoint is set up with a Linear Stream Array (vs. Secondary Stream Array) */ @@ -640,6 +642,7 @@ struct xhci_ep_ctx { /* bit 6 reserved */ /* bit 7 is Host Initiate Disable - for disabling stream selection */ #define MAX_BURST(p) (((p)&0xff) << 8) +#define CTX_TO_MAX_BURST(p) (((p) >> 8) & 0xff) #define MAX_PACKET(p) (((p)&0xffff) << 16) #define MAX_PACKET_MASK (0xffff << 16) #define MAX_PACKET_DECODED(p) (((p) >> 16) & 0xffff) @@ -652,6 +655,7 @@ struct xhci_ep_ctx { /* tx_info bitmasks */ #define AVG_TRB_LENGTH_FOR_EP(p) ((p) & 0xffff) #define MAX_ESIT_PAYLOAD_FOR_EP(p) (((p) & 0xffff) << 16) +#define CTX_TO_MAX_ESIT_PAYLOAD(p) (((p) >> 16) & 0xffff) /* deq bitmasks */ #define EP_CTX_CYCLE_MASK (1 << 0) @@ -670,6 +674,11 @@ struct xhci_input_control_ctx { __le32 rsvd2[6]; }; +#define EP_IS_ADDED(ctrl_ctx, i) \ + (le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1))) +#define EP_IS_DROPPED(ctrl_ctx, i) \ + (le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) + /* Represents everything that is needed to issue a command on the command ring. * It's useful to pre-allocate these for commands that cannot fail due to * out-of-memory errors, like freeing streams. @@ -731,6 +740,22 @@ struct xhci_stream_info { #define SMALL_STREAM_ARRAY_SIZE 256 #define MEDIUM_STREAM_ARRAY_SIZE 1024 +/* Some Intel xHCI host controllers need software to keep track of the bus + * bandwidth. Keep track of endpoint info here. Each root port is allocated + * the full bus bandwidth. We must also treat TTs (including each port under a + * multi-TT hub) as a separate bandwidth domain. The direct memory interface + * (DMI) also limits the total bandwidth (across all domains) that can be used. + */ +struct xhci_bw_info { + unsigned int ep_interval; + /* mult and num_packets are zero-based */ + unsigned int mult; + unsigned int num_packets; + unsigned int max_packet_size; + unsigned int max_esit_payload; + unsigned int type; +}; + struct xhci_virt_ep { struct xhci_ring *ring; /* Related to endpoints that are configured to use stream IDs only */ @@ -772,6 +797,7 @@ struct xhci_virt_ep { * process the missed tds on the endpoint ring. */ bool skip; + struct xhci_bw_info bw_info; }; enum xhci_overhead_type { @@ -1485,6 +1511,11 @@ unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc); unsigned int xhci_get_endpoint_flag_from_index(unsigned int ep_index); unsigned int xhci_last_valid_endpoint(u32 added_ctxs); void xhci_endpoint_zero(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_host_endpoint *ep); +void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info); +void xhci_update_bw_info(struct xhci_hcd *xhci, + struct xhci_container_ctx *in_ctx, + struct xhci_input_control_ctx *ctrl_ctx, + struct xhci_virt_device *virt_dev); void xhci_endpoint_copy(struct xhci_hcd *xhci, struct xhci_container_ctx *in_ctx, struct xhci_container_ctx *out_ctx, -- cgit v0.10.2 From 2e27980e6eb78114c4ecbaad1ba71836e3887d18 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Sep 2011 11:05:50 -0700 Subject: xhci: Track interval bandwidth tables per port/TT. In order to update the root port or TT's bandwidth interval table, we will need to keep track of a list of endpoints, per interval. That way we can easily know the new largest max packet size when we have to remove an endpoint. Add an endpoint list for each root port or TT structure, sorted by endpoint max packet size. Insert new endpoints into the list such that the head of the list always has the endpoint with the greatest max packet size. Only insert endpoints and update the interval table with new information when those endpoints are periodic. Make sure to update the number of active TTs when we add or drop periodic endpoints. A TT is only considered active if it has one or more periodic endpoints attached (control and bulk are best effort, and counted in the 20% reserved on the high speed bus). If the number of active endpoints for a TT was zero, and it's now non-zero, increment the number of active TTs for the rootport. If the number of active endpoints was non-zero, and it's now zero, decrement the number of active TTs. We have to be careful when we're checking the bandwidth for a new configuration/alt setting. If we don't have enough bandwidth, we need to be able to "roll back" the bandwidth information stored in the endpoint and the root port/TT interval bandwidth table. We can't just create a copy of the interval bandwidth table, modify it, and check the bandwidth with the copy because we have lists of endpoints and entries can't be on more than one list. Instead, we copy the old endpoint bandwidth information, and use it to revert the interval table when the bandwidth check fails. We don't check the bandwidth after endpoints are dropped from the interval table when a device is reset or freed after a disconnect, because having endpoints use less bandwidth should not push the bandwidth usage over the limits. Besides which, we can't fail a device disconnect. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 9451d94..1c5c9ba 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -783,6 +783,7 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) { struct xhci_virt_device *dev; int i; + int old_active_eps = 0; /* Slot ID 0 is reserved */ if (slot_id == 0 || !xhci->devs[slot_id]) @@ -793,15 +794,29 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id) if (!dev) return; + if (dev->tt_info) + old_active_eps = dev->tt_info->active_eps; + for (i = 0; i < 31; ++i) { if (dev->eps[i].ring) xhci_ring_free(xhci, dev->eps[i].ring); if (dev->eps[i].stream_info) xhci_free_stream_info(xhci, dev->eps[i].stream_info); + /* Endpoints on the TT/root port lists should have been removed + * when usb_disable_device() was called for the device. + * We can't drop them anyway, because the udev might have gone + * away by this point, and we can't tell what speed it was. + */ + if (!list_empty(&dev->eps[i].bw_endpoint_list)) + xhci_warn(xhci, "Slot %u endpoint %u " + "not removed from BW list!\n", + slot_id, i); } /* If this is a hub, free the TT(s) from the TT list */ xhci_free_tt_info(xhci, dev, slot_id); + /* If necessary, update the number of active TTs on this root port */ + xhci_update_tt_active_eps(xhci, dev, old_active_eps); if (dev->ring_cache) { for (i = 0; i < dev->num_rings_cached; i++) @@ -855,6 +870,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, for (i = 0; i < 31; i++) { xhci_init_endpoint_timer(xhci, &dev->eps[i]); INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list); + INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list); } /* Allocate endpoint 0 ring */ @@ -1994,7 +2010,7 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) __le32 __iomem *addr; u32 offset; unsigned int num_ports; - int i, port_index; + int i, j, port_index; addr = &xhci->cap_regs->hcc_params; offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr)); @@ -2012,8 +2028,14 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) xhci->rh_bw = kzalloc(sizeof(*xhci->rh_bw)*num_ports, flags); if (!xhci->rh_bw) return -ENOMEM; - for (i = 0; i < num_ports; i++) + for (i = 0; i < num_ports; i++) { + struct xhci_interval_bw_table *bw_table; + INIT_LIST_HEAD(&xhci->rh_bw[i].tts); + bw_table = &xhci->rh_bw[i].bw_table; + for (j = 0; j < XHCI_MAX_INTERVAL; j++) + INIT_LIST_HEAD(&bw_table->interval_bw[j].endpoints); + } /* * For whatever reason, the first capability offset is from the diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 8279146..51c4d38 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1747,6 +1747,241 @@ static void xhci_finish_resource_reservation(struct xhci_hcd *xhci, xhci->num_active_eps); } +/* Run the algorithm on the bandwidth table. If this table is part of a + * TT, see if we need to update the number of active TTs. + */ +static int xhci_check_bw_table(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps) +{ + return 0; +} + +static bool xhci_is_async_ep(unsigned int ep_type) +{ + return (ep_type != ISOC_OUT_EP && ep_type != INT_OUT_EP && + ep_type != ISOC_IN_EP && + ep_type != INT_IN_EP); +} + +void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, + struct xhci_bw_info *ep_bw, + struct xhci_interval_bw_table *bw_table, + struct usb_device *udev, + struct xhci_virt_ep *virt_ep, + struct xhci_tt_bw_info *tt_info) +{ + struct xhci_interval_bw *interval_bw; + int normalized_interval; + + if (xhci_is_async_ep(ep_bw->type) || + list_empty(&virt_ep->bw_endpoint_list)) + return; + + /* For LS/FS devices, we need to translate the interval expressed in + * microframes to frames. + */ + if (udev->speed == USB_SPEED_HIGH) + normalized_interval = ep_bw->ep_interval; + else + normalized_interval = ep_bw->ep_interval - 3; + + if (normalized_interval == 0) + bw_table->interval0_esit_payload -= ep_bw->max_esit_payload; + interval_bw = &bw_table->interval_bw[normalized_interval]; + interval_bw->num_packets -= ep_bw->num_packets; + switch (udev->speed) { + case USB_SPEED_LOW: + interval_bw->overhead[LS_OVERHEAD_TYPE] -= 1; + break; + case USB_SPEED_FULL: + interval_bw->overhead[FS_OVERHEAD_TYPE] -= 1; + break; + case USB_SPEED_HIGH: + interval_bw->overhead[HS_OVERHEAD_TYPE] -= 1; + break; + case USB_SPEED_SUPER: + case USB_SPEED_UNKNOWN: + case USB_SPEED_WIRELESS: + /* Should never happen because only LS/FS/HS endpoints will get + * added to the endpoint list. + */ + return; + } + if (tt_info) + tt_info->active_eps -= 1; + list_del_init(&virt_ep->bw_endpoint_list); +} + +static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci, + struct xhci_bw_info *ep_bw, + struct xhci_interval_bw_table *bw_table, + struct usb_device *udev, + struct xhci_virt_ep *virt_ep, + struct xhci_tt_bw_info *tt_info) +{ + struct xhci_interval_bw *interval_bw; + struct xhci_virt_ep *smaller_ep; + int normalized_interval; + + if (xhci_is_async_ep(ep_bw->type)) + return; + + /* For LS/FS devices, we need to translate the interval expressed in + * microframes to frames. + */ + if (udev->speed == USB_SPEED_HIGH) + normalized_interval = ep_bw->ep_interval; + else + normalized_interval = ep_bw->ep_interval - 3; + + if (normalized_interval == 0) + bw_table->interval0_esit_payload += ep_bw->max_esit_payload; + interval_bw = &bw_table->interval_bw[normalized_interval]; + interval_bw->num_packets += ep_bw->num_packets; + switch (udev->speed) { + case USB_SPEED_LOW: + interval_bw->overhead[LS_OVERHEAD_TYPE] += 1; + break; + case USB_SPEED_FULL: + interval_bw->overhead[FS_OVERHEAD_TYPE] += 1; + break; + case USB_SPEED_HIGH: + interval_bw->overhead[HS_OVERHEAD_TYPE] += 1; + break; + case USB_SPEED_SUPER: + case USB_SPEED_UNKNOWN: + case USB_SPEED_WIRELESS: + /* Should never happen because only LS/FS/HS endpoints will get + * added to the endpoint list. + */ + return; + } + + if (tt_info) + tt_info->active_eps += 1; + /* Insert the endpoint into the list, largest max packet size first. */ + list_for_each_entry(smaller_ep, &interval_bw->endpoints, + bw_endpoint_list) { + if (ep_bw->max_packet_size >= + smaller_ep->bw_info.max_packet_size) { + /* Add the new ep before the smaller endpoint */ + list_add_tail(&virt_ep->bw_endpoint_list, + &smaller_ep->bw_endpoint_list); + return; + } + } + /* Add the new endpoint at the end of the list. */ + list_add_tail(&virt_ep->bw_endpoint_list, + &interval_bw->endpoints); +} + +void xhci_update_tt_active_eps(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps) +{ + struct xhci_root_port_bw_info *rh_bw_info; + if (!virt_dev->tt_info) + return; + + rh_bw_info = &xhci->rh_bw[virt_dev->real_port - 1]; + if (old_active_eps == 0 && + virt_dev->tt_info->active_eps != 0) { + rh_bw_info->num_active_tts += 1; + } else if (old_active_eps != 0 && + virt_dev->tt_info->active_eps == 0) { + rh_bw_info->num_active_tts -= 1; + } +} + +static int xhci_reserve_bandwidth(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + struct xhci_container_ctx *in_ctx) +{ + struct xhci_bw_info ep_bw_info[31]; + int i; + struct xhci_input_control_ctx *ctrl_ctx; + int old_active_eps = 0; + + if (virt_dev->udev->speed == USB_SPEED_SUPER) + return 0; + + if (virt_dev->tt_info) + old_active_eps = virt_dev->tt_info->active_eps; + + ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); + + for (i = 0; i < 31; i++) { + if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i)) + continue; + + /* Make a copy of the BW info in case we need to revert this */ + memcpy(&ep_bw_info[i], &virt_dev->eps[i].bw_info, + sizeof(ep_bw_info[i])); + /* Drop the endpoint from the interval table if the endpoint is + * being dropped or changed. + */ + if (EP_IS_DROPPED(ctrl_ctx, i)) + xhci_drop_ep_from_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + /* Overwrite the information stored in the endpoints' bw_info */ + xhci_update_bw_info(xhci, virt_dev->in_ctx, ctrl_ctx, virt_dev); + for (i = 0; i < 31; i++) { + /* Add any changed or added endpoints to the interval table */ + if (EP_IS_ADDED(ctrl_ctx, i)) + xhci_add_ep_to_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + + if (!xhci_check_bw_table(xhci, virt_dev, old_active_eps)) { + /* Ok, this fits in the bandwidth we have. + * Update the number of active TTs. + */ + xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps); + return 0; + } + + /* We don't have enough bandwidth for this, revert the stored info. */ + for (i = 0; i < 31; i++) { + if (!EP_IS_ADDED(ctrl_ctx, i) && !EP_IS_DROPPED(ctrl_ctx, i)) + continue; + + /* Drop the new copies of any added or changed endpoints from + * the interval table. + */ + if (EP_IS_ADDED(ctrl_ctx, i)) { + xhci_drop_ep_from_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + /* Revert the endpoint back to its old information */ + memcpy(&virt_dev->eps[i].bw_info, &ep_bw_info[i], + sizeof(ep_bw_info[i])); + /* Add any changed or dropped endpoints back into the table */ + if (EP_IS_DROPPED(ctrl_ctx, i)) + xhci_add_ep_to_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + virt_dev->udev, + &virt_dev->eps[i], + virt_dev->tt_info); + } + return -ENOMEM; +} + + /* Issue a configure endpoint command or evaluate context command * and wait for it to finish. */ @@ -1779,6 +2014,14 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, xhci->num_active_eps); return -ENOMEM; } + if ((xhci->quirks & XHCI_SW_BW_CHECKING) && + xhci_reserve_bandwidth(xhci, virt_dev, in_ctx)) { + if ((xhci->quirks & XHCI_EP_LIMIT_QUIRK)) + xhci_free_host_resources(xhci, in_ctx); + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_warn(xhci, "Not enough bandwidth\n"); + return -ENOMEM; + } if (command) { cmd_completion = command->completion; @@ -1912,7 +2155,6 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); } - xhci_update_bw_info(xhci, virt_dev->in_ctx, ctrl_ctx, virt_dev); xhci_zero_in_ctx(xhci, virt_dev); /* * Install any rings for completely new endpoints or changed endpoints, @@ -2528,6 +2770,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) int timeleft; int last_freed_endpoint; struct xhci_slot_ctx *slot_ctx; + int old_active_eps = 0; ret = xhci_check_args(hcd, udev, NULL, 0, false, __func__); if (ret <= 0) @@ -2669,8 +2912,18 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); last_freed_endpoint = i; } + if (!list_empty(&virt_dev->eps[i].bw_endpoint_list)) + xhci_drop_ep_from_interval_table(xhci, + &virt_dev->eps[i].bw_info, + virt_dev->bw_table, + udev, + &virt_dev->eps[i], + virt_dev->tt_info); xhci_clear_endpoint_bw_info(&virt_dev->eps[i].bw_info); } + /* If necessary, update the number of active TTs on this root port */ + xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps); + xhci_dbg(xhci, "Output context after successful reset device cmd:\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint); ret = 0; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index af15b90..050f07b 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -797,7 +797,9 @@ struct xhci_virt_ep { * process the missed tds on the endpoint ring. */ bool skip; + /* Bandwidth checking storage */ struct xhci_bw_info bw_info; + struct list_head bw_endpoint_list; }; enum xhci_overhead_type { @@ -808,6 +810,10 @@ enum xhci_overhead_type { struct xhci_interval_bw { unsigned int num_packets; + /* Sorted by max packet size. + * Head of the list is the greatest max packet size. + */ + struct list_head endpoints; /* How many endpoints of each speed are present. */ unsigned int overhead[3]; }; @@ -1511,6 +1517,15 @@ unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc); unsigned int xhci_get_endpoint_flag_from_index(unsigned int ep_index); unsigned int xhci_last_valid_endpoint(u32 added_ctxs); void xhci_endpoint_zero(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_host_endpoint *ep); +void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, + struct xhci_bw_info *ep_bw, + struct xhci_interval_bw_table *bw_table, + struct usb_device *udev, + struct xhci_virt_ep *virt_ep, + struct xhci_tt_bw_info *tt_info); +void xhci_update_tt_active_eps(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps); void xhci_clear_endpoint_bw_info(struct xhci_bw_info *bw_info); void xhci_update_bw_info(struct xhci_hcd *xhci, struct xhci_container_ctx *in_ctx, -- cgit v0.10.2 From c29eea621900f18287d50519f72cb9113746d75a Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Sep 2011 11:05:52 -0700 Subject: xhci: Implement HS/FS/LS bandwidth checking. Now that we have a bandwidth interval table per root port or TT that describes the endpoint bandwidth information, we can finally use it to check whether the bus bandwidth is oversubscribed for a new device configuration/alternate interface setting. The complication for this algorithm is that the bit of hardware logic that creates the bus schedule is only 12-bit logic. In order to make sure it can represent the maximum bus bandwidth in 12 bits, it has to convert the endpoint max packet size and max esit payload into "blocks" (basically a less-precise representation). The block size for each speed of device is different, aside from low speed and full speed. In order to make sure we don't allow a setup where the scheduler might fail, we also have to do the bandwidth checking in blocks. After checking that the endpoints fit in the schedule, we store the bandwidth used for this root port or TT. If this is a FS/LS device under an external HS hub, we also update the TT bandwidth and the root port bandwidth (if this is a newly activated or deactivated TT). I won't go into the details of the algorithm, as it's pretty well documented in the comments. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 51c4d38..40b82f7 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1747,13 +1747,275 @@ static void xhci_finish_resource_reservation(struct xhci_hcd *xhci, xhci->num_active_eps); } -/* Run the algorithm on the bandwidth table. If this table is part of a - * TT, see if we need to update the number of active TTs. +unsigned int xhci_get_block_size(struct usb_device *udev) +{ + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + return FS_BLOCK; + case USB_SPEED_HIGH: + return HS_BLOCK; + case USB_SPEED_SUPER: + return SS_BLOCK; + case USB_SPEED_UNKNOWN: + case USB_SPEED_WIRELESS: + default: + /* Should never happen */ + return 1; + } +} + +unsigned int xhci_get_largest_overhead(struct xhci_interval_bw *interval_bw) +{ + if (interval_bw->overhead[LS_OVERHEAD_TYPE]) + return LS_OVERHEAD; + if (interval_bw->overhead[FS_OVERHEAD_TYPE]) + return FS_OVERHEAD; + return HS_OVERHEAD; +} + +/* If we are changing a LS/FS device under a HS hub, + * make sure (if we are activating a new TT) that the HS bus has enough + * bandwidth for this new TT. + */ +static int xhci_check_tt_bw_table(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev, + int old_active_eps) +{ + struct xhci_interval_bw_table *bw_table; + struct xhci_tt_bw_info *tt_info; + + /* Find the bandwidth table for the root port this TT is attached to. */ + bw_table = &xhci->rh_bw[virt_dev->real_port - 1].bw_table; + tt_info = virt_dev->tt_info; + /* If this TT already had active endpoints, the bandwidth for this TT + * has already been added. Removing all periodic endpoints (and thus + * making the TT enactive) will only decrease the bandwidth used. + */ + if (old_active_eps) + return 0; + if (old_active_eps == 0 && tt_info->active_eps != 0) { + if (bw_table->bw_used + TT_HS_OVERHEAD > HS_BW_LIMIT) + return -ENOMEM; + return 0; + } + /* Not sure why we would have no new active endpoints... + * + * Maybe because of an Evaluate Context change for a hub update or a + * control endpoint 0 max packet size change? + * FIXME: skip the bandwidth calculation in that case. + */ + return 0; +} + +/* + * This algorithm is a very conservative estimate of the worst-case scheduling + * scenario for any one interval. The hardware dynamically schedules the + * packets, so we can't tell which microframe could be the limiting factor in + * the bandwidth scheduling. This only takes into account periodic endpoints. + * + * Obviously, we can't solve an NP complete problem to find the minimum worst + * case scenario. Instead, we come up with an estimate that is no less than + * the worst case bandwidth used for any one microframe, but may be an + * over-estimate. + * + * We walk the requirements for each endpoint by interval, starting with the + * smallest interval, and place packets in the schedule where there is only one + * possible way to schedule packets for that interval. In order to simplify + * this algorithm, we record the largest max packet size for each interval, and + * assume all packets will be that size. + * + * For interval 0, we obviously must schedule all packets for each interval. + * The bandwidth for interval 0 is just the amount of data to be transmitted + * (the sum of all max ESIT payload sizes, plus any overhead per packet times + * the number of packets). + * + * For interval 1, we have two possible microframes to schedule those packets + * in. For this algorithm, if we can schedule the same number of packets for + * each possible scheduling opportunity (each microframe), we will do so. The + * remaining number of packets will be saved to be transmitted in the gaps in + * the next interval's scheduling sequence. + * + * As we move those remaining packets to be scheduled with interval 2 packets, + * we have to double the number of remaining packets to transmit. This is + * because the intervals are actually powers of 2, and we would be transmitting + * the previous interval's packets twice in this interval. We also have to be + * sure that when we look at the largest max packet size for this interval, we + * also look at the largest max packet size for the remaining packets and take + * the greater of the two. + * + * The algorithm continues to evenly distribute packets in each scheduling + * opportunity, and push the remaining packets out, until we get to the last + * interval. Then those packets and their associated overhead are just added + * to the bandwidth used. */ static int xhci_check_bw_table(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, int old_active_eps) { + unsigned int bw_reserved; + unsigned int max_bandwidth; + unsigned int bw_used; + unsigned int block_size; + struct xhci_interval_bw_table *bw_table; + unsigned int packet_size = 0; + unsigned int overhead = 0; + unsigned int packets_transmitted = 0; + unsigned int packets_remaining = 0; + unsigned int i; + + if (virt_dev->udev->speed == USB_SPEED_HIGH) { + max_bandwidth = HS_BW_LIMIT; + /* Convert percent of bus BW reserved to blocks reserved */ + bw_reserved = DIV_ROUND_UP(HS_BW_RESERVED * max_bandwidth, 100); + } else { + max_bandwidth = FS_BW_LIMIT; + bw_reserved = DIV_ROUND_UP(FS_BW_RESERVED * max_bandwidth, 100); + } + + bw_table = virt_dev->bw_table; + /* We need to translate the max packet size and max ESIT payloads into + * the units the hardware uses. + */ + block_size = xhci_get_block_size(virt_dev->udev); + + /* If we are manipulating a LS/FS device under a HS hub, double check + * that the HS bus has enough bandwidth if we are activing a new TT. + */ + if (virt_dev->tt_info) { + xhci_dbg(xhci, "Recalculating BW for rootport %u\n", + virt_dev->real_port); + if (xhci_check_tt_bw_table(xhci, virt_dev, old_active_eps)) { + xhci_warn(xhci, "Not enough bandwidth on HS bus for " + "newly activated TT.\n"); + return -ENOMEM; + } + xhci_dbg(xhci, "Recalculating BW for TT slot %u port %u\n", + virt_dev->tt_info->slot_id, + virt_dev->tt_info->ttport); + } else { + xhci_dbg(xhci, "Recalculating BW for rootport %u\n", + virt_dev->real_port); + } + + /* Add in how much bandwidth will be used for interval zero, or the + * rounded max ESIT payload + number of packets * largest overhead. + */ + bw_used = DIV_ROUND_UP(bw_table->interval0_esit_payload, block_size) + + bw_table->interval_bw[0].num_packets * + xhci_get_largest_overhead(&bw_table->interval_bw[0]); + + for (i = 1; i < XHCI_MAX_INTERVAL; i++) { + unsigned int bw_added; + unsigned int largest_mps; + unsigned int interval_overhead; + + /* + * How many packets could we transmit in this interval? + * If packets didn't fit in the previous interval, we will need + * to transmit that many packets twice within this interval. + */ + packets_remaining = 2 * packets_remaining + + bw_table->interval_bw[i].num_packets; + + /* Find the largest max packet size of this or the previous + * interval. + */ + if (list_empty(&bw_table->interval_bw[i].endpoints)) + largest_mps = 0; + else { + struct xhci_virt_ep *virt_ep; + struct list_head *ep_entry; + + ep_entry = bw_table->interval_bw[i].endpoints.next; + virt_ep = list_entry(ep_entry, + struct xhci_virt_ep, bw_endpoint_list); + /* Convert to blocks, rounding up */ + largest_mps = DIV_ROUND_UP( + virt_ep->bw_info.max_packet_size, + block_size); + } + if (largest_mps > packet_size) + packet_size = largest_mps; + + /* Use the larger overhead of this or the previous interval. */ + interval_overhead = xhci_get_largest_overhead( + &bw_table->interval_bw[i]); + if (interval_overhead > overhead) + overhead = interval_overhead; + + /* How many packets can we evenly distribute across + * (1 << (i + 1)) possible scheduling opportunities? + */ + packets_transmitted = packets_remaining >> (i + 1); + + /* Add in the bandwidth used for those scheduled packets */ + bw_added = packets_transmitted * (overhead + packet_size); + + /* How many packets do we have remaining to transmit? */ + packets_remaining = packets_remaining % (1 << (i + 1)); + + /* What largest max packet size should those packets have? */ + /* If we've transmitted all packets, don't carry over the + * largest packet size. + */ + if (packets_remaining == 0) { + packet_size = 0; + overhead = 0; + } else if (packets_transmitted > 0) { + /* Otherwise if we do have remaining packets, and we've + * scheduled some packets in this interval, take the + * largest max packet size from endpoints with this + * interval. + */ + packet_size = largest_mps; + overhead = interval_overhead; + } + /* Otherwise carry over packet_size and overhead from the last + * time we had a remainder. + */ + bw_used += bw_added; + if (bw_used > max_bandwidth) { + xhci_warn(xhci, "Not enough bandwidth. " + "Proposed: %u, Max: %u\n", + bw_used, max_bandwidth); + return -ENOMEM; + } + } + /* + * Ok, we know we have some packets left over after even-handedly + * scheduling interval 15. We don't know which microframes they will + * fit into, so we over-schedule and say they will be scheduled every + * microframe. + */ + if (packets_remaining > 0) + bw_used += overhead + packet_size; + + if (!virt_dev->tt_info && virt_dev->udev->speed == USB_SPEED_HIGH) { + unsigned int port_index = virt_dev->real_port - 1; + + /* OK, we're manipulating a HS device attached to a + * root port bandwidth domain. Include the number of active TTs + * in the bandwidth used. + */ + bw_used += TT_HS_OVERHEAD * + xhci->rh_bw[port_index].num_active_tts; + } + + xhci_dbg(xhci, "Final bandwidth: %u, Limit: %u, Reserved: %u, " + "Available: %u " "percent\n", + bw_used, max_bandwidth, bw_reserved, + (max_bandwidth - bw_used - bw_reserved) * 100 / + max_bandwidth); + + bw_used += bw_reserved; + if (bw_used > max_bandwidth) { + xhci_warn(xhci, "Not enough bandwidth. Proposed: %u, Max: %u\n", + bw_used, max_bandwidth); + return -ENOMEM; + } + + bw_table->bw_used = bw_used; return 0; } @@ -1888,9 +2150,11 @@ void xhci_update_tt_active_eps(struct xhci_hcd *xhci, if (old_active_eps == 0 && virt_dev->tt_info->active_eps != 0) { rh_bw_info->num_active_tts += 1; + rh_bw_info->bw_table.bw_used += TT_HS_OVERHEAD; } else if (old_active_eps != 0 && virt_dev->tt_info->active_eps == 0) { rh_bw_info->num_active_tts -= 1; + rh_bw_info->bw_table.bw_used -= TT_HS_OVERHEAD; } } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 050f07b..b224b20 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -756,6 +756,49 @@ struct xhci_bw_info { unsigned int type; }; +/* "Block" sizes in bytes the hardware uses for different device speeds. + * The logic in this part of the hardware limits the number of bits the hardware + * can use, so must represent bandwidth in a less precise manner to mimic what + * the scheduler hardware computes. + */ +#define FS_BLOCK 1 +#define HS_BLOCK 4 +#define SS_BLOCK 16 +#define DMI_BLOCK 32 + +/* Each device speed has a protocol overhead (CRC, bit stuffing, etc) associated + * with each byte transferred. SuperSpeed devices have an initial overhead to + * set up bursts. These are in blocks, see above. LS overhead has already been + * translated into FS blocks. + */ +#define DMI_OVERHEAD 8 +#define DMI_OVERHEAD_BURST 4 +#define SS_OVERHEAD 8 +#define SS_OVERHEAD_BURST 32 +#define HS_OVERHEAD 26 +#define FS_OVERHEAD 20 +#define LS_OVERHEAD 128 +/* The TTs need to claim roughly twice as much bandwidth (94 bytes per + * microframe ~= 24Mbps) of the HS bus as the devices can actually use because + * of overhead associated with split transfers crossing microframe boundaries. + * 31 blocks is pure protocol overhead. + */ +#define TT_HS_OVERHEAD (31 + 94) +#define TT_DMI_OVERHEAD (25 + 12) + +/* Bandwidth limits in blocks */ +#define FS_BW_LIMIT 1285 +#define TT_BW_LIMIT 1320 +#define HS_BW_LIMIT 1607 +#define SS_BW_LIMIT_IN 3906 +#define DMI_BW_LIMIT_IN 3906 +#define SS_BW_LIMIT_OUT 3906 +#define DMI_BW_LIMIT_OUT 3906 + +/* Percentage of bus bandwidth reserved for non-periodic transfers */ +#define FS_BW_RESERVED 10 +#define HS_BW_RESERVED 20 + struct xhci_virt_ep { struct xhci_ring *ring; /* Related to endpoints that are configured to use stream IDs only */ @@ -823,6 +866,8 @@ struct xhci_interval_bw { struct xhci_interval_bw_table { unsigned int interval0_esit_payload; struct xhci_interval_bw interval_bw[XHCI_MAX_INTERVAL]; + /* Includes reserved bandwidth for async endpoints */ + unsigned int bw_used; }; @@ -1397,6 +1442,7 @@ struct xhci_hcd { #define XHCI_EP_LIMIT_QUIRK (1 << 5) #define XHCI_BROKEN_MSI (1 << 6) #define XHCI_RESET_ON_RESUME (1 << 7) +#define XHCI_SW_BW_CHECKING (1 << 8) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ -- cgit v0.10.2 From 86cc558ea52b123d7657e609557d60dc121eedd6 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Sep 2011 11:05:54 -0700 Subject: xhci: Add software BW checking quirk to Intel PPT xHCI The xHCI host controller in the Intel Panther Point chipset needs to have software check whether new devices will fit in the available bus bandwidth. Activate the software bandwidth checking quirk when we find the right PCI device. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index cb16de2..50d3fcf 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -136,6 +136,7 @@ static int xhci_pci_setup(struct usb_hcd *hcd) xhci->quirks |= XHCI_SPURIOUS_SUCCESS; xhci->quirks |= XHCI_EP_LIMIT_QUIRK; xhci->limit_active_eps = 64; + xhci->quirks |= XHCI_SW_BW_CHECKING; } if (pdev->vendor == PCI_VENDOR_ID_ETRON && pdev->device == PCI_DEVICE_ID_ASROCK_P67) { -- cgit v0.10.2 From fd984d242afbe13bd94c60b6754feda93be69bd7 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Fri, 2 Sep 2011 11:05:56 -0700 Subject: xhci: Don't print short isoc packets. Now that the xHCI driver always return a status value of zero for isochronous URBs, when the last TD of an isochronous URB is short, the local variable "status" stays set to -EINPROGRESS. When xHCI driver debugging is turned on, this causes the log file to fill with messages like this: [ 38.859282] xhci_hcd 0000:00:14.0: Giveback URB ffff88013ad47800, len = 1408, expected = 580, status = -115 Don't print out the status of an URB for isochronous URBs. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 58a6e26..d14f3fb 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2173,7 +2173,8 @@ cleanup: if ((urb->actual_length != urb->transfer_buffer_length && (urb->transfer_flags & URB_SHORT_NOT_OK)) || - status != 0) + (status != 0 && + !usb_endpoint_xfer_isoc(&urb->ep->desc))) xhci_dbg(xhci, "Giveback URB %p, len = %d, " "expected = %x, status = %d\n", urb, urb->actual_length, -- cgit v0.10.2 From 2ffdea25f054439c31c24b248faa647685280571 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 2 Sep 2011 11:05:57 -0700 Subject: xHCI: refine td allocation In xhci_urb_enqueue(), allocate a block of memory for all the TDs instead of allocating memory for each of them separately. This reduces the number of kzalloc calling when an isochronous usb is submitted. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 1c5c9ba..c4b8959 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1666,18 +1666,10 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci, void xhci_urb_free_priv(struct xhci_hcd *xhci, struct urb_priv *urb_priv) { - int last; - - if (!urb_priv) - return; - - last = urb_priv->length - 1; - if (last >= 0) { - int i; - for (i = 0; i <= last; i++) - kfree(urb_priv->td[i]); + if (urb_priv) { + kfree(urb_priv->td[0]); + kfree(urb_priv); } - kfree(urb_priv); } void xhci_free_command(struct xhci_hcd *xhci, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 40b82f7..6440bd2 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1035,6 +1035,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_td *buffer; unsigned long flags; int ret = 0; unsigned int slot_id, ep_index; @@ -1065,13 +1066,15 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) if (!urb_priv) return -ENOMEM; + buffer = kzalloc(size * sizeof(struct xhci_td), mem_flags); + if (!buffer) { + kfree(urb_priv); + return -ENOMEM; + } + for (i = 0; i < size; i++) { - urb_priv->td[i] = kzalloc(sizeof(struct xhci_td), mem_flags); - if (!urb_priv->td[i]) { - urb_priv->length = i; - xhci_urb_free_priv(xhci, urb_priv); - return -ENOMEM; - } + urb_priv->td[i] = buffer; + buffer++; } urb_priv->length = size; -- cgit v0.10.2 From 73ddc2474bcc5234fa0a4e406e015ed16d16b6c8 Mon Sep 17 00:00:00 2001 From: "sifram.rajas@gmail.com" Date: Fri, 2 Sep 2011 11:06:00 -0700 Subject: xhci: Redundant check in xhci_check_args for xhci->devs The xhci_hcd->devs is an array of pointers rather than pointer to pointer. Hence this check is not required. Signed-off-by: Sifram Rajas Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6440bd2..dd0dc15 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -945,8 +945,7 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, return -ENODEV; if (check_virt_dev) { - if (!udev->slot_id || !xhci->devs - || !xhci->devs[udev->slot_id]) { + if (!udev->slot_id || !xhci->devs[udev->slot_id]) { printk(KERN_DEBUG "xHCI %s called with unaddressed " "device\n", func); return -EINVAL; -- cgit v0.10.2 From e16da02fcdf1c5e824432f88abf42623dafdf191 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Thu, 11 Nov 2010 15:43:11 -0800 Subject: USB: storage: Use normalized sense when emulating autosense This patch solves two things: 1) Enables autosense emulation code to correctly interpret descriptor format sense data, and 2) Fixes a bug whereby the autosense emulation code would overwrite descriptor format sense data with SENSE KEY HARDWARE ERROR in fixed format, to incorrectly look like this: Oct 21 14:11:07 localhost kernel: sd 7:0:0:0: [sdc] Sense Key : Recovered Error [current] [descriptor] Oct 21 14:11:07 localhost kernel: Descriptor sense data with sense descriptors (in hex): Oct 21 14:11:07 localhost kernel: 72 01 04 1d 00 00 00 0e 09 0c 00 00 00 00 00 00 Oct 21 14:11:07 localhost kernel: 00 4f 00 c2 00 50 Oct 21 14:11:07 localhost kernel: sd 7:0:0:0: [sdc] ASC=0x4 ASCQ=0x1d Signed-off-by: Luben Tuikov Acked-by: Alan Stern Acked-by: Matthew Dharm Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index e8ae21b..ff32390 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -691,6 +691,9 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) int temp_result; struct scsi_eh_save ses; int sense_size = US_SENSE_SIZE; + struct scsi_sense_hdr sshdr; + const u8 *scdd; + u8 fm_ili; /* device supports and needs bigger sense buffer */ if (us->fflags & US_FL_SANE_SENSE) @@ -774,32 +777,30 @@ Retry_Sense: srb->sense_buffer[7] = (US_SENSE_SIZE - 8); } + scsi_normalize_sense(srb->sense_buffer, SCSI_SENSE_BUFFERSIZE, + &sshdr); + US_DEBUGP("-- Result from auto-sense is %d\n", temp_result); US_DEBUGP("-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", - srb->sense_buffer[0], - srb->sense_buffer[2] & 0xf, - srb->sense_buffer[12], - srb->sense_buffer[13]); + sshdr.response_code, sshdr.sense_key, + sshdr.asc, sshdr.ascq); #ifdef CONFIG_USB_STORAGE_DEBUG - usb_stor_show_sense( - srb->sense_buffer[2] & 0xf, - srb->sense_buffer[12], - srb->sense_buffer[13]); + usb_stor_show_sense(sshdr.sense_key, sshdr.asc, sshdr.ascq); #endif /* set the result so the higher layers expect this data */ srb->result = SAM_STAT_CHECK_CONDITION; + scdd = scsi_sense_desc_find(srb->sense_buffer, + SCSI_SENSE_BUFFERSIZE, 4); + fm_ili = (scdd ? scdd[3] : srb->sense_buffer[2]) & 0xA0; + /* We often get empty sense data. This could indicate that * everything worked or that there was an unspecified * problem. We have to decide which. */ - if ( /* Filemark 0, ignore EOM, ILI 0, no sense */ - (srb->sense_buffer[2] & 0xaf) == 0 && - /* No ASC or ASCQ */ - srb->sense_buffer[12] == 0 && - srb->sense_buffer[13] == 0) { - + if (sshdr.sense_key == 0 && sshdr.asc == 0 && sshdr.ascq == 0 && + fm_ili == 0) { /* If things are really okay, then let's show that. * Zero out the sense buffer so the higher layers * won't realize we did an unsolicited auto-sense. @@ -814,7 +815,10 @@ Retry_Sense: */ } else { srb->result = DID_ERROR << 16; - srb->sense_buffer[2] = HARDWARE_ERROR; + if ((sshdr.response_code & 0x72) == 0x72) + srb->sense_buffer[1] = HARDWARE_ERROR; + else + srb->sense_buffer[2] = HARDWARE_ERROR; } } } -- cgit v0.10.2 From c5a48592d874ddef8c7880311581eccf0eb30c3b Mon Sep 17 00:00:00 2001 From: Jim Wylder Date: Tue, 6 Sep 2011 21:07:20 -0500 Subject: USB: for usb_autopm_get_interface_async -EINPROGRESS is not an error A return value of -EINPROGRESS from pm_runtime_get indicates that the device is already resuming due to a previous call. Internally, usb_autopm_get_interface_async doesn't treat this as an error and increments the usage count, but passes the error status along to the caller. The logical assumption of the caller is that any negative return value reflects the device not resuming and the pm_usage_cnt not being incremented. Since the usage count is being incremented and the device is resuming, return success (0) instead. Signed-off-by: James Wylder Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 34e3da5..14b83f2 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1583,7 +1583,7 @@ int usb_autopm_get_interface_async(struct usb_interface *intf) dev_vdbg(&intf->dev, "%s: cnt %d -> %d\n", __func__, atomic_read(&intf->dev.power.usage_count), status); - if (status > 0) + if (status > 0 || status == -EINPROGRESS) status = 0; return status; } -- cgit v0.10.2 From de76cc2ba2116322f1bcc26f5b22d6092bb63a0d Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Fri, 2 Sep 2011 08:17:17 +0200 Subject: musb_gadget: Fix for spurious interrupts on endpoint zero. There is a multi-year old bug in the MUSB hardware which is not documented. It causes spurious interrupts and have various symptoms, like endless "SetupEnd came in a wrong ep0stage" messages. The fix is taken from the FreeBSD's musb driver. How to reproduce: For example issue clear-stall on a couple of endpoints very fast, like one request per 125us. After a while the bug triggers and the musb-chip becomes unusable until next re-enumeration. Signed-off-by: Hans Petter Selasky Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 9378b35..6a0d046 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -679,6 +679,14 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) musb_readb(mbase, MUSB_FADDR), decode_ep0stage(musb->ep0_state)); + if (csr & MUSB_CSR0_P_DATAEND) { + /* + * If DATAEND is set we should not call the callback, + * hence the status stage is not complete. + */ + return IRQ_HANDLED; + } + /* I sent a stall.. need to acknowledge it now.. */ if (csr & MUSB_CSR0_P_SENTSTALL) { musb_writew(regs, MUSB_CSR0, -- cgit v0.10.2 From 28c9fc68ebd32d473a8787d05c74e3f39c6c866b Mon Sep 17 00:00:00 2001 From: Klaus Schwarzkopf Date: Fri, 9 Sep 2011 16:10:44 +0200 Subject: usb gadget: clean up FSF boilerplate text remove the following two paragraphs as they are not needed: This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc.,59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Signed-off-by: Klaus Schwarzkopf Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index d65d839..ded8d6f 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/amd5536udc.h index 1d1c754..f87e29c 100644 --- a/drivers/usb/gadget/amd5536udc.h +++ b/drivers/usb/gadget/amd5536udc.h @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef AMD5536UDC_H diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index d01fa5b..0a1b6d1 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -9,16 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. */ #undef VERBOSE_DEBUG diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h index 108ca54..3c0315b 100644 --- a/drivers/usb/gadget/at91_udc.h +++ b/drivers/usb/gadget/at91_udc.h @@ -7,16 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef AT91_UDC_H diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index b1c1afb..672674c 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c index 470981a..4eedfe5 100644 --- a/drivers/usb/gadget/ci13xxx_msm.c +++ b/drivers/usb/gadget/ci13xxx_msm.c @@ -3,17 +3,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License 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 Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * */ #include diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 8065464..c77aca1 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -7,15 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index b2c0013..7542a72 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -7,15 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index 8beefdd..f855ecf 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -4,7 +4,6 @@ * Copyright (C) 2010 Stephane Duverger * * Released under the GPLv2. - * */ /* verbose messages */ diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 7b06d39..ab8f1b4 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index cdca7eb..596a0b4 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -7,16 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index aafc84f..0cd764d 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 3691a0c..11c07cb 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index 046c6d0..1a7b2dd 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index c161a9a..6b1c20b 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -12,15 +12,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index 83a266b..b211342 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c @@ -7,15 +7,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c index ca660d4..6d87f28 100644 --- a/drivers/usb/gadget/f_loopback.c +++ b/drivers/usb/gadget/f_loopback.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index ae69ed7..aab8ede 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -13,15 +13,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 394502a..e3f74bf 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index 8f3eab1..3490770 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU - * General Public License 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 */ #include diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 3ea4666..704d1d9 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -11,15 +11,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c index e18b4f5..168906d 100644 --- a/drivers/usb/gadget/f_sourcesink.c +++ b/drivers/usb/gadget/f_sourcesink.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index 3dc5375..c154064 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 7a8b9aa..2022fe49 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #include diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/f_uvc.h index e18a663..abf8329 100644 --- a/drivers/usb/gadget/f_uvc.h +++ b/drivers/usb/gadget/f_uvc.h @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #ifndef _F_UVC_H_ diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index d9ee6c3..cd9cbba 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include #include diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/fusb300_udc.h index f51aa2e..92745bd 100644 --- a/drivers/usb/gadget/fusb300_udc.h +++ b/drivers/usb/gadget/fusb300_udc.h @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 704c280..0519d77 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #define pr_fmt(fmt) "g_ffs: " fmt diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c index 9fb5750..f888c3e 100644 --- a/drivers/usb/gadget/hid.c +++ b/drivers/usb/gadget/hid.c @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/gadget/imx_udc.h b/drivers/usb/gadget/imx_udc.h index 7136c24..d118fb7 100644 --- a/drivers/usb/gadget/imx_udc.h +++ b/drivers/usb/gadget/imx_udc.h @@ -8,11 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __LINUX_USB_GADGET_IMX_H diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 1b24099..a392ec0 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index 5bf9942..6e44499 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -5,16 +5,6 @@ * 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. - * */ diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h index f1d9c1b..1bec66f 100644 --- a/drivers/usb/gadget/langwell_udc.h +++ b/drivers/usb/gadget/langwell_udc.h @@ -5,16 +5,6 @@ * 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. - * */ #include diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 5e597c3..c27e5e8 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h index 7b93d57..9d9f7e3 100644 --- a/drivers/usb/gadget/m66592-udc.h +++ b/drivers/usb/gadget/m66592-udc.h @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #ifndef __M66592_UDC_H__ diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index d3eb274..d2a9dcb 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 8c7b747..7e7f515 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index 62ee508..8953003 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -14,15 +14,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define DEBUG */ diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h index df886ce..b0e52fc 100644 --- a/drivers/usb/gadget/ndis.h +++ b/drivers/usb/gadget/ndis.h @@ -10,12 +10,6 @@ * * This source code is offered for use in the public domain. You may * use, modify or distribute it freely. - * - * This code is distributed in the hope that it will be useful but - * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY - * DISCLAIMED. This includes but is not limited to warranties of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * */ #ifndef _LINUX_NDIS_H diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 8d3673f..3c14c4e 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -33,15 +33,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #undef DEBUG /* messages on error and most fault paths */ diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h index c368522..a844be0 100644 --- a/drivers/usb/gadget/net2280.h +++ b/drivers/usb/gadget/net2280.h @@ -11,15 +11,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index b7a7799..ae9c89b 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -10,15 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #undef DEBUG diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c index b69ae3e..550d6dc 100644 --- a/drivers/usb/gadget/pch_udc.c +++ b/drivers/usb/gadget/pch_udc.c @@ -4,15 +4,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index a341dde..5d3e697 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index 7862465..a33fc17 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -11,16 +11,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h index f572c56..4634cd3 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_GADGET_PXA25X_H diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index d21455f..18b6b09 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -8,16 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include #include diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h index b01696e..7f4e8f4 100644 --- a/drivers/usb/gadget/pxa27x_udc.h +++ b/drivers/usb/gadget/pxa27x_udc.h @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_GADGET_PXA27X_H diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 61d0c65..bca3f41 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h index 503f766..a373c4e 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -8,16 +8,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #ifndef __R8A66597_H__ diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 2572854..3f587b0 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -10,16 +10,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h index 9e0bece..a48f619 100644 --- a/drivers/usb/gadget/s3c2410_udc.h +++ b/drivers/usb/gadget/s3c2410_udc.h @@ -9,16 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #ifndef _S3C2410_UDC_H diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index d3dd227..571d973 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index dfed4c1..29c854b 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define VERBOSE_DEBUG */ diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index c966440..8012357 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -9,15 +9,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __U_ETHER_H diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/uvc.h index 5b79194..b80d514 100644 --- a/drivers/usb/gadget/uvc.h +++ b/drivers/usb/gadget/uvc.h @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #ifndef _UVC_GADGET_H_ diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index aa0ad34..d776adb 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #include diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index 52f8f9e..927291c 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #include diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/uvc_video.c index b08f354..b0e53a8 100644 --- a/drivers/usb/gadget/uvc_video.c +++ b/drivers/usb/gadget/uvc_video.c @@ -8,7 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ #include diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c index df6882d..668fe12 100644 --- a/drivers/usb/gadget/webcam.c +++ b/drivers/usb/gadget/webcam.c @@ -8,8 +8,8 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * */ + #include #include #include diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c index 00e2fd2..20697cc 100644 --- a/drivers/usb/gadget/zero.c +++ b/drivers/usb/gadget/zero.c @@ -8,15 +8,6 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -- cgit v0.10.2 From b4626c10928c13ee73b013dcbc23676333e79b59 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 13 Sep 2011 13:49:41 -0500 Subject: USB: option: convert interface blacklisting to bitfields It's cleaner than the array stuff, and we're about to add a bunch more blacklist entries. Second, there are devices that need both the sendsetup and the reserved interface blacklists, which the current code can't accommodate. Signed-off-by: Dan Williams Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index fe22e90..d4fc3c3 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -475,31 +475,24 @@ enum option_blacklist_reason { OPTION_BLACKLIST_RESERVED_IF = 2 }; +#define MAX_BL_NUM 8 struct option_blacklist_info { - const u32 infolen; /* number of interface numbers on blacklist */ - const u8 *ifaceinfo; /* pointer to the array holding the numbers */ - enum option_blacklist_reason reason; + /* bitfield of interface numbers for OPTION_BLACKLIST_SENDSETUP */ + const unsigned long sendsetup; + /* bitfield of interface numbers for OPTION_BLACKLIST_RESERVED_IF */ + const unsigned long reserved; }; -static const u8 four_g_w14_no_sendsetup[] = { 0, 1 }; static const struct option_blacklist_info four_g_w14_blacklist = { - .infolen = ARRAY_SIZE(four_g_w14_no_sendsetup), - .ifaceinfo = four_g_w14_no_sendsetup, - .reason = OPTION_BLACKLIST_SENDSETUP + .sendsetup = BIT(0) | BIT(1), }; -static const u8 alcatel_x200_no_sendsetup[] = { 0, 1 }; static const struct option_blacklist_info alcatel_x200_blacklist = { - .infolen = ARRAY_SIZE(alcatel_x200_no_sendsetup), - .ifaceinfo = alcatel_x200_no_sendsetup, - .reason = OPTION_BLACKLIST_SENDSETUP + .sendsetup = BIT(0) | BIT(1), }; -static const u8 zte_k3765_z_no_sendsetup[] = { 0, 1, 2 }; static const struct option_blacklist_info zte_k3765_z_blacklist = { - .infolen = ARRAY_SIZE(zte_k3765_z_no_sendsetup), - .ifaceinfo = zte_k3765_z_no_sendsetup, - .reason = OPTION_BLACKLIST_SENDSETUP + .sendsetup = BIT(0) | BIT(1) | BIT(2), }; static const struct usb_device_id option_ids[] = { @@ -1255,21 +1248,28 @@ static int option_probe(struct usb_serial *serial, return 0; } -static enum option_blacklist_reason is_blacklisted(const u8 ifnum, - const struct option_blacklist_info *blacklist) +static bool is_blacklisted(const u8 ifnum, enum option_blacklist_reason reason, + const struct option_blacklist_info *blacklist) { - const u8 *info; - int i; + unsigned long num; + const unsigned long *intf_list; if (blacklist) { - info = blacklist->ifaceinfo; + if (reason == OPTION_BLACKLIST_SENDSETUP) + intf_list = &blacklist->sendsetup; + else if (reason == OPTION_BLACKLIST_RESERVED_IF) + intf_list = &blacklist->reserved; + else { + BUG_ON(reason); + return false; + } - for (i = 0; i < blacklist->infolen; i++) { - if (info[i] == ifnum) - return blacklist->reason; + for_each_set_bit(num, intf_list, MAX_BL_NUM + 1) { + if (num == ifnum) + return true; } } - return OPTION_BLACKLIST_NONE; + return false; } static void option_instat_callback(struct urb *urb) @@ -1343,9 +1343,8 @@ static int option_send_setup(struct usb_serial_port *port) int val = 0; dbg("%s", __func__); - if (is_blacklisted(ifNum, - (struct option_blacklist_info *) intfdata->private) - == OPTION_BLACKLIST_SENDSETUP) { + if (is_blacklisted(ifNum, OPTION_BLACKLIST_SENDSETUP, + (struct option_blacklist_info *) intfdata->private)) { dbg("No send_setup on blacklisted interface #%d\n", ifNum); return -EIO; } -- cgit v0.10.2 From 0d905fd5ece4ab65e8407c450077744e1c8f661b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 13 Sep 2011 13:51:13 -0500 Subject: USB: option: convert Huawei K3765, K4505, K4605 reservered interface to blacklist That's what the blacklist is for... Signed-off-by: Dan Williams Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index d4fc3c3..fc9e874 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -495,6 +495,10 @@ static const struct option_blacklist_info zte_k3765_z_blacklist = { .sendsetup = BIT(0) | BIT(1) | BIT(2), }; +static const struct option_blacklist_info huawei_cdc12_blacklist = { + .reserved = BIT(1) | BIT(2), +}; + static const struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -592,12 +596,15 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3806, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x31) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x32) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x31) }, @@ -1207,10 +1214,35 @@ static void __exit option_exit(void) module_init(option_init); module_exit(option_exit); +static bool is_blacklisted(const u8 ifnum, enum option_blacklist_reason reason, + const struct option_blacklist_info *blacklist) +{ + unsigned long num; + const unsigned long *intf_list; + + if (blacklist) { + if (reason == OPTION_BLACKLIST_SENDSETUP) + intf_list = &blacklist->sendsetup; + else if (reason == OPTION_BLACKLIST_RESERVED_IF) + intf_list = &blacklist->reserved; + else { + BUG_ON(reason); + return false; + } + + for_each_set_bit(num, intf_list, MAX_BL_NUM + 1) { + if (num == ifnum) + return true; + } + } + return false; +} + static int option_probe(struct usb_serial *serial, const struct usb_device_id *id) { struct usb_wwan_intf_private *data; + /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */ if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID && serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 && @@ -1223,14 +1255,14 @@ static int option_probe(struct usb_serial *serial, serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff) return -ENODEV; - /* Don't bind network interfaces on Huawei K3765, K4505 & K4605 */ - if (serial->dev->descriptor.idVendor == HUAWEI_VENDOR_ID && - (serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K3765 || - serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4505 || - serial->dev->descriptor.idProduct == HUAWEI_PRODUCT_K4605) && - (serial->interface->cur_altsetting->desc.bInterfaceNumber == 1 || - serial->interface->cur_altsetting->desc.bInterfaceNumber == 2)) - return -ENODEV; + /* Don't bind reserved interfaces (like network ones) which often have + * the same class/subclass/protocol as the serial interfaces. Look at + * the Windows driver .INF files for reserved interface numbers. + */ + if (is_blacklisted( + serial->interface->cur_altsetting->desc.bInterfaceNumber, + OPTION_BLACKLIST_RESERVED_IF, + (const struct option_blacklist_info *) id->driver_info)) /* Don't bind network interface on Samsung GT-B3730, it is handled by a separate module */ if (serial->dev->descriptor.idVendor == SAMSUNG_VENDOR_ID && @@ -1239,7 +1271,6 @@ static int option_probe(struct usb_serial *serial, return -ENODEV; data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); - if (!data) return -ENOMEM; data->send_setup = option_send_setup; @@ -1248,30 +1279,6 @@ static int option_probe(struct usb_serial *serial, return 0; } -static bool is_blacklisted(const u8 ifnum, enum option_blacklist_reason reason, - const struct option_blacklist_info *blacklist) -{ - unsigned long num; - const unsigned long *intf_list; - - if (blacklist) { - if (reason == OPTION_BLACKLIST_SENDSETUP) - intf_list = &blacklist->sendsetup; - else if (reason == OPTION_BLACKLIST_RESERVED_IF) - intf_list = &blacklist->reserved; - else { - BUG_ON(reason); - return false; - } - - for_each_set_bit(num, intf_list, MAX_BL_NUM + 1) { - if (num == ifnum) - return true; - } - } - return false; -} - static void option_instat_callback(struct urb *urb) { int err; -- cgit v0.10.2 From eb05ce567a81c592c58f4bdb96eb91ce96661c30 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 13 Sep 2011 13:51:45 -0500 Subject: USB: option: add ZTE product 0x0037 to sendsetup blacklist Signed-off-by: Dan Williams Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index fc9e874..7a336f7 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -491,6 +491,10 @@ static const struct option_blacklist_info alcatel_x200_blacklist = { .sendsetup = BIT(0) | BIT(1), }; +static const struct option_blacklist_info zte_0037_blacklist = { + .sendsetup = BIT(0) | BIT(1), +}; + static const struct option_blacklist_info zte_k3765_z_blacklist = { .sendsetup = BIT(0) | BIT(1) | BIT(2), }; @@ -743,7 +747,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0037, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&zte_0037_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0038, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0039, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0040, 0xff, 0xff, 0xff) }, -- cgit v0.10.2 From c58a76cdd7ab5a945a44fd2d64f6faf40323f95b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 13 Sep 2011 13:52:52 -0500 Subject: USB: option: add various ZTE device network interfaces to the blacklist IDs found in the Windows driver's ZTEusbnet.inf file from the ZTE MF100 drivers (O2 UK). Also fixes the ZTE MF626 device since it really is distinct from the 4G Systems stick and apparently needs the net interface blacklisted too, while there's no indication (yet) that the 4G Systems stick does. Signed-off-by: Dan Williams Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 7a336f7..89ae1f6 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -497,12 +497,34 @@ static const struct option_blacklist_info zte_0037_blacklist = { static const struct option_blacklist_info zte_k3765_z_blacklist = { .sendsetup = BIT(0) | BIT(1) | BIT(2), + .reserved = BIT(4), }; static const struct option_blacklist_info huawei_cdc12_blacklist = { .reserved = BIT(1) | BIT(2), }; +static const struct option_blacklist_info net_intf1_blacklist = { + .reserved = BIT(1), +}; + +static const struct option_blacklist_info net_intf3_blacklist = { + .reserved = BIT(3), +}; + +static const struct option_blacklist_info net_intf4_blacklist = { + .reserved = BIT(4), +}; + +static const struct option_blacklist_info net_intf5_blacklist = { + .reserved = BIT(5), +}; + +static const struct option_blacklist_info zte_mf626_blacklist = { + .sendsetup = BIT(0) | BIT(1), + .reserved = BIT(4), +}; + static const struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -709,7 +731,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864E) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UC864G) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */ - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0003, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0004, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0005, 0xff, 0xff, 0xff) }, @@ -724,26 +747,30 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x000f, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0010, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0011, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0012, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0013, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0014, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF628, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0016, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0017, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0018, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0019, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0020, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0021, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0022, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0023, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0024, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0025, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, /* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0026, 0xff, 0xff, 0xff) }, */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0028, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0029, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0030, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF626, 0xff, - 0xff, 0xff), .driver_info = (kernel_ulong_t)&four_g_w14_blacklist }, + 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_mf626_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0032, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0033, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0034, 0xff, 0xff, 0xff) }, @@ -752,24 +779,30 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0038, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0039, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0040, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0042, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0043, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0044, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0048, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0049, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0050, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0051, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0052, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, /* { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0053, 0xff, 0xff, 0xff) }, */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0054, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0055, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0056, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0057, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0058, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0059, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0061, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0062, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0063, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0064, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0065, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0066, 0xff, 0xff, 0xff) }, @@ -784,11 +817,13 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0083, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0086, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0087, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0104, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0105, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0106, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0108, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0113, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&net_intf5_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0117, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0118, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0121, 0xff, 0xff, 0xff) }, -- cgit v0.10.2 From e538dfdae85244fd2c4231725d82cc1f1bc4942c Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 30 Aug 2011 17:11:19 +0200 Subject: usb: Provide usb_speed_string() function In a few places in the kernel, the code prints a human-readable USB device speed (eg. "high speed"). This involves a switch statement sometimes wrapped around in ({ ... }) block leading to code repetition. To mitigate this issue, this commit introduces usb_speed_string() function, which returns a human-readable name of provided speed. It also changes a few places switch was used to use this new function. This changes a bit the way the speed is printed in few instances at the same time standardising it. Signed-off-by: Michal Nazarewicz Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 2651852..97c4cc7 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -12,6 +12,11 @@ menuconfig USB_SUPPORT if USB_SUPPORT +config USB_COMMON + tristate + default y + depends on USB || USB_GADGET + # Host-side USB depends on having a host controller # NOTE: dummy_hcd is always an option, but it's ignored here ... # NOTE: SL-811 option should be board-specific ... diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 969b0a5..75eca76 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -53,3 +53,5 @@ obj-$(CONFIG_USB_MUSB_HDRC) += musb/ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ obj-$(CONFIG_USB_OTG_UTILS) += otg/ obj-$(CONFIG_USB_GADGET) += gadget/ + +obj-$(CONFIG_USB_COMMON) += usb-common.o diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 338f91f..3edc01b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2793,7 +2793,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, int i, j, retval; unsigned delay = HUB_SHORT_RESET_TIME; enum usb_device_speed oldspeed = udev->speed; - char *speed, *type; + const char *speed; int devnum = udev->devnum; /* root hub ports have a slightly longer reset period @@ -2853,25 +2853,16 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, default: goto fail; } - - type = ""; - switch (udev->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - case USB_SPEED_SUPER: - speed = "super"; - break; - case USB_SPEED_WIRELESS: - speed = "variable"; - type = "Wireless "; - break; - default: speed = "?"; break; - } + + if (udev->speed == USB_SPEED_WIRELESS) + speed = "variable speed Wireless"; + else + speed = usb_speed_string(udev->speed); + if (udev->speed != USB_SPEED_SUPER) dev_info(&udev->dev, - "%s %s speed %sUSB device number %d using %s\n", - (udev->config) ? "reset" : "new", speed, type, + "%s %s USB device number %d using %s\n", + (udev->config) ? "reset" : "new", speed, devnum, udev->bus->controller->driver->name); /* Set up TT records, if needed */ diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index ded8d6f..4730016 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -3005,13 +3005,8 @@ __acquires(dev->lock) /* link up all endpoints */ udc_setup_endpoints(dev); - if (dev->gadget.speed == USB_SPEED_HIGH) { - dev_info(&dev->pdev->dev, "Connect: speed = %s\n", - "high"); - } else if (dev->gadget.speed == USB_SPEED_FULL) { - dev_info(&dev->pdev->dev, "Connect: speed = %s\n", - "full"); - } + dev_info(&dev->pdev->dev, "Connect: %s\n", + usb_speed_string(dev->gadget.speed)); /* init ep 0 */ activate_control_endpoints(dev); diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c index 722c468..271a9d8 100644 --- a/drivers/usb/gadget/atmel_usba_udc.c +++ b/drivers/usb/gadget/atmel_usba_udc.c @@ -1718,13 +1718,12 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) spin_lock(&udc->lock); } - if (status & USBA_HIGH_SPEED) { - DBG(DBG_BUS, "High-speed bus reset detected\n"); + if (status & USBA_HIGH_SPEED) udc->gadget.speed = USB_SPEED_HIGH; - } else { - DBG(DBG_BUS, "Full-speed bus reset detected\n"); + else udc->gadget.speed = USB_SPEED_FULL; - } + DBG(DBG_BUS, "%s bus reset detected\n", + usb_speed_string(udc->gadget.speed)); ep0 = &usba_ep[0]; ep0->desc = &usba_ep0_desc; diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index c77aca1..e74fd55 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -617,25 +617,9 @@ static int set_config(struct usb_composite_dev *cdev, result = 0; } - INFO(cdev, "%s speed config #%d: %s\n", - ({ char *speed; - switch (gadget->speed) { - case USB_SPEED_LOW: - speed = "low"; - break; - case USB_SPEED_FULL: - speed = "full"; - break; - case USB_SPEED_HIGH: - speed = "high"; - break; - case USB_SPEED_SUPER: - speed = "super"; - break; - default: - speed = "?"; - break; - } ; speed; }), number, c ? c->label : "unconfigured"); + INFO(cdev, "%s config #%d: %s\n", + usb_speed_string(gadget->speed), + number, c ? c->label : "unconfigured"); if (!c) goto done; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 39ece40..4ac8084 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -2862,17 +2862,10 @@ static int do_set_config(struct fsg_dev *fsg, u8 new_config) fsg->config = new_config; if ((rc = do_set_interface(fsg, 0)) != 0) fsg->config = 0; // Reset on errors - else { - char *speed; - - switch (fsg->gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - INFO(fsg, "%s speed config #%d\n", speed, fsg->config); - } + else + INFO(fsg, "%s config #%d\n", + usb_speed_string(fsg->gadget->speed), + fsg->config); } return rc; } diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index d699350..b2c44e1 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -1715,34 +1715,31 @@ static void dtd_complete_irq(struct fsl_udc *udc) } } +static inline enum usb_device_speed portscx_device_speed(u32 reg) +{ + switch (speed & PORTSCX_PORT_SPEED_MASK) { + case PORTSCX_PORT_SPEED_HIGH: + return USB_SPEED_HIGH; + case PORTSCX_PORT_SPEED_FULL: + return USB_SPEED_FULL; + case PORTSCX_PORT_SPEED_LOW: + return USB_SPEED_LOW; + default: + return USB_SPEED_UNKNOWN; + } +} + /* Process a port change interrupt */ static void port_change_irq(struct fsl_udc *udc) { - u32 speed; - if (udc->bus_reset) udc->bus_reset = 0; /* Bus resetting is finished */ - if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) { + if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) /* Get the speed */ - speed = (fsl_readl(&dr_regs->portsc1) - & PORTSCX_PORT_SPEED_MASK); - switch (speed) { - case PORTSCX_PORT_SPEED_HIGH: - udc->gadget.speed = USB_SPEED_HIGH; - break; - case PORTSCX_PORT_SPEED_FULL: - udc->gadget.speed = USB_SPEED_FULL; - break; - case PORTSCX_PORT_SPEED_LOW: - udc->gadget.speed = USB_SPEED_LOW; - break; - default: - udc->gadget.speed = USB_SPEED_UNKNOWN; - break; - } - } + udc->gadget.speed = + portscx_device_speed(fsl_readl(&dr_regs->portsc1)); /* Update USB state */ if (!udc->resume_state) @@ -2167,20 +2164,8 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, default: s = "None"; break; } - s;} ), ( { - char *s; - switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) { - case PORTSCX_PORT_SPEED_FULL: - s = "Full Speed"; break; - case PORTSCX_PORT_SPEED_LOW: - s = "Low Speed"; break; - case PORTSCX_PORT_SPEED_HIGH: - s = "High Speed"; break; - default: - s = "Undefined"; break; - } - s; - } ), + s;} ), + usb_speed_string(portscx_device_speed(tmp_reg)), (tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ? "Normal PHY mode" : "Low power mode", (tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" : diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 8b9220e..893b967 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -640,17 +640,8 @@ gmidi_set_config(struct gmidi_device *dev, unsigned number, gfp_t gfp_flags) if (result) { gmidi_reset_config(dev); } else { - char *speed; - - switch (gadget->speed) { - case USB_SPEED_LOW: speed = "low"; break; - case USB_SPEED_FULL: speed = "full"; break; - case USB_SPEED_HIGH: speed = "high"; break; - default: speed = "?"; break; - } - dev->config = number; - INFO(dev, "%s speed\n", speed); + INFO(dev, "%s speed\n", usb_speed_string(gadget->speed)); } return result; } diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index 6e44499..c88158f 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -1690,20 +1690,7 @@ static ssize_t show_langwell_udc(struct device *_dev, "BmAttributes: %d\n\n", LPM_PTS(tmp_reg), (tmp_reg & LPM_STS) ? 1 : 0, - ({ - char *s; - switch (LPM_PSPD(tmp_reg)) { - case LPM_SPEED_FULL: - s = "Full Speed"; break; - case LPM_SPEED_LOW: - s = "Low Speed"; break; - case LPM_SPEED_HIGH: - s = "High Speed"; break; - default: - s = "Unknown Speed"; break; - } - s; - }), + usb_speed_string(lpm_device_speed(tmp_reg)), (tmp_reg & LPM_PFSC) ? "Force Full Speed" : "Not Force", (tmp_reg & LPM_PHCD) ? "Disabled" : "Enabled", LPM_BA(tmp_reg)); @@ -2647,12 +2634,24 @@ done: dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__); } +static inline enum usb_device_speed lpm_device_speed(u32 reg) +{ + switch (LPM_PSPD(reg)) { + case LPM_SPEED_HIGH: + return USB_SPEED_HIGH; + case LPM_SPEED_FULL: + return USB_SPEED_FULL; + case LPM_SPEED_LOW: + return USB_SPEED_LOW; + default: + return USB_SPEED_UNKNOWN; + } +} /* port change detect interrupt handler */ static void handle_port_change(struct langwell_udc *dev) { u32 portsc1, devlc; - u32 speed; dev_vdbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -2667,24 +2666,9 @@ static void handle_port_change(struct langwell_udc *dev) /* bus reset is finished */ if (!(portsc1 & PORTS_PR)) { /* get the speed */ - speed = LPM_PSPD(devlc); - switch (speed) { - case LPM_SPEED_HIGH: - dev->gadget.speed = USB_SPEED_HIGH; - break; - case LPM_SPEED_FULL: - dev->gadget.speed = USB_SPEED_FULL; - break; - case LPM_SPEED_LOW: - dev->gadget.speed = USB_SPEED_LOW; - break; - default: - dev->gadget.speed = USB_SPEED_UNKNOWN; - break; - } - dev_vdbg(&dev->pdev->dev, - "speed = %d, dev->gadget.speed = %d\n", - speed, dev->gadget.speed); + dev->gadget.speed = lpm_device_speed(devlc); + dev_vdbg(&dev->pdev->dev, "dev->gadget.speed = %d\n", + dev->gadget.speed); } /* LPM L0 to L1 */ diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index 6fef1c0..08a4a36 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -1764,8 +1764,8 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat) dev->gadget.speed = USB_SPEED_HIGH; else dev->gadget.speed = USB_SPEED_FULL; - dev_dbg(dev->dev, "%s speed\n", - (dev->gadget.speed == USB_SPEED_HIGH) ? "high" : "full"); + dev_dbg(dev->dev, "%s\n", + usb_speed_string(dev->gadget.speed)); } ep = &dev->ep[0]; diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 3c14c4e..db508d0 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2257,9 +2257,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat) else dev->gadget.speed = USB_SPEED_FULL; net2280_led_speed (dev, dev->gadget.speed); - DEBUG (dev, "%s speed\n", - (dev->gadget.speed == USB_SPEED_HIGH) - ? "high" : "full"); + DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed)); } ep = &dev->ep [0]; diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 5d3e697..68a0efb 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -962,23 +962,15 @@ printer_set_config(struct printer_dev *dev, unsigned number) usb_gadget_vbus_draw(dev->gadget, dev->gadget->is_otg ? 8 : 100); } else { - char *speed; unsigned power; power = 2 * config_desc.bMaxPower; usb_gadget_vbus_draw(dev->gadget, power); - switch (gadget->speed) { - case USB_SPEED_FULL: speed = "full"; break; -#ifdef CONFIG_USB_GADGET_DUALSPEED - case USB_SPEED_HIGH: speed = "high"; break; -#endif - default: speed = "?"; break; - } - dev->config = number; - INFO(dev, "%s speed config #%d: %d mA, %s\n", - speed, number, power, driver_desc); + INFO(dev, "%s config #%d: %d mA, %s\n", + usb_speed_string(gadget->speed), + number, power, driver_desc); } return result; } diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 39b134d..a552453 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -1951,30 +1951,26 @@ static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg) case S3C_DSTS_EnumSpd_FS: case S3C_DSTS_EnumSpd_FS48: hsotg->gadget.speed = USB_SPEED_FULL; - dev_info(hsotg->dev, "new device is full-speed\n"); - ep0_mps = EP0_MPS_LIMIT; ep_mps = 64; break; case S3C_DSTS_EnumSpd_HS: - dev_info(hsotg->dev, "new device is high-speed\n"); hsotg->gadget.speed = USB_SPEED_HIGH; - ep0_mps = EP0_MPS_LIMIT; ep_mps = 512; break; case S3C_DSTS_EnumSpd_LS: hsotg->gadget.speed = USB_SPEED_LOW; - dev_info(hsotg->dev, "new device is low-speed\n"); - /* note, we don't actually support LS in this driver at the * moment, and the documentation seems to imply that it isn't * supported by the PHYs on some of the devices. */ break; } + dev_info(hsotg->dev, "new device is %s\n", + usb_speed_string(hsotg->gadget.speed)); /* we should now know the maximum packet size for an * endpoint, so set the endpoints to a default value. */ diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 05ba472..5e77a46 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -375,23 +375,8 @@ static ssize_t usb_udc_speed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - struct usb_gadget *gadget = udc->gadget; - - switch (gadget->speed) { - case USB_SPEED_LOW: - return snprintf(buf, PAGE_SIZE, "low-speed\n"); - case USB_SPEED_FULL: - return snprintf(buf, PAGE_SIZE, "full-speed\n"); - case USB_SPEED_HIGH: - return snprintf(buf, PAGE_SIZE, "high-speed\n"); - case USB_SPEED_WIRELESS: - return snprintf(buf, PAGE_SIZE, "wireless\n"); - case USB_SPEED_SUPER: - return snprintf(buf, PAGE_SIZE, "super-speed\n"); - case USB_SPEED_UNKNOWN: /* FALLTHROUGH */ - default: - return snprintf(buf, PAGE_SIZE, "UNKNOWN\n"); - } + return snprintf(buf, PAGE_SIZE, "%s\n", + usb_speed_string(udc->gadget->speed)); } static DEVICE_ATTR(speed, S_IRUSR, usb_udc_speed_show, NULL); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 930962f..bd6d008 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -2300,25 +2300,8 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_set_intfdata(intf, dev); dev_info(&intf->dev, "%s\n", info->name); - dev_info(&intf->dev, "%s speed {control%s%s%s%s%s} tests%s\n", - ({ char *tmp; - switch (udev->speed) { - case USB_SPEED_LOW: - tmp = "low"; - break; - case USB_SPEED_FULL: - tmp = "full"; - break; - case USB_SPEED_HIGH: - tmp = "high"; - break; - case USB_SPEED_SUPER: - tmp = "super"; - break; - default: - tmp = "unknown"; - break; - }; tmp; }), + dev_info(&intf->dev, "%s {control%s%s%s%s%s} tests%s\n", + usb_speed_string(udev->speed), info->ctrl_out ? " in/out" : "", rtest, wtest, irtest, iwtest, diff --git a/drivers/usb/usb-common.c b/drivers/usb/usb-common.c new file mode 100644 index 0000000..d29503e --- /dev/null +++ b/drivers/usb/usb-common.c @@ -0,0 +1,35 @@ +/* + * Provides code common for host and device side USB. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + * If either host side (ie. CONFIG_USB=y) or device side USB stack + * (ie. CONFIG_USB_GADGET=y) is compiled in the kernel, this module is + * compiled-in as well. Otherwise, if either of the two stacks is + * compiled as module, this file is compiled as module as well. + */ + +#include +#include +#include + +const char *usb_speed_string(enum usb_device_speed speed) +{ + static const char *const names[] = { + [USB_SPEED_UNKNOWN] = "UNKNOWN", + [USB_SPEED_LOW] = "low-speed", + [USB_SPEED_FULL] = "full-speed", + [USB_SPEED_HIGH] = "high-speed", + [USB_SPEED_WIRELESS] = "wireless", + [USB_SPEED_SUPER] = "super-speed", + }; + + if (speed < 0 || speed >= ARRAY_SIZE(names)) + speed = USB_SPEED_UNKNOWN; + return names[speed]; +} +EXPORT_SYMBOL_GPL(usb_speed_string); + +MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 1ded281..f32a64e 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -868,6 +868,18 @@ enum usb_device_speed { USB_SPEED_SUPER, /* usb 3.0 */ }; +#ifdef __KERNEL__ + +/** + * usb_speed_string() - Returns human readable-name of the speed. + * @speed: The speed to return human-readable name for. If it's not + * any of the speeds defined in usb_device_speed enum, string for + * USB_SPEED_UNKNOWN will be returned. + */ +extern const char *usb_speed_string(enum usb_device_speed speed); + +#endif + enum usb_device_state { /* NOTATTACHED isn't in the USB spec, and this state acts * the same as ATTACHED ... but it's clearer this way. -- cgit v0.10.2 From fba9e546eac984d46c4a16253344d7297c24d75c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Sun, 4 Sep 2011 21:56:02 +0200 Subject: s3c-hsudc: implement vbus_draw hook When a transceiver is available use otg_set_power to submit the target current to it. Signed-off-by: Heiko Stuebner Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c index 25829b4..8d54f89 100644 --- a/drivers/usb/gadget/s3c-hsudc.c +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -1241,10 +1241,24 @@ static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget) return s3c_hsudc_read_frameno(to_hsudc(gadget)); } +static int s3c_hsudc_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct s3c_hsudc *hsudc = the_controller; + + if (!hsudc) + return -ENODEV; + + if (hsudc->transceiver) + return otg_set_power(hsudc->transceiver, mA); + + return -EOPNOTSUPP; +} + static struct usb_gadget_ops s3c_hsudc_gadget_ops = { .get_frame = s3c_hsudc_gadget_getframe, .start = s3c_hsudc_start, .stop = s3c_hsudc_stop, + .vbus_draw = s3c_hsudc_vbus_draw, }; static int s3c_hsudc_probe(struct platform_device *pdev) -- cgit v0.10.2 From 8582d86143c690c68cc42f996def466a035bee34 Mon Sep 17 00:00:00 2001 From: Fabian Godehardt Date: Thu, 1 Sep 2011 14:15:46 +0200 Subject: USB: g_printer: fix bug in unregistration The allocated chardevice region range is only 1 device but on unregister it currently tries to deregister 2. Found this while doing a insmod/rmmod/insmod/rm... of the module which seemed to eat major numbers. Signed-off-by: Fabian Godehardt Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index 68a0efb..65a8834 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -1594,7 +1594,7 @@ cleanup(void) if (status) ERROR(dev, "usb_gadget_unregister_driver %x\n", status); - unregister_chrdev_region(g_printer_devno, 2); + unregister_chrdev_region(g_printer_devno, 1); class_destroy(usb_gadget_class); mutex_unlock(&usb_printer_gadget.lock_printer_io); } -- cgit v0.10.2 From 839f245f8f1e7d7efd7ba12a7d735f13e8293a2b Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Thu, 1 Sep 2011 14:05:15 -0700 Subject: usb-storage: fix realtek cr configuration A typo in the configuration variable name prevents from activating the USB autosuspend on the device. Signed-off-by: Vincent Palatin Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index bedc4b9..fe2d803 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -42,7 +42,7 @@ config USB_STORAGE_REALTEK config REALTEK_AUTOPM bool "Realtek Card Reader autosuspend support" - depends on USB_STORAGE_REALTEK && CONFIG_PM_RUNTIME + depends on USB_STORAGE_REALTEK && PM_RUNTIME default y config USB_STORAGE_DATAFAB -- cgit v0.10.2 From 65e52f41fa944cef2e6d4222b8c54f46cc575214 Mon Sep 17 00:00:00 2001 From: Denis Pershin Date: Sun, 4 Sep 2011 17:37:21 +0700 Subject: usb: cdc-acm: Owen SI-30 support here is the patch to support Owen SI-30 device. This is a pulse counter controller. http://www.owen.ru/en/catalog/93788515 usb-drivers output: T: Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 4 Spd=12 MxCh= 0 D: Ver= 2.00 Cls=02(commc) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 P: Vendor=03eb ProdID=0030 Rev=01.01 C: #Ifs= 2 Cfg#= 1 Atr=c0 MxPwr=0mA I: If#= 0 Alt= 0 #EPs= 1 Cls=02(commc) Sub=02 Prot=00 Driver=cdc_acm I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=cdc_acm This patch is installed on my home system which receives data from this controller connected to cold water counter. Signed-off-by: Denis Pershin Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index f69a185..3ec6699 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1534,6 +1534,9 @@ static const struct usb_device_id acm_ids[] = { { NOKIA_PCSUITE_ACM_INFO(0x03cd), }, /* Nokia C7 */ { SAMSUNG_PCSUITE_ACM_INFO(0x6651), }, /* Samsung GTi8510 (INNOV8) */ + /* Support for Owen devices */ + { USB_DEVICE(0x03eb, 0x0030), }, /* Owen SI30 */ + /* NOTE: non-Nokia COMM/ACM/0xff is likely MSFT RNDIS... NOT a modem! */ /* Support Lego NXT using pbLua firmware */ -- cgit v0.10.2 From 0412560e18b4330366653819c0c5e73a743ff7e8 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 5 Sep 2011 21:05:55 +0800 Subject: usb: ehci: remove wmb in qh_update qh_refresh is always called when the qh is idle and has not been linked into hardware queue, so EHCI will not access overlay of the qh at this time. Just before linking qh into hardware queue, there has already one wmb to order writing qh descriptor and writing dma address of the qh into hardware queue, so HC can always see up-to-date qh descriptor once the qh is fetched with its dma address by EHCI. Signed-off-by: Alan Stern Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 6ce0b3a..726bd9d 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -111,8 +111,6 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) } } - /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */ - wmb (); hw->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING); } -- cgit v0.10.2 From 9a971dda8208e0982094f29ef34bd190f2a081bd Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 5 Sep 2011 21:05:56 +0800 Subject: usb: ehci: only prepare zero packet for out transfer if required Obviously, ZLP is only required for transfer of OUT direction, so just take same policy with UHCI for ZLP packet. Signed-off-by: Ming Lei Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 726bd9d..ef59258 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -722,7 +722,8 @@ qh_urb_transaction ( /* * control requests may need a terminating data "status" ack; - * bulk ones may need a terminating short packet (zero length). + * other OUT ones may need a terminating short packet + * (zero length). */ if (likely (urb->transfer_buffer_length != 0)) { int one_more = 0; @@ -731,7 +732,7 @@ qh_urb_transaction ( one_more = 1; token ^= 0x0100; /* "in" <--> "out" */ token |= QTD_TOGGLE; /* force DATA1 */ - } else if (usb_pipebulk (urb->pipe) + } else if (usb_pipeout(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) && !(urb->transfer_buffer_length % maxpacket)) { one_more = 1; -- cgit v0.10.2 From fcda37cb42cc0aa039a2d1e06ec801e4e9f417f4 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 5 Sep 2011 21:05:57 +0800 Subject: usb: ehci: fix comment for EHCI_SHRINK_JIFFIES EHCI_SHRINK_JIFFIES should be 5ms, which was just used originally, and not 200ms, so fix it. Signed-off-by: Ming Lei Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 8696489..2f0cf86 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -95,7 +95,7 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ #define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) - /* 200-ms async qh unlink delay */ + /* 5-ms async qh unlink delay */ /* Initial IRQ latency: faster than hw default */ static int log2_irq_thresh = 0; // 0 to 6 -- cgit v0.10.2 From 41f05dedeabb0e2cb03734de383db3f0ddecf9e0 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Mon, 5 Sep 2011 21:05:58 +0800 Subject: usb: ehci: remove the 1st wmb in qh_append_tds According to ehci spec 4.10.2, Advance Queue If the fetched qTD has its Active bit set to a zero, the host controller aborts the queue advance and follows the queue head's horizontal pointer to the next schedule data structure. the 'qtd' will be linked into qh hardware queue after the line below *dummy = *qtd; is executed and observed by EHCI HC, but EHCI HC won't have chance to fetch the qtd descriptor pointed by 'qtd' in qh_append_tds until the line below dummy->hw_token = token; #set Active bit here is executed by CPU and observed by EHCI HC. There is already one 'wmb' to order writing to 'dummy'/'qtd' descriptors and writing 'token' to 'dummy' descriptor(set Active bit), so the 1st wmb is not needed and can be removed. Signed-off-by: Alan Stern Signed-off-by: Ming Lei Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index ef59258..4e4066c 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1056,7 +1056,7 @@ static struct ehci_qh *qh_append_tds ( */ token = qtd->hw_token; qtd->hw_token = HALT_BIT(ehci); - wmb (); + dummy = qh->dummy; dma = dummy->qtd_dma; -- cgit v0.10.2 From b5dd18d8747010e3f3eb1cc76a49f94291938559 Mon Sep 17 00:00:00 2001 From: Yong Zhang Date: Wed, 7 Sep 2011 16:10:52 +0800 Subject: USB: irq: Remove IRQF_DISABLED This flag is a NOOP and can be removed now. Signed-off-by: Yong Zhang Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index ce22f4a..a004db3 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -242,7 +242,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) pci_set_master(dev); - retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); if (retval != 0) goto unmap_registers; set_hs_companion(dev, hcd); diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 0a1b6d1..e10cf3c 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1810,7 +1810,7 @@ static int __init at91udc_probe(struct platform_device *pdev) /* request UDC and maybe VBUS irqs */ udc->udp_irq = platform_get_irq(pdev, 0); retval = request_irq(udc->udp_irq, at91_udc_irq, - IRQF_DISABLED, driver_name, udc); + 0, driver_name, udc); if (retval < 0) { DBG("request irq %d failed\n", udc->udp_irq); goto fail1; @@ -1838,7 +1838,7 @@ static int __init at91udc_probe(struct platform_device *pdev) jiffies + VBUS_POLL_TIMEOUT); } else { if (request_irq(udc->board.vbus_pin, at91_vbus_irq, - IRQF_DISABLED, driver_name, udc)) { + 0, driver_name, udc)) { DBG("request vbus irq %d failed\n", udc->board.vbus_pin); retval = -EBUSY; diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index cd9cbba..e593f28 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -1469,7 +1469,7 @@ static int __init fusb300_probe(struct platform_device *pdev) fusb300->gadget.name = udc_name; fusb300->reg = reg; - ret = request_irq(ires->start, fusb300_irq, IRQF_DISABLED | IRQF_SHARED, + ret = request_irq(ires->start, fusb300_irq, IRQF_SHARED, udc_name, fusb300); if (ret < 0) { pr_err("request_irq error (%d)\n", ret); @@ -1477,7 +1477,7 @@ static int __init fusb300_probe(struct platform_device *pdev) } ret = request_irq(ires1->start, fusb300_irq, - IRQF_DISABLED | IRQF_SHARED, udc_name, fusb300); + IRQF_SHARED, udc_name, fusb300); if (ret < 0) { pr_err("request_irq1 error (%d)\n", ret); goto clean_up; diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index bf08bfc..2d978c0 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -1478,7 +1478,7 @@ static int __init imx_udc_probe(struct platform_device *pdev) for (i = 0; i < IMX_USB_NB_EP + 1; i++) { ret = request_irq(imx_usb->usbd_int[i], intr_handler(i), - IRQF_DISABLED, driver_name, imx_usb); + 0, driver_name, imx_usb); if (ret) { dev_err(&pdev->dev, "can't get irq %i, err %d\n", imx_usb->usbd_int[i], ret); diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index c27e5e8..91d0af2 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -1664,7 +1664,7 @@ static int __init m66592_probe(struct platform_device *pdev) m66592->timer.data = (unsigned long)m66592; m66592->reg = reg; - ret = request_irq(ires->start, m66592_irq, IRQF_DISABLED | IRQF_SHARED, + ret = request_irq(ires->start, m66592_irq, IRQF_SHARED, udc_name, m66592); if (ret < 0) { pr_err("request_irq error (%d)\n", ret); diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 263dec4..0d0e9e3 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2049,7 +2049,7 @@ int mv_udc_probe(struct platform_device *dev) } udc->irq = r->start; if (request_irq(udc->irq, mv_udc_irq, - IRQF_DISABLED | IRQF_SHARED, driver_name, udc)) { + IRQF_SHARED, driver_name, udc)) { dev_err(&dev->dev, "Request irq %d for UDC failed\n", udc->irq); retval = -ENODEV; diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index ae9c89b..788989a 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -2958,7 +2958,7 @@ known: } #ifdef USE_ISO status = request_irq(pdev->resource[3].start, omap_udc_iso_irq, - IRQF_DISABLED, "omap_udc iso", udc); + 0, "omap_udc iso", udc); if (status != 0) { ERR("can't get irq %d, err %d\n", (int) pdev->resource[3].start, status); diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c index a33fc17..c090a7e 100644 --- a/drivers/usb/gadget/pxa25x_udc.c +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -2190,7 +2190,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) /* irq setup after old hardware state is cleaned up */ retval = request_irq(irq, pxa25x_udc_irq, - IRQF_DISABLED, driver_name, dev); + 0, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %d, err %d\n", driver_name, irq, retval); @@ -2202,7 +2202,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) if (machine_is_lubbock()) { retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + IRQF_SAMPLE_RANDOM, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", @@ -2211,7 +2211,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev) } retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + IRQF_SAMPLE_RANDOM, driver_name, dev); if (retval != 0) { pr_err("%s: can't get irq %i, err %d\n", diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index bca3f41..d3f81c9 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1633,7 +1633,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) disable_controller(r8a66597); /* make sure controller is disabled */ - ret = request_irq(irq, r8a66597_irq, IRQF_DISABLED | IRQF_SHARED, + ret = request_irq(irq, r8a66597_irq, IRQF_SHARED, udc_name, r8a66597); if (ret < 0) { printk(KERN_ERR "request_irq error (%d)\n", ret); diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c index 3f587b0..b864377 100644 --- a/drivers/usb/gadget/s3c2410_udc.c +++ b/drivers/usb/gadget/s3c2410_udc.c @@ -1893,7 +1893,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) /* irq setup after old hardware state is cleaned up */ retval = request_irq(IRQ_USBD, s3c2410_udc_irq, - IRQF_DISABLED, gadget_name, udc); + 0, gadget_name, udc); if (retval != 0) { dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval); @@ -1917,7 +1917,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev) } retval = request_irq(irq, s3c2410_udc_vbus_irq, - IRQF_DISABLED | IRQF_TRIGGER_RISING + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, gadget_name, udc); diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c index 4d2e88d..afb6743 100644 --- a/drivers/usb/host/ehci-ath79.c +++ b/drivers/usb/host/ehci-ath79.c @@ -163,7 +163,7 @@ static int ehci_ath79_probe(struct platform_device *pdev) goto err_release_region; } - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto err_iounmap; diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index 4363fea..65719e8 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -181,7 +181,7 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) ehci->hcs_params = readl(&ehci->caps->hcs_params); ret = usb_add_hcd(hcd, pdev->resource[1].start, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); return ret; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 3bf9f16..e90344a 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -134,7 +134,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, /* Don't need to set host mode here. It will be done by tdi_reset() */ - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval != 0) goto err4; diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 555a73c..55978fc 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -236,7 +236,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) priv->hcd = hcd; platform_set_drvdata(pdev, priv); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto err_add; diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index c3ba3ed..ba1f513 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -155,7 +155,7 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); goto err3; diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 4524032..e39b029 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -228,7 +228,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_err(dev, "failed to add hcd with err %d\n", ret); goto err_add_hcd; diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 395bdb0..a68a2a5 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -277,7 +277,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) printk(KERN_WARNING "Orion ehci -USB phy version isn't supported.\n"); } - err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) goto err4; diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 64626a7..2dc32da 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -167,7 +167,7 @@ static int __devinit ps3_ehci_probe(struct ps3_system_bus_device *dev) ps3_system_bus_set_drvdata(dev, hcd); - result = usb_add_hcd(hcd, virq, IRQF_DISABLED); + result = usb_add_hcd(hcd, virq, 0); if (result) { dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n", diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c index 64bcf66..024b65c 100644 --- a/drivers/usb/host/ehci-s5p.c +++ b/drivers/usb/host/ehci-s5p.c @@ -136,7 +136,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); - err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); goto fail; diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 86a95bb..9d9cf47 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -168,7 +168,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev) clk_enable(priv->fclk); clk_enable(priv->iclk); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret != 0) { dev_err(&pdev->dev, "Failed to add hcd"); goto fail_add_hcd; diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index dbf1e4e..b115b0b 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -154,7 +154,7 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) ehci->clk = usbh_clk; spear_start_ehci(ehci); - retval = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) goto fail_add_hcd; diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 02b2bfd..db9d1b4 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -674,7 +674,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) } #endif - err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) { dev_err(&pdev->dev, "Failed to add USB HCD\n"); goto fail; diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c index 47d7496..54d1ab8 100644 --- a/drivers/usb/host/ehci-vt8500.c +++ b/drivers/usb/host/ehci-vt8500.c @@ -133,7 +133,7 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev) ehci_port_power(ehci, 1); ret = usb_add_hcd(hcd, pdev->resource[1].start, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); return ret; diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 484a74f..4ed6d19 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -689,7 +689,7 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) } ret = request_irq(fhci->timer->irq, fhci_frame_limit_timer_irq, - IRQF_DISABLED, "qe timer (usb)", hcd); + 0, "qe timer (usb)", hcd); if (ret) { dev_err(dev, "failed to request timer irq"); goto err_timer_irq; @@ -748,7 +748,7 @@ static int __devinit of_fhci_probe(struct platform_device *ofdev) out_be16(&fhci->regs->usb_event, 0xffff); out_be16(&fhci->regs->usb_mask, 0); - ret = usb_add_hcd(hcd, usb_irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, usb_irq, 0); if (ret < 0) goto err_add_hcd; diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index af05718..2ee18cf 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -1891,7 +1891,7 @@ static int imx21_probe(struct platform_device *pdev) dev_info(imx21->dev, "Hardware HC revision: 0x%02X\n", (readl(imx21->regs + USBOTG_HWMODE) >> 16) & 0xFF); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret != 0) { dev_err(imx21->dev, "usb_add_hcd() returned %d\n", ret); goto failed_add_hcd; diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index baae4cc..d91e5f2 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1639,7 +1639,7 @@ static int __devinit isp116x_probe(struct platform_device *pdev) goto err6; } - ret = usb_add_hcd(hcd, irq, irqflags | IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, irqflags); if (ret) goto err6; diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 21efca9..e5fd8aa 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2773,7 +2773,7 @@ static int __devinit isp1362_probe(struct platform_device *pdev) if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL) irq_flags |= IRQF_TRIGGER_LOW; - retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_SHARED); if (retval != 0) goto err6; pr_info("%s, irq %d\n", hcd->product_desc, irq); diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 7ee3005..d2f6b2d 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -79,7 +79,7 @@ static int of_isp1760_probe(struct platform_device *dev) devflags |= ISP1760_FLAG_DREQ_POL_HIGH; hcd = isp1760_register(memory.start, res_len, virq, - IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev), + IRQF_SHARED, &dev->dev, dev_name(&dev->dev), devflags); if (IS_ERR(hcd)) { ret = PTR_ERR(hcd); @@ -240,7 +240,7 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev, dev->dev.dma_mask = NULL; hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq, - IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev), + IRQF_SHARED, &dev->dev, dev_name(&dev->dev), devflags); if (IS_ERR(hcd)) { ret_status = -ENODEV; @@ -313,7 +313,7 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev) resource_size_t mem_size; struct isp1760_platform_data *priv = pdev->dev.platform_data; unsigned int devflags = 0; - unsigned long irqflags = IRQF_SHARED | IRQF_DISABLED; + unsigned long irqflags = IRQF_SHARED; mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem_res) { diff --git a/drivers/usb/host/ohci-ath79.c b/drivers/usb/host/ohci-ath79.c index c620c50..18d574d 100644 --- a/drivers/usb/host/ohci-ath79.c +++ b/drivers/usb/host/ohci-ath79.c @@ -111,7 +111,7 @@ static int ohci_ath79_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret) goto err_stop_hcd; diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c index 958d985f..6b7bc50 100644 --- a/drivers/usb/host/ohci-au1xxx.c +++ b/drivers/usb/host/ohci-au1xxx.c @@ -218,7 +218,7 @@ static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); ret = usb_add_hcd(hcd, pdev->resource[1].start, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (ret == 0) { platform_set_drvdata(pdev, hcd); return ret; diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 6aca2c4..8435097 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -344,7 +344,7 @@ static int usb_hcd_da8xx_probe(const struct hc_driver *driver, error = -ENODEV; goto err4; } - error = usb_add_hcd(hcd, irq, IRQF_DISABLED); + error = usb_add_hcd(hcd, irq, 0); if (error) goto err4; diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c index 4e68161..dc45d48 100644 --- a/drivers/usb/host/ohci-ep93xx.c +++ b/drivers/usb/host/ohci-ep93xx.c @@ -81,7 +81,7 @@ static int usb_hcd_ep93xx_probe(const struct hc_driver *driver, ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED); + retval = usb_add_hcd(hcd, pdev->resource[1].start, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-octeon.c b/drivers/usb/host/ohci-octeon.c index d8b4564..d469bf9 100644 --- a/drivers/usb/host/ohci-octeon.c +++ b/drivers/usb/host/ohci-octeon.c @@ -164,7 +164,7 @@ static int ohci_octeon_drv_probe(struct platform_device *pdev) ohci_hcd_init(ohci); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); goto err3; diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 5645f70..e4b8782 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -14,7 +14,7 @@ * This file is licenced under the GPL. */ -#include /* IRQF_DISABLED */ +#include #include #include #include @@ -363,7 +363,7 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver, retval = -ENXIO; goto err3; } - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, 0); if (retval) goto err3; diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index 853ad8d..516ebc4 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -180,7 +180,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret) { dev_dbg(dev, "failed to add hcd with err %d\n", ret); goto err_add_hcd; diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c index 653d6a6..9ad8bee 100644 --- a/drivers/usb/host/ohci-pnx4008.c +++ b/drivers/usb/host/ohci-pnx4008.c @@ -398,7 +398,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev) ohci_hcd_init(ohci); dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret == 0) return ret; diff --git a/drivers/usb/host/ohci-pnx8550.c b/drivers/usb/host/ohci-pnx8550.c index 28467e2..f13d08f 100644 --- a/drivers/usb/host/ohci-pnx8550.c +++ b/drivers/usb/host/ohci-pnx8550.c @@ -107,7 +107,7 @@ int usb_hcd_pnx8550_probe (const struct hc_driver *driver, ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED); + retval = usb_add_hcd(hcd, dev->resource[1].start, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 0c12f4e..d24cc89d 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -143,7 +143,7 @@ static int __devinit ohci_hcd_ppc_of_probe(struct platform_device *op) ohci_hcd_init(ohci); - rv = usb_add_hcd(hcd, irq, IRQF_DISABLED); + rv = usb_add_hcd(hcd, irq, 0); if (rv == 0) return 0; diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c index c0f595c..1514b70 100644 --- a/drivers/usb/host/ohci-ppc-soc.c +++ b/drivers/usb/host/ohci-ppc-soc.c @@ -80,7 +80,7 @@ static int usb_hcd_ppc_soc_probe(const struct hc_driver *driver, #endif ohci_hcd_init(ohci); - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index 7009504..6fd4fa1 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -164,7 +164,7 @@ static int __devinit ps3_ohci_probe(struct ps3_system_bus_device *dev) ps3_system_bus_set_drvdata(dev, hcd); - result = usb_add_hcd(hcd, virq, IRQF_DISABLED); + result = usb_add_hcd(hcd, virq, 0); if (result) { dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n", diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 80be547..29dfefe 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -359,7 +359,7 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index 7c9a4d5..a1877c4 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -384,7 +384,7 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver, ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED); + retval = usb_add_hcd(hcd, dev->resource[1].start, 0); if (retval != 0) goto err_ioremap; diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index 4204d972..4bde4f9 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -143,7 +143,7 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver, sa1111_start_hc(dev); ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, dev->irq[1], IRQF_DISABLED); + retval = usb_add_hcd(hcd, dev->irq[1], 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-sh.c b/drivers/usb/host/ohci-sh.c index 14cecb5..afc4eb6 100644 --- a/drivers/usb/host/ohci-sh.c +++ b/drivers/usb/host/ohci-sh.c @@ -109,7 +109,7 @@ static int ohci_hcd_sh_probe(struct platform_device *pdev) hcd->regs = (void __iomem *)res->start; hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret != 0) { err("Failed to add hcd"); usb_put_hcd(hcd); diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index 78918ca..968cea2 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -165,7 +165,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); if (retval) goto err5; diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 4fd4bea..6987465 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -152,7 +152,7 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) spear_start_ohci(ohci_p); ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), IRQF_DISABLED); + retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0); if (retval == 0) return retval; diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c index c4aea3b..5ba1859 100644 --- a/drivers/usb/host/ohci-ssb.c +++ b/drivers/usb/host/ohci-ssb.c @@ -169,7 +169,7 @@ static int ssb_ohci_attach(struct ssb_device *dev) hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) goto err_put_hcd; - err = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); + err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED); if (err) goto err_iounmap; diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index 57ad127..06331d9 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -244,7 +244,7 @@ static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev) ohci = hcd_to_ohci(hcd); ohci_hcd_init(ohci); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, 0); if (ret) goto err_add_hcd; diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index a6f2564..e84ca19 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2519,7 +2519,7 @@ static int __devinit r8a66597_probe(struct platform_device *pdev) hcd->rsrc_start = res->start; hcd->has_tt = 1; - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger); + ret = usb_add_hcd(hcd, irq, irq_trigger); if (ret != 0) { dev_err(&pdev->dev, "Failed to add hcd\n"); goto clean_up3; diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 1a99624..961d663 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1729,7 +1729,7 @@ sl811h_probe(struct platform_device *dev) * Use resource IRQ flags if set by platform device setup. */ irqflags |= IRQF_SHARED; - retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | irqflags); + retval = usb_add_hcd(hcd, irq, irqflags); if (retval != 0) goto err6; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 50d3fcf..a111099 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -223,7 +223,7 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; retval = usb_add_hcd(xhci->shared_hcd, dev->irq, - IRQF_DISABLED | IRQF_SHARED); + IRQF_SHARED); if (retval) goto put_usb3_hcd; /* Roothub already marked as USB 3.0 speed */ diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index f70c5a5..57a6085 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -408,7 +408,7 @@ dma_controller_create(struct musb *musb, void __iomem *base) controller->controller.channel_program = dma_channel_program; controller->controller.channel_abort = dma_channel_abort; - if (request_irq(irq, dma_controller_irq, IRQF_DISABLED, + if (request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), &controller->controller)) { dev_err(dev, "request_irq %d failed!\n", irq); dma_controller_destroy(&controller->controller); diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index ca9b690..8c86787 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -899,7 +899,7 @@ static int otg_bind(struct isp1301 *isp) if (otg_dev) status = request_irq(otg_dev->resource[1].start, omap_otg_irq, - IRQF_DISABLED, DRIVER_NAME, isp); + 0, DRIVER_NAME, isp); else status = -ENODEV; diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index a577f8f..621f6cc 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -145,7 +145,7 @@ int usbhs_mod_probe(struct usbhs_priv *priv) /* irq settings */ ret = request_irq(priv->irq, usbhs_interrupt, - IRQF_DISABLED, dev_name(dev), priv); + 0, dev_name(dev), priv); if (ret) { dev_err(dev, "irq request err\n"); goto mod_init_gadget_err; -- cgit v0.10.2 From 25aa55488c3644b19e33ba79c564191b8e33f477 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 14 Sep 2011 12:33:16 -0400 Subject: USB: document ehci-hcd's "companion" sysfs attribute This patch (as1484) adds documentation for ehci-hcd's "companion" sysfs attribute, which was added to the kernel over four years ago but never documented. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/ABI/testing/sysfs-bus-pci-drivers-ehci_hcd b/Documentation/ABI/testing/sysfs-bus-pci-drivers-ehci_hcd new file mode 100644 index 0000000..60c60fa --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-pci-drivers-ehci_hcd @@ -0,0 +1,46 @@ +What: /sys/bus/pci/drivers/ehci_hcd/.../companion + /sys/bus/usb/devices/usbN/../companion +Date: January 2007 +KernelVersion: 2.6.21 +Contact: Alan Stern +Description: + PCI-based EHCI USB controllers (i.e., high-speed USB-2.0 + controllers) are often implemented along with a set of + "companion" full/low-speed USB-1.1 controllers. When a + high-speed device is plugged in, the connection is routed + to the EHCI controller; when a full- or low-speed device + is plugged in, the connection is routed to the companion + controller. + + Sometimes you want to force a high-speed device to connect + at full speed, which can be accomplished by forcing the + connection to be routed to the companion controller. + That's what this file does. Writing a port number to the + file causes connections on that port to be routed to the + companion controller, and writing the negative of a port + number returns the port to normal operation. + + For example: To force the high-speed device attached to + port 4 on bus 2 to run at full speed: + + echo 4 >/sys/bus/usb/devices/usb2/../companion + + To return the port to high-speed operation: + + echo -4 >/sys/bus/usb/devices/usb2/../companion + + Reading the file gives the list of ports currently forced + to the companion controller. + + Note: Some EHCI controllers do not have companions; they + may contain an internal "transaction translator" or they + may be attached directly to a "rate-matching hub". This + mechanism will not work with such controllers. Also, it + cannot be used to force a port on a high-speed hub to + connect at full speed. + + Note: When this file was first added, it appeared in a + different sysfs directory. The location given above is + correct for 2.6.35 (and probably several earlier kernel + versions as well). + -- cgit v0.10.2 From 2394d67e446bf616a0885167d5f0d397bdacfdfc Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Tue, 13 Sep 2011 08:42:21 +0200 Subject: USB: add RESET_RESUME for webcams shown to be quirky The new runtime PM code has shown that many webcams suffer from a race condition that may crash them upon resume. Runtime PM is especially prone to show the problem because it retains power to the cameras at all times. However system suspension may also crash the devices and retain power to the devices. The only way to solve this problem without races is in usbcore with the RESET_RESUME quirk. Signed-off-by: Oliver Neukum Signed-off-by: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 81ce6a8..38f0510 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -38,6 +38,24 @@ static const struct usb_device_id usb_quirk_list[] = { /* Creative SB Audigy 2 NX */ { USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Webcam C200 */ + { USB_DEVICE(0x046d, 0x0802), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam C250 */ + { USB_DEVICE(0x046d, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam B/C500 */ + { USB_DEVICE(0x046d, 0x0807), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam Pro 9000 */ + { USB_DEVICE(0x046d, 0x0809), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam C310 */ + { USB_DEVICE(0x046d, 0x081b), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* Logitech Webcam C270 */ + { USB_DEVICE(0x046d, 0x0825), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Harmony 700-series */ { USB_DEVICE(0x046d, 0xc122), .driver_info = USB_QUIRK_DELAY_INIT }, @@ -69,6 +87,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x06a3, 0x0006), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Guillemot Webcam Hercules Dualpix Exchange*/ + { USB_DEVICE(0x06f8, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME }, + /* M-Systems Flash Disk Pioneers */ { USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME }, -- cgit v0.10.2 From 45e1892e70b85d251b2e69f4122a04e509ebdf00 Mon Sep 17 00:00:00 2001 From: edwin_rong Date: Fri, 16 Sep 2011 16:53:37 +0800 Subject: USB: Realtek cr: Fix driver freeze issue After auto-delink command is triggered, the CSW won't be sent back to host side, in which scenario, the USB Mass Storage driver will wait for the completion of the URB for MAX_SCHEDULE_TIMEOUT. Signed-off-by: edwin_rong Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 232167a..f664c86 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -293,6 +293,52 @@ static int rts51x_bulk_transport(struct us_data *us, u8 lun, return USB_STOR_TRANSPORT_ERROR; } +static int rts51x_bulk_transport_special(struct us_data *us, u8 lun, + u8 *cmd, int cmd_len, u8 *buf, int buf_len, + enum dma_data_direction dir, int *act_len) +{ + struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf; + int result; + unsigned int cswlen; + unsigned int cbwlen = US_BULK_CB_WRAP_LEN; + + /* set up the command wrapper */ + bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb->DataTransferLength = cpu_to_le32(buf_len); + bcb->Flags = (dir == DMA_FROM_DEVICE) ? 1 << 7 : 0; + bcb->Tag = ++us->tag; + bcb->Lun = lun; + bcb->Length = cmd_len; + + /* copy the command payload */ + memset(bcb->CDB, 0, sizeof(bcb->CDB)); + memcpy(bcb->CDB, cmd, bcb->Length); + + /* send it to out endpoint */ + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, + bcb, cbwlen, NULL); + if (result != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; + + /* DATA STAGE */ + /* send/receive data payload, if there is any */ + + if (buf && buf_len) { + unsigned int pipe = (dir == DMA_FROM_DEVICE) ? + us->recv_bulk_pipe : us->send_bulk_pipe; + result = usb_stor_bulk_transfer_buf(us, pipe, + buf, buf_len, NULL); + if (result == USB_STOR_XFER_ERROR) + return USB_STOR_TRANSPORT_ERROR; + } + + /* get CSW for device status */ + result = usb_bulk_msg(us->pusb_dev, us->recv_bulk_pipe, bcs, + US_BULK_CS_WRAP_LEN, &cswlen, 250); + return result; +} + /* Determine what the maximum LUN supported is */ static int rts51x_get_max_lun(struct us_data *us) { @@ -459,6 +505,29 @@ static int enable_oscillator(struct us_data *us) return 0; } +static int __do_config_autodelink(struct us_data *us, u8 *data, u16 len) +{ + int retval; + u16 addr = 0xFE47; + u8 cmnd[12] = {0}; + + US_DEBUGP("%s, addr = 0x%x, len = %d\n", __FUNCTION__, addr, len); + + cmnd[0] = 0xF0; + cmnd[1] = 0x0E; + cmnd[2] = (u8)(addr >> 8); + cmnd[3] = (u8)addr; + cmnd[4] = (u8)(len >> 8); + cmnd[5] = (u8)len; + + retval = rts51x_bulk_transport_special(us, 0, cmnd, 12, data, len, DMA_TO_DEVICE, NULL); + if (retval != USB_STOR_TRANSPORT_GOOD) { + return -EIO; + } + + return 0; +} + static int do_config_autodelink(struct us_data *us, int enable, int force) { int retval; @@ -479,7 +548,8 @@ static int do_config_autodelink(struct us_data *us, int enable, int force) US_DEBUGP("In %s,set 0xfe47 to 0x%x\n", __func__, value); - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; @@ -511,7 +581,8 @@ static int config_autodelink_after_power_on(struct us_data *us) SET_BIT(value, 7); - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; @@ -532,7 +603,8 @@ static int config_autodelink_after_power_on(struct us_data *us) CLR_BIT(value, 7); } - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; @@ -609,7 +681,8 @@ static int config_autodelink_before_power_down(struct us_data *us) if (CHECK_ID(chip, 0x0138, 0x3882)) SET_BIT(value, 2); - retval = rts51x_write_mem(us, 0xFE47, &value, 1); + /* retval = rts51x_write_mem(us, 0xFE47, &value, 1); */ + retval = __do_config_autodelink(us, &value, 1); if (retval < 0) return -EIO; } -- cgit v0.10.2 From d78265992425ccb5753db9c56d9703c91b963e4b Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 13 Sep 2011 16:41:10 -0700 Subject: usb/xhci: ignore xhci version while checking for the link quirk instead of reading the xhci interface version each time _even_ if the quirk is not required, simply check if the quirk flag is set. This flag is only set of the module parameter is set and here is where I moved the version check to. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index dd0dc15..7583b24 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -316,7 +316,7 @@ int xhci_init(struct usb_hcd *hcd) xhci_dbg(xhci, "xhci_init\n"); spin_lock_init(&xhci->lock); - if (link_quirk) { + if (xhci->hci_version == 0x95 && link_quirk) { xhci_dbg(xhci, "QUIRK: Not clearing Link TRB chain bits.\n"); xhci->quirks |= XHCI_LINK_TRB_QUIRK; } else { diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index b224b20..13f1c23 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1526,9 +1526,7 @@ static inline void xhci_write_64(struct xhci_hcd *xhci, static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci) { - u32 temp = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); - return ((HC_VERSION(temp) == 0x95) && - (xhci->quirks & XHCI_LINK_TRB_QUIRK)); + return xhci->quirks & XHCI_LINK_TRB_QUIRK; } /* xHCI debugging */ -- cgit v0.10.2 From 75d7cf72ab9fa01dc70877aa5c68e8ef477229dc Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Tue, 13 Sep 2011 16:41:11 -0700 Subject: usbcore: refine warm reset logic Current waiting time for warm(BH) reset in hub_port_warm_reset() is too short for xHC host to complete the warm reset and report a BH reset change. This patch increases the waiting time for warm reset and merges the function into hub_port_reset(), so it can handle both cold reset and warm reset, and factor out hub_port_finish_reset() to make the code looks cleaner. This fixes the issue that driver fails to clear BH reset change and port is "dead". Signed-off-by: Andiry Xu Acked-by: Alan Stern Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3edc01b..aa336f7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2025,11 +2025,12 @@ static unsigned hub_is_wusb(struct usb_hub *hub) #define HUB_ROOT_RESET_TIME 50 /* times are in msec */ #define HUB_SHORT_RESET_TIME 10 +#define HUB_BH_RESET_TIME 50 #define HUB_LONG_RESET_TIME 200 #define HUB_RESET_TIMEOUT 500 static int hub_port_wait_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, unsigned int delay) + struct usb_device *udev, unsigned int delay, bool warm) { int delay_time, ret; u16 portstatus; @@ -2046,28 +2047,39 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (ret < 0) return ret; - /* Device went away? */ - if (!(portstatus & USB_PORT_STAT_CONNECTION)) - return -ENOTCONN; - - /* bomb out completely if the connection bounced */ - if ((portchange & USB_PORT_STAT_C_CONNECTION)) - return -ENOTCONN; - - /* if we`ve finished resetting, then break out of the loop */ - if (!(portstatus & USB_PORT_STAT_RESET) && - (portstatus & USB_PORT_STAT_ENABLE)) { - if (hub_is_wusb(hub)) - udev->speed = USB_SPEED_WIRELESS; - else if (hub_is_superspeed(hub->hdev)) - udev->speed = USB_SPEED_SUPER; - else if (portstatus & USB_PORT_STAT_HIGH_SPEED) - udev->speed = USB_SPEED_HIGH; - else if (portstatus & USB_PORT_STAT_LOW_SPEED) - udev->speed = USB_SPEED_LOW; - else - udev->speed = USB_SPEED_FULL; - return 0; + /* + * Some buggy devices require a warm reset to be issued even + * when the port appears not to be connected. + */ + if (!warm) { + /* Device went away? */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) + return -ENOTCONN; + + /* bomb out completely if the connection bounced */ + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + return -ENOTCONN; + + /* if we`ve finished resetting, then break out of + * the loop + */ + if (!(portstatus & USB_PORT_STAT_RESET) && + (portstatus & USB_PORT_STAT_ENABLE)) { + if (hub_is_wusb(hub)) + udev->speed = USB_SPEED_WIRELESS; + else if (hub_is_superspeed(hub->hdev)) + udev->speed = USB_SPEED_SUPER; + else if (portstatus & USB_PORT_STAT_HIGH_SPEED) + udev->speed = USB_SPEED_HIGH; + else if (portstatus & USB_PORT_STAT_LOW_SPEED) + udev->speed = USB_SPEED_LOW; + else + udev->speed = USB_SPEED_FULL; + return 0; + } + } else { + if (portchange & USB_PORT_STAT_C_BH_RESET) + return 0; } /* switch to the long delay after two short delay failures */ @@ -2075,35 +2087,84 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, delay = HUB_LONG_RESET_TIME; dev_dbg (hub->intfdev, - "port %d not reset yet, waiting %dms\n", - port1, delay); + "port %d not %sreset yet, waiting %dms\n", + port1, warm ? "warm " : "", delay); } return -EBUSY; } +static void hub_port_finish_reset(struct usb_hub *hub, int port1, + struct usb_device *udev, int *status, bool warm) +{ + switch (*status) { + case 0: + if (!warm) { + struct usb_hcd *hcd; + /* TRSTRCY = 10 ms; plus some extra */ + msleep(10 + 40); + update_devnum(udev, 0); + hcd = bus_to_hcd(udev->bus); + if (hcd->driver->reset_device) { + *status = hcd->driver->reset_device(hcd, udev); + if (*status < 0) { + dev_err(&udev->dev, "Cannot reset " + "HCD device state\n"); + break; + } + } + } + /* FALL THROUGH */ + case -ENOTCONN: + case -ENODEV: + clear_port_feature(hub->hdev, + port1, USB_PORT_FEAT_C_RESET); + /* FIXME need disconnect() for NOTATTACHED device */ + if (warm) { + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_BH_PORT_RESET); + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + } else { + usb_set_device_state(udev, *status + ? USB_STATE_NOTATTACHED + : USB_STATE_DEFAULT); + } + break; + } +} + +/* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */ static int hub_port_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, unsigned int delay) + struct usb_device *udev, unsigned int delay, bool warm) { int i, status; - struct usb_hcd *hcd; - hcd = bus_to_hcd(udev->bus); - /* Block EHCI CF initialization during the port reset. - * Some companion controllers don't like it when they mix. - */ - down_read(&ehci_cf_port_reset_rwsem); + if (!warm) { + /* Block EHCI CF initialization during the port reset. + * Some companion controllers don't like it when they mix. + */ + down_read(&ehci_cf_port_reset_rwsem); + } else { + if (!hub_is_superspeed(hub->hdev)) { + dev_err(hub->intfdev, "only USB3 hub support " + "warm reset\n"); + return -EINVAL; + } + } /* Reset the port */ for (i = 0; i < PORT_RESET_TRIES; i++) { - status = set_port_feature(hub->hdev, - port1, USB_PORT_FEAT_RESET); - if (status) + status = set_port_feature(hub->hdev, port1, (warm ? + USB_PORT_FEAT_BH_PORT_RESET : + USB_PORT_FEAT_RESET)); + if (status) { dev_err(hub->intfdev, - "cannot reset port %d (err = %d)\n", - port1, status); - else { - status = hub_port_wait_reset(hub, port1, udev, delay); + "cannot %sreset port %d (err = %d)\n", + warm ? "warm " : "", port1, status); + } else { + status = hub_port_wait_reset(hub, port1, udev, delay, + warm); if (status && status != -ENOTCONN) dev_dbg(hub->intfdev, "port_wait_reset: err = %d\n", @@ -2111,34 +2172,14 @@ static int hub_port_reset(struct usb_hub *hub, int port1, } /* return on disconnect or reset */ - switch (status) { - case 0: - /* TRSTRCY = 10 ms; plus some extra */ - msleep(10 + 40); - update_devnum(udev, 0); - if (hcd->driver->reset_device) { - status = hcd->driver->reset_device(hcd, udev); - if (status < 0) { - dev_err(&udev->dev, "Cannot reset " - "HCD device state\n"); - break; - } - } - /* FALL THROUGH */ - case -ENOTCONN: - case -ENODEV: - clear_port_feature(hub->hdev, - port1, USB_PORT_FEAT_C_RESET); - /* FIXME need disconnect() for NOTATTACHED device */ - usb_set_device_state(udev, status - ? USB_STATE_NOTATTACHED - : USB_STATE_DEFAULT); + if (status == 0 || status == -ENOTCONN || status == -ENODEV) { + hub_port_finish_reset(hub, port1, udev, &status, warm); goto done; } dev_dbg (hub->intfdev, - "port %d not enabled, trying reset again...\n", - port1); + "port %d not enabled, trying %sreset again...\n", + port1, warm ? "warm " : ""); delay = HUB_LONG_RESET_TIME; } @@ -2146,45 +2187,11 @@ static int hub_port_reset(struct usb_hub *hub, int port1, "Cannot enable port %i. Maybe the USB cable is bad?\n", port1); - done: - up_read(&ehci_cf_port_reset_rwsem); - return status; -} - -/* Warm reset a USB3 protocol port */ -static int hub_port_warm_reset(struct usb_hub *hub, int port) -{ - int ret; - u16 portstatus, portchange; - - if (!hub_is_superspeed(hub->hdev)) { - dev_err(hub->intfdev, "only USB3 hub support warm reset\n"); - return -EINVAL; - } - - /* Warm reset the port */ - ret = set_port_feature(hub->hdev, - port, USB_PORT_FEAT_BH_PORT_RESET); - if (ret) { - dev_err(hub->intfdev, "cannot warm reset port %d\n", port); - return ret; - } - - msleep(20); - ret = hub_port_status(hub, port, &portstatus, &portchange); - - if (portchange & USB_PORT_STAT_C_RESET) - clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET); - - if (portchange & USB_PORT_STAT_C_BH_RESET) - clear_port_feature(hub->hdev, port, - USB_PORT_FEAT_C_BH_PORT_RESET); - - if (portchange & USB_PORT_STAT_C_LINK_STATE) - clear_port_feature(hub->hdev, port, - USB_PORT_FEAT_C_PORT_LINK_STATE); +done: + if (!warm) + up_read(&ehci_cf_port_reset_rwsem); - return ret; + return status; } /* Check if a port is power on */ @@ -2814,7 +2821,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, /* Reset the device; full speed may morph to high speed */ /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ - retval = hub_port_reset(hub, port1, udev, delay); + retval = hub_port_reset(hub, port1, udev, delay, false); if (retval < 0) /* error or disconnect */ goto fail; /* success, speed is known */ @@ -2935,7 +2942,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, buf->bMaxPacketSize0; kfree(buf); - retval = hub_port_reset(hub, port1, udev, delay); + retval = hub_port_reset(hub, port1, udev, delay, false); if (retval < 0) /* error or disconnect */ goto fail; if (oldspeed != udev->speed) { @@ -3556,7 +3563,8 @@ static void hub_events(void) (portstatus & USB_PORT_STAT_LINK_STATE) == USB_SS_PORT_LS_SS_INACTIVE) { dev_dbg(hub_dev, "warm reset port %d\n", i); - hub_port_warm_reset(hub, i); + hub_port_reset(hub, i, NULL, + HUB_BH_RESET_TIME, true); } if (connect_change) -- cgit v0.10.2 From 170c026347c867a71e27713b98c58b266146c468 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 13 Sep 2011 16:41:12 -0700 Subject: xhci: Fix mult base in endpoint bandwidth info. The "Mult" bits in the SuperSpeed Endpoint Companion Descriptor are zero-based, and the xHCI host controller wants them to be zero-based in the input context. However, for the bandwidth math, we want them to be one-based. Fix this. Fix the documentation about the endpoint bandwidth mult variable in the xhci.h file, which says it is zero-based. Also fix the documentation about num_packets, which is also one-based, not zero-based. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index c4b8959..94a8b28 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1473,11 +1473,12 @@ void xhci_update_bw_info(struct xhci_hcd *xhci, /* Added or changed endpoint */ bw_info->ep_interval = CTX_TO_EP_INTERVAL( le32_to_cpu(ep_ctx->ep_info)); - bw_info->mult = CTX_TO_EP_MULT( - le32_to_cpu(ep_ctx->ep_info)); - /* Number of packets is zero-based in the input context, - * but we want one-based for the interval table. + /* Number of packets and mult are zero-based in the + * input context, but we want one-based for the + * interval table. */ + bw_info->mult = CTX_TO_EP_MULT( + le32_to_cpu(ep_ctx->ep_info)) + 1; bw_info->num_packets = CTX_TO_MAX_BURST( le32_to_cpu(ep_ctx->ep_info2)) + 1; bw_info->max_packet_size = MAX_PACKET_DECODED( diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 13f1c23..752a500 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -747,8 +747,9 @@ struct xhci_stream_info { * (DMI) also limits the total bandwidth (across all domains) that can be used. */ struct xhci_bw_info { + /* ep_interval is zero-based */ unsigned int ep_interval; - /* mult and num_packets are zero-based */ + /* mult and num_packets are one-based */ unsigned int mult; unsigned int num_packets; unsigned int max_packet_size; -- cgit v0.10.2 From 2b69899934c63b7b9432568584fb4c4a2924f40c Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Tue, 13 Sep 2011 16:41:13 -0700 Subject: xhci: USB 3.0 BW checking. The Intel Panther Point xHCI host tracks SuperSpeed endpoints in a different way than USB 2.0/1.1 endpoints. The bandwidth interval tables are not used, and instead the bandwidth is calculated in a very simple way. Bandwidth for SuperSpeed endpoints is tracked individually in each direction, since each direction has the full USB 3.0 bandwidth available. 10% of the bus bandwidth is reserved for non-periodic transfers. This checking would be more complex if we had USB 3.0 LPM enabled, because an additional latency for isochronous ping times need to be taken into account. However, we don't have USB 3.0 LPM support in Linux yet. Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7583b24..54cb762 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1810,6 +1810,22 @@ static int xhci_check_tt_bw_table(struct xhci_hcd *xhci, return 0; } +static int xhci_check_ss_bw(struct xhci_hcd *xhci, + struct xhci_virt_device *virt_dev) +{ + unsigned int bw_reserved; + + bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_IN, 100); + if (virt_dev->bw_table->ss_bw_in > (SS_BW_LIMIT_IN - bw_reserved)) + return -ENOMEM; + + bw_reserved = DIV_ROUND_UP(SS_BW_RESERVED*SS_BW_LIMIT_OUT, 100); + if (virt_dev->bw_table->ss_bw_out > (SS_BW_LIMIT_OUT - bw_reserved)) + return -ENOMEM; + + return 0; +} + /* * This algorithm is a very conservative estimate of the worst-case scheduling * scenario for any one interval. The hardware dynamically schedules the @@ -1866,6 +1882,9 @@ static int xhci_check_bw_table(struct xhci_hcd *xhci, unsigned int packets_remaining = 0; unsigned int i; + if (virt_dev->udev->speed == USB_SPEED_SUPER) + return xhci_check_ss_bw(xhci, virt_dev); + if (virt_dev->udev->speed == USB_SPEED_HIGH) { max_bandwidth = HS_BW_LIMIT; /* Convert percent of bus BW reserved to blocks reserved */ @@ -2028,6 +2047,25 @@ static bool xhci_is_async_ep(unsigned int ep_type) ep_type != INT_IN_EP); } +static bool xhci_is_sync_in_ep(unsigned int ep_type) +{ + return (ep_type == ISOC_IN_EP || ep_type != INT_IN_EP); +} + +static unsigned int xhci_get_ss_bw_consumed(struct xhci_bw_info *ep_bw) +{ + unsigned int mps = DIV_ROUND_UP(ep_bw->max_packet_size, SS_BLOCK); + + if (ep_bw->ep_interval == 0) + return SS_OVERHEAD_BURST + + (ep_bw->mult * ep_bw->num_packets * + (SS_OVERHEAD + mps)); + return DIV_ROUND_UP(ep_bw->mult * ep_bw->num_packets * + (SS_OVERHEAD + mps + SS_OVERHEAD_BURST), + 1 << ep_bw->ep_interval); + +} + void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, struct xhci_bw_info *ep_bw, struct xhci_interval_bw_table *bw_table, @@ -2038,10 +2076,24 @@ void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, struct xhci_interval_bw *interval_bw; int normalized_interval; - if (xhci_is_async_ep(ep_bw->type) || - list_empty(&virt_ep->bw_endpoint_list)) + if (xhci_is_async_ep(ep_bw->type)) return; + if (udev->speed == USB_SPEED_SUPER) { + if (xhci_is_sync_in_ep(ep_bw->type)) + xhci->devs[udev->slot_id]->bw_table->ss_bw_in -= + xhci_get_ss_bw_consumed(ep_bw); + else + xhci->devs[udev->slot_id]->bw_table->ss_bw_out -= + xhci_get_ss_bw_consumed(ep_bw); + return; + } + + /* SuperSpeed endpoints never get added to intervals in the table, so + * this check is only valid for HS/FS/LS devices. + */ + if (list_empty(&virt_ep->bw_endpoint_list)) + return; /* For LS/FS devices, we need to translate the interval expressed in * microframes to frames. */ @@ -2091,6 +2143,16 @@ static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci, if (xhci_is_async_ep(ep_bw->type)) return; + if (udev->speed == USB_SPEED_SUPER) { + if (xhci_is_sync_in_ep(ep_bw->type)) + xhci->devs[udev->slot_id]->bw_table->ss_bw_in += + xhci_get_ss_bw_consumed(ep_bw); + else + xhci->devs[udev->slot_id]->bw_table->ss_bw_out += + xhci_get_ss_bw_consumed(ep_bw); + return; + } + /* For LS/FS devices, we need to translate the interval expressed in * microframes to frames. */ @@ -2169,9 +2231,6 @@ static int xhci_reserve_bandwidth(struct xhci_hcd *xhci, struct xhci_input_control_ctx *ctrl_ctx; int old_active_eps = 0; - if (virt_dev->udev->speed == USB_SPEED_SUPER) - return 0; - if (virt_dev->tt_info) old_active_eps = virt_dev->tt_info->active_eps; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 752a500..2882074 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -799,6 +799,7 @@ struct xhci_bw_info { /* Percentage of bus bandwidth reserved for non-periodic transfers */ #define FS_BW_RESERVED 10 #define HS_BW_RESERVED 20 +#define SS_BW_RESERVED 10 struct xhci_virt_ep { struct xhci_ring *ring; @@ -869,6 +870,8 @@ struct xhci_interval_bw_table { struct xhci_interval_bw interval_bw[XHCI_MAX_INTERVAL]; /* Includes reserved bandwidth for async endpoints */ unsigned int bw_used; + unsigned int ss_bw_in; + unsigned int ss_bw_out; }; -- cgit v0.10.2 From 10d674a82e553cb8a1f41027bb3c3e309b3f6804 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Wed, 14 Sep 2011 14:24:52 -0700 Subject: USB: When hot reset for USB3 fails, try warm reset. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a hot reset (standard USB port reset) fails on a USB 3.0 port, the host controller transitions to the "Error" state. It reports the port link state as "Inactive", sets the link state change flag, and (if the device disconnects) also reports the disconnect and connect change status. It's also supposed to transition the link state to "RxDetect", but the NEC µPD720200 xHCI host does not. Unfortunately, Harald found that the combination of the NEC µPD720200 and a LogiLink USB 3.0 to SATA adapter triggered this issue. The USB core would reset the device, the port would go into this error state, and the device would never be enumerated. This combination works under Windows, but not under Linux. When a hot reset fails on a USB 3.0 port, and the link state is reported as Inactive, fall back to a warm port reset instead. Harald confirms that with a warm port reset (along with all the change bits being correctly cleared), the USB 3.0 device will successfully enumerate. Harald also had to add two other patches ("xhci: Set change bit when warm reset change is set." and "usbcore: refine warm reset logic") to make this setup work. Since the warm reset refinement patch is not destined for the stable kernels (it's too big), this patch should not be backported either. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=41752 Signed-off-by: Sarah Sharp Tested-by: Harald Brennich Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index aa336f7..1c15512 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2029,6 +2029,17 @@ static unsigned hub_is_wusb(struct usb_hub *hub) #define HUB_LONG_RESET_TIME 200 #define HUB_RESET_TIMEOUT 500 +static int hub_port_reset(struct usb_hub *hub, int port1, + struct usb_device *udev, unsigned int delay, bool warm); + +/* Is a USB 3.0 port in the Inactive state? */ +static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus) +{ + return hub_is_superspeed(hub->hdev) && + (portstatus & USB_PORT_STAT_LINK_STATE) == + USB_SS_PORT_LS_SS_INACTIVE; +} + static int hub_port_wait_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay, bool warm) { @@ -2052,6 +2063,38 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, * when the port appears not to be connected. */ if (!warm) { + /* + * Some buggy devices can cause an NEC host controller + * to transition to the "Error" state after a hot port + * reset. This will show up as the port state in + * "Inactive", and the port may also report a + * disconnect. Forcing a warm port reset seems to make + * the device work. + * + * See https://bugzilla.kernel.org/show_bug.cgi?id=41752 + */ + if (hub_port_inactive(hub, portstatus)) { + int ret; + + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + if (portchange & USB_PORT_STAT_C_LINK_STATE) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + if (portchange & USB_PORT_STAT_C_RESET) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_RESET); + dev_dbg(hub->intfdev, "hot reset failed, warm reset port %d\n", + port1); + ret = hub_port_reset(hub, port1, + udev, HUB_BH_RESET_TIME, + true); + if ((portchange & USB_PORT_STAT_C_CONNECTION)) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + return ret; + } /* Device went away? */ if (!(portstatus & USB_PORT_STAT_CONNECTION)) return -ENOTCONN; -- cgit v0.10.2 From 0e6c7f746ea99089fb3263709075c20485a479ae Mon Sep 17 00:00:00 2001 From: Kautuk Consul Date: Mon, 19 Sep 2011 16:53:12 -0700 Subject: xhci-mem.c: Check for ring->first_seg != NULL There are 2 situations wherein the xhci_ring* might not get freed: - When xhci_ring_alloc() -> xhci_segment_alloc() returns NULL and we goto the fail: label in xhci_ring_alloc. In this case, the ring will not get kfreed. - When the num_segs argument to xhci_ring_alloc is passed as 0 and we try to free the rung after that. ( This doesn't really happen as of now in the code but we seem to be entertaining num_segs=0 in xhci_ring_alloc ) This should be backported to kernels as old as 2.6.31. Signed-off-by: Kautuk Consul Signed-off-by: Sarah Sharp Cc: stable@kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 94a8b28..e4513ab 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -112,18 +112,20 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring) struct xhci_segment *seg; struct xhci_segment *first_seg; - if (!ring || !ring->first_seg) + if (!ring) return; - first_seg = ring->first_seg; - seg = first_seg->next; - xhci_dbg(xhci, "Freeing ring at %p\n", ring); - while (seg != first_seg) { - struct xhci_segment *next = seg->next; - xhci_segment_free(xhci, seg); - seg = next; + if (ring->first_seg) { + first_seg = ring->first_seg; + seg = first_seg->next; + xhci_dbg(xhci, "Freeing ring at %p\n", ring); + while (seg != first_seg) { + struct xhci_segment *next = seg->next; + xhci_segment_free(xhci, seg); + seg = next; + } + xhci_segment_free(xhci, first_seg); + ring->first_seg = NULL; } - xhci_segment_free(xhci, first_seg); - ring->first_seg = NULL; kfree(ring); } -- cgit v0.10.2 From 27ccaaa507f6211afc277786d461d1884951d038 Mon Sep 17 00:00:00 2001 From: Kautuk Consul Date: Mon, 19 Sep 2011 16:53:16 -0700 Subject: xhci-mem.c: xhci_segment_free: No need for checking seg argument The seg argument to xhci_segment_free is never passed as NULL, so no need to check for this in xhci_segment_free. Signed-off-by: Kautuk Consul Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index e4513ab..bc6ec0c 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -61,8 +61,6 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci, gfp_t flag static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) { - if (!seg) - return; if (seg->trbs) { xhci_dbg(xhci, "Freeing DMA segment at %p (virtual) 0x%llx (DMA)\n", seg->trbs, (unsigned long long)seg->dma); -- cgit v0.10.2 From 68c79e5756903229fa96826a2493c2265a3b395f Mon Sep 17 00:00:00 2001 From: Richard Hartmann Date: Tue, 20 Sep 2011 20:50:51 +0200 Subject: USB: qcserial: Add support for Sierra Wireless MC8355/Gobi 3000 Simple patch to make qcserial recognize the USB id of the Sierra Wireless MC8355 which is based on the Gobi 3000 chip. Both UMTS and GPS work fine. Signed-off-by: Richard Hartmann Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index aeccc7f..d8651ea 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -84,6 +84,7 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */ {USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */ {USB_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */ + {USB_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); -- cgit v0.10.2 From 598f0b703506da841d3459dc0c48506be14d1778 Mon Sep 17 00:00:00 2001 From: Eric Benoit Date: Sat, 24 Sep 2011 02:04:50 -0400 Subject: USB: pl2303: add id for SMART device Add vendor and product ID for the SMART USB to serial adapter. These were meant to be used with their SMART Board whiteboards, but can be re-purposed for other tasks. Tested and working (at at least 9600 bps). Signed-off-by: Eric Benoit Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 0ae840f..9083d1e 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -92,6 +92,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) }, { USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) }, { USB_DEVICE(WINCHIPHEAD_VENDOR_ID, WINCHIPHEAD_USBSER_PRODUCT_ID) }, + { USB_DEVICE(SMART_VENDOR_ID, SMART_PRODUCT_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index ca0d237..3d10d7f 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -148,3 +148,8 @@ /* WinChipHead USB->RS 232 adapter */ #define WINCHIPHEAD_VENDOR_ID 0x4348 #define WINCHIPHEAD_USBSER_PRODUCT_ID 0x5523 + +/* SMART USB Serial Adapter */ +#define SMART_VENDOR_ID 0x0b8c +#define SMART_PRODUCT_ID 0x2303 + -- cgit v0.10.2 From 865569ba5a8b0cc840e2b9c16ca6b42d6d5306b4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 8 Sep 2011 14:12:18 +0300 Subject: usb: gadget: udc-core: fix bug on soft_connect interface We should not be using dev_get_drvdata() because we never call dev_set_drvdata(). Let's use container_of() as all other sysfs attributes. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index 5e77a46..022baec 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c @@ -356,7 +356,7 @@ static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store); static ssize_t usb_udc_softconn_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) { - struct usb_udc *udc = dev_get_drvdata(dev); + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); if (sysfs_streq(buf, "connect")) { usb_gadget_connect(udc->gadget); -- cgit v0.10.2 From 236c448cb6e7f82096101e1ace4b77f8b38f82c8 Mon Sep 17 00:00:00 2001 From: Johannes Stezenbach Date: Thu, 8 Sep 2011 15:39:15 +0200 Subject: usbmon vs. tcpdump: fix dropped packet count Report the number of dropped packets instead of zero when using the binary usbmon interface with tcpdump. # tcpdump -i usbmon1 -w dump tcpdump: listening on usbmon1, link-type USB_LINUX_MMAPPED (USB with padded Linux header), capture size 65535 bytes ^C2155 packets captured 2155 packets received by filter 1019 packets dropped by kernel Signed-off-by: Johannes Stezenbach Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index a09dbd2..a04b2ff 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1101,7 +1101,7 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg nevents = mon_bin_queued(rp); sp = (struct mon_bin_stats __user *)arg; - if (put_user(rp->cnt_lost, &sp->dropped)) + if (put_user(ndropped, &sp->dropped)) return -EFAULT; if (put_user(nevents, &sp->queued)) return -EFAULT; -- cgit v0.10.2 From 2093c6b49c8f1dc581d8953aca71297d4cace55e Mon Sep 17 00:00:00 2001 From: Matthieu CASTET Date: Sat, 2 Jul 2011 19:47:33 +0200 Subject: EHCI : introduce a common ehci_setup This allow to clean duplicated code in most of SOC driver. Signed-off-by: Matthieu CASTET Acked-by: Alan Stern Cc: stable # fixes 3.1 build error Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2f0cf86..05abbcd 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -768,6 +768,35 @@ static int ehci_run (struct usb_hcd *hcd) return 0; } +static int __maybe_unused ehci_setup (struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + ehci->regs = (void __iomem *)ehci->caps + + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + ehci->sbrn = HCD_USB2; + + retval = ehci_halt(ehci); + if (retval) + return retval; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci_reset(ehci); + + return 0; +} + /*-------------------------------------------------------------------------*/ static irqreturn_t ehci_irq (struct usb_hcd *hcd) -- cgit v0.10.2 From 5023829969f56b78a16da94f34c605bbbb344018 Mon Sep 17 00:00:00 2001 From: Shaun Silk Date: Mon, 26 Sep 2011 11:26:43 +1000 Subject: USB: gadget: u_serial.c: fixed a brace coding style issue Signed-off-by: Shaun Silk Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c index a8aa469..3a4a664 100644 --- a/drivers/usb/gadget/u_serial.c +++ b/drivers/usb/gadget/u_serial.c @@ -552,9 +552,8 @@ recycle: /* Push from tty to ldisc; without low_latency set this is handled by * a workqueue, so we won't get callbacks and can hold port_lock */ - if (tty && do_push) { + if (tty && do_push) tty_flip_buffer_push(tty); - } /* We want our data queue to become empty ASAP, keeping data -- cgit v0.10.2 From 3148bf041d169a083aa31bd69bedd5bfb7ffe215 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:47 -0700 Subject: usbcore: get BOS descriptor set This commit gets BOS(Binary Device Object Store) descriptor set for Super Speed devices and High Speed devices which support BOS descriptor. BOS descriptor is used to report additional USB device-level capabilities that are not reported via the Device descriptor. By getting BOS descriptor set, driver can check device's device-level capability such as LPM capability. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 9d5e07a..f4bdd0c 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -755,3 +755,106 @@ err2: dev_err(ddev, "out of memory\n"); return result; } + +void usb_release_bos_descriptor(struct usb_device *dev) +{ + if (dev->bos) { + kfree(dev->bos->desc); + kfree(dev->bos); + dev->bos = NULL; + } +} + +/* Get BOS descriptor set */ +int usb_get_bos_descriptor(struct usb_device *dev) +{ + struct device *ddev = &dev->dev; + struct usb_bos_descriptor *bos; + struct usb_dev_cap_header *cap; + unsigned char *buffer; + int length, total_len, num, i; + int ret; + + bos = kzalloc(sizeof(struct usb_bos_descriptor), GFP_KERNEL); + if (!bos) + return -ENOMEM; + + /* Get BOS descriptor */ + ret = usb_get_descriptor(dev, USB_DT_BOS, 0, bos, USB_DT_BOS_SIZE); + if (ret < USB_DT_BOS_SIZE) { + dev_err(ddev, "unable to get BOS descriptor\n"); + if (ret >= 0) + ret = -ENOMSG; + kfree(bos); + return ret; + } + + length = bos->bLength; + total_len = le16_to_cpu(bos->wTotalLength); + num = bos->bNumDeviceCaps; + kfree(bos); + if (total_len < length) + return -EINVAL; + + dev->bos = kzalloc(sizeof(struct usb_host_bos), GFP_KERNEL); + if (!dev->bos) + return -ENOMEM; + + /* Now let's get the whole BOS descriptor set */ + buffer = kzalloc(total_len, GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; + goto err; + } + dev->bos->desc = (struct usb_bos_descriptor *)buffer; + + ret = usb_get_descriptor(dev, USB_DT_BOS, 0, buffer, total_len); + if (ret < total_len) { + dev_err(ddev, "unable to get BOS descriptor set\n"); + if (ret >= 0) + ret = -ENOMSG; + goto err; + } + total_len -= length; + + for (i = 0; i < num; i++) { + buffer += length; + cap = (struct usb_dev_cap_header *)buffer; + length = cap->bLength; + + if (total_len < length) + break; + total_len -= length; + + if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) { + dev_warn(ddev, "descriptor type invalid, skip\n"); + continue; + } + + switch (cap->bDevCapabilityType) { + case USB_CAP_TYPE_WIRELESS_USB: + /* Wireless USB cap descriptor is handled by wusb */ + break; + case USB_CAP_TYPE_EXT: + dev->bos->ext_cap = + (struct usb_ext_cap_descriptor *)buffer; + break; + case USB_SS_CAP_TYPE: + dev->bos->ss_cap = + (struct usb_ss_cap_descriptor *)buffer; + break; + case CONTAINER_ID_TYPE: + dev->bos->ss_id = + (struct usb_ss_container_id_descriptor *)buffer; + break; + default: + break; + } + } + + return 0; + +err: + usb_release_bos_descriptor(dev); + return ret; +} diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 1c15512..7a25143 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3083,6 +3083,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, goto fail; } + if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) + usb_get_bos_descriptor(udev); + retval = 0; /* notify HCD that we have a device connected and addressed */ if (hcd->driver->update_device) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 8706fc9..73cd900 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -225,6 +225,7 @@ static void usb_release_dev(struct device *dev) hcd = bus_to_hcd(udev->bus); usb_destroy_configuration(udev); + usb_release_bos_descriptor(udev); usb_put_hcd(hcd); kfree(udev->product); kfree(udev->manufacturer); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index d44d4b7..0d023cd 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -28,6 +28,8 @@ extern int usb_remove_device(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); +extern int usb_get_bos_descriptor(struct usb_device *dev); +extern void usb_release_bos_descriptor(struct usb_device *dev); extern char *usb_cache_string(struct usb_device *udev, int index); extern int usb_set_configuration(struct usb_device *dev, int configuration); extern int usb_choose_configuration(struct usb_device *udev); diff --git a/include/linux/usb.h b/include/linux/usb.h index c19f910..90ab9dc 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -292,6 +292,16 @@ struct usb_host_config { int extralen; }; +/* USB2.0 and USB3.0 device BOS descriptor set */ +struct usb_host_bos { + struct usb_bos_descriptor *desc; + + /* wireless cap descriptor is handled by wusb */ + struct usb_ext_cap_descriptor *ext_cap; + struct usb_ss_cap_descriptor *ss_cap; + struct usb_ss_container_id_descriptor *ss_id; +}; + int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr); #define usb_get_extra_descriptor(ifpoint, type, ptr) \ @@ -381,6 +391,7 @@ struct usb_tt; * @ep0: endpoint 0 data (default control pipe) * @dev: generic device interface * @descriptor: USB device descriptor + * @bos: USB device BOS descriptor set * @config: all of the device's configs * @actconfig: the active configuration * @ep_in: array of IN endpoints @@ -442,6 +453,7 @@ struct usb_device { struct device dev; struct usb_device_descriptor descriptor; + struct usb_host_bos *bos; struct usb_host_config *config; struct usb_host_config *actconfig; -- cgit v0.10.2 From 1ff4df56846d10379939166713bb2908e6a5ee0e Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:48 -0700 Subject: usbcore: check device's LPM capability Check device's LPM capability by examining the bmAttibutes field of the USB2.0 Extension Descriptor. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 7a25143..4ffc3d1 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3083,8 +3083,14 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, goto fail; } - if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) - usb_get_bos_descriptor(udev); + if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) { + retval = usb_get_bos_descriptor(udev); + if (!retval) { + if (udev->bos->ext_cap && (USB_LPM_SUPPORT & + le32_to_cpu(udev->bos->ext_cap->bmAttributes))) + udev->lpm_capable = 1; + } + } retval = 0; /* notify HCD that we have a device connected and addressed */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 90ab9dc..1d00d9bc 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -410,6 +410,7 @@ struct usb_tt; * FIXME -- complete doc * @authenticated: Crypto authentication passed * @wusb: device is Wireless USB + * @lpm_capable: device supports LPM * @string_langid: language ID for strings * @product: iProduct string, if present (static) * @manufacturer: iManufacturer string, if present (static) @@ -472,6 +473,7 @@ struct usb_device { unsigned authorized:1; unsigned authenticated:1; unsigned wusb:1; + unsigned lpm_capable:1; int string_langid; /* static strings from the device */ -- cgit v0.10.2 From c9682dffceb4bb3bdf6df4c0c87c4b887b03f5b7 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:48 -0700 Subject: xHCI: set link state Introduce xhci_set_link_state() to remove redundant codes. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 7a62b02..ef03c18 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -392,13 +392,25 @@ static int xhci_get_ports(struct usb_hcd *hcd, __le32 __iomem ***port_array) return max_ports; } +void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 link_state) +{ + u32 temp; + + temp = xhci_readl(xhci, port_array[port_id]); + temp = xhci_port_state_to_neutral(temp); + temp &= ~PORT_PLS_MASK; + temp |= PORT_LINK_STROBE | link_state; + xhci_writel(xhci, temp, port_array[port_id]); +} + int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports; unsigned long flags; - u32 temp, temp1, status; + u32 temp, status; int retval = 0; __le32 __iomem **port_array; int slot_id; @@ -472,11 +484,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_dbg(xhci, "Resume USB2 port %d\n", wIndex + 1); bus_state->resume_done[wIndex] = 0; - temp1 = xhci_port_state_to_neutral(temp); - temp1 &= ~PORT_PLS_MASK; - temp1 |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp1, port_array[wIndex]); - + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); xhci_dbg(xhci, "set port %d resume\n", wIndex + 1); slot_id = xhci_find_slot_id_by_port(hcd, xhci, @@ -573,10 +582,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_stop_device(xhci, slot_id, 1); spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U3; - xhci_writel(xhci, temp, port_array[wIndex]); + xhci_set_link_state(xhci, port_array, wIndex, XDEV_U3); spin_unlock_irqrestore(&xhci->lock, flags); msleep(10); /* wait device to enter */ @@ -610,10 +616,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } } - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | link_state; - xhci_writel(xhci, temp, port_array[wIndex]); + xhci_set_link_state(xhci, port_array, wIndex, + link_state); spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); /* wait device to enter */ @@ -677,24 +681,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if ((temp & PORT_PE) == 0) goto error; - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_RESUME; - xhci_writel(xhci, temp, - port_array[wIndex]); - - spin_unlock_irqrestore(&xhci->lock, - flags); + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_RESUME); + spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); spin_lock_irqsave(&xhci->lock, flags); - - temp = xhci_readl(xhci, - port_array[wIndex]); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, - port_array[wIndex]); + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); } bus_state->port_c_suspend |= 1 << wIndex; @@ -910,25 +903,18 @@ int xhci_bus_resume(struct usb_hcd *hcd) if (test_bit(port_index, &bus_state->bus_suspended) && (temp & PORT_PLS_MASK)) { if (DEV_SUPERSPEED(temp)) { - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, port_array[port_index]); + xhci_set_link_state(xhci, port_array, + port_index, XDEV_U0); } else { - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_RESUME; - xhci_writel(xhci, temp, port_array[port_index]); + xhci_set_link_state(xhci, port_array, + port_index, XDEV_RESUME); spin_unlock_irqrestore(&xhci->lock, flags); msleep(20); spin_lock_irqsave(&xhci->lock, flags); - temp = xhci_readl(xhci, port_array[port_index]); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, port_array[port_index]); + xhci_set_link_state(xhci, port_array, + port_index, XDEV_U0); } /* wait for the port to enter U0 and report port link * state change. diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index d14f3fb..0ef4f20 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1329,10 +1329,8 @@ static void handle_port_status(struct xhci_hcd *xhci, if (DEV_SUPERSPEED(temp)) { xhci_dbg(xhci, "resume SS port %d\n", port_id); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, port_array[faked_port_index]); + xhci_set_link_state(xhci, port_array, faked_port_index, + XDEV_U0); slot_id = xhci_find_slot_id_by_port(hcd, xhci, faked_port_index); if (!slot_id) { diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 2882074..92eba69 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1717,6 +1717,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id); /* xHCI roothub code */ +void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 link_state); int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength); int xhci_hub_status_data(struct usb_hcd *hcd, char *buf); -- cgit v0.10.2 From d2f52c9e585bbb1a3c164e02b8dcd0d996c67353 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:49 -0700 Subject: xHCI: test and clear RWC bit Introduce xhci_test_and_clear_bit() to clear RWC bit in PORTSC register. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index ef03c18..d7be6d7 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -404,6 +404,20 @@ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, xhci_writel(xhci, temp, port_array[port_id]); } +/* Test and clear port RWC bit */ +void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 port_bit) +{ + u32 temp; + + temp = xhci_readl(xhci, port_array[port_id]); + if (temp & port_bit) { + temp = xhci_port_state_to_neutral(temp); + temp |= port_bit; + xhci_writel(xhci, temp, port_array[port_id]); + } +} + int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { @@ -924,12 +938,8 @@ int xhci_bus_resume(struct usb_hcd *hcd) spin_lock_irqsave(&xhci->lock, flags); /* Clear PLC */ - temp = xhci_readl(xhci, port_array[port_index]); - if (temp & PORT_PLC) { - temp = xhci_port_state_to_neutral(temp); - temp |= PORT_PLC; - xhci_writel(xhci, temp, port_array[port_index]); - } + xhci_test_and_clear_bit(xhci, port_array, port_index, + PORT_PLC); slot_id = xhci_find_slot_id_by_port(hcd, xhci, port_index + 1); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 0ef4f20..30b3577 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1340,10 +1340,8 @@ static void handle_port_status(struct xhci_hcd *xhci, xhci_ring_device(xhci, slot_id); xhci_dbg(xhci, "resume SS port %d finished\n", port_id); /* Clear PORT_PLC */ - temp = xhci_readl(xhci, port_array[faked_port_index]); - temp = xhci_port_state_to_neutral(temp); - temp |= PORT_PLC; - xhci_writel(xhci, temp, port_array[faked_port_index]); + xhci_test_and_clear_bit(xhci, port_array, + faked_port_index, PORT_PLC); } else { xhci_dbg(xhci, "resume HS port %d\n", port_id); bus_state->resume_done[faked_port_index] = jiffies + diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 92eba69..fa921cb 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1719,6 +1719,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id, /* xHCI roothub code */ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array, int port_id, u32 link_state); +void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, + int port_id, u32 port_bit); int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength); int xhci_hub_status_data(struct usb_hcd *hcd, char *buf); -- cgit v0.10.2 From 6fd4562178508a0949c9fdecd8558d8b10d671bd Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:50 -0700 Subject: xHCI: Clear PLC for USB2 root hub ports When the link state changes, xHC will report a port status change event and set the PORT_PLC bit, for both USB3 and USB2 root hub ports. The PLC will be cleared by usbcore for USB3 root hub ports, but not for USB2 ports, because they do not report USB_PORT_STAT_C_LINK_STATE in wPortChange. Clear it for USB2 root hub ports in handle_port_status(). Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 30b3577..a367963 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1352,6 +1352,10 @@ static void handle_port_status(struct xhci_hcd *xhci, } } + if (hcd->speed != HCD_USB3) + xhci_test_and_clear_bit(xhci, port_array, faked_port_index, + PORT_PLC); + cleanup: /* Update event ring dequeue pointer before dropping the lock */ inc_deq(xhci, xhci->event_ring, true); -- cgit v0.10.2 From fc71ff7583b14347fa1cb592b698f088ecff36e3 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:51 -0700 Subject: xHCI: Check host USB2 LPM capability Check the host's USB2 LPM capability. USB2 software LPM support is optional for xHCI 0.96 hosts. xHCI 1.0 hosts should support software LPM, and may support hardware LPM. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h index ce5c9e5..c7f3312 100644 --- a/drivers/usb/host/xhci-ext-caps.h +++ b/drivers/usb/host/xhci-ext-caps.h @@ -65,6 +65,12 @@ /* bits 1:2, 5:12, and 17:19 need to be preserved; bits 21:28 should be zero */ #define XHCI_LEGACY_DISABLE_SMI ((0x3 << 1) + (0xff << 5) + (0x7 << 17)) +/* USB 2.0 xHCI 0.96 L1C capability - section 7.2.2.1.3.2 */ +#define XHCI_L1C (1 << 16) + +/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */ +#define XHCI_HLC (1 << 19) + /* command register values to disable interrupts and halt the HC */ /* start/stop HC execution - do not write unless HC is halted*/ #define XHCI_CMD_RUN (1 << 0) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index bc6ec0c..9c7ddf0 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1959,6 +1959,23 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, if (port_offset == 0 || (port_offset + port_count - 1) > num_ports) /* WTF? "Valid values are ‘1’ to MaxPorts" */ return; + + /* Check the host's USB2 LPM capability */ + if ((xhci->hci_version == 0x96) && (major_revision != 0x03) && + (temp & XHCI_L1C)) { + xhci_dbg(xhci, "xHCI 0.96: support USB2 software lpm\n"); + xhci->sw_lpm_support = 1; + } + + if ((xhci->hci_version >= 0x100) && (major_revision != 0x03)) { + xhci_dbg(xhci, "xHCI 1.0: support USB2 software lpm\n"); + xhci->sw_lpm_support = 1; + if (temp & XHCI_HLC) { + xhci_dbg(xhci, "xHCI 1.0: support USB2 hardware lpm\n"); + xhci->hw_lpm_support = 1; + } + } + port_offset--; for (i = port_offset; i < (port_offset + port_count); i++) { /* Duplicate entry. Ignore the port if the revisions differ. */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index fa921cb..8673f98 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1459,6 +1459,10 @@ struct xhci_hcd { /* Array of pointers to USB 2.0 PORTSC registers */ __le32 __iomem **usb2_ports; unsigned int num_usb2_ports; + /* support xHCI 0.96 spec USB2 software LPM */ + unsigned sw_lpm_support:1; + /* support xHCI 1.0 spec USB2 hardware LPM */ + unsigned hw_lpm_support:1; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ -- cgit v0.10.2 From 9574323c39d1f8359a04843075d89c9f32d8b7e6 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:51 -0700 Subject: xHCI: test USB2 software LPM This patch tests USB2 software LPM for a USB2 LPM-capable device. When a lpm-capable device is addressed, if the host also supports software LPM, apply a test by putting the device into L1 state and resume it to see if the device can do L1 suspend/resume successfully. If the device fails to enter L1 or resume from L1 state, it may not function normally and usbcore may disconnect and re-enumerate it. In this case, store the device's Vid and Pid information, make sure the host will not test LPM for it twice. The test result is per device/host. Some devices claim to be lpm-capable, but fail to enter L1 or resume. So the test is necessary. The xHCI 1.0 errata has modified the USB2.0 LPM implementation. It redefines the HIRD field to BESL, and adds another register Port Hardware LPM Control (PORTHLPMC). However, this should not affect the LPM behavior on xHC which does not implement 1.0 errata. USB2.0 LPM errata defines a new bit BESL in the device's USB 2.0 extension descriptor. If the device reports it uses BESL, driver should use BESL instead of HIRD for it. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 9c7ddf0..3ec2ac9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1685,6 +1685,8 @@ void xhci_free_command(struct xhci_hcd *xhci, void xhci_mem_cleanup(struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + struct dev_info *dev_info, *next; + unsigned long flags; int size; int i; @@ -1742,6 +1744,13 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) scratchpad_free(xhci); + spin_lock_irqsave(&xhci->lock, flags); + list_for_each_entry_safe(dev_info, next, &xhci->lpm_failed_devs, list) { + list_del(&dev_info->list); + kfree(dev_info); + } + spin_unlock_irqrestore(&xhci->lock, flags); + xhci->num_usb2_ports = 0; xhci->num_usb3_ports = 0; kfree(xhci->usb2_ports); @@ -2328,6 +2337,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) if (xhci_setup_port_arrays(xhci, flags)) goto fail; + INIT_LIST_HEAD(&xhci->lpm_failed_devs); + return 0; fail: diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index a111099..213a7d7 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -345,6 +345,10 @@ static const struct hc_driver xhci_pci_hc_driver = { .hub_status_data = xhci_hub_status_data, .bus_suspend = xhci_bus_suspend, .bus_resume = xhci_bus_resume, + /* + * call back when device connected and addressed + */ + .update_device = xhci_update_device, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 54cb762..b0649a4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3539,6 +3539,187 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) return 0; } +#ifdef CONFIG_USB_SUSPEND + +/* BESL to HIRD Encoding array for USB2 LPM */ +static int xhci_besl_encoding[16] = {125, 150, 200, 300, 400, 500, 1000, 2000, + 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000}; + +/* Calculate HIRD/BESL for USB2 PORTPMSC*/ +static int xhci_calculate_hird_besl(int u2del, bool use_besl) +{ + int hird; + + if (use_besl) { + for (hird = 0; hird < 16; hird++) { + if (xhci_besl_encoding[hird] >= u2del) + break; + } + } else { + if (u2del <= 50) + hird = 0; + else + hird = (u2del - 51) / 75 + 1; + + if (hird > 15) + hird = 15; + } + + return hird; +} + +static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd, + struct usb_device *udev) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct dev_info *dev_info; + __le32 __iomem **port_array; + __le32 __iomem *addr, *pm_addr; + u32 temp, dev_id; + unsigned int port_num; + unsigned long flags; + int u2del, hird; + int ret; + + if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support || + !udev->lpm_capable) + return -EINVAL; + + /* we only support lpm for non-hub device connected to root hub yet */ + if (!udev->parent || udev->parent->parent || + udev->descriptor.bDeviceClass == USB_CLASS_HUB) + return -EINVAL; + + spin_lock_irqsave(&xhci->lock, flags); + + /* Look for devices in lpm_failed_devs list */ + dev_id = le16_to_cpu(udev->descriptor.idVendor) << 16 | + le16_to_cpu(udev->descriptor.idProduct); + list_for_each_entry(dev_info, &xhci->lpm_failed_devs, list) { + if (dev_info->dev_id == dev_id) { + ret = -EINVAL; + goto finish; + } + } + + port_array = xhci->usb2_ports; + port_num = udev->portnum - 1; + + if (port_num > HCS_MAX_PORTS(xhci->hcs_params1)) { + xhci_dbg(xhci, "invalid port number %d\n", udev->portnum); + ret = -EINVAL; + goto finish; + } + + /* + * Test USB 2.0 software LPM. + * FIXME: some xHCI 1.0 hosts may implement a new register to set up + * hardware-controlled USB 2.0 LPM. See section 5.4.11 and 4.23.5.1.1.1 + * in the June 2011 errata release. + */ + xhci_dbg(xhci, "test port %d software LPM\n", port_num); + /* + * Set L1 Device Slot and HIRD/BESL. + * Check device's USB 2.0 extension descriptor to determine whether + * HIRD or BESL shoule be used. See USB2.0 LPM errata. + */ + pm_addr = port_array[port_num] + 1; + u2del = HCS_U2_LATENCY(xhci->hcs_params3); + if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2)) + hird = xhci_calculate_hird_besl(u2del, 1); + else + hird = xhci_calculate_hird_besl(u2del, 0); + + temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird); + xhci_writel(xhci, temp, pm_addr); + + /* Set port link state to U2(L1) */ + addr = port_array[port_num]; + xhci_set_link_state(xhci, port_array, port_num, XDEV_U2); + + /* wait for ACK */ + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(10); + spin_lock_irqsave(&xhci->lock, flags); + + /* Check L1 Status */ + ret = handshake(xhci, pm_addr, PORT_L1S_MASK, PORT_L1S_SUCCESS, 125); + if (ret != -ETIMEDOUT) { + /* enter L1 successfully */ + temp = xhci_readl(xhci, addr); + xhci_dbg(xhci, "port %d entered L1 state, port status 0x%x\n", + port_num, temp); + ret = 0; + } else { + temp = xhci_readl(xhci, pm_addr); + xhci_dbg(xhci, "port %d software lpm failed, L1 status %d\n", + port_num, temp & PORT_L1S_MASK); + ret = -EINVAL; + } + + /* Resume the port */ + xhci_set_link_state(xhci, port_array, port_num, XDEV_U0); + + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(10); + spin_lock_irqsave(&xhci->lock, flags); + + /* Clear PLC */ + xhci_test_and_clear_bit(xhci, port_array, port_num, PORT_PLC); + + /* Check PORTSC to make sure the device is in the right state */ + if (!ret) { + temp = xhci_readl(xhci, addr); + xhci_dbg(xhci, "resumed port %d status 0x%x\n", port_num, temp); + if (!(temp & PORT_CONNECT) || !(temp & PORT_PE) || + (temp & PORT_PLS_MASK) != XDEV_U0) { + xhci_dbg(xhci, "port L1 resume fail\n"); + ret = -EINVAL; + } + } + + if (ret) { + /* Insert dev to lpm_failed_devs list */ + xhci_warn(xhci, "device LPM test failed, may disconnect and " + "re-enumerate\n"); + dev_info = kzalloc(sizeof(struct dev_info), GFP_ATOMIC); + if (!dev_info) { + ret = -ENOMEM; + goto finish; + } + dev_info->dev_id = dev_id; + INIT_LIST_HEAD(&dev_info->list); + list_add(&dev_info->list, &xhci->lpm_failed_devs); + } else { + xhci_ring_device(xhci, udev->slot_id); + } + +finish: + spin_unlock_irqrestore(&xhci->lock, flags); + return ret; +} + +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + int ret; + + ret = xhci_usb2_software_lpm_test(hcd, udev); + if (!ret) + xhci_dbg(xhci, "software LPM test succeed\n"); + + return 0; +} + +#else + +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + return 0; +} + +#endif /* CONFIG_USB_SUSPEND */ + /* Once a hub descriptor is fetched for a device, we need to update the xHC's * internal data structures for the device. */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 8673f98..b24c4fc 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -272,6 +272,7 @@ struct xhci_op_regs { */ #define PORT_PLS_MASK (0xf << 5) #define XDEV_U0 (0x0 << 5) +#define XDEV_U2 (0x2 << 5) #define XDEV_U3 (0x3 << 5) #define XDEV_RESUME (0xf << 5) /* true: port has power (see HCC_PPC) */ @@ -362,7 +363,11 @@ struct xhci_op_regs { /* Bits 24:31 for port testing */ /* USB2 Protocol PORTSPMSC */ -#define PORT_RWE (1 << 0x3) +#define PORT_L1S_MASK 7 +#define PORT_L1S_SUCCESS 1 +#define PORT_RWE (1 << 3) +#define PORT_HIRD(p) (((p) & 0xf) << 4) +#define PORT_L1DS(p) (((p) & 0xff) << 8) /** * struct xhci_intr_reg - Interrupt Register Set @@ -1324,6 +1329,12 @@ struct s3_save { u64 erst_dequeue; }; +/* Use for lpm */ +struct dev_info { + u32 dev_id; + struct list_head list; +}; + struct xhci_bus_state { unsigned long bus_suspended; unsigned long next_statechange; @@ -1387,6 +1398,8 @@ struct xhci_hcd { struct xhci_erst erst; /* Scratchpad */ struct xhci_scratchpad *scratchpad; + /* Store LPM test failed devices' information */ + struct list_head lpm_failed_devs; /* slot enabling and address device helpers */ struct completion addr_dev; @@ -1663,6 +1676,7 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsigned int num_eps, gfp_t mem_flags); int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev); int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); -- cgit v0.10.2 From 65580b4321eb36f16ae8b5987bfa1bb948fc5112 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:52 -0700 Subject: xHCI: set USB2 hardware LPM If the device pass the USB2 software LPM and the host supports hardware LPM, enable hardware LPM for the device to let the host decide when to put the link into lower power state. If hardware LPM is enabled for a port and driver wants to put it into suspend, it must first disable hardware LPM, resume the port into U0, and then suspend the port. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 14b83f2..adf5ca8 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1700,6 +1700,20 @@ int usb_runtime_idle(struct device *dev) return 0; } +int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + int ret = -EPERM; + + if (hcd->driver->set_usb2_hw_lpm) { + ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable); + if (!ret) + udev->usb2_hw_lpm_enabled = enable; + } + + return ret; +} + #endif /* CONFIG_USB_SUSPEND */ struct bus_type usb_bus_type = { diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 4ffc3d1..d6cc832 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2392,6 +2392,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) } } + /* disable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_enabled == 1) + usb_set_usb2_hardware_lpm(udev, 0); + /* see 7.1.7.6 */ if (hub_is_superspeed(hub->hdev)) status = set_port_feature(hub->hdev, @@ -2603,7 +2607,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) if (status < 0) { dev_dbg(&udev->dev, "can't resume, status %d\n", status); hub_port_logical_disconnect(hub, port1); + } else { + /* Try to enable USB2 hardware LPM */ + if (udev->usb2_hw_lpm_capable == 1) + usb_set_usb2_hardware_lpm(udev, 1); } + return status; } diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 0d023cd..3888778 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -82,6 +82,7 @@ extern int usb_remote_wakeup(struct usb_device *dev); extern int usb_runtime_suspend(struct device *dev); extern int usb_runtime_resume(struct device *dev); extern int usb_runtime_idle(struct device *dev); +extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable); #else @@ -96,6 +97,10 @@ static inline int usb_remote_wakeup(struct usb_device *udev) return 0; } +static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable) +{ + return 0; +} #endif extern struct bus_type usb_bus_type; diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index d7be6d7..9f844d4 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -574,10 +574,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, switch (wValue) { case USB_PORT_FEAT_SUSPEND: temp = xhci_readl(xhci, port_array[wIndex]); + if ((temp & PORT_PLS_MASK) != XDEV_U0) { + /* Resume the port to U0 first */ + xhci_set_link_state(xhci, port_array, wIndex, + XDEV_U0); + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(10); + spin_lock_irqsave(&xhci->lock, flags); + } /* In spec software should not attempt to suspend * a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ + temp = xhci_readl(xhci, port_array[wIndex]); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) || (temp & PORT_PLS_MASK) >= XDEV_U3) { xhci_warn(xhci, "USB core suspending device " diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 213a7d7..e66e2b0 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -349,6 +349,7 @@ static const struct hc_driver xhci_pci_hc_driver = { * call back when device connected and addressed */ .update_device = xhci_update_device, + .set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index b0649a4..4648cc0 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3286,6 +3286,11 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); } + if (udev->usb2_hw_lpm_enabled) { + xhci_set_usb2_hardware_lpm(hcd, udev, 0); + udev->usb2_hw_lpm_enabled = 0; + } + spin_lock_irqsave(&xhci->lock, flags); /* Don't disable the slot if the host controller is dead. */ state = xhci_readl(xhci, &xhci->op_regs->status); @@ -3699,20 +3704,87 @@ finish: return ret; } +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + __le32 __iomem **port_array; + __le32 __iomem *pm_addr; + u32 temp; + unsigned int port_num; + unsigned long flags; + int u2del, hird; + + if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support || + !udev->lpm_capable) + return -EPERM; + + if (!udev->parent || udev->parent->parent || + udev->descriptor.bDeviceClass == USB_CLASS_HUB) + return -EPERM; + + if (udev->usb2_hw_lpm_capable != 1) + return -EPERM; + + spin_lock_irqsave(&xhci->lock, flags); + + port_array = xhci->usb2_ports; + port_num = udev->portnum - 1; + pm_addr = port_array[port_num] + 1; + temp = xhci_readl(xhci, pm_addr); + + xhci_dbg(xhci, "%s port %d USB2 hardware LPM\n", + enable ? "enable" : "disable", port_num); + + u2del = HCS_U2_LATENCY(xhci->hcs_params3); + if (le32_to_cpu(udev->bos->ext_cap->bmAttributes) & (1 << 2)) + hird = xhci_calculate_hird_besl(u2del, 1); + else + hird = xhci_calculate_hird_besl(u2del, 0); + + if (enable) { + temp &= ~PORT_HIRD_MASK; + temp |= PORT_HIRD(hird) | PORT_RWE; + xhci_writel(xhci, temp, pm_addr); + temp = xhci_readl(xhci, pm_addr); + temp |= PORT_HLE; + xhci_writel(xhci, temp, pm_addr); + } else { + temp &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK); + xhci_writel(xhci, temp, pm_addr); + } + + spin_unlock_irqrestore(&xhci->lock, flags); + return 0; +} + int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int ret; ret = xhci_usb2_software_lpm_test(hcd, udev); - if (!ret) + if (!ret) { xhci_dbg(xhci, "software LPM test succeed\n"); + if (xhci->hw_lpm_support == 1) { + udev->usb2_hw_lpm_capable = 1; + ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1); + if (!ret) + udev->usb2_hw_lpm_enabled = 1; + } + } return 0; } #else +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable) +{ + return 0; +} + int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) { return 0; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index b24c4fc..e738466 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -367,7 +367,9 @@ struct xhci_op_regs { #define PORT_L1S_SUCCESS 1 #define PORT_RWE (1 << 3) #define PORT_HIRD(p) (((p) & 0xf) << 4) +#define PORT_HIRD_MASK (0xf << 4) #define PORT_L1DS(p) (((p) & 0xff) << 8) +#define PORT_HLE (1 << 16) /** * struct xhci_intr_reg - Interrupt Register Set @@ -1677,6 +1679,8 @@ int xhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, gfp_t mem_flags); int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev); int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev); +int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, + struct usb_device *udev, int enable); int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags); diff --git a/include/linux/usb.h b/include/linux/usb.h index 1d00d9bc..6f49a1b 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -411,6 +411,8 @@ struct usb_tt; * @authenticated: Crypto authentication passed * @wusb: device is Wireless USB * @lpm_capable: device supports LPM + * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM + * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled * @string_langid: language ID for strings * @product: iProduct string, if present (static) * @manufacturer: iManufacturer string, if present (static) @@ -474,6 +476,8 @@ struct usb_device { unsigned authenticated:1; unsigned wusb:1; unsigned lpm_capable:1; + unsigned usb2_hw_lpm_capable:1; + unsigned usb2_hw_lpm_enabled:1; int string_langid; /* static strings from the device */ diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 0097136..a4cd6c5 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -343,6 +343,7 @@ struct hc_driver { * address is set */ int (*update_device)(struct usb_hcd *, struct usb_device *); + int (*set_usb2_hw_lpm)(struct usb_hcd *, struct usb_device *, int); }; extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb); -- cgit v0.10.2 From c1045e87b2cd293d53dff19779ea46b19195d593 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:53 -0700 Subject: usbcore: add sysfs support to xHCI usb2 hardware LPM This patch adds sysfs support to xHCI usb2 hardware LPM, so developer can enable and disable usb2 hardware LPM manually for test purpose. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index 294aa86..e647378 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -142,3 +142,18 @@ Description: such devices. Users: usb_modeswitch + +What: /sys/bus/usb/devices/.../power/usb2_hardware_lpm +Date: September 2011 +Contact: Andiry Xu +Description: + If CONFIG_USB_SUSPEND is set and a USB 2.0 lpm-capable device + is plugged in to a xHCI host which support link PM, it will + perform a LPM test; if the test is passed and host supports + USB2 hardware LPM (xHCI 1.0 feature), USB2 hardware LPM will + be enabled for the device and the USB device directory will + contain a file named power/usb2_hardware_lpm. The file holds + a string value (enable or disable) indicating whether or not + USB2 hardware LPM is enabled for the device. Developer can + write y/Y/1 or n/N/0 to the file to enable/disable the + feature. diff --git a/Documentation/usb/power-management.txt b/Documentation/usb/power-management.txt index c9ffa9c..9d85d96 100644 --- a/Documentation/usb/power-management.txt +++ b/Documentation/usb/power-management.txt @@ -487,3 +487,29 @@ succeed, it may still remain active and thus cause the system to resume as soon as the system suspend is complete. Or the remote wakeup may fail and get lost. Which outcome occurs depends on timing and on the hardware and firmware design. + + + xHCI hardware link PM + --------------------- + +xHCI host controller provides hardware link power management to usb2.0 +(xHCI 1.0 feature) and usb3.0 devices which support link PM. By +enabling hardware LPM, the host can automatically put the device into +lower power state(L1 for usb2.0 devices, or U1/U2 for usb3.0 devices), +which state device can enter and resume very quickly. + +The user interface for controlling USB2 hardware LPM is located in the +power/ subdirectory of each USB device's sysfs directory, that is, in +/sys/bus/usb/devices/.../power/ where "..." is the device's ID. The +relevant attribute files is usb2_hardware_lpm. + + power/usb2_hardware_lpm + + When a USB2 device which support LPM is plugged to a + xHCI host root hub which support software LPM, the + host will run a software LPM test for it; if the device + enters L1 state and resume successfully and the host + supports USB2 hardware LPM, this file will show up and + driver will enable hardware LPM for the device. You + can write y/Y/1 or n/N/0 to the file to enable/disable + USB2 hardware LPM manually. This is for test purpose mainly. diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index cf05b97..662c0cf 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -412,6 +412,56 @@ set_level(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); +static ssize_t +show_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + const char *p; + + if (udev->usb2_hw_lpm_enabled == 1) + p = "enabled"; + else + p = "disabled"; + + return sprintf(buf, "%s\n", p); +} + +static ssize_t +set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + bool value; + int ret; + + usb_lock_device(udev); + + ret = strtobool(buf, &value); + + if (!ret) + ret = usb_set_usb2_hardware_lpm(udev, value); + + usb_unlock_device(udev); + + if (!ret) + return count; + + return ret; +} + +static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm, + set_usb2_hardware_lpm); + +static struct attribute *usb2_hardware_lpm_attr[] = { + &dev_attr_usb2_hardware_lpm.attr, + NULL, +}; +static struct attribute_group usb2_hardware_lpm_attr_group = { + .name = power_group_name, + .attrs = usb2_hardware_lpm_attr, +}; + static struct attribute *power_attrs[] = { &dev_attr_autosuspend.attr, &dev_attr_level.attr, @@ -428,13 +478,20 @@ static int add_power_attributes(struct device *dev) { int rc = 0; - if (is_usb_device(dev)) + if (is_usb_device(dev)) { + struct usb_device *udev = to_usb_device(dev); rc = sysfs_merge_group(&dev->kobj, &power_attr_group); + if (udev->usb2_hw_lpm_capable == 1) + rc = sysfs_merge_group(&dev->kobj, + &usb2_hardware_lpm_attr_group); + } + return rc; } static void remove_power_attributes(struct device *dev) { + sysfs_unmerge_group(&dev->kobj, &usb2_hardware_lpm_attr_group); sysfs_unmerge_group(&dev->kobj, &power_attr_group); } -- cgit v0.10.2 From 7e393a834b41001174a8fb3ae3bc23a749467760 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:54 -0700 Subject: xHCI: AMD isoc link TRB chain bit quirk Setting the chain (CH) bit in the link TRB of isochronous transfer rings is required by AMD 0.96 xHCI host controller to successfully transverse multi-TRB TD that span through different memory segments. When a Missed Service Error event occurs, if the chain bit is not set in the link TRB and the host skips TDs which just across a link TRB, the host may falsely recognize the link TRB as a normal TRB. You can see this may cause big trouble - the host does not jump to the right address which is pointed by the link TRB, but continue fetching the memory which is after the link TRB address, which may not even belong to the host, and the result cannot be predicted. This causes some big problems. Without the former patch I sent: "xHCI: prevent infinite loop when processing MSE event", the system may hang. With that patch applied, system does not hang, but the host still access wrong memory address and isoc transfer will fail. With this patch, isochronous transfer works as expected. This patch should be applied to kernels as old as 2.6.36, which was when the first isochronous support was added for the xHCI host controller. Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Cc: stable@kernel.org Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 3ec2ac9..a6ff825 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -79,7 +79,7 @@ static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg) * related flags, such as End TRB, Toggle Cycle, and no snoop. */ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, - struct xhci_segment *next, bool link_trbs) + struct xhci_segment *next, bool link_trbs, bool isoc) { u32 val; @@ -95,7 +95,9 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, val &= ~TRB_TYPE_BITMASK; val |= TRB_TYPE(TRB_LINK); /* Always set the chain bit with 0.95 hardware */ - if (xhci_link_trb_quirk(xhci)) + /* Set chain bit for isoc rings on AMD 0.96 host */ + if (xhci_link_trb_quirk(xhci) || + (isoc && (xhci->quirks & XHCI_AMD_0x96_HOST))) val |= TRB_CHAIN; prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); } @@ -152,7 +154,7 @@ static void xhci_initialize_ring_info(struct xhci_ring *ring) * See section 4.9.1 and figures 15 and 16. */ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, - unsigned int num_segs, bool link_trbs, gfp_t flags) + unsigned int num_segs, bool link_trbs, bool isoc, gfp_t flags) { struct xhci_ring *ring; struct xhci_segment *prev; @@ -178,12 +180,12 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, next = xhci_segment_alloc(xhci, flags); if (!next) goto fail; - xhci_link_segments(xhci, prev, next, link_trbs); + xhci_link_segments(xhci, prev, next, link_trbs, isoc); prev = next; num_segs--; } - xhci_link_segments(xhci, prev, ring->first_seg, link_trbs); + xhci_link_segments(xhci, prev, ring->first_seg, link_trbs, isoc); if (link_trbs) { /* See section 4.9.2.1 and 6.4.4.1 */ @@ -229,14 +231,14 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci, * pointers to the beginning of the ring. */ static void xhci_reinit_cached_ring(struct xhci_hcd *xhci, - struct xhci_ring *ring) + struct xhci_ring *ring, bool isoc) { struct xhci_segment *seg = ring->first_seg; do { memset(seg->trbs, 0, sizeof(union xhci_trb)*TRBS_PER_SEGMENT); /* All endpoint rings have link TRBs */ - xhci_link_segments(xhci, seg, seg->next, 1); + xhci_link_segments(xhci, seg, seg->next, 1, isoc); seg = seg->next; } while (seg != ring->first_seg); xhci_initialize_ring_info(ring); @@ -540,7 +542,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, */ for (cur_stream = 1; cur_stream < num_streams; cur_stream++) { stream_info->stream_rings[cur_stream] = - xhci_ring_alloc(xhci, 1, true, mem_flags); + xhci_ring_alloc(xhci, 1, true, false, mem_flags); cur_ring = stream_info->stream_rings[cur_stream]; if (!cur_ring) goto cleanup_rings; @@ -874,7 +876,7 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, } /* Allocate endpoint 0 ring */ - dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, flags); + dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, false, flags); if (!dev->eps[0].ring) goto fail; @@ -1315,10 +1317,10 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, */ if (usb_endpoint_xfer_isoc(&ep->desc)) virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 8, true, mem_flags); + xhci_ring_alloc(xhci, 8, true, true, mem_flags); else virt_dev->eps[ep_index].new_ring = - xhci_ring_alloc(xhci, 1, true, mem_flags); + xhci_ring_alloc(xhci, 1, true, false, mem_flags); if (!virt_dev->eps[ep_index].new_ring) { /* Attempt to use the ring cache */ if (virt_dev->num_rings_cached == 0) @@ -1327,7 +1329,8 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, virt_dev->ring_cache[virt_dev->num_rings_cached]; virt_dev->ring_cache[virt_dev->num_rings_cached] = NULL; virt_dev->num_rings_cached--; - xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring); + xhci_reinit_cached_ring(xhci, virt_dev->eps[ep_index].new_ring, + usb_endpoint_xfer_isoc(&ep->desc) ? true : false); } virt_dev->eps[ep_index].skip = false; ep_ring = virt_dev->eps[ep_index].new_ring; @@ -2236,7 +2239,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) goto fail; /* Set up the command ring to have one segments for now. */ - xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, flags); + xhci->cmd_ring = xhci_ring_alloc(xhci, 1, true, false, flags); if (!xhci->cmd_ring) goto fail; xhci_dbg(xhci, "Allocated command ring at %p\n", xhci->cmd_ring); @@ -2267,7 +2270,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * the event ring segment table (ERST). Section 4.9.3. */ xhci_dbg(xhci, "// Allocating event ring\n"); - xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, flags); + xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, false, false, + flags); if (!xhci->event_ring) goto fail; if (xhci_check_trb_in_td_math(xhci, flags) < 0) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index e66e2b0..732837e 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -128,6 +128,9 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (pdev->vendor == PCI_VENDOR_ID_NEC) xhci->quirks |= XHCI_NEC_HOST; + if (pdev->vendor == PCI_VENDOR_ID_AMD && xhci->hci_version == 0x96) + xhci->quirks |= XHCI_AMD_0x96_HOST; + /* AMD PLL quirk */ if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info()) xhci->quirks |= XHCI_AMD_PLL_FIX; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index a367963..e4b7f00 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -185,7 +185,7 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring, bool consumer * prepare_transfer()? */ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, - bool consumer, bool more_trbs_coming) + bool consumer, bool more_trbs_coming, bool isoc) { u32 chain; union xhci_trb *next; @@ -212,11 +212,13 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, if (!chain && !more_trbs_coming) break; - /* If we're not dealing with 0.95 hardware, + /* If we're not dealing with 0.95 hardware or + * isoc rings on AMD 0.96 host, * carry over the chain bit of the previous TRB * (which may mean the chain bit is cleared). */ - if (!xhci_link_trb_quirk(xhci)) { + if (!(isoc && (xhci->quirks & XHCI_AMD_0x96_HOST)) + && !xhci_link_trb_quirk(xhci)) { next->link.control &= cpu_to_le32(~TRB_CHAIN); next->link.control |= @@ -2391,7 +2393,7 @@ irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd) * prepare_transfer()? */ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, - bool consumer, bool more_trbs_coming, + bool consumer, bool more_trbs_coming, bool isoc, u32 field1, u32 field2, u32 field3, u32 field4) { struct xhci_generic_trb *trb; @@ -2401,7 +2403,7 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, trb->field[1] = cpu_to_le32(field2); trb->field[2] = cpu_to_le32(field3); trb->field[3] = cpu_to_le32(field4); - inc_enq(xhci, ring, consumer, more_trbs_coming); + inc_enq(xhci, ring, consumer, more_trbs_coming, isoc); } /* @@ -2409,7 +2411,7 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, * FIXME allocate segments if the ring is full. */ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, - u32 ep_state, unsigned int num_trbs, gfp_t mem_flags) + u32 ep_state, unsigned int num_trbs, bool isoc, gfp_t mem_flags) { /* Make sure the endpoint has been added to xHC schedule */ switch (ep_state) { @@ -2451,10 +2453,11 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, next = ring->enqueue; while (last_trb(xhci, ring, ring->enq_seg, next)) { - /* If we're not dealing with 0.95 hardware, - * clear the chain bit. + /* If we're not dealing with 0.95 hardware or isoc rings + * on AMD 0.96 host, clear the chain bit. */ - if (!xhci_link_trb_quirk(xhci)) + if (!xhci_link_trb_quirk(xhci) && !(isoc && + (xhci->quirks & XHCI_AMD_0x96_HOST))) next->link.control &= cpu_to_le32(~TRB_CHAIN); else next->link.control |= cpu_to_le32(TRB_CHAIN); @@ -2487,6 +2490,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, unsigned int num_trbs, struct urb *urb, unsigned int td_index, + bool isoc, gfp_t mem_flags) { int ret; @@ -2504,7 +2508,7 @@ static int prepare_transfer(struct xhci_hcd *xhci, ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK, - num_trbs, mem_flags); + num_trbs, isoc, mem_flags); if (ret) return ret; @@ -2727,7 +2731,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, 0, mem_flags); + num_trbs, urb, 0, false, mem_flags); if (trb_buff_len < 0) return trb_buff_len; @@ -2822,7 +2826,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, more_trbs_coming = true; else more_trbs_coming = false; - queue_trb(xhci, ep_ring, false, more_trbs_coming, + queue_trb(xhci, ep_ring, false, more_trbs_coming, false, lower_32_bits(addr), upper_32_bits(addr), length_field, @@ -2913,7 +2917,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, 0, mem_flags); + num_trbs, urb, 0, false, mem_flags); if (ret < 0) return ret; @@ -2985,7 +2989,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, more_trbs_coming = true; else more_trbs_coming = false; - queue_trb(xhci, ep_ring, false, more_trbs_coming, + queue_trb(xhci, ep_ring, false, more_trbs_coming, false, lower_32_bits(addr), upper_32_bits(addr), length_field, @@ -3045,7 +3049,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, num_trbs++; ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, - num_trbs, urb, 0, mem_flags); + num_trbs, urb, 0, false, mem_flags); if (ret < 0) return ret; @@ -3078,7 +3082,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } } - queue_trb(xhci, ep_ring, false, true, + queue_trb(xhci, ep_ring, false, true, false, setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16, le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16, TRB_LEN(8) | TRB_INTR_TARGET(0), @@ -3098,7 +3102,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (urb->transfer_buffer_length > 0) { if (setup->bRequestType & USB_DIR_IN) field |= TRB_DIR_IN; - queue_trb(xhci, ep_ring, false, true, + queue_trb(xhci, ep_ring, false, true, false, lower_32_bits(urb->transfer_dma), upper_32_bits(urb->transfer_dma), length_field, @@ -3114,7 +3118,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field = 0; else field = TRB_DIR_IN; - queue_trb(xhci, ep_ring, false, false, + queue_trb(xhci, ep_ring, false, false, false, 0, 0, TRB_INTR_TARGET(0), @@ -3263,7 +3267,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, trbs_per_td = count_isoc_trbs_needed(xhci, urb, i); ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, - urb->stream_id, trbs_per_td, urb, i, mem_flags); + urb->stream_id, trbs_per_td, urb, i, true, + mem_flags); if (ret < 0) { if (i == 0) return ret; @@ -3333,7 +3338,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, remainder | TRB_INTR_TARGET(0); - queue_trb(xhci, ep_ring, false, more_trbs_coming, + queue_trb(xhci, ep_ring, false, more_trbs_coming, true, lower_32_bits(addr), upper_32_bits(addr), length_field, @@ -3415,7 +3420,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, * Do not insert any td of the urb to the ring if the check failed. */ ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK, - num_trbs, mem_flags); + num_trbs, true, mem_flags); if (ret) return ret; @@ -3474,7 +3479,7 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2, reserved_trbs++; ret = prepare_ring(xhci, xhci->cmd_ring, EP_STATE_RUNNING, - reserved_trbs, GFP_ATOMIC); + reserved_trbs, false, GFP_ATOMIC); if (ret < 0) { xhci_err(xhci, "ERR: No room for command on command ring\n"); if (command_must_succeed) @@ -3482,8 +3487,8 @@ static int queue_command(struct xhci_hcd *xhci, u32 field1, u32 field2, "unfailable commands failed.\n"); return ret; } - queue_trb(xhci, xhci->cmd_ring, false, false, field1, field2, field3, - field4 | xhci->cmd_ring->cycle_state); + queue_trb(xhci, xhci->cmd_ring, false, false, false, field1, field2, + field3, field4 | xhci->cmd_ring->cycle_state); return 0; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index e738466..4050656 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1462,6 +1462,7 @@ struct xhci_hcd { #define XHCI_BROKEN_MSI (1 << 6) #define XHCI_RESET_ON_RESUME (1 << 7) #define XHCI_SW_BW_CHECKING (1 << 8) +#define XHCI_AMD_0x96_HOST (1 << 9) unsigned int num_active_eps; unsigned int limit_active_eps; /* There are two roothubs to keep track of bus suspend info for */ -- cgit v0.10.2 From cd68176abf1098af83c968da274fe908eb7a8a72 Mon Sep 17 00:00:00 2001 From: Andiry Xu Date: Fri, 23 Sep 2011 14:19:55 -0700 Subject: xHCI: fix debug message Fix the debug message in xhci_address_device(). Signed-off-by: Andiry Xu Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 4648cc0..7bf8677 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3476,7 +3476,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) * command on a timeout. */ if (timeleft <= 0) { - xhci_warn(xhci, "%s while waiting for a slot\n", + xhci_warn(xhci, "%s while waiting for address device command\n", timeleft == 0 ? "Timeout" : "Signal"); /* FIXME cancel the address device command */ return -ETIME; -- cgit v0.10.2 From 7b7200098011a3f2b66f702b6fe47122cba585a3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 23 Sep 2011 14:19:56 -0700 Subject: usb/host: introduce USB_ARCH_HAS_XHCI to make it look like OHCI and EHCI, we introduce that symbol and USB_XHCI_HCD depend on that instead of PCI. [bigeasy@linutronix.de: wire up USB_ARCH_HAS_HCD] Signed-off-by: Felipe Balbi Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 97c4cc7..251279e 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -24,6 +24,7 @@ config USB_ARCH_HAS_HCD boolean default y if USB_ARCH_HAS_OHCI default y if USB_ARCH_HAS_EHCI + default y if USB_ARCH_HAS_XHCI default y if PCMCIA && !M32R # sl811_cs default y if ARM # SL-811 default y if BLACKFIN # SL-811 @@ -77,6 +78,11 @@ config USB_ARCH_HAS_EHCI default y if ARCH_MMP default PCI +# some non-PCI HCDs implement xHCI +config USB_ARCH_HAS_XHCI + boolean + default PCI + # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. config USB tristate "Support for Host-side USB" diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 13e1f67..3e5b235 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -19,7 +19,7 @@ config USB_C67X00_HCD config USB_XHCI_HCD tristate "xHCI HCD (USB 3.0) support (EXPERIMENTAL)" - depends on USB && PCI && EXPERIMENTAL + depends on USB && USB_ARCH_HAS_XHCI && EXPERIMENTAL ---help--- The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0 "SuperSpeed" host controller hardware. -- cgit v0.10.2 From 3fd1ec58734e38a3f48776b6831ad0279d92d094 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 23 Sep 2011 14:19:57 -0700 Subject: usb/xhci: group MSI interrupt registration into its own function This patch moves the complete MSI/MSI-X/Legacy dance into its own function. There is however one difference: If the XHCI_BROKEN_MSI flag is set then we don't free and register the irq, we simply return. This is preparation for later PCI decouple. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 7bf8677..583a3e3 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -397,6 +397,45 @@ static int xhci_run_finished(struct xhci_hcd *xhci) return 0; } +static int xhci_try_enable_msi(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + int ret; + + /* + * Some Fresco Logic host controllers advertise MSI, but fail to + * generate interrupts. Don't even try to enable MSI. + */ + if (xhci->quirks & XHCI_BROKEN_MSI) + return 0; + + /* unregister the legacy interrupt */ + if (hcd->irq) + free_irq(hcd->irq, hcd); + hcd->irq = -1; + + ret = xhci_setup_msix(xhci); + if (ret) + /* fall back to msi*/ + ret = xhci_setup_msi(xhci); + + if (!ret) + /* hcd->irq is -1, we have MSI */ + return 0; + + /* fall back to legacy interrupt*/ + ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, + hcd->irq_descr, hcd); + if (ret) { + xhci_err(xhci, "request interrupt %d failed\n", + pdev->irq); + return ret; + } + hcd->irq = pdev->irq; + return 0; +} + /* * Start the HC after it was halted. * @@ -413,9 +452,8 @@ int xhci_run(struct usb_hcd *hcd) { u32 temp; u64 temp_64; - u32 ret; + int ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); /* Start the xHCI host controller running only after the USB 2.0 roothub * is setup. @@ -426,34 +464,10 @@ int xhci_run(struct usb_hcd *hcd) return xhci_run_finished(xhci); xhci_dbg(xhci, "xhci_run\n"); - /* unregister the legacy interrupt */ - if (hcd->irq) - free_irq(hcd->irq, hcd); - hcd->irq = -1; - /* Some Fresco Logic host controllers advertise MSI, but fail to - * generate interrupts. Don't even try to enable MSI. - */ - if (xhci->quirks & XHCI_BROKEN_MSI) - goto legacy_irq; - - ret = xhci_setup_msix(xhci); + ret = xhci_try_enable_msi(hcd); if (ret) - /* fall back to msi*/ - ret = xhci_setup_msi(xhci); - - if (ret) { -legacy_irq: - /* fall back to legacy interrupt*/ - ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, - hcd->irq_descr, hcd); - if (ret) { - xhci_err(xhci, "request interrupt %d failed\n", - pdev->irq); - return ret; - } - hcd->irq = pdev->irq; - } + return ret; #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING init_timer(&xhci->event_ring_timer); -- cgit v0.10.2 From 421aa841a134f6a743111cf44d0c6d3b45e3cf8c Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 23 Sep 2011 14:19:58 -0700 Subject: usb/xhci: hide MSI code behind PCI bars The MSI related fuctionality requires a few structs which are not available if CONFIG_PCI is not enabled. This is a prepartion to allow xhci be built without CONFIG_PCI set. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 583a3e3..a8c3e8a 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -175,28 +175,19 @@ int xhci_reset(struct xhci_hcd *xhci) return handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000); } -/* - * Free IRQs - * free all IRQs request - */ -static void xhci_free_irq(struct xhci_hcd *xhci) +#ifdef CONFIG_PCI +static int xhci_free_msi(struct xhci_hcd *xhci) { int i; - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - - /* return if using legacy interrupt */ - if (xhci_to_hcd(xhci)->irq >= 0) - return; - if (xhci->msix_entries) { - for (i = 0; i < xhci->msix_count; i++) - if (xhci->msix_entries[i].vector) - free_irq(xhci->msix_entries[i].vector, - xhci_to_hcd(xhci)); - } else if (pdev->irq >= 0) - free_irq(pdev->irq, xhci_to_hcd(xhci)); + if (!xhci->msix_entries) + return -EINVAL; - return; + for (i = 0; i < xhci->msix_count; i++) + if (xhci->msix_entries[i].vector) + free_irq(xhci->msix_entries[i].vector, + xhci_to_hcd(xhci)); + return 0; } /* @@ -224,6 +215,28 @@ static int xhci_setup_msi(struct xhci_hcd *xhci) } /* + * Free IRQs + * free all IRQs request + */ +static void xhci_free_irq(struct xhci_hcd *xhci) +{ + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + int ret; + + /* return if using legacy interrupt */ + if (xhci_to_hcd(xhci)->irq >= 0) + return; + + ret = xhci_free_msi(xhci); + if (!ret) + return; + if (pdev->irq >= 0) + free_irq(pdev->irq, xhci_to_hcd(xhci)); + + return; +} + +/* * Set up MSI-X */ static int xhci_setup_msix(struct xhci_hcd *xhci) @@ -302,6 +315,72 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) return; } +static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) +{ + int i; + + if (xhci->msix_entries) { + for (i = 0; i < xhci->msix_count; i++) + synchronize_irq(xhci->msix_entries[i].vector); + } +} + +static int xhci_try_enable_msi(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); + int ret; + + /* + * Some Fresco Logic host controllers advertise MSI, but fail to + * generate interrupts. Don't even try to enable MSI. + */ + if (xhci->quirks & XHCI_BROKEN_MSI) + return 0; + + /* unregister the legacy interrupt */ + if (hcd->irq) + free_irq(hcd->irq, hcd); + hcd->irq = -1; + + ret = xhci_setup_msix(xhci); + if (ret) + /* fall back to msi*/ + ret = xhci_setup_msi(xhci); + + if (!ret) + /* hcd->irq is -1, we have MSI */ + return 0; + + /* fall back to legacy interrupt*/ + ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, + hcd->irq_descr, hcd); + if (ret) { + xhci_err(xhci, "request interrupt %d failed\n", + pdev->irq); + return ret; + } + hcd->irq = pdev->irq; + return 0; +} + +#else + +static int xhci_try_enable_msi(struct usb_hcd *hcd) +{ + return 0; +} + +static void xhci_cleanup_msix(struct xhci_hcd *xhci) +{ +} + +static void xhci_msix_sync_irqs(struct xhci_hcd *xhci) +{ +} + +#endif + /* * Initialize memory for HCD and xHC (one-time init). * @@ -397,45 +476,6 @@ static int xhci_run_finished(struct xhci_hcd *xhci) return 0; } -static int xhci_try_enable_msi(struct usb_hcd *hcd) -{ - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); - int ret; - - /* - * Some Fresco Logic host controllers advertise MSI, but fail to - * generate interrupts. Don't even try to enable MSI. - */ - if (xhci->quirks & XHCI_BROKEN_MSI) - return 0; - - /* unregister the legacy interrupt */ - if (hcd->irq) - free_irq(hcd->irq, hcd); - hcd->irq = -1; - - ret = xhci_setup_msix(xhci); - if (ret) - /* fall back to msi*/ - ret = xhci_setup_msi(xhci); - - if (!ret) - /* hcd->irq is -1, we have MSI */ - return 0; - - /* fall back to legacy interrupt*/ - ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED, - hcd->irq_descr, hcd); - if (ret) { - xhci_err(xhci, "request interrupt %d failed\n", - pdev->irq); - return ret; - } - hcd->irq = pdev->irq; - return 0; -} - /* * Start the HC after it was halted. * @@ -708,7 +748,6 @@ int xhci_suspend(struct xhci_hcd *xhci) int rc = 0; struct usb_hcd *hcd = xhci_to_hcd(xhci); u32 command; - int i; spin_lock_irq(&xhci->lock); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); @@ -744,10 +783,7 @@ int xhci_suspend(struct xhci_hcd *xhci) /* step 5: remove core well power */ /* synchronize irq when using MSI-X */ - if (xhci->msix_entries) { - for (i = 0; i < xhci->msix_count; i++) - synchronize_irq(xhci->msix_entries[i].vector); - } + xhci_msix_sync_irqs(xhci); return rc; } -- cgit v0.10.2 From 22d45f01a836c2f5826b8b4b9e029e5f79afec57 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 23 Sep 2011 14:19:59 -0700 Subject: usb/xhci: replace pci_*_consistent() with dma_*_coherent() pci_*_consistent() calls dma_*_coherent() with GFP_ATOMIC and requires pci_dev struct. This is a preparion for later where we no longer have the pci struct around. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index a6ff825..42a22b8 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -317,7 +317,7 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci, struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - pci_free_consistent(pdev, + dma_free_coherent(&pdev->dev, sizeof(struct xhci_stream_ctx)*num_stream_ctxs, stream_ctx, dma); else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) @@ -345,9 +345,9 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci, struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE) - return pci_alloc_consistent(pdev, + return dma_alloc_coherent(&pdev->dev, sizeof(struct xhci_stream_ctx)*num_stream_ctxs, - dma); + dma, mem_flags); else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE) return dma_pool_alloc(xhci->small_streams_pool, mem_flags, dma); @@ -1551,10 +1551,9 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) if (!xhci->scratchpad) goto fail_sp; - xhci->scratchpad->sp_array = - pci_alloc_consistent(to_pci_dev(dev), + xhci->scratchpad->sp_array = dma_alloc_coherent(dev, num_sp * sizeof(u64), - &xhci->scratchpad->sp_dma); + &xhci->scratchpad->sp_dma, flags); if (!xhci->scratchpad->sp_array) goto fail_sp2; @@ -1571,8 +1570,8 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma); for (i = 0; i < num_sp; i++) { dma_addr_t dma; - void *buf = pci_alloc_consistent(to_pci_dev(dev), - xhci->page_size, &dma); + void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma, + flags); if (!buf) goto fail_sp5; @@ -1585,7 +1584,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) fail_sp5: for (i = i - 1; i >= 0; i--) { - pci_free_consistent(to_pci_dev(dev), xhci->page_size, + dma_free_coherent(dev, xhci->page_size, xhci->scratchpad->sp_buffers[i], xhci->scratchpad->sp_dma_buffers[i]); } @@ -1595,7 +1594,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) kfree(xhci->scratchpad->sp_buffers); fail_sp3: - pci_free_consistent(to_pci_dev(dev), num_sp * sizeof(u64), + dma_free_coherent(dev, num_sp * sizeof(u64), xhci->scratchpad->sp_array, xhci->scratchpad->sp_dma); @@ -1619,13 +1618,13 @@ static void scratchpad_free(struct xhci_hcd *xhci) num_sp = HCS_MAX_SCRATCHPAD(xhci->hcs_params2); for (i = 0; i < num_sp; i++) { - pci_free_consistent(pdev, xhci->page_size, + dma_free_coherent(&pdev->dev, xhci->page_size, xhci->scratchpad->sp_buffers[i], xhci->scratchpad->sp_dma_buffers[i]); } kfree(xhci->scratchpad->sp_dma_buffers); kfree(xhci->scratchpad->sp_buffers); - pci_free_consistent(pdev, num_sp * sizeof(u64), + dma_free_coherent(&pdev->dev, num_sp * sizeof(u64), xhci->scratchpad->sp_array, xhci->scratchpad->sp_dma); kfree(xhci->scratchpad); @@ -1701,7 +1700,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) } size = sizeof(struct xhci_erst_entry)*(xhci->erst.num_entries); if (xhci->erst.entries) - pci_free_consistent(pdev, size, + dma_free_coherent(&pdev->dev, size, xhci->erst.entries, xhci->erst.erst_dma_addr); xhci->erst.entries = NULL; xhci_dbg(xhci, "Freed ERST\n"); @@ -1741,7 +1740,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci_write_64(xhci, 0, &xhci->op_regs->dcbaa_ptr); if (xhci->dcbaa) - pci_free_consistent(pdev, sizeof(*xhci->dcbaa), + dma_free_coherent(&pdev->dev, sizeof(*xhci->dcbaa), xhci->dcbaa, xhci->dcbaa->dma); xhci->dcbaa = NULL; @@ -2197,8 +2196,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * Section 5.4.8 - doorbell array must be * "physically contiguous and 64-byte (cache line) aligned". */ - xhci->dcbaa = pci_alloc_consistent(to_pci_dev(dev), - sizeof(*xhci->dcbaa), &dma); + xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma, + GFP_KERNEL); if (!xhci->dcbaa) goto fail; memset(xhci->dcbaa, 0, sizeof *(xhci->dcbaa)); @@ -2232,7 +2231,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) dma_pool_create("xHCI 1KB stream ctx arrays", dev, MEDIUM_STREAM_ARRAY_SIZE, 16, 0); /* Any stream context array bigger than MEDIUM_STREAM_ARRAY_SIZE - * will be allocated with pci_alloc_consistent() + * will be allocated with dma_alloc_coherent() */ if (!xhci->small_streams_pool || !xhci->medium_streams_pool) @@ -2277,8 +2276,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) if (xhci_check_trb_in_td_math(xhci, flags) < 0) goto fail; - xhci->erst.entries = pci_alloc_consistent(to_pci_dev(dev), - sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS, &dma); + xhci->erst.entries = dma_alloc_coherent(dev, + sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma, + GFP_KERNEL); if (!xhci->erst.entries) goto fail; xhci_dbg(xhci, "// Allocated event ring segment table at 0x%llx\n", -- cgit v0.10.2 From da3c9c4fc5ff47da0febb7658c51d20d22e34f58 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 23 Sep 2011 14:20:00 -0700 Subject: usb/xhci: refactor xhci_pci_setup() xhci_pci_setup() is split into three pieces: - xhci_gen_setup() The major remaining of xhci_pci_setup() is now containing the generic part of the xhci setup. It allocates the xhci struct, setup hcs_params? and friends, performs xhci_halt(), xhci_init and so one. It also obtains the quirks via a callback - xhci_pci_quirks() It checks the origin of the xhci core and sets core specific quirks. - xhci_pci_setup() PCI specific setup functions. Besides calling xhci_gen_setup() with xhci_pci_quirks() as an argument it performs PCI specific setup like obtaining the address of sbrn via a PCI config space. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 732837e..ca56af5 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -51,11 +51,12 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev) return 0; } -/* called during probe() after chip reset completes */ -static int xhci_pci_setup(struct usb_hcd *hcd) +typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); + +static int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) { struct xhci_hcd *xhci; - struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + struct device *dev = hcd->self.controller; int retval; u32 temp; @@ -107,6 +108,44 @@ static int xhci_pci_setup(struct usb_hcd *hcd) xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params); xhci_print_registers(xhci); + get_quirks(dev, xhci); + + /* Make sure the HC is halted. */ + retval = xhci_halt(xhci); + if (retval) + goto error; + + xhci_dbg(xhci, "Resetting HCD\n"); + /* Reset the internal HC memory state and registers. */ + retval = xhci_reset(xhci); + if (retval) + goto error; + xhci_dbg(xhci, "Reset complete\n"); + + temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + if (HCC_64BIT_ADDR(temp)) { + xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); + } else { + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); + } + + xhci_dbg(xhci, "Calling HCD init\n"); + /* Initialize HCD and host controller data structures. */ + retval = xhci_init(hcd); + if (retval) + goto error; + xhci_dbg(xhci, "Called HCD init\n"); + return 0; +error: + kfree(xhci); + return retval; +} + +static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) +{ + struct pci_dev *pdev = to_pci_dev(dev); + /* Look for vendor-specific quirks */ if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC && pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK) { @@ -146,33 +185,22 @@ static int xhci_pci_setup(struct usb_hcd *hcd) xhci->quirks |= XHCI_RESET_ON_RESUME; xhci_dbg(xhci, "QUIRK: Resetting on resume\n"); } +} - /* Make sure the HC is halted. */ - retval = xhci_halt(xhci); - if (retval) - goto error; +/* called during probe() after chip reset completes */ +static int xhci_pci_setup(struct usb_hcd *hcd) +{ + struct xhci_hcd *xhci; + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + int retval; - xhci_dbg(xhci, "Resetting HCD\n"); - /* Reset the internal HC memory state and registers. */ - retval = xhci_reset(xhci); + retval = xhci_gen_setup(hcd, xhci_pci_quirks); if (retval) - goto error; - xhci_dbg(xhci, "Reset complete\n"); - - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - if (HCC_64BIT_ADDR(temp)) { - xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); - } else { - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); - } + return retval; - xhci_dbg(xhci, "Calling HCD init\n"); - /* Initialize HCD and host controller data structures. */ - retval = xhci_init(hcd); - if (retval) - goto error; - xhci_dbg(xhci, "Called HCD init\n"); + xhci = hcd_to_xhci(hcd); + if (!usb_hcd_is_primary_hcd(hcd)) + return 0; pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn); xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn); @@ -182,7 +210,6 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (!retval) return retval; -error: kfree(xhci); return retval; } -- cgit v0.10.2 From 552e0c4f12fef0ad1caf21d2b57ac3a2735edef3 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 23 Sep 2011 14:20:01 -0700 Subject: usb/xhci: move xhci_gen_setup() away from -pci. xhci_gen_setup() is generic so it can be used to perform the bare xhci setup even on non-pci based platform. The typedef for the function pointer is moved into the headerfile Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index ca56af5..e2c82d1 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -51,97 +51,6 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev) return 0; } -typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); - -static int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) -{ - struct xhci_hcd *xhci; - struct device *dev = hcd->self.controller; - int retval; - u32 temp; - - hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2; - - if (usb_hcd_is_primary_hcd(hcd)) { - xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); - if (!xhci) - return -ENOMEM; - *((struct xhci_hcd **) hcd->hcd_priv) = xhci; - xhci->main_hcd = hcd; - /* Mark the first roothub as being USB 2.0. - * The xHCI driver will register the USB 3.0 roothub. - */ - hcd->speed = HCD_USB2; - hcd->self.root_hub->speed = USB_SPEED_HIGH; - /* - * USB 2.0 roothub under xHCI has an integrated TT, - * (rate matching hub) as opposed to having an OHCI/UHCI - * companion controller. - */ - hcd->has_tt = 1; - } else { - /* xHCI private pointer was set in xhci_pci_probe for the second - * registered roothub. - */ - xhci = hcd_to_xhci(hcd); - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - if (HCC_64BIT_ADDR(temp)) { - xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); - } else { - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); - } - return 0; - } - - xhci->cap_regs = hcd->regs; - xhci->op_regs = hcd->regs + - HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase)); - xhci->run_regs = hcd->regs + - (xhci_readl(xhci, &xhci->cap_regs->run_regs_off) & RTSOFF_MASK); - /* Cache read-only capability registers */ - xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1); - xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2); - xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); - xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); - xhci->hci_version = HC_VERSION(xhci->hcc_params); - xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - xhci_print_registers(xhci); - - get_quirks(dev, xhci); - - /* Make sure the HC is halted. */ - retval = xhci_halt(xhci); - if (retval) - goto error; - - xhci_dbg(xhci, "Resetting HCD\n"); - /* Reset the internal HC memory state and registers. */ - retval = xhci_reset(xhci); - if (retval) - goto error; - xhci_dbg(xhci, "Reset complete\n"); - - temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); - if (HCC_64BIT_ADDR(temp)) { - xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); - } else { - dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); - } - - xhci_dbg(xhci, "Calling HCD init\n"); - /* Initialize HCD and host controller data structures. */ - retval = xhci_init(hcd); - if (retval) - goto error; - xhci_dbg(xhci, "Called HCD init\n"); - return 0; -error: - kfree(xhci); - return retval; -} - static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) { struct pci_dev *pdev = to_pci_dev(dev); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index a8c3e8a..2409bbe 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3944,6 +3944,95 @@ int xhci_get_frame(struct usb_hcd *hcd) return xhci_readl(xhci, &xhci->run_regs->microframe_index) >> 3; } +int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) +{ + struct xhci_hcd *xhci; + struct device *dev = hcd->self.controller; + int retval; + u32 temp; + + hcd->self.sg_tablesize = TRBS_PER_SEGMENT - 2; + + if (usb_hcd_is_primary_hcd(hcd)) { + xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); + if (!xhci) + return -ENOMEM; + *((struct xhci_hcd **) hcd->hcd_priv) = xhci; + xhci->main_hcd = hcd; + /* Mark the first roothub as being USB 2.0. + * The xHCI driver will register the USB 3.0 roothub. + */ + hcd->speed = HCD_USB2; + hcd->self.root_hub->speed = USB_SPEED_HIGH; + /* + * USB 2.0 roothub under xHCI has an integrated TT, + * (rate matching hub) as opposed to having an OHCI/UHCI + * companion controller. + */ + hcd->has_tt = 1; + } else { + /* xHCI private pointer was set in xhci_pci_probe for the second + * registered roothub. + */ + xhci = hcd_to_xhci(hcd); + temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + if (HCC_64BIT_ADDR(temp)) { + xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); + } else { + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); + } + return 0; + } + + xhci->cap_regs = hcd->regs; + xhci->op_regs = hcd->regs + + HC_LENGTH(xhci_readl(xhci, &xhci->cap_regs->hc_capbase)); + xhci->run_regs = hcd->regs + + (xhci_readl(xhci, &xhci->cap_regs->run_regs_off) & RTSOFF_MASK); + /* Cache read-only capability registers */ + xhci->hcs_params1 = xhci_readl(xhci, &xhci->cap_regs->hcs_params1); + xhci->hcs_params2 = xhci_readl(xhci, &xhci->cap_regs->hcs_params2); + xhci->hcs_params3 = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); + xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hc_capbase); + xhci->hci_version = HC_VERSION(xhci->hcc_params); + xhci->hcc_params = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + xhci_print_registers(xhci); + + get_quirks(dev, xhci); + + /* Make sure the HC is halted. */ + retval = xhci_halt(xhci); + if (retval) + goto error; + + xhci_dbg(xhci, "Resetting HCD\n"); + /* Reset the internal HC memory state and registers. */ + retval = xhci_reset(xhci); + if (retval) + goto error; + xhci_dbg(xhci, "Reset complete\n"); + + temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params); + if (HCC_64BIT_ADDR(temp)) { + xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(64)); + } else { + dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); + } + + xhci_dbg(xhci, "Calling HCD init\n"); + /* Initialize HCD and host controller data structures. */ + retval = xhci_init(hcd); + if (retval) + goto error; + xhci_dbg(xhci, "Called HCD init\n"); + return 0; +error: + kfree(xhci); + return retval; +} + MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 4050656..ad11c38 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1647,6 +1647,7 @@ void xhci_unregister_pci(void); #endif /* xHCI host controller glue */ +typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); void xhci_quiesce(struct xhci_hcd *xhci); int xhci_halt(struct xhci_hcd *xhci); int xhci_reset(struct xhci_hcd *xhci); @@ -1654,6 +1655,7 @@ int xhci_init(struct usb_hcd *hcd); int xhci_run(struct usb_hcd *hcd); void xhci_stop(struct usb_hcd *hcd); void xhci_shutdown(struct usb_hcd *hcd); +int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); #ifdef CONFIG_PM int xhci_suspend(struct xhci_hcd *xhci); -- cgit v0.10.2 From 0cc47d547d7a482434926331265b3713381b1e60 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 23 Sep 2011 14:20:02 -0700 Subject: usb/xhci: remove CONFIG_PCI in xhci.c's probe function This removes the need of ifdefs within the init function and with it the headache about the correct clean without bus X but with bus/platform Y & Z. xhci-pci is only compiled if CONFIG_PCI is selected which can be de-selected now without trouble. For now the result is kinda useless because we have no other glue code. However, since nobody is using USB_ARCH_HAS_XHCI then it should not be an issue :) Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Sarah Sharp Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 624a362..ed48a5d 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -11,8 +11,9 @@ fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o -xhci-hcd-y := xhci.o xhci-mem.o xhci-pci.o +xhci-hcd-y := xhci.o xhci-mem.o xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o +xhci-hcd-$(CONFIG_PCI) += xhci-pci.o obj-$(CONFIG_USB_WHCI_HCD) += whci/ diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index e2c82d1..9f51f88 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -320,12 +320,12 @@ static struct pci_driver xhci_pci_driver = { #endif }; -int xhci_register_pci(void) +int __init xhci_register_pci(void) { return pci_register_driver(&xhci_pci_driver); } -void xhci_unregister_pci(void) +void __exit xhci_unregister_pci(void) { pci_unregister_driver(&xhci_pci_driver); } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 2409bbe..1ff95a0 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4039,16 +4039,13 @@ MODULE_LICENSE("GPL"); static int __init xhci_hcd_init(void) { -#ifdef CONFIG_PCI - int retval = 0; + int retval; retval = xhci_register_pci(); - if (retval < 0) { printk(KERN_DEBUG "Problem registering PCI driver."); return retval; } -#endif /* * Check the compiler generated sizes of structures that must be laid * out in specific ways for hardware access. @@ -4073,8 +4070,6 @@ module_init(xhci_hcd_init); static void __exit xhci_hcd_cleanup(void) { -#ifdef CONFIG_PCI xhci_unregister_pci(); -#endif } module_exit(xhci_hcd_cleanup); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ad11c38..3c8fbd2 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1644,6 +1644,9 @@ void xhci_free_command(struct xhci_hcd *xhci, /* xHCI PCI glue */ int xhci_register_pci(void); void xhci_unregister_pci(void); +#else +static inline int xhci_register_pci(void) { return 0; } +static inline void xhci_unregister_pci(void) {} #endif /* xHCI host controller glue */ -- cgit v0.10.2 From a6eeeb9f45b5a417f574f3bc799b7122270bf59b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 26 Sep 2011 11:23:38 -0400 Subject: USB: Update USB default wakeup settings This patch (as1486) implements the kernel's new wakeup policy for USB host controllers. Since they don't generate wakeup requests on their but merely forward requests from their root hubs toward the CPU, they should be enabled for wakeup by default. Also, to be compliant with both the old and new policies, root hubs should not be enabled for remote wakeup by default. Userspace must enable it explicitly if it is desired. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 73cbbd8..100d4b0 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2429,7 +2429,7 @@ int usb_add_hcd(struct usb_hcd *hcd, * but drivers can override it in reset() if needed, along with * recording the overall controller's system wakeup capability. */ - device_init_wakeup(&rhdev->dev, 1); + device_set_wakeup_capable(&rhdev->dev, 1); /* HCD_FLAG_RH_RUNNING doesn't matter until the root hub is * registered. But since the controller can die at any time, @@ -2478,6 +2478,13 @@ int usb_add_hcd(struct usb_hcd *hcd, } if (hcd->uses_new_polling && HCD_POLL_RH(hcd)) usb_hcd_poll_rh_status(hcd); + + /* + * Host controllers don't generate their own wakeup requests; + * they only forward requests from the root hub. Therefore + * controllers should always be enabled for remote wakeup. + */ + device_wakeup_enable(hcd->self.controller); return retval; error_create_attr_group: -- cgit v0.10.2 From 5c12e7856d75f68c1ca8372d6cc76cdbc71763c0 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 26 Sep 2011 11:25:26 -0400 Subject: USB: UHCI: improve comments and logic for root-hub suspend This patch (as1488) improves the comments and logic in uhci-hcd's suspend routine. The existing comments are hard to understand and don't give a good idea of what's really going on. The question of whether EGSM (Enter Global Suspend Mode) and RD (enable Resume Detect interrupts) can be useful when they're not both set is difficult. The spec doesn't give any details on how they interact with system wakeup, although clearly they are meant to be used together. To be safe, the patch changes the subroutine so that neither bit gets set unless they both do. There shouldn't be any functional changes from this; only systems that are designed badly or broken in some way need to avoid using those bits. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index fba99b1..c8ae199 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -294,50 +294,50 @@ __acquires(uhci->lock) * and that remote wakeups should be enabled. */ egsm_enable = USBCMD_EGSM; - uhci->RD_enable = 1; int_enable = USBINTR_RESUME; wakeup_enable = 1; - /* In auto-stop mode wakeups must always be detected, but - * Resume-Detect interrupts may be prohibited. (In the absence - * of CONFIG_PM, they are always disallowed.) + /* + * In auto-stop mode, we must be able to detect new connections. + * The user can force us to poll by disabling remote wakeup; + * otherwise we will use the EGSM/RD mechanism. */ if (auto_stop) { if (!device_may_wakeup(&rhdev->dev)) - int_enable = 0; + egsm_enable = int_enable = 0; + } - /* In bus-suspend mode wakeups may be disabled, but if they are - * allowed then so are Resume-Detect interrupts. - */ - } else { #ifdef CONFIG_PM + /* + * In bus-suspend mode, we use the wakeup setting specified + * for the root hub. + */ + else { if (!rhdev->do_remote_wakeup) wakeup_enable = 0; -#endif } +#endif - /* EGSM causes the root hub to echo a 'K' signal (resume) out any - * port which requests a remote wakeup. According to the USB spec, - * every hub is supposed to do this. But if we are ignoring - * remote-wakeup requests anyway then there's no point to it. - * We also shouldn't enable EGSM if it's broken. - */ - if (!wakeup_enable || global_suspend_mode_is_broken(uhci)) - egsm_enable = 0; - - /* If we're ignoring wakeup events then there's no reason to - * enable Resume-Detect interrupts. We also shouldn't enable - * them if they are broken or disallowed. + /* + * UHCI doesn't distinguish between wakeup requests from downstream + * devices and local connect/disconnect events. There's no way to + * enable one without the other; both are controlled by EGSM. Thus + * if wakeups are disallowed then EGSM must be turned off -- in which + * case remote wakeup requests from downstream during system sleep + * will be lost. + * + * In addition, if EGSM is broken then we can't use it. Likewise, + * if Resume-Detect interrupts are broken then we can't use them. * - * This logic may lead us to enabling RD but not EGSM. The UHCI - * spec foolishly says that RD works only when EGSM is on, but - * there's no harm in enabling it anyway -- perhaps some chips - * will implement it! + * Finally, neither EGSM nor RD is useful by itself. Without EGSM, + * the RD status bit will never get set. Without RD, the controller + * won't generate interrupts to tell the system about wakeup events. */ - if (!wakeup_enable || resume_detect_interrupts_are_broken(uhci) || - !int_enable) - uhci->RD_enable = int_enable = 0; + if (!wakeup_enable || global_suspend_mode_is_broken(uhci) || + resume_detect_interrupts_are_broken(uhci)) + egsm_enable = int_enable = 0; + uhci->RD_enable = !!int_enable; uhci_writew(uhci, int_enable, USBINTR); uhci_writew(uhci, egsm_enable | USBCMD_CF, USBCMD); mb(); @@ -364,10 +364,12 @@ __acquires(uhci->lock) uhci->rh_state = new_state; uhci->is_stopped = UHCI_IS_STOPPED; - /* If interrupts don't work and remote wakeup is enabled then - * the suspended root hub needs to be polled. + /* + * If remote wakeup is enabled but either EGSM or RD interrupts + * doesn't work, then we won't get an interrupt when a wakeup event + * occurs. Thus the suspended root hub needs to be polled. */ - if (!int_enable && wakeup_enable) + if (wakeup_enable && (!int_enable || !egsm_enable)) set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); else clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); -- cgit v0.10.2 From aec01c5895051849ed842dc5b8794017a7751f28 Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Mon, 26 Sep 2011 10:18:29 -0500 Subject: USB: pid_ns: ensure pid is not freed during kill_pid_info_as_uid Alan Stern points out that after spin_unlock(&ps->lock) there is no guarantee that ps->pid won't be freed. Since kill_pid_info_as_uid() is called after the spin_unlock(), the pid passed to it must be pinned. Reported-by: Alan Stern Signed-off-by: Serge Hallyn Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 37518df..eea53eb 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -407,7 +407,7 @@ static void async_completed(struct urb *urb) sinfo.si_errno = as->status; sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = as->userurb; - pid = as->pid; + pid = get_pid(as->pid); uid = as->uid; euid = as->euid; secid = as->secid; @@ -422,9 +422,11 @@ static void async_completed(struct urb *urb) cancel_bulk_urbs(ps, as->bulk_addr); spin_unlock(&ps->lock); - if (signr) + if (signr) { kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid, euid, secid); + put_pid(pid); + } wake_up(&ps->wait); } -- cgit v0.10.2 From 393cbb5151ecda9f9e14e3082d048dd27a1ff9f6 Mon Sep 17 00:00:00 2001 From: Matthias Dellweg <2500@gmx.de> Date: Sun, 25 Sep 2011 14:26:25 +0200 Subject: usb/core/devio.c: Check for printer class specific request In the usb printer class specific request get_device_id the value of wIndex is (interface << 8 | altsetting) instead of just interface. This enables the detection of some printers with libusb. Acked-by: Alan Stern Signed-off-by: Matthias Dellweg <2500@gmx.de> Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index eea53eb..0ca54e2 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -609,9 +609,10 @@ static int findintfep(struct usb_device *dev, unsigned int ep) } static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, - unsigned int index) + unsigned int request, unsigned int index) { int ret = 0; + struct usb_host_interface *alt_setting; if (ps->dev->state != USB_STATE_UNAUTHENTICATED && ps->dev->state != USB_STATE_ADDRESS @@ -620,6 +621,19 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype)) return 0; + /* + * check for the special corner case 'get_device_id' in the printer + * class specification, where wIndex is (interface << 8 | altsetting) + * instead of just interface + */ + if (requesttype == 0xa1 && request == 0) { + alt_setting = usb_find_alt_setting(ps->dev->actconfig, + index >> 8, index & 0xff); + if (alt_setting + && alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER) + index >>= 8; + } + index &= 0xff; switch (requesttype & USB_RECIP_MASK) { case USB_RECIP_ENDPOINT: @@ -772,7 +786,8 @@ static int proc_control(struct dev_state *ps, void __user *arg) if (copy_from_user(&ctrl, arg, sizeof(ctrl))) return -EFAULT; - ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex); + ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.bRequest, + ctrl.wIndex); if (ret) return ret; wLength = ctrl.wLength; /* To suppress 64k PAGE_SIZE warning */ @@ -1102,7 +1117,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, kfree(dr); return -EINVAL; } - ret = check_ctrlrecip(ps, dr->bRequestType, + ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest, le16_to_cpup(&dr->wIndex)); if (ret) { kfree(dr); -- cgit v0.10.2 From 1bfac90d1b8e63a4d44158c3445d8fda3fb6d5eb Mon Sep 17 00:00:00 2001 From: Rigbert Hamisch Date: Tue, 27 Sep 2011 10:46:43 +0200 Subject: USB: qcserial: add device ID for "HP un2430 Mobile Broadband Module" add device ID for "HP un2430 Mobile Broadband Module" Signed-off-by: Rigbert Hamisch Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index d8651ea..b9bb247 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -28,6 +28,7 @@ static const struct usb_device_id id_table[] = { {USB_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ {USB_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */ {USB_DEVICE(0x03f0, 0x201d)}, /* HP un2400 Gobi QDL Device */ + {USB_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */ {USB_DEVICE(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */ {USB_DEVICE(0x04da, 0x250c)}, /* Panasonic Gobi QDL device */ {USB_DEVICE(0x413c, 0x8172)}, /* Dell Gobi Modem device */ -- cgit v0.10.2 From edb2b255a0bebac5aeb17c7613aeb76ba4e6c63c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 27 Sep 2011 09:25:19 +0300 Subject: USB: message: cleanup min_t() cast in usb_sg_init() "length" is type size_t so the cast to unsigned int truncates the upper bytes. This isn't an issue in real life (I've checked the callers) but it's a bit messy. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 0b5ec23..b3bdfed 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -435,7 +435,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, len = sg->length; if (length) { - len = min_t(unsigned, len, length); + len = min_t(size_t, len, length); length -= len; if (length == 0) io->entries = i + 1; -- cgit v0.10.2 From d178bc3a708f39cbfefc3fab37032d3f2511b4ec Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Mon, 26 Sep 2011 10:45:18 -0500 Subject: user namespace: usb: make usb urbs user namespace aware (v2) Add to the dev_state and alloc_async structures the user namespace corresponding to the uid and euid. Pass these to kill_pid_info_as_uid(), which can then implement a proper, user-namespace-aware uid check. Changelog: Sep 20: Per Oleg's suggestion: Instead of caching and passing user namespace, uid, and euid each separately, pass a struct cred. Sep 26: Address Alan Stern's comments: don't define a struct cred at usbdev_open(), and take and put a cred at async_completed() to ensure it lasts for the duration of kill_pid_info_as_cred(). Signed-off-by: Serge Hallyn Cc: Oleg Nesterov Cc: "Eric W. Biederman" Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 0ca54e2..e3beaf2 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -68,7 +69,7 @@ struct dev_state { wait_queue_head_t wait; /* wake up if a request completed */ unsigned int discsignr; struct pid *disc_pid; - uid_t disc_uid, disc_euid; + const struct cred *cred; void __user *disccontext; unsigned long ifclaimed; u32 secid; @@ -79,7 +80,7 @@ struct async { struct list_head asynclist; struct dev_state *ps; struct pid *pid; - uid_t uid, euid; + const struct cred *cred; unsigned int signr; unsigned int ifnum; void __user *userbuffer; @@ -248,6 +249,7 @@ static struct async *alloc_async(unsigned int numisoframes) static void free_async(struct async *as) { put_pid(as->pid); + put_cred(as->cred); kfree(as->urb->transfer_buffer); kfree(as->urb->setup_packet); usb_free_urb(as->urb); @@ -393,9 +395,8 @@ static void async_completed(struct urb *urb) struct dev_state *ps = as->ps; struct siginfo sinfo; struct pid *pid = NULL; - uid_t uid = 0; - uid_t euid = 0; u32 secid = 0; + const struct cred *cred = NULL; int signr; spin_lock(&ps->lock); @@ -408,8 +409,7 @@ static void async_completed(struct urb *urb) sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = as->userurb; pid = get_pid(as->pid); - uid = as->uid; - euid = as->euid; + cred = get_cred(as->cred); secid = as->secid; } snoop(&urb->dev->dev, "urb complete\n"); @@ -423,9 +423,9 @@ static void async_completed(struct urb *urb) spin_unlock(&ps->lock); if (signr) { - kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid, - euid, secid); + kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred, secid); put_pid(pid); + put_cred(cred); } wake_up(&ps->wait); @@ -672,7 +672,6 @@ static int usbdev_open(struct inode *inode, struct file *file) { struct usb_device *dev = NULL; struct dev_state *ps; - const struct cred *cred = current_cred(); int ret; ret = -ENOMEM; @@ -722,8 +721,7 @@ static int usbdev_open(struct inode *inode, struct file *file) init_waitqueue_head(&ps->wait); ps->discsignr = 0; ps->disc_pid = get_pid(task_pid(current)); - ps->disc_uid = cred->uid; - ps->disc_euid = cred->euid; + ps->cred = get_current_cred(); ps->disccontext = NULL; ps->ifclaimed = 0; security_task_getsecid(current, &ps->secid); @@ -765,6 +763,7 @@ static int usbdev_release(struct inode *inode, struct file *file) usb_unlock_device(dev); usb_put_dev(dev); put_pid(ps->disc_pid); + put_cred(ps->cred); as = async_getcompleted(ps); while (as) { @@ -1065,7 +1064,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, struct usb_host_endpoint *ep; struct async *as; struct usb_ctrlrequest *dr = NULL; - const struct cred *cred = current_cred(); unsigned int u, totlen, isofrmlen; int ret, ifnum = -1; int is_in; @@ -1279,8 +1277,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->signr = uurb->signr; as->ifnum = ifnum; as->pid = get_pid(task_pid(current)); - as->uid = cred->uid; - as->euid = cred->euid; + as->cred = get_current_cred(); security_task_getsecid(current, &as->secid); if (!is_in && uurb->buffer_length > 0) { if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, @@ -1998,9 +1995,8 @@ static void usbdev_remove(struct usb_device *udev) sinfo.si_errno = EPIPE; sinfo.si_code = SI_ASYNCIO; sinfo.si_addr = ps->disccontext; - kill_pid_info_as_uid(ps->discsignr, &sinfo, - ps->disc_pid, ps->disc_uid, - ps->disc_euid, ps->secid); + kill_pid_info_as_cred(ps->discsignr, &sinfo, + ps->disc_pid, ps->cred, ps->secid); } } } diff --git a/include/linux/sched.h b/include/linux/sched.h index 4ac2c05..57ddb52 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2166,7 +2166,8 @@ extern int force_sigsegv(int, struct task_struct *); extern int force_sig_info(int, struct siginfo *, struct task_struct *); extern int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp); extern int kill_pid_info(int sig, struct siginfo *info, struct pid *pid); -extern int kill_pid_info_as_uid(int, struct siginfo *, struct pid *, uid_t, uid_t, u32); +extern int kill_pid_info_as_cred(int, struct siginfo *, struct pid *, + const struct cred *, u32); extern int kill_pgrp(struct pid *pid, int sig, int priv); extern int kill_pid(struct pid *pid, int sig, int priv); extern int kill_proc_info(int, struct siginfo *, pid_t); diff --git a/kernel/signal.c b/kernel/signal.c index 291c970..d252be2 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1344,13 +1344,24 @@ int kill_proc_info(int sig, struct siginfo *info, pid_t pid) return error; } +static int kill_as_cred_perm(const struct cred *cred, + struct task_struct *target) +{ + const struct cred *pcred = __task_cred(target); + if (cred->user_ns != pcred->user_ns) + return 0; + if (cred->euid != pcred->suid && cred->euid != pcred->uid && + cred->uid != pcred->suid && cred->uid != pcred->uid) + return 0; + return 1; +} + /* like kill_pid_info(), but doesn't use uid/euid of "current" */ -int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, - uid_t uid, uid_t euid, u32 secid) +int kill_pid_info_as_cred(int sig, struct siginfo *info, struct pid *pid, + const struct cred *cred, u32 secid) { int ret = -EINVAL; struct task_struct *p; - const struct cred *pcred; unsigned long flags; if (!valid_signal(sig)) @@ -1362,10 +1373,7 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, ret = -ESRCH; goto out_unlock; } - pcred = __task_cred(p); - if (si_fromuser(info) && - euid != pcred->suid && euid != pcred->uid && - uid != pcred->suid && uid != pcred->uid) { + if (si_fromuser(info) && !kill_as_cred_perm(cred, p)) { ret = -EPERM; goto out_unlock; } @@ -1384,7 +1392,7 @@ out_unlock: rcu_read_unlock(); return ret; } -EXPORT_SYMBOL_GPL(kill_pid_info_as_uid); +EXPORT_SYMBOL_GPL(kill_pid_info_as_cred); /* * kill_something_info() interprets pid in interesting ways just like kill(2). -- cgit v0.10.2 From 5b253d88cc6c65a23cefc457a5a4ef139913c5fc Mon Sep 17 00:00:00 2001 From: Jon Levell Date: Thu, 29 Sep 2011 20:42:52 +0100 Subject: USB: add quirk for Logitech C300 web cam My webcam is a Logitech C300 and I get "chipmunk"ed squeaky sound. The following trivial patch fixes it. Signed-off-by: Jon Levell Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 38f0510..d6a8d82 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -44,6 +44,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Logitech Webcam C250 */ { USB_DEVICE(0x046d, 0x0804), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Webcam C300 */ + { USB_DEVICE(0x046d, 0x0805), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Logitech Webcam B/C500 */ { USB_DEVICE(0x046d, 0x0807), .driver_info = USB_QUIRK_RESET_RESUME }, -- cgit v0.10.2 From 2646021e1d5235c6c2143d27405fb057e5560d2b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 30 Sep 2011 10:58:36 +0300 Subject: usb: dwc3: ep0: Make USB30CV happy with SetAddress According to USB 3.0 Specification, a SetAddress() while device is in Configured State has an unspecified behavior (see Section 9.4.6). Still USB30CV wasn't happy with my Stall reply. To make that thing happy, just accept the SetAddress() always. No problems have been observed thus far. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index b66d969..e058961 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -421,7 +421,6 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) { - int ret = 0; u32 addr; u32 reg; @@ -429,29 +428,17 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) if (addr > 127) return -EINVAL; - switch (dwc->dev_state) { - case DWC3_DEFAULT_STATE: - case DWC3_ADDRESS_STATE: - /* - * Not sure if we should program DevAddr now or later - */ - reg = dwc3_readl(dwc->regs, DWC3_DCFG); - reg &= ~(DWC3_DCFG_DEVADDR_MASK); - reg |= DWC3_DCFG_DEVADDR(addr); - dwc3_writel(dwc->regs, DWC3_DCFG, reg); - - if (addr) - dwc->dev_state = DWC3_ADDRESS_STATE; - else - dwc->dev_state = DWC3_DEFAULT_STATE; - break; + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~(DWC3_DCFG_DEVADDR_MASK); + reg |= DWC3_DCFG_DEVADDR(addr); + dwc3_writel(dwc->regs, DWC3_DCFG, reg); - case DWC3_CONFIGURED_STATE: - ret = -EINVAL; - break; - } + if (addr) + dwc->dev_state = DWC3_ADDRESS_STATE; + else + dwc->dev_state = DWC3_DEFAULT_STATE; - return ret; + return 0; } static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) -- cgit v0.10.2 From d95b09b90187f06062c0ec9fa05ff3151bd3e0bb Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 30 Sep 2011 10:58:37 +0300 Subject: usb: dwc3: ep0: ignore direction on 2-stage transfer We don't need to care about direction on a two stage transfer. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index e058961..d45e215 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -525,15 +525,15 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc, len = le16_to_cpu(ctrl->wLength); if (!len) { - dwc->three_stage_setup = 0; + dwc->three_stage_setup = false; + dwc->ep0_expect_in = false; dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS; } else { - dwc->three_stage_setup = 1; + dwc->three_stage_setup = true; + dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN); dwc->ep0_next_event = DWC3_EP0_NRDY_DATA; } - dwc->ep0_expect_in = !!(ctrl->bRequestType & USB_DIR_IN); - if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) ret = dwc3_ep0_std_request(dwc, ctrl); else -- cgit v0.10.2 From 25355be64ef319894c4de2034af1bdeb79dc0bd4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 30 Sep 2011 10:58:38 +0300 Subject: usb: dwc3: ep0: fix debug message The way it was before was really meaningless. Now it looks saner. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index d45e215..95ce96a 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -740,8 +740,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) { dev_vdbg(dwc->dev, "Expected %d got %d\n", - DEPEVT_STATUS_CONTROL_DATA, - event->status); + dwc->ep0_next_event, + DWC3_EP0_NRDY_DATA); dwc3_ep0_stall_and_restart(dwc); return; @@ -769,8 +769,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) { dev_vdbg(dwc->dev, "Expected %d got %d\n", - DEPEVT_STATUS_CONTROL_STATUS, - event->status); + dwc->ep0_next_event, + DWC3_EP0_NRDY_STATUS); dwc3_ep0_stall_and_restart(dwc); return; -- cgit v0.10.2 From 9876cbe2e2ec685ee620056ba1f2cc2f13a2e01a Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 30 Sep 2011 10:58:39 +0300 Subject: usb: dwc3: ep0: remove second giveback in error case We already give requests back in dwc3_ep0_stall_and_restart() so doing it again here will most likely corrupt the list. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 95ce96a..e48dce5 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -587,7 +587,6 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc, /* for some reason we did not get everything out */ dwc3_ep0_stall_and_restart(dwc); - dwc3_gadget_giveback(dep, r, -ECONNRESET); } else { /* * handle the case where we have to send a zero packet. This -- cgit v0.10.2 From b147f3572db230522d158312055f3d28f96fbc92 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 30 Sep 2011 10:58:40 +0300 Subject: usb: dwc3: ep0: fix debug output Use "ep0in" and "ep0out" instead "ep1in" and "ep0out" which is confusing and not consistent with the remaining output. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index e48dce5..2d5725e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -785,7 +785,7 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc, dev_dbg(dwc->dev, "%s while ep%d%s in state '%s'\n", dwc3_ep_event_string(event->endpoint_event), - epnum, (epnum & 1) ? "in" : "out", + epnum >> 1, (epnum & 1) ? "in" : "out", dwc3_ep0_state_string(dwc->ep0state)); switch (event->endpoint_event) { -- cgit v0.10.2 From 49a25cc9a7effe2993e65229c2ea0be726919bcf Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 30 Sep 2011 10:58:41 +0300 Subject: usb: dwc: remove "All rights reserved" statement. Some people think that this line is not compatible with the GPL. The statement was required due to the Buenos Aires Convention and is now deprecated. I remove it because it is said that it is pointless nowdays. Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 64ba097..cfb0745 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -2,7 +2,6 @@ * core.c - DesignWare USB3 DRD Controller Core file * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. * * Authors: Felipe Balbi , * Sebastian Andrzej Siewior diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 07d2018..0231eba 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -2,7 +2,6 @@ * core.h - DesignWare USB3 DRD Core Header * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. * * Authors: Felipe Balbi , * Sebastian Andrzej Siewior diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index ee3ba73..5894ee8 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -2,7 +2,6 @@ * debug.h - DesignWare USB3 DRD Controller Debug Header * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. * * Authors: Felipe Balbi , * Sebastian Andrzej Siewior diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 20d329f..da1ad77 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -2,7 +2,6 @@ * debugfs.c - DesignWare USB3 DRD Controller DebugFS file * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. * * Authors: Felipe Balbi , * Sebastian Andrzej Siewior diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 72cc92b..64ce3fc 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -2,7 +2,6 @@ * dwc3-omap.c - OMAP Specific Glue layer * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. * * Authors: Felipe Balbi , * Sebastian Andrzej Siewior diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index e3b77d2..f77c000 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -2,7 +2,6 @@ * dwc3-pci.c - PCI Specific glue layer * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. * * Authors: Felipe Balbi , * Sebastian Andrzej Siewior diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 2d5725e..cc27c4e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -2,7 +2,6 @@ * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. * * Authors: Felipe Balbi , * Sebastian Andrzej Siewior diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 859257a..b2820aa 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2,7 +2,6 @@ * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. * * Authors: Felipe Balbi , * Sebastian Andrzej Siewior diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 4809ac8..b025651 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -2,7 +2,6 @@ * gadget.h - DesignWare USB3 DRD Gadget Header * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. * * Authors: Felipe Balbi , * Sebastian Andrzej Siewior diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index 0c4e2a9..bc957db 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -2,7 +2,6 @@ * io.h - DesignWare USB3 DRD IO Header * * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. * * Authors: Felipe Balbi , * Sebastian Andrzej Siewior -- cgit v0.10.2 From b23c843992b659d537514e6493d673284f5d6724 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 30 Sep 2011 10:58:42 +0300 Subject: usb: dwc3: gadget: fix DEPSTARTCFG for non-EP0 EPs DEPSTARTCFG for non-EP0 EPs must only be sent once per config [ balbi@ti.com : changed config_start to start_config_issued ] Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 0231eba..502582c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -529,6 +529,7 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) * @ep0_status_pending: ep0 status response without a req is pending * @ep0_bounced: true when we used bounce buffer * @ep0_expect_in: true when we expect a DATA IN transfer + * @start_config_issued: true when StartConfig command has been issued * @ep0_next_event: hold the next expected event * @ep0state: state of endpoint zero * @link_state: link state @@ -576,6 +577,7 @@ struct dwc3 { unsigned ep0_status_pending:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; + unsigned start_config_issued:1; enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index cc27c4e..2def48e 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -455,6 +455,7 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl) u32 cfg; int ret; + dwc->start_config_issued = false; cfg = le16_to_cpu(ctrl->wValue); switch (dwc->dev_state) { diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b2820aa..9c0174a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -237,8 +237,12 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) if (dep->number != 1) { cmd = DWC3_DEPCMD_DEPSTARTCFG; /* XferRscIdx == 0 for ep0 and 2 for the remaining */ - if (dep->number > 1) + if (dep->number > 1) { + if (dwc->start_config_issued) + return 0; + dwc->start_config_issued = true; cmd |= DWC3_DEPCMD_PARAM(2); + } return dwc3_send_gadget_ep_cmd(dwc, 0, cmd, ¶ms); } @@ -1161,6 +1165,8 @@ static int dwc3_gadget_start(struct usb_gadget *g, reg |= DWC3_DCFG_SUPERSPEED; dwc3_writel(dwc->regs, DWC3_DCFG, reg); + dwc->start_config_issued = false; + /* Start with SuperSpeed Default */ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); @@ -1592,6 +1598,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) dwc3_stop_active_transfers(dwc); dwc3_disconnect_gadget(dwc); + dwc->start_config_issued = false; dwc->gadget.speed = USB_SPEED_UNKNOWN; } @@ -1643,6 +1650,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) dwc3_stop_active_transfers(dwc); dwc3_clear_stall_all_ep(dwc); + dwc->start_config_issued = false; /* Reset device address to zero */ reg = dwc3_readl(dwc->regs, DWC3_DCFG); -- cgit v0.10.2 From 82828ca77b8b63b817d9bb540145a4cafa9be582 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 30 Sep 2011 10:58:43 +0300 Subject: usb: dwc3: gadget: driver should not wait for RxFIFO to drain An older version of the databook said to wait for the FIFO to drain, but that has been removed from the newer databooks. Waiting for RxFIFO to drain caused problems when testing against one of the host controllers available in the market. After talking to one of the RTL engineers, he stated that we should _not_ wait for RxFIFO to drain. Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9c0174a..fd5d2bd 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1656,25 +1656,6 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_DEVADDR_MASK); dwc3_writel(dwc->regs, DWC3_DCFG, reg); - - /* - * Wait for RxFifo to drain - * - * REVISIT probably shouldn't wait forever. - * In case Hardware ends up in a screwed up - * case, we error out, notify the user and, - * maybe, WARN() or BUG() but leave the rest - * of the kernel working fine. - * - * REVISIT the below is rather CPU intensive, - * maybe we should read and if it doesn't work - * sleep (not busy wait) for a few useconds. - * - * REVISIT why wait until the RXFIFO is empty anyway? - */ - while (!(dwc3_readl(dwc->regs, DWC3_DSTS) - & DWC3_DSTS_RXFIFOEMPTY)) - cpu_relax(); } static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed) -- cgit v0.10.2 From 5275455a6e6ae6b57303834b0afd267bb9657ffc Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 30 Sep 2011 10:58:44 +0300 Subject: usb: dwc3: gadget: make DWC3_EP_WEDGE do the right thing This makes DWC3_EP_WEDGE do the right thing, which is prevent DWC3_EP_WEDGE from ever being cleared by a ClearFeature(HALT) command. [ balbi@ti.com : allowed set_wedge to send SetHalt command to controller ] Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index fd5d2bd..16f2ba6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -894,6 +894,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) else dep->flags |= DWC3_EP_STALL; } else { + if (dep->flags & DWC3_EP_WEDGE) + return 0; + ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_CLEARSTALL, ¶ms); if (ret) @@ -903,6 +906,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value) else dep->flags &= ~DWC3_EP_STALL; } + return ret; } @@ -936,7 +940,7 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep) dep->flags |= DWC3_EP_WEDGE; - return usb_ep_set_halt(ep); + return dwc3_gadget_ep_set_halt(ep, 1); } /* -------------------------------------------------------------------------- */ -- cgit v0.10.2 From c439ef87c33a1df30f528cb152151465c2529cac Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 30 Sep 2011 10:58:45 +0300 Subject: usb: dwc3: gadget: fix DMA offset calculation Fix offset calculation in dwc3_trb_dma_offset() Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 16f2ba6..ee5c1a5 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -189,7 +189,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, struct dwc3_trb_hw *trb) { - u32 offset = trb - dep->trb_pool; + u32 offset = (char *) trb - (char *) dep->trb_pool; return dep->trb_pool_dma + offset; } -- cgit v0.10.2 From a4af9008bb69f49df3abf816d48e224aca810af4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 30 Sep 2011 10:58:46 +0300 Subject: usb: dwc3: gadget: add support for Bursts We already have the value from gadget drivers, just need to pass it to our controller. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index ee5c1a5..524ff91 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -259,6 +259,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, params.param0.depcfg.ep_type = usb_endpoint_type(desc); params.param0.depcfg.max_packet_size = usb_endpoint_maxp(desc); + params.param0.depcfg.burst_size = dep->endpoint.maxburst; params.param1.depcfg.xfer_complete_enable = true; params.param1.depcfg.xfer_not_ready_enable = true; -- cgit v0.10.2 From 879631aa658be2c1307758223b6d15236f9f6335 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 30 Sep 2011 10:58:47 +0300 Subject: usb: dwc3: gadget: implement streams support The following patch adds support for streams to dwc3 driver. While at that, also fix one small issue on endpoint disable where we should clear all flags not only ENABLED. Reviewied-by: Paul Zimmerman Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/usb/dwc3.txt b/Documentation/usb/dwc3.txt index 2f65853..7b590ed 100644 --- a/Documentation/usb/dwc3.txt +++ b/Documentation/usb/dwc3.txt @@ -3,14 +3,6 @@ ~~~~~~ Please pick something while reading :) -- Implement streaming support for BULK endpoints - Tatyana's patch "usb: Add streams support to the gadget framework" - introduces streaming support for the gadget driver. - Every usb_request has new field called stream_id which holds its id. - Every usb_ep has a field num_supported_strms which describes the max - number of streams supported (for this ep). - UAS is AFAIK the only gadget with streaming support. - - Convert interrupt handler to per-ep-thread-irq As it turns out some DWC3-commands ~1ms to complete. Currently we spin diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 502582c..c6de53c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -329,6 +329,7 @@ struct dwc3_event_buffer { * @interval: the intervall on which the ISOC transfer is started * @name: a human readable name e.g. ep1out-bulk * @direction: true for TX, false for RX + * @stream_capable: true when streams are enabled */ struct dwc3_ep { struct usb_ep endpoint; @@ -362,6 +363,7 @@ struct dwc3_ep { char name[20]; unsigned direction:1; + unsigned stream_capable:1; }; enum dwc3_phy { @@ -650,6 +652,10 @@ struct dwc3_event_depevt { #define DEPEVT_STATUS_IOC (1 << 2) #define DEPEVT_STATUS_LST (1 << 3) +/* Stream event only */ +#define DEPEVT_STREAMEVT_FOUND 1 +#define DEPEVT_STREAMEVT_NOTFOUND 2 + /* Control-only Status */ #define DEPEVT_STATUS_CONTROL_SETUP 0 #define DEPEVT_STATUS_CONTROL_DATA 1 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 524ff91..8d85023 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -264,6 +264,12 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, params.param1.depcfg.xfer_complete_enable = true; params.param1.depcfg.xfer_not_ready_enable = true; + if (usb_endpoint_xfer_bulk(desc) && dep->endpoint.max_streams) { + params.param1.depcfg.stream_capable = true; + params.param1.depcfg.stream_event_enable = true; + dep->stream_capable = true; + } + if (usb_endpoint_xfer_isoc(desc)) params.param1.depcfg.xfer_in_progress_enable = true; @@ -391,15 +397,16 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) struct dwc3 *dwc = dep->dwc; u32 reg; - dep->flags &= ~DWC3_EP_ENABLED; dwc3_remove_requests(dwc, dep); reg = dwc3_readl(dwc->regs, DWC3_DALEPENA); reg &= ~DWC3_DALEPENA_EP(dep->number); dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); + dep->stream_capable = false; dep->desc = NULL; dep->type = 0; + dep->flags = 0; return 0; } @@ -633,6 +640,9 @@ static struct dwc3_request *dwc3_prepare_trbs(struct dwc3_ep *dep, trb.lst = last_one; } + if (usb_endpoint_xfer_bulk(dep->desc) && dep->stream_capable) + trb.sid_sofn = req->request.stream_id; + switch (usb_endpoint_type(dep->desc)) { case USB_ENDPOINT_XFER_CONTROL: trb.trbctl = DWC3_TRBCTL_CONTROL_SETUP; @@ -1505,12 +1515,28 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, } break; + case DWC3_DEPEVT_STREAMEVT: + if (!usb_endpoint_xfer_bulk(dep->desc)) { + dev_err(dwc->dev, "Stream event for non-Bulk %s\n", + dep->name); + return; + } + + switch (event->status) { + case DEPEVT_STREAMEVT_FOUND: + dev_vdbg(dwc->dev, "Stream %d found and started\n", + event->parameters); + + break; + case DEPEVT_STREAMEVT_NOTFOUND: + /* FALLTHROUGH */ + default: + dev_dbg(dwc->dev, "Couldn't find suitable stream\n"); + } + break; case DWC3_DEPEVT_RXTXFIFOEVT: dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name); break; - case DWC3_DEPEVT_STREAMEVT: - dev_dbg(dwc->dev, "%s Stream Event\n", dep->name); - break; case DWC3_DEPEVT_EPCMDCMPLT: dwc3_ep_cmd_compl(dep, event); break; -- cgit v0.10.2 From a32994998c2ffa161159b79bb68e683e2746c330 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 30 Sep 2011 10:58:48 +0300 Subject: usb: dwc3: add struct dwc3_hwparams That structure will hold a copy of readonly GHWPARAMS* registers for ease accessing by the driver. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index c6de53c..a763ba7 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -507,6 +507,30 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) } /** + * dwc3_hwparams - copy of HWPARAMS registers + * @hwparams0 - GHWPARAMS0 + * @hwparams1 - GHWPARAMS1 + * @hwparams2 - GHWPARAMS2 + * @hwparams3 - GHWPARAMS3 + * @hwparams4 - GHWPARAMS4 + * @hwparams5 - GHWPARAMS5 + * @hwparams6 - GHWPARAMS6 + * @hwparams7 - GHWPARAMS7 + * @hwparams8 - GHWPARAMS8 + */ +struct dwc3_hwparams { + u32 hwparams0; + u32 hwparams1; + u32 hwparams2; + u32 hwparams3; + u32 hwparams4; + u32 hwparams5; + u32 hwparams6; + u32 hwparams7; + u32 hwparams8; +}; + +/** * struct dwc3 - representation of our controller * @ctrl_req: usb control request which is used for ep0 * @ep0_trb: trb which is used for the ctrl_req @@ -537,6 +561,7 @@ static inline void dwc3_trb_to_nat(struct dwc3_trb_hw *hw, struct dwc3_trb *nat) * @link_state: link state * @speed: device speed (super, high, full, low) * @mem: points to start of memory which is used for this struct. + * @hwparams: copy of hwparams registers * @root: debugfs root folder pointer */ struct dwc3 { @@ -589,6 +614,7 @@ struct dwc3 { u8 speed; void *mem; + struct dwc3_hwparams hwparams; struct dentry *root; }; -- cgit v0.10.2 From 26ceca9750260997ab82bb84dac122de1e441658 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 30 Sep 2011 10:58:49 +0300 Subject: usb: dwc3: core: cache GHWPARAMS* registers cache the contents of GHWPARAMS* registers in our device structure for easy access. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index cfb0745..d35f905 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -229,6 +229,21 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc) } } +static void __devinit dwc3_cache_hwparams(struct dwc3 *dwc) +{ + struct dwc3_hwparams *parms = &dwc->hwparams; + + parms->hwparams0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0); + parms->hwparams1 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS1); + parms->hwparams2 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS2); + parms->hwparams3 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS3); + parms->hwparams4 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS4); + parms->hwparams5 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS5); + parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); + parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7); + parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8); +} + /** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure @@ -283,6 +298,8 @@ static int __devinit dwc3_core_init(struct dwc3 *dwc) goto err1; } + dwc3_cache_hwparams(dwc); + return 0; err1: -- cgit v0.10.2 From aabb70752361a8b8ca44142a942a5bd133c4d304 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 30 Sep 2011 10:58:50 +0300 Subject: usb: dwc3: gadget: allow clock gating to work The dwc3 core has internal clock gating support. Let's allow that to happen by clearing the disable bit in GCTL register. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index a763ba7..29a8e16 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -161,6 +161,7 @@ #define DWC3_GCTL_CORESOFTRESET (1 << 11) #define DWC3_GCTL_SCALEDOWN(n) (n << 4) #define DWC3_GCTL_DISSCRAMBLE (1 << 3) +#define DWC3_GCTL_DSBLCLKGTNG (1 << 0) /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) @@ -170,6 +171,11 @@ #define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) #define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17) +/* Global HWPARAMS1 Register */ +#define DWC3_GHWPARAMS1_EN_PWROPT(n) ((n & (3 << 24)) >> 24) +#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 +#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1 + /* Device Configuration Register */ #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8d85023..fd1ac4d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1164,6 +1164,14 @@ static int dwc3_gadget_start(struct usb_gadget *g, reg &= ~DWC3_GCTL_DISSCRAMBLE; reg |= DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_DEVICE); + switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams0)) { + case DWC3_GHWPARAMS1_EN_PWROPT_CLK: + reg &= ~DWC3_GCTL_DSBLCLKGTNG; + break; + default: + dev_dbg(dwc->dev, "No power optimization available\n"); + } + /* * WORKAROUND: DWC3 revisions <1.90a have a bug * when The device fails to connect at SuperSpeed -- cgit v0.10.2 From dc1c70a774b6fe3744b330d58bb9cf802f7eac89 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 30 Sep 2011 10:58:51 +0300 Subject: usb: dwc3: convert structures into bitshifts our parameter structures need to be written to HW, so instead of assuming little endian, we convert those into bit shifts. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 2def48e..69a4e43 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -103,10 +103,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, dwc3_trb_to_hw(&trb, trb_hw); memset(¶ms, 0, sizeof(params)); - params.param0.depstrtxfer.transfer_desc_addr_high = - upper_32_bits(dwc->ep0_trb_addr); - params.param1.depstrtxfer.transfer_desc_addr_low = - lower_32_bits(dwc->ep0_trb_addr); + params.param0 = upper_32_bits(dwc->ep0_trb_addr); + params.param1 = lower_32_bits(dwc->ep0_trb_addr); ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_STARTTRANSFER, ¶ms); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index fd1ac4d..fa824cf 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -158,12 +158,12 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, dev_vdbg(dwc->dev, "%s: cmd '%s' params %08x %08x %08x\n", dep->name, - dwc3_gadget_ep_cmd_string(cmd), params->param0.raw, - params->param1.raw, params->param2.raw); + dwc3_gadget_ep_cmd_string(cmd), params->param0, + params->param1, params->param2); - dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0.raw); - dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1.raw); - dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2.raw); + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0); + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1); + dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2); dwc3_writel(dwc->regs, DWC3_DEPCMD(ep), cmd | DWC3_DEPCMD_CMDACT); do { @@ -257,21 +257,21 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, memset(¶ms, 0x00, sizeof(params)); - params.param0.depcfg.ep_type = usb_endpoint_type(desc); - params.param0.depcfg.max_packet_size = usb_endpoint_maxp(desc); - params.param0.depcfg.burst_size = dep->endpoint.maxburst; + params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc)) + | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc)) + | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst); - params.param1.depcfg.xfer_complete_enable = true; - params.param1.depcfg.xfer_not_ready_enable = true; + params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN + | DWC3_DEPCFG_XFER_NOT_READY_EN; if (usb_endpoint_xfer_bulk(desc) && dep->endpoint.max_streams) { - params.param1.depcfg.stream_capable = true; - params.param1.depcfg.stream_event_enable = true; + params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE + | DWC3_DEPCFG_STREAM_EVENT_EN; dep->stream_capable = true; } if (usb_endpoint_xfer_isoc(desc)) - params.param1.depcfg.xfer_in_progress_enable = true; + params.param1 |= DWC3_DEPCFG_XFER_IN_PROGRESS_EN; /* * We are doing 1:1 mapping for endpoints, meaning @@ -279,17 +279,17 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, * so on. We consider the direction bit as part of the physical * endpoint number. So USB endpoint 0x81 is 0x03. */ - params.param1.depcfg.ep_number = dep->number; + params.param1 |= DWC3_DEPCFG_EP_NUMBER(dep->number); /* * We must use the lower 16 TX FIFOs even though * HW might have more */ if (dep->direction) - params.param0.depcfg.fifo_number = dep->number >> 1; + params.param0 |= DWC3_DEPCFG_FIFO_NUMBER(dep->number >> 1); if (desc->bInterval) { - params.param1.depcfg.binterval_m1 = desc->bInterval - 1; + params.param1 |= DWC3_DEPCFG_BINTERVAL_M1(desc->bInterval - 1); dep->interval = 1 << (desc->bInterval - 1); } @@ -303,7 +303,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) memset(¶ms, 0x00, sizeof(params)); - params.param0.depxfercfg.number_xfer_resources = 1; + params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1); return dwc3_send_gadget_ep_cmd(dwc, dep->number, DWC3_DEPCMD_SETTRANSFRESOURCE, ¶ms); @@ -719,10 +719,8 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, } memset(¶ms, 0, sizeof(params)); - params.param0.depstrtxfer.transfer_desc_addr_high = - upper_32_bits(req->trb_dma); - params.param1.depstrtxfer.transfer_desc_addr_low = - lower_32_bits(req->trb_dma); + params.param0 = upper_32_bits(req->trb_dma); + params.param1 = lower_32_bits(req->trb_dma); if (start_new) cmd = DWC3_DEPCMD_STARTTRANSFER; diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index b025651..71145a44 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -47,120 +47,35 @@ struct dwc3; #define to_dwc3_ep(ep) (container_of(ep, struct dwc3_ep, endpoint)) #define gadget_to_dwc(g) (container_of(g, struct dwc3, gadget)) -/** - * struct dwc3_gadget_ep_depcfg_param1 - DEPCMDPAR0 for DEPCFG command - * @interrupt_number: self-explanatory - * @reserved7_5: set to zero - * @xfer_complete_enable: event generated when transfer completed - * @xfer_in_progress_enable: event generated when transfer in progress - * @xfer_not_ready_enable: event generated when transfer not read - * @fifo_error_enable: generates events when FIFO Underrun (IN eps) - * or FIFO Overrun (OUT) eps - * @reserved_12: set to zero - * @stream_event_enable: event generated on stream - * @reserved14_15: set to zero - * @binterval_m1: bInterval minus 1 - * @stream_capable: this EP is capable of handling streams - * @ep_number: self-explanatory - * @bulk_based: Set to ‘1’ if this isochronous endpoint represents a bulk - * data stream that ignores the relationship of bus time to the - * intervals programmed in TRBs. - * @fifo_based: Set to ‘1’ if this isochronous endpoint represents a - * FIFO-based data stream where TRBs have fixed values and are never - * written back by the core. - */ -struct dwc3_gadget_ep_depcfg_param1 { - u32 interrupt_number:5; - u32 reserved7_5:3; /* set to zero */ - u32 xfer_complete_enable:1; - u32 xfer_in_progress_enable:1; - u32 xfer_not_ready_enable:1; - u32 fifo_error_enable:1; /* IN-underrun, OUT-overrun */ - u32 reserved12:1; /* set to zero */ - u32 stream_event_enable:1; - u32 reserved14_15:2; - u32 binterval_m1:8; /* bInterval minus 1 */ - u32 stream_capable:1; - u32 ep_number:5; - u32 bulk_based:1; - u32 fifo_based:1; -} __packed; - -/** - * struct dwc3_gadget_ep_depcfg_param0 - Parameter 0 for DEPCFG - * @reserved0: set to zero - * @ep_type: Endpoint Type (control, bulk, iso, interrupt) - * @max_packet_size: max packet size in bytes - * @reserved16_14: set to zero - * @fifo_number: self-explanatory - * @burst_size: burst size minus 1 - * @data_sequence_number: Must be 0 when an endpoint is initially configured - * May be non-zero when an endpoint is configured after a power transition - * that requires a save/restore. - * @ignore_sequence_number: Set to ‘1’ to avoid resetting the sequence - * number. This setting is used by software to modify the DEPEVTEN - * event enable bits without modifying other endpoint settings. - */ -struct dwc3_gadget_ep_depcfg_param0 { - u32 reserved0:1; - u32 ep_type:2; - u32 max_packet_size:11; - u32 reserved16_14:3; - u32 fifo_number:5; - u32 burst_size:4; - u32 data_sequence_number:5; - u32 ignore_sequence_number:1; -} __packed; - -/** - * struct dwc3_gadget_ep_depxfercfg_param0 - Parameter 0 of DEPXFERCFG - * @number_xfer_resources: Defines the number of Transfer Resources allocated - * to this endpoint. This field must be set to 1. - * @reserved16_31: set to zero; - */ -struct dwc3_gadget_ep_depxfercfg_param0 { - u32 number_xfer_resources:16; - u32 reserved16_31:16; -} __packed; - -/** - * struct dwc3_gadget_ep_depstrtxfer_param1 - Parameter 1 of DEPSTRTXFER - * @transfer_desc_addr_low: Indicates the lower 32 bits of the external - * memory's start address for the transfer descriptor. Because TRBs - * must be aligned to a 16-byte boundary, the lower 4 bits of this - * address must be 0. - */ -struct dwc3_gadget_ep_depstrtxfer_param1 { - u32 transfer_desc_addr_low; -} __packed; - -/** - * struct dwc3_gadget_ep_depstrtxfer_param1 - Parameter 1 of DEPSTRTXFER - * @transfer_desc_addr_high: Indicates the higher 32 bits of the external - * memory’s start address for the transfer descriptor. - */ -struct dwc3_gadget_ep_depstrtxfer_param0 { - u32 transfer_desc_addr_high; -} __packed; +/* DEPCFG parameter 1 */ +#define DWC3_DEPCFG_INT_NUM(n) ((n) << 0) +#define DWC3_DEPCFG_XFER_COMPLETE_EN (1 << 8) +#define DWC3_DEPCFG_XFER_IN_PROGRESS_EN (1 << 9) +#define DWC3_DEPCFG_XFER_NOT_READY_EN (1 << 10) +#define DWC3_DEPCFG_FIFO_ERROR_EN (1 << 11) +#define DWC3_DEPCFG_STREAM_EVENT_EN (1 << 13) +#define DWC3_DEPCFG_BINTERVAL_M1(n) ((n) << 16) +#define DWC3_DEPCFG_STREAM_CAPABLE (1 << 24) +#define DWC3_DEPCFG_EP_NUMBER(n) ((n) << 25) +#define DWC3_DEPCFG_BULK_BASED (1 << 30) +#define DWC3_DEPCFG_FIFO_BASED (1 << 31) + +/* DEPCFG parameter 0 */ +#define DWC3_DEPCFG_EP_TYPE(n) ((n) << 1) +#define DWC3_DEPCFG_MAX_PACKET_SIZE(n) ((n) << 3) +#define DWC3_DEPCFG_FIFO_NUMBER(n) ((n) << 17) +#define DWC3_DEPCFG_BURST_SIZE(n) ((n) << 22) +#define DWC3_DEPCFG_DATA_SEQ_NUM(n) ((n) << 26) +#define DWC3_DEPCFG_IGN_SEQ_NUM (1 << 31) + +/* DEPXFERCFG parameter 0 */ +#define DWC3_DEPXFERCFG_NUM_XFER_RES(n) ((n) & 0xffff) struct dwc3_gadget_ep_cmd_params { - union { - u32 raw; - } param2; - - union { - u32 raw; - struct dwc3_gadget_ep_depcfg_param1 depcfg; - struct dwc3_gadget_ep_depstrtxfer_param1 depstrtxfer; - } param1; - - union { - u32 raw; - struct dwc3_gadget_ep_depcfg_param0 depcfg; - struct dwc3_gadget_ep_depxfercfg_param0 depxfercfg; - struct dwc3_gadget_ep_depstrtxfer_param0 depstrtxfer; - } param0; -} __packed; + u32 param2; + u32 param1; + u32 param0; +}; /* -------------------------------------------------------------------------- */ -- cgit v0.10.2 From 2204fdee84eba175d58b6f78a9659dcd7806f532 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Fri, 30 Sep 2011 18:08:59 -0400 Subject: usb: Add module.h to various dwc3 drivers These files uses the full set of MODULE_ macros and so need to include module.h directly. Signed-off-by: Paul Gortmaker Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index d35f905..717ebc9 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -51,6 +51,7 @@ #include #include +#include #include "core.h" #include "gadget.h" diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 64ce3fc..062552b 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -46,6 +46,7 @@ #include #include #include +#include #include "io.h" -- cgit v0.10.2 From 74bdf22b5c3858b06af46f19d05c23e76c40a3bb Mon Sep 17 00:00:00 2001 From: Hakan Kvist Date: Mon, 3 Oct 2011 13:41:15 +0200 Subject: USB: ftdi_sio: add PID for Sony Ericsson Urban Add PID 0xfc8a, 0xfc8b for device Sony Ericsson Urban Signed-off-by: Hakan Kvist Signed-off-by: Oskar Andero Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 5cfd87e..f971c98 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -207,6 +207,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_XF_640_PID) }, { USB_DEVICE(FTDI_VID, FTDI_XF_642_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DSS20_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_URBAN_0_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_URBAN_1_PID) }, { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) }, { USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MTXORB_0_PID) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index bf5227a..4d231ee 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -420,9 +420,11 @@ #define PROTEGO_SPECIAL_4 0xFC73 /* special/unknown device */ /* - * DSS-20 Sync Station for Sony Ericsson P800 + * Sony Ericsson product ids */ -#define FTDI_DSS20_PID 0xFC82 +#define FTDI_DSS20_PID 0xFC82 /* DSS-20 Sync Station for Sony Ericsson P800 */ +#define FTDI_URBAN_0_PID 0xFC8A /* Sony Ericsson Urban, uart #0 */ +#define FTDI_URBAN_1_PID 0xFC8B /* Sony Ericsson Urban, uart #1 */ /* www.irtrans.de device */ #define FTDI_IRTRANS_PID 0xFC60 /* Product Id */ -- cgit v0.10.2 From 276532ba9666b36974cbe16f303fc8be99c9da17 Mon Sep 17 00:00:00 2001 From: Harro Haan Date: Mon, 10 Oct 2011 14:38:27 +0200 Subject: USB: fix ehci alignment error The Kirkwood gave an unaligned memory access error on line 742 of drivers/usb/host/echi-hcd.c: "ehci->last_periodic_enable = ktime_get_real();" Signed-off-by: Harro Haan Cc: stable Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index a4cd6c5..03354d5 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -178,7 +178,7 @@ struct usb_hcd { * this structure. */ unsigned long hcd_priv[0] - __attribute__ ((aligned(sizeof(unsigned long)))); + __attribute__ ((aligned(sizeof(s64)))); }; /* 2.4 does this a bit differently ... */ -- cgit v0.10.2 From 0a85577627fc81936d3dbdfb349964024adcb01d Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 30 Sep 2011 20:07:08 +0900 Subject: usb: gadget: r8a66597-udc: change prototype of r8a66597_write_fifo Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index d3f81c9..60c62e4 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -723,7 +723,7 @@ static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req) /* write fifo */ if (req->req.buf) { if (size > 0) - r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size); + r8a66597_write_fifo(r8a66597, ep, buf, size); if ((size == 0) || ((size % ep->ep.maxpacket) != 0)) r8a66597_bset(r8a66597, BVAL, ep->fifoctr); } @@ -770,7 +770,7 @@ static void irq_packet_write(struct r8a66597_ep *ep, /* write fifo */ if (req->req.buf) { - r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size); + r8a66597_write_fifo(r8a66597, ep, buf, size); if ((size == 0) || ((size % ep->ep.maxpacket) != 0) || ((bufsize != ep->ep.maxpacket) diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h index a373c4e..31047f8 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -173,11 +173,11 @@ static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, } static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, - unsigned long offset, + struct r8a66597_ep *ep, unsigned char *buf, int len) { - void __iomem *fifoaddr = r8a66597->reg + offset; + void __iomem *fifoaddr = r8a66597->reg + ep->fifoaddr; int adj = 0; int i; -- cgit v0.10.2 From 5c481a639068ac34bd1dcd183f37b7a65d0e3841 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 30 Sep 2011 20:07:21 +0900 Subject: usb: gadget: r8a66597-udc: add function for external controller R8A66597 has the pin of WR0 and WR1. So, if one write-pin of CPU connects to the pins, we have to change the setting of FIFOSEL register in the controller. If we don't change the setting, the controller cannot send the data of odd length. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h index 31047f8..f9ac2d7 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -172,6 +172,21 @@ static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, iowrite16(val, r8a66597->reg + offset); } +static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, + u16 val, u16 pat, unsigned long offset) +{ + u16 tmp; + tmp = r8a66597_read(r8a66597, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + r8a66597_write(r8a66597, tmp, offset); +} + +#define r8a66597_bclr(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, 0, val, offset) +#define r8a66597_bset(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, val, 0, offset) + static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, struct r8a66597_ep *ep, unsigned char *buf, @@ -205,18 +220,12 @@ static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, adj = 0x01; /* 16-bit wide */ } + if (r8a66597->pdata->wr0_shorted_to_wr1) + r8a66597_bclr(r8a66597, MBW_16, ep->fifosel); for (i = 0; i < len; i++) iowrite8(buf[i], fifoaddr + adj - (i & adj)); -} - -static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, - u16 val, u16 pat, unsigned long offset) -{ - u16 tmp; - tmp = r8a66597_read(r8a66597, offset); - tmp = tmp & (~pat); - tmp = tmp | val; - r8a66597_write(r8a66597, tmp, offset); + if (r8a66597->pdata->wr0_shorted_to_wr1) + r8a66597_bclr(r8a66597, MBW_16, ep->fifosel); } static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata) @@ -241,11 +250,6 @@ static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata) return clock; } -#define r8a66597_bclr(r8a66597, val, offset) \ - r8a66597_mdfy(r8a66597, 0, val, offset) -#define r8a66597_bset(r8a66597, val, offset) \ - r8a66597_mdfy(r8a66597, val, 0, offset) - #define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) #define enable_irq_ready(r8a66597, pipenum) \ -- cgit v0.10.2 From 12158f4280b4d42ef03b70a47d11b48dd8aad511 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 30 Sep 2011 20:07:30 +0900 Subject: usb: gadget: r8a66597-udc: use dev_*() instead of printk() This patch also fix the balance of braces. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 60c62e4..34abb12 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -105,13 +105,15 @@ static inline u16 control_reg_get_pid(struct r8a66597 *r8a66597, u16 pipenum) u16 pid = 0; unsigned long offset; - if (pipenum == 0) + if (pipenum == 0) { pid = r8a66597_read(r8a66597, DCPCTR) & PID; - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); pid = r8a66597_read(r8a66597, offset) & PID; - } else - printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } return pid; } @@ -121,13 +123,15 @@ static inline void control_reg_set_pid(struct r8a66597 *r8a66597, u16 pipenum, { unsigned long offset; - if (pipenum == 0) + if (pipenum == 0) { r8a66597_mdfy(r8a66597, pid, PID, DCPCTR); - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); r8a66597_mdfy(r8a66597, pid, PID, offset); - } else - printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } } static inline void pipe_start(struct r8a66597 *r8a66597, u16 pipenum) @@ -150,13 +154,15 @@ static inline u16 control_reg_get(struct r8a66597 *r8a66597, u16 pipenum) u16 ret = 0; unsigned long offset; - if (pipenum == 0) + if (pipenum == 0) { ret = r8a66597_read(r8a66597, DCPCTR); - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); ret = r8a66597_read(r8a66597, offset); - } else - printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } return ret; } @@ -167,13 +173,15 @@ static inline void control_reg_sqclr(struct r8a66597 *r8a66597, u16 pipenum) pipe_stop(r8a66597, pipenum); - if (pipenum == 0) + if (pipenum == 0) { r8a66597_bset(r8a66597, SQCLR, DCPCTR); - else if (pipenum < R8A66597_MAX_NUM_PIPE) { + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { offset = get_pipectr_addr(pipenum); r8a66597_bset(r8a66597, SQCLR, offset); - } else - printk(KERN_ERR "unexpect pipe num(%d)\n", pipenum); + } else { + dev_err(r8a66597_to_dev(r8a66597), "unexpect pipe num (%d)\n", + pipenum); + } } static inline int get_buffer_size(struct r8a66597 *r8a66597, u16 pipenum) @@ -287,17 +295,18 @@ static void pipe_buffer_release(struct r8a66597 *r8a66597, if (info->pipe == 0) return; - if (is_bulk_pipe(info->pipe)) + if (is_bulk_pipe(info->pipe)) { r8a66597->bulk--; - else if (is_interrupt_pipe(info->pipe)) + } else if (is_interrupt_pipe(info->pipe)) { r8a66597->interrupt--; - else if (is_isoc_pipe(info->pipe)) { + } else if (is_isoc_pipe(info->pipe)) { r8a66597->isochronous--; if (info->type == R8A66597_BULK) r8a66597->bulk--; - } else - printk(KERN_ERR "ep_release: unexpect pipenum (%d)\n", - info->pipe); + } else { + dev_err(r8a66597_to_dev(r8a66597), + "ep_release: unexpect pipenum (%d)\n", info->pipe); + } } static void pipe_initialize(struct r8a66597_ep *ep) @@ -371,7 +380,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, case USB_ENDPOINT_XFER_BULK: if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) { if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { - printk(KERN_ERR "bulk pipe is insufficient\n"); + dev_err(r8a66597_to_dev(r8a66597), + "bulk pipe is insufficient\n"); return -ENODEV; } else { info.pipe = R8A66597_BASE_PIPENUM_ISOC @@ -387,7 +397,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, break; case USB_ENDPOINT_XFER_INT: if (r8a66597->interrupt >= R8A66597_MAX_NUM_INT) { - printk(KERN_ERR "interrupt pipe is insufficient\n"); + dev_err(r8a66597_to_dev(r8a66597), + "interrupt pipe is insufficient\n"); return -ENODEV; } info.pipe = R8A66597_BASE_PIPENUM_INT + r8a66597->interrupt; @@ -396,7 +407,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, break; case USB_ENDPOINT_XFER_ISOC: if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { - printk(KERN_ERR "isochronous pipe is insufficient\n"); + dev_err(r8a66597_to_dev(r8a66597), + "isochronous pipe is insufficient\n"); return -ENODEV; } info.pipe = R8A66597_BASE_PIPENUM_ISOC + r8a66597->isochronous; @@ -404,7 +416,7 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, counter = &r8a66597->isochronous; break; default: - printk(KERN_ERR "unexpect xfer type\n"); + dev_err(r8a66597_to_dev(r8a66597), "unexpect xfer type\n"); return -EINVAL; } ep->type = info.type; @@ -419,7 +431,8 @@ static int alloc_pipe_config(struct r8a66597_ep *ep, ret = pipe_buffer_setting(r8a66597, &info); if (ret < 0) { - printk(KERN_ERR "pipe_buffer_setting fail\n"); + dev_err(r8a66597_to_dev(r8a66597), + "pipe_buffer_setting fail\n"); return ret; } @@ -554,7 +567,8 @@ static void start_ep0(struct r8a66597_ep *ep, struct r8a66597_request *req) control_end(ep->r8a66597, 0); break; default: - printk(KERN_ERR "start_ep0: unexpect ctsq(%x)\n", ctsq); + dev_err(r8a66597_to_dev(ep->r8a66597), + "start_ep0: unexpect ctsq(%x)\n", ctsq); break; } } @@ -708,7 +722,8 @@ static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req) do { tmp = r8a66597_read(r8a66597, ep->fifoctr); if (i++ > 100000) { - printk(KERN_ERR "pipe0 is busy. maybe cpu i/o bus" + dev_err(r8a66597_to_dev(r8a66597), + "pipe0 is busy. maybe cpu i/o bus " "conflict. please power off this controller."); return; } @@ -759,7 +774,8 @@ static void irq_packet_write(struct r8a66597_ep *ep, if (unlikely((tmp & FRDY) == 0)) { pipe_stop(r8a66597, pipenum); pipe_irq_disable(r8a66597, pipenum); - printk(KERN_ERR "write fifo not ready. pipnum=%d\n", pipenum); + dev_err(r8a66597_to_dev(r8a66597), + "write fifo not ready. pipnum=%d\n", pipenum); return; } @@ -809,7 +825,7 @@ static void irq_packet_read(struct r8a66597_ep *ep, req->req.status = -EPIPE; pipe_stop(r8a66597, pipenum); pipe_irq_disable(r8a66597, pipenum); - printk(KERN_ERR "read fifo not ready"); + dev_err(r8a66597_to_dev(r8a66597), "read fifo not ready"); return; } @@ -1085,7 +1101,7 @@ static void r8a66597_update_usb_speed(struct r8a66597 *r8a66597) break; default: r8a66597->gadget.speed = USB_SPEED_UNKNOWN; - printk(KERN_ERR "USB speed unknown\n"); + dev_err(r8a66597_to_dev(r8a66597), "USB speed unknown\n"); } } @@ -1148,7 +1164,8 @@ __acquires(r8a66597->lock) control_end(r8a66597, 0); break; default: - printk(KERN_ERR "ctrl_stage: unexpect ctsq(%x)\n", ctsq); + dev_err(r8a66597_to_dev(r8a66597), + "ctrl_stage: unexpect ctsq(%x)\n", ctsq); break; } } @@ -1448,13 +1465,15 @@ static int r8a66597_start(struct usb_gadget_driver *driver, retval = device_add(&r8a66597->gadget.dev); if (retval) { - printk(KERN_ERR "device_add error (%d)\n", retval); + dev_err(r8a66597_to_dev(r8a66597), "device_add error (%d)\n", + retval); goto error; } retval = bind(&r8a66597->gadget); if (retval) { - printk(KERN_ERR "bind to driver error (%d)\n", retval); + dev_err(r8a66597_to_dev(r8a66597), + "bind to driver error (%d)\n", retval); device_del(&r8a66597->gadget.dev); goto error; } @@ -1569,7 +1588,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENODEV; - printk(KERN_ERR "platform_get_resource error.\n"); + dev_err(&pdev->dev, "platform_get_resource error.\n"); goto clean_up; } @@ -1579,14 +1598,14 @@ static int __init r8a66597_probe(struct platform_device *pdev) if (irq < 0) { ret = -ENODEV; - printk(KERN_ERR "platform_get_irq error.\n"); + dev_err(&pdev->dev, "platform_get_irq error.\n"); goto clean_up; } reg = ioremap(res->start, resource_size(res)); if (reg == NULL) { ret = -ENOMEM; - printk(KERN_ERR "ioremap error.\n"); + dev_err(&pdev->dev, "ioremap error.\n"); goto clean_up; } @@ -1594,7 +1613,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL); if (r8a66597 == NULL) { ret = -ENOMEM; - printk(KERN_ERR "kzalloc error\n"); + dev_err(&pdev->dev, "kzalloc error\n"); goto clean_up; } @@ -1636,7 +1655,7 @@ static int __init r8a66597_probe(struct platform_device *pdev) ret = request_irq(irq, r8a66597_irq, IRQF_SHARED, udc_name, r8a66597); if (ret < 0) { - printk(KERN_ERR "request_irq error (%d)\n", ret); + dev_err(&pdev->dev, "request_irq error (%d)\n", ret); goto clean_up2; } diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h index f9ac2d7..832ee59 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -114,6 +114,7 @@ struct r8a66597 { #define gadget_to_r8a66597(_gadget) \ container_of(_gadget, struct r8a66597, gadget) #define r8a66597_to_gadget(r8a66597) (&r8a66597->gadget) +#define r8a66597_to_dev(r8a66597) (r8a66597->gadget.dev.parent) static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) { -- cgit v0.10.2 From b8a56e17e18cca2402b390c10b8d7f3cd0f6265b Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Fri, 30 Sep 2011 20:07:38 +0900 Subject: usb: gadget: r8a66597-udc: add support for SUDMAC SH7757 has a USB function with internal DMA controller (SUDMAC). This patch supports the SUDMAC. The SUDMAC is incompatible with general-purpose DMAC. So, it doesn't use dmaengine. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 34abb12..035879b 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -18,13 +18,14 @@ #include #include #include +#include #include #include #include "r8a66597-udc.h" -#define DRIVER_VERSION "2009-08-18" +#define DRIVER_VERSION "2011-09-26" static const char udc_name[] = "r8a66597_udc"; static const char *r8a66597_ep_name[] = { @@ -184,6 +185,54 @@ static inline void control_reg_sqclr(struct r8a66597 *r8a66597, u16 pipenum) } } +static void control_reg_sqset(struct r8a66597 *r8a66597, u16 pipenum) +{ + unsigned long offset; + + pipe_stop(r8a66597, pipenum); + + if (pipenum == 0) { + r8a66597_bset(r8a66597, SQSET, DCPCTR); + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + r8a66597_bset(r8a66597, SQSET, offset); + } else { + dev_err(r8a66597_to_dev(r8a66597), + "unexpect pipe num(%d)\n", pipenum); + } +} + +static u16 control_reg_sqmon(struct r8a66597 *r8a66597, u16 pipenum) +{ + unsigned long offset; + + if (pipenum == 0) { + return r8a66597_read(r8a66597, DCPCTR) & SQMON; + } else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + return r8a66597_read(r8a66597, offset) & SQMON; + } else { + dev_err(r8a66597_to_dev(r8a66597), + "unexpect pipe num(%d)\n", pipenum); + } + + return 0; +} + +static u16 save_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum) +{ + return control_reg_sqmon(r8a66597, pipenum); +} + +static void restore_usb_toggle(struct r8a66597 *r8a66597, u16 pipenum, + u16 toggle) +{ + if (toggle) + control_reg_sqset(r8a66597, pipenum); + else + control_reg_sqclr(r8a66597, pipenum); +} + static inline int get_buffer_size(struct r8a66597 *r8a66597, u16 pipenum) { u16 tmp; @@ -220,18 +269,51 @@ static inline unsigned short mbw_value(struct r8a66597 *r8a66597) return MBW_16; } +static void r8a66597_change_curpipe(struct r8a66597 *r8a66597, u16 pipenum, + u16 isel, u16 fifosel) +{ + u16 tmp, mask, loop; + int i = 0; + + if (!pipenum) { + mask = ISEL | CURPIPE; + loop = isel; + } else { + mask = CURPIPE; + loop = pipenum; + } + r8a66597_mdfy(r8a66597, loop, mask, fifosel); + + do { + tmp = r8a66597_read(r8a66597, fifosel); + if (i++ > 1000000) { + dev_err(r8a66597_to_dev(r8a66597), + "r8a66597: register%x, loop %x " + "is timeout\n", fifosel, loop); + break; + } + ndelay(1); + } while ((tmp & mask) != loop); +} + static inline void pipe_change(struct r8a66597 *r8a66597, u16 pipenum) { struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum]; if (ep->use_dma) - return; + r8a66597_bclr(r8a66597, DREQE, ep->fifosel); r8a66597_mdfy(r8a66597, pipenum, CURPIPE, ep->fifosel); ndelay(450); - r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel); + if (r8a66597_is_sudmac(r8a66597) && ep->use_dma) + r8a66597_bclr(r8a66597, mbw_value(r8a66597), ep->fifosel); + else + r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel); + + if (ep->use_dma) + r8a66597_bset(r8a66597, DREQE, ep->fifosel); } static int pipe_buffer_setting(struct r8a66597 *r8a66597, @@ -336,9 +418,15 @@ static void r8a66597_ep_setting(struct r8a66597 *r8a66597, ep->fifoaddr = CFIFO; ep->fifosel = CFIFOSEL; ep->fifoctr = CFIFOCTR; - ep->fifotrn = 0; ep->pipectr = get_pipectr_addr(pipenum); + if (is_bulk_pipe(pipenum) || is_isoc_pipe(pipenum)) { + ep->pipetre = get_pipetre_addr(pipenum); + ep->pipetrn = get_pipetrn_addr(pipenum); + } else { + ep->pipetre = 0; + ep->pipetrn = 0; + } ep->pipenum = pipenum; ep->ep.maxpacket = usb_endpoint_maxp(desc); r8a66597->pipenum2ep[pipenum] = ep; @@ -498,6 +586,124 @@ static void start_ep0_write(struct r8a66597_ep *ep, } } +static void disable_fifosel(struct r8a66597 *r8a66597, u16 pipenum, + u16 fifosel) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, fifosel) & CURPIPE; + if (tmp == pipenum) + r8a66597_change_curpipe(r8a66597, 0, 0, fifosel); +} + +static void change_bfre_mode(struct r8a66597 *r8a66597, u16 pipenum, + int enable) +{ + struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum]; + u16 tmp, toggle; + + /* check current BFRE bit */ + r8a66597_write(r8a66597, pipenum, PIPESEL); + tmp = r8a66597_read(r8a66597, PIPECFG) & R8A66597_BFRE; + if ((enable && tmp) || (!enable && !tmp)) + return; + + /* change BFRE bit */ + pipe_stop(r8a66597, pipenum); + disable_fifosel(r8a66597, pipenum, CFIFOSEL); + disable_fifosel(r8a66597, pipenum, D0FIFOSEL); + disable_fifosel(r8a66597, pipenum, D1FIFOSEL); + + toggle = save_usb_toggle(r8a66597, pipenum); + + r8a66597_write(r8a66597, pipenum, PIPESEL); + if (enable) + r8a66597_bset(r8a66597, R8A66597_BFRE, PIPECFG); + else + r8a66597_bclr(r8a66597, R8A66597_BFRE, PIPECFG); + + /* initialize for internal BFRE flag */ + r8a66597_bset(r8a66597, ACLRM, ep->pipectr); + r8a66597_bclr(r8a66597, ACLRM, ep->pipectr); + + restore_usb_toggle(r8a66597, pipenum, toggle); +} + +static int sudmac_alloc_channel(struct r8a66597 *r8a66597, + struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + struct r8a66597_dma *dma; + + if (!r8a66597_is_sudmac(r8a66597)) + return -ENODEV; + + /* Check transfer type */ + if (!is_bulk_pipe(ep->pipenum)) + return -EIO; + + if (r8a66597->dma.used) + return -EBUSY; + + /* set SUDMAC parameters */ + dma = &r8a66597->dma; + dma->used = 1; + if (ep->desc->bEndpointAddress & USB_DIR_IN) { + dma->dir = 1; + } else { + dma->dir = 0; + change_bfre_mode(r8a66597, ep->pipenum, 1); + } + + /* set r8a66597_ep paramters */ + ep->use_dma = 1; + ep->dma = dma; + ep->fifoaddr = D0FIFO; + ep->fifosel = D0FIFOSEL; + ep->fifoctr = D0FIFOCTR; + + /* dma mapping */ + req->req.dma = dma_map_single(r8a66597_to_dev(ep->r8a66597), + req->req.buf, req->req.length, + dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + return 0; +} + +static void sudmac_free_channel(struct r8a66597 *r8a66597, + struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + if (!r8a66597_is_sudmac(r8a66597)) + return; + + dma_unmap_single(r8a66597_to_dev(ep->r8a66597), + req->req.dma, req->req.length, + ep->dma->dir ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + r8a66597_bclr(r8a66597, DREQE, ep->fifosel); + r8a66597_change_curpipe(r8a66597, 0, 0, ep->fifosel); + + ep->dma->used = 0; + ep->use_dma = 0; + ep->fifoaddr = CFIFO; + ep->fifosel = CFIFOSEL; + ep->fifoctr = CFIFOCTR; +} + +static void sudmac_start(struct r8a66597 *r8a66597, struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + BUG_ON(req->req.length == 0); + + r8a66597_sudmac_write(r8a66597, LBA_WAIT, CH0CFG); + r8a66597_sudmac_write(r8a66597, req->req.dma, CH0BA); + r8a66597_sudmac_write(r8a66597, req->req.length, CH0BBC); + r8a66597_sudmac_write(r8a66597, CH0ENDE, DINTCTRL); + + r8a66597_sudmac_write(r8a66597, DEN, CH0DEN); +} + static void start_packet_write(struct r8a66597_ep *ep, struct r8a66597_request *req) { @@ -508,11 +714,29 @@ static void start_packet_write(struct r8a66597_ep *ep, disable_irq_empty(r8a66597, ep->pipenum); pipe_start(r8a66597, ep->pipenum); - tmp = r8a66597_read(r8a66597, ep->fifoctr); - if (unlikely((tmp & FRDY) == 0)) - pipe_irq_enable(r8a66597, ep->pipenum); - else - irq_packet_write(ep, req); + if (req->req.length == 0) { + transfer_complete(ep, req, 0); + } else { + r8a66597_write(r8a66597, ~(1 << ep->pipenum), BRDYSTS); + if (sudmac_alloc_channel(r8a66597, ep, req) < 0) { + /* PIO mode */ + pipe_change(r8a66597, ep->pipenum); + disable_irq_empty(r8a66597, ep->pipenum); + pipe_start(r8a66597, ep->pipenum); + tmp = r8a66597_read(r8a66597, ep->fifoctr); + if (unlikely((tmp & FRDY) == 0)) + pipe_irq_enable(r8a66597, ep->pipenum); + else + irq_packet_write(ep, req); + } else { + /* DMA mode */ + pipe_change(r8a66597, ep->pipenum); + disable_irq_nrdy(r8a66597, ep->pipenum); + pipe_start(r8a66597, ep->pipenum); + enable_irq_nrdy(r8a66597, ep->pipenum); + sudmac_start(r8a66597, ep, req); + } + } } static void start_packet_read(struct r8a66597_ep *ep, @@ -527,17 +751,26 @@ static void start_packet_read(struct r8a66597_ep *ep, pipe_start(r8a66597, pipenum); pipe_irq_enable(r8a66597, pipenum); } else { - if (ep->use_dma) { - r8a66597_bset(r8a66597, TRCLR, ep->fifosel); - pipe_change(r8a66597, pipenum); - r8a66597_bset(r8a66597, TRENB, ep->fifosel); + pipe_stop(r8a66597, pipenum); + if (ep->pipetre) { + enable_irq_nrdy(r8a66597, pipenum); + r8a66597_write(r8a66597, TRCLR, ep->pipetre); r8a66597_write(r8a66597, - (req->req.length + ep->ep.maxpacket - 1) - / ep->ep.maxpacket, - ep->fifotrn); + DIV_ROUND_UP(req->req.length, ep->ep.maxpacket), + ep->pipetrn); + r8a66597_bset(r8a66597, TRENB, ep->pipetre); + } + + if (sudmac_alloc_channel(r8a66597, ep, req) < 0) { + /* PIO mode */ + change_bfre_mode(r8a66597, ep->pipenum, 0); + pipe_start(r8a66597, pipenum); /* trigger once */ + pipe_irq_enable(r8a66597, pipenum); + } else { + pipe_change(r8a66597, pipenum); + sudmac_start(r8a66597, ep, req); + pipe_start(r8a66597, pipenum); /* trigger once */ } - pipe_start(r8a66597, pipenum); /* trigger once */ - pipe_irq_enable(r8a66597, pipenum); } } @@ -694,6 +927,9 @@ __acquires(r8a66597->lock) if (!list_empty(&ep->queue)) restart = 1; + if (ep->use_dma) + sudmac_free_channel(ep->r8a66597, ep, req); + spin_unlock(&ep->r8a66597->lock); req->req.complete(&ep->ep, &req->req); spin_lock(&ep->r8a66597->lock); @@ -1170,6 +1406,65 @@ __acquires(r8a66597->lock) } } +static void sudmac_finish(struct r8a66597 *r8a66597, struct r8a66597_ep *ep) +{ + u16 pipenum; + struct r8a66597_request *req; + u32 len; + int i = 0; + + pipenum = ep->pipenum; + pipe_change(r8a66597, pipenum); + + while (!(r8a66597_read(r8a66597, ep->fifoctr) & FRDY)) { + udelay(1); + if (unlikely(i++ >= 10000)) { /* timeout = 10 msec */ + dev_err(r8a66597_to_dev(r8a66597), + "%s: FRDY was not set (%d)\n", + __func__, pipenum); + return; + } + } + + r8a66597_bset(r8a66597, BCLR, ep->fifoctr); + req = get_request_from_ep(ep); + + /* prepare parameters */ + len = r8a66597_sudmac_read(r8a66597, CH0CBC); + req->req.actual += len; + + /* clear */ + r8a66597_sudmac_write(r8a66597, CH0STCLR, DSTSCLR); + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (len % ep->ep.maxpacket)) { + if (ep->dma->dir) { + disable_irq_ready(r8a66597, pipenum); + enable_irq_empty(r8a66597, pipenum); + } else { + /* Clear the interrupt flag for next transfer */ + r8a66597_write(r8a66597, ~(1 << pipenum), BRDYSTS); + transfer_complete(ep, req, 0); + } + } +} + +static void r8a66597_sudmac_irq(struct r8a66597 *r8a66597) +{ + u32 irqsts; + struct r8a66597_ep *ep; + u16 pipenum; + + irqsts = r8a66597_sudmac_read(r8a66597, DINTSTS); + if (irqsts & CH0ENDS) { + r8a66597_sudmac_write(r8a66597, CH0ENDC, DINTSTSCLR); + pipenum = (r8a66597_read(r8a66597, D0FIFOSEL) & CURPIPE); + ep = r8a66597->pipenum2ep[pipenum]; + sudmac_finish(r8a66597, ep); + } +} + static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) { struct r8a66597 *r8a66597 = _r8a66597; @@ -1180,6 +1475,9 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) u16 savepipe; u16 mask0; + if (r8a66597_is_sudmac(r8a66597)) + r8a66597_sudmac_irq(r8a66597); + spin_lock(&r8a66597->lock); intsts0 = r8a66597_read(r8a66597, INTSTS0); @@ -1556,6 +1854,8 @@ static int __exit r8a66597_remove(struct platform_device *pdev) usb_del_gadget_udc(&r8a66597->gadget); del_timer_sync(&r8a66597->timer); iounmap(r8a66597->reg); + if (r8a66597->pdata->sudmac) + iounmap(r8a66597->sudmac_reg); free_irq(platform_get_irq(pdev, 0), r8a66597); r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); #ifdef CONFIG_HAVE_CLK @@ -1572,6 +1872,26 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r) { } +static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597, + struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac"); + if (!res) { + dev_err(&pdev->dev, "platform_get_resource error(sudmac).\n"); + return -ENODEV; + } + + r8a66597->sudmac_reg = ioremap(res->start, resource_size(res)); + if (r8a66597->sudmac_reg == NULL) { + dev_err(&pdev->dev, "ioremap error(sudmac).\n"); + return -ENOMEM; + } + + return 0; +} + static int __init r8a66597_probe(struct platform_device *pdev) { #ifdef CONFIG_HAVE_CLK @@ -1649,6 +1969,11 @@ static int __init r8a66597_probe(struct platform_device *pdev) clk_enable(r8a66597->clk); } #endif + if (r8a66597->pdata->sudmac) { + ret = r8a66597_sudmac_ioremap(r8a66597, pdev); + if (ret < 0) + goto clean_up2; + } disable_controller(r8a66597); /* make sure controller is disabled */ @@ -1681,7 +2006,6 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->ep[0].fifoaddr = CFIFO; r8a66597->ep[0].fifosel = CFIFOSEL; r8a66597->ep[0].fifoctr = CFIFOCTR; - r8a66597->ep[0].fifotrn = 0; r8a66597->ep[0].pipectr = get_pipectr_addr(0); r8a66597->pipenum2ep[0] = &r8a66597->ep[0]; r8a66597->epaddr2ep[0] = &r8a66597->ep[0]; @@ -1714,6 +2038,8 @@ clean_up2: #endif clean_up: if (r8a66597) { + if (r8a66597->sudmac_reg) + iounmap(r8a66597->sudmac_reg); if (r8a66597->ep0_req) r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h index 832ee59..8e3de61 100644 --- a/drivers/usb/gadget/r8a66597-udc.h +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -43,6 +43,7 @@ ((pipenum >= R8A66597_BASE_PIPENUM_ISOC) && \ (pipenum < (R8A66597_BASE_PIPENUM_ISOC + R8A66597_MAX_NUM_ISOC))) +#define r8a66597_is_sudmac(r8a66597) (r8a66597->pdata->sudmac) struct r8a66597_pipe_info { u16 pipe; u16 epnum; @@ -60,6 +61,7 @@ struct r8a66597_request { struct r8a66597_ep { struct usb_ep ep; struct r8a66597 *r8a66597; + struct r8a66597_dma *dma; struct list_head queue; unsigned busy:1; @@ -75,13 +77,20 @@ struct r8a66597_ep { unsigned char fifoaddr; unsigned char fifosel; unsigned char fifoctr; - unsigned char fifotrn; unsigned char pipectr; + unsigned char pipetre; + unsigned char pipetrn; +}; + +struct r8a66597_dma { + unsigned used:1; + unsigned dir:1; /* 1 = IN(write), 0 = OUT(read) */ }; struct r8a66597 { spinlock_t lock; void __iomem *reg; + void __iomem *sudmac_reg; #ifdef CONFIG_HAVE_CLK struct clk *clk; @@ -94,6 +103,7 @@ struct r8a66597 { struct r8a66597_ep ep[R8A66597_MAX_NUM_PIPE]; struct r8a66597_ep *pipenum2ep[R8A66597_MAX_NUM_PIPE]; struct r8a66597_ep *epaddr2ep[16]; + struct r8a66597_dma dma; struct timer_list timer; struct usb_request *ep0_req; /* for internal request */ @@ -251,7 +261,21 @@ static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata) return clock; } +static inline u32 r8a66597_sudmac_read(struct r8a66597 *r8a66597, + unsigned long offset) +{ + return ioread32(r8a66597->sudmac_reg + offset); +} + +static inline void r8a66597_sudmac_write(struct r8a66597 *r8a66597, u32 val, + unsigned long offset) +{ + iowrite32(val, r8a66597->sudmac_reg + offset); +} + #define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) +#define get_pipetre_addr(pipenum) (PIPE1TRE + (pipenum - 1) * 4) +#define get_pipetrn_addr(pipenum) (PIPE1TRN + (pipenum - 1) * 4) #define enable_irq_ready(r8a66597, pipenum) \ enable_pipe_irq(r8a66597, pipenum, BRDYENB) diff --git a/include/linux/usb/r8a66597.h b/include/linux/usb/r8a66597.h index b6b8660..55805f9 100644 --- a/include/linux/usb/r8a66597.h +++ b/include/linux/usb/r8a66597.h @@ -48,6 +48,9 @@ struct r8a66597_platdata { /* (external controller only) set one = WR0_N shorted to WR1_N */ unsigned wr0_shorted_to_wr1:1; + + /* set one = using SUDMAC */ + unsigned sudmac:1; }; /* Register definitions */ @@ -417,5 +420,62 @@ struct r8a66597_platdata { #define USBSPD 0x00C0 #define RTPORT 0x0001 +/* SUDMAC registers */ +#define CH0CFG 0x00 +#define CH1CFG 0x04 +#define CH0BA 0x10 +#define CH1BA 0x14 +#define CH0BBC 0x18 +#define CH1BBC 0x1C +#define CH0CA 0x20 +#define CH1CA 0x24 +#define CH0CBC 0x28 +#define CH1CBC 0x2C +#define CH0DEN 0x30 +#define CH1DEN 0x34 +#define DSTSCLR 0x38 +#define DBUFCTRL 0x3C +#define DINTCTRL 0x40 +#define DINTSTS 0x44 +#define DINTSTSCLR 0x48 +#define CH0SHCTRL 0x50 +#define CH1SHCTRL 0x54 + +/* SUDMAC Configuration Registers */ +#define SENDBUFM 0x1000 /* b12: Transmit Buffer Mode */ +#define RCVENDM 0x0100 /* b8: Receive Data Transfer End Mode */ +#define LBA_WAIT 0x0030 /* b5-4: Local Bus Access Wait */ + +/* DMA Enable Registers */ +#define DEN 0x0001 /* b1: DMA Transfer Enable */ + +/* DMA Status Clear Register */ +#define CH1STCLR 0x0002 /* b2: Ch1 DMA Status Clear */ +#define CH0STCLR 0x0001 /* b1: Ch0 DMA Status Clear */ + +/* DMA Buffer Control Register */ +#define CH1BUFW 0x0200 /* b9: Ch1 DMA Buffer Data Transfer Enable */ +#define CH0BUFW 0x0100 /* b8: Ch0 DMA Buffer Data Transfer Enable */ +#define CH1BUFS 0x0002 /* b2: Ch1 DMA Buffer Data Status */ +#define CH0BUFS 0x0001 /* b1: Ch0 DMA Buffer Data Status */ + +/* DMA Interrupt Control Register */ +#define CH1ERRE 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Enable */ +#define CH0ERRE 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Enable */ +#define CH1ENDE 0x0002 /* b2: Ch1 DMA Transfer End Int Enable */ +#define CH0ENDE 0x0001 /* b1: Ch0 DMA Transfer End Int Enable */ + +/* DMA Interrupt Status Register */ +#define CH1ERRS 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Status */ +#define CH0ERRS 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Status */ +#define CH1ENDS 0x0002 /* b2: Ch1 DMA Transfer End Int Status */ +#define CH0ENDS 0x0001 /* b1: Ch0 DMA Transfer End Int Status */ + +/* DMA Interrupt Status Clear Register */ +#define CH1ERRC 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Stat Clear */ +#define CH0ERRC 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Stat Clear */ +#define CH1ENDC 0x0002 /* b2: Ch1 DMA Transfer End Int Stat Clear */ +#define CH0ENDC 0x0001 /* b1: Ch0 DMA Transfer End Int Stat Clear */ + #endif /* __LINUX_USB_R8A66597_H */ -- cgit v0.10.2 From 56526c0b8d752bf243a21722455807518925685f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 7 Oct 2011 13:57:42 -0700 Subject: usb gadget: fix langwell_udc.c build error Move function to fix langwell_udc.c build error: drivers/usb/gadget/langwell_udc.c: In function 'show_langwell_udc': drivers/usb/gadget/langwell_udc.c:1693:3: error: implicit declaration of function 'lpm_device_speed' drivers/usb/gadget/langwell_udc.c: At top level: drivers/usb/gadget/langwell_udc.c:2637:37: error: conflicting types for 'lpm_device_speed' drivers/usb/gadget/langwell_udc.c:1693:20: note: previous implicit declaration of 'lpm_device_speed' was here Signed-off-by: Randy Dunlap Acked-by: Felipe Balbi Cc: linux-usb@vger.kernel.org diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index c88158f..cb728d3 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -1562,6 +1562,20 @@ static ssize_t show_function(struct device *_dev, static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); +static inline enum usb_device_speed lpm_device_speed(u32 reg) +{ + switch (LPM_PSPD(reg)) { + case LPM_SPEED_HIGH: + return USB_SPEED_HIGH; + case LPM_SPEED_FULL: + return USB_SPEED_FULL; + case LPM_SPEED_LOW: + return USB_SPEED_LOW; + default: + return USB_SPEED_UNKNOWN; + } +} + /* device "langwell_udc" sysfs attribute file */ static ssize_t show_langwell_udc(struct device *_dev, struct device_attribute *attr, char *buf) @@ -2634,20 +2648,6 @@ done: dev_vdbg(&dev->pdev->dev, "<--- %s()\n", __func__); } -static inline enum usb_device_speed lpm_device_speed(u32 reg) -{ - switch (LPM_PSPD(reg)) { - case LPM_SPEED_HIGH: - return USB_SPEED_HIGH; - case LPM_SPEED_FULL: - return USB_SPEED_FULL; - case LPM_SPEED_LOW: - return USB_SPEED_LOW; - default: - return USB_SPEED_UNKNOWN; - } -} - /* port change detect interrupt handler */ static void handle_port_change(struct langwell_udc *dev) { -- cgit v0.10.2 From d5daf49b58661ec4af7a55b277176efbf945ca05 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 28 Sep 2011 16:41:32 +0200 Subject: USB: gadget: midi: add midi function driver This patch adds f_midi.c to implement a USB gadget function that works with the composite framework, so it can be combined with other USB functions. The code for the ALSA/MIDI logic was taken from the midi device gadget, other parts have been rewritten to benefit from the dynamic descriptor allocation features. This was successfully tested on an OMAP3 board. Signed-off-by: Daniel Mack Cc: Greg Kroah-Hartman Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c new file mode 100644 index 0000000..5064ea4 --- /dev/null +++ b/drivers/usb/gadget/f_midi.c @@ -0,0 +1,923 @@ +/* + * f_midi.c -- USB MIDI class function driver + * + * Copyright (C) 2006 Thumtronics Pty Ltd. + * Developed for Thumtronics by Grey Innovation + * Ben Williamson + * + * Rewritten for the composite framework + * Copyright (C) 2011 Daniel Mack + * + * Based on drivers/usb/gadget/f_audio.c, + * Copyright (C) 2008 Bryan Wu + * Copyright (C) 2008 Analog Devices, Inc + * + * and drivers/usb/gadget/midi.c, + * Copyright (C) 2006 Thumtronics Pty Ltd. + * Ben Williamson + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +MODULE_AUTHOR("Ben Williamson"); +MODULE_LICENSE("GPL v2"); + +static const char f_midi_shortname[] = "f_midi"; +static const char f_midi_longname[] = "MIDI Gadget"; + +/* + * This is a gadget, and the IN/OUT naming is from the host's perspective. + * USB -> OUT endpoint -> rawmidi + * USB <- IN endpoint <- rawmidi + */ +struct gmidi_in_port { + struct f_midi *midi; + int active; + uint8_t cable; /* cable number << 4 */ + uint8_t state; +#define STATE_UNKNOWN 0 +#define STATE_1PARAM 1 +#define STATE_2PARAM_1 2 +#define STATE_2PARAM_2 3 +#define STATE_SYSEX_0 4 +#define STATE_SYSEX_1 5 +#define STATE_SYSEX_2 6 + uint8_t data[2]; +}; + +struct f_midi { + struct usb_function func; + struct usb_gadget *gadget; + struct usb_ep *in_ep, *out_ep; + struct snd_card *card; + struct snd_rawmidi *rmidi; + struct snd_rawmidi_substream *in_substream; + struct snd_rawmidi_substream *out_substream; + + /* For the moment we only support one port in + each direction, but in_port is kept as a + separate struct so we can have more later. */ + struct gmidi_in_port in_port; + unsigned long out_triggered; + struct tasklet_struct tasklet; + int index; + char *id; + unsigned int buflen, qlen; +}; + +static inline struct f_midi *func_to_midi(struct usb_function *f) +{ + return container_of(f, struct f_midi, func); +} + +static void f_midi_transmit(struct f_midi *midi, struct usb_request *req); + +DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); +DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); +DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1); + +/* B.3.1 Standard AC Interface Descriptor */ +static struct usb_interface_descriptor ac_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + /* .bNumEndpoints = DYNAMIC */ + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + /* .iInterface = DYNAMIC */ +}; + +/* B.3.2 Class-Specific AC Interface Descriptor */ +static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = { + .bLength = UAC_DT_AC_HEADER_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, + .bcdADC = cpu_to_le16(0x0100), + .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)), + .bInCollection = 1, + /* .baInterfaceNr = DYNAMIC */ +}; + +/* B.4.1 Standard MS Interface Descriptor */ +static struct usb_interface_descriptor ms_interface_desc __initdata = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + /* .bInterfaceNumber = DYNAMIC */ + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, + /* .iInterface = DYNAMIC */ +}; + +/* B.4.2 Class-Specific MS Interface Descriptor */ +static struct usb_ms_header_descriptor ms_header_desc __initdata = { + .bLength = USB_DT_MS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_HEADER, + .bcdMSC = cpu_to_le16(0x0100), + .wTotalLength = cpu_to_le16(USB_DT_MS_HEADER_SIZE + + 2*USB_DT_MIDI_IN_SIZE + + 2*USB_DT_MIDI_OUT_SIZE(1)), +}; + +#define JACK_IN_EMB 1 +#define JACK_IN_EXT 2 +#define JACK_OUT_EMB 3 +#define JACK_OUT_EXT 4 + +/* B.4.3 MIDI IN Jack Descriptors */ +static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = { + .bLength = USB_DT_MIDI_IN_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, + .bJackType = USB_MS_EMBEDDED, + .bJackID = JACK_IN_EMB, +}; + +static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = { + .bLength = USB_DT_MIDI_IN_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, + .bJackType = USB_MS_EXTERNAL, + .bJackID = JACK_IN_EXT, +}; + +/* B.4.4 MIDI OUT Jack Descriptors */ +static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = { + .bLength = USB_DT_MIDI_OUT_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, + .bJackType = USB_MS_EMBEDDED, + .bJackID = JACK_OUT_EMB, + .bNrInputPins = 1, + .pins = { + [0] = { + .baSourceID = JACK_IN_EXT, + .baSourcePin = 1, + } + } +}; + +static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = { + .bLength = USB_DT_MIDI_OUT_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, + .bJackType = USB_MS_EXTERNAL, + .bJackID = JACK_OUT_EXT, + .bNrInputPins = 1, + .pins = { + [0] = { + .baSourceID = JACK_IN_EMB, + .baSourcePin = 1, + } + } +}; + +/* B.5.1 Standard Bulk OUT Endpoint Descriptor */ +static struct usb_endpoint_descriptor bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */ +static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = { + .bLength = USB_DT_MS_ENDPOINT_SIZE(1), + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = USB_MS_GENERAL, + .bNumEmbMIDIJack = 1, + .baAssocJackID = { + [0] = JACK_IN_EMB, + } +}; + +/* B.6.1 Standard Bulk IN Endpoint Descriptor */ +static struct usb_endpoint_descriptor bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ +static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = { + .bLength = USB_DT_MS_ENDPOINT_SIZE(1), + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = USB_MS_GENERAL, + .bNumEmbMIDIJack = 1, + .baAssocJackID = { + [0] = JACK_OUT_EMB, + } +}; + +static struct usb_descriptor_header *midi_function[] = { + (struct usb_descriptor_header *)&ac_interface_desc, + (struct usb_descriptor_header *)&ac_header_desc, + (struct usb_descriptor_header *)&ms_interface_desc, + + (struct usb_descriptor_header *)&ms_header_desc, + (struct usb_descriptor_header *)&jack_in_emb_desc, + (struct usb_descriptor_header *)&jack_in_ext_desc, + (struct usb_descriptor_header *)&jack_out_emb_desc, + (struct usb_descriptor_header *)&jack_out_ext_desc, + /* If you add more jacks, update ms_header_desc.wTotalLength */ + + (struct usb_descriptor_header *)&bulk_out_desc, + (struct usb_descriptor_header *)&ms_out_desc, + (struct usb_descriptor_header *)&bulk_in_desc, + (struct usb_descriptor_header *)&ms_in_desc, + NULL, +}; + +/* string IDs are assigned dynamically */ + +#define STRING_FUNC_IDX 0 + +static struct usb_string midi_string_defs[] = { + [STRING_FUNC_IDX].s = "MIDI function", + { } /* end of list */ +}; + +static struct usb_gadget_strings midi_stringtab = { + .language = 0x0409, /* en-us */ + .strings = midi_string_defs, +}; + +static struct usb_gadget_strings *midi_strings[] = { + &midi_stringtab, + NULL, +}; + +static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req) { + req->length = length; + req->buf = kmalloc(length, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + return req; +} + +static void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +static const uint8_t f_midi_cin_length[] = { + 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 +}; + +/* + * Receives a chunk of MIDI data. + */ +static void f_midi_read_data(struct usb_ep *ep, int cable, + uint8_t *data, int length) +{ + struct f_midi *midi = ep->driver_data; + /* cable is ignored, because for now we only have one. */ + + if (!midi->out_substream) + /* Nobody is listening - throw it on the floor. */ + return; + + if (!test_bit(midi->out_substream->number, &midi->out_triggered)) + return; + + snd_rawmidi_receive(midi->out_substream, data, length); +} + +static void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req) +{ + unsigned int i; + u8 *buf = req->buf; + + for (i = 0; i + 3 < req->actual; i += 4) + if (buf[i] != 0) { + int cable = buf[i] >> 4; + int length = f_midi_cin_length[buf[i] & 0x0f]; + f_midi_read_data(ep, cable, &buf[i + 1], length); + } +} + +static void +f_midi_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_midi *midi = ep->driver_data; + struct usb_composite_dev *cdev = midi->func.config->cdev; + int status = req->status; + + switch (status) { + case 0: /* normal completion */ + if (ep == midi->out_ep) { + /* We received stuff. req is queued again, below */ + f_midi_handle_out_data(ep, req); + } else if (ep == midi->in_ep) { + /* Our transmit completed. See if there's more to go. + * f_midi_transmit eats req, don't queue it again. */ + f_midi_transmit(midi, req); + return; + } + break; + + /* this endpoint is normally active while we're configured */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status, + req->actual, req->length); + if (ep == midi->out_ep) + f_midi_handle_out_data(ep, req); + + free_ep_req(ep, req); + return; + + case -EOVERFLOW: /* buffer overrun on read means that + * we didn't provide a big enough buffer. + */ + default: + DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); + break; + case -EREMOTEIO: /* short read */ + break; + } + + status = usb_ep_queue(ep, req, GFP_ATOMIC); + if (status) { + ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n", + ep->name, req->length, status); + usb_ep_set_halt(ep); + /* FIXME recover later ... somehow */ + } +} + +static int f_midi_start_ep(struct f_midi *midi, + struct usb_function *f, + struct usb_ep *ep) +{ + int err; + struct usb_composite_dev *cdev = f->config->cdev; + + if (ep->driver_data) + usb_ep_disable(ep); + + err = config_ep_by_speed(midi->gadget, f, ep); + if (err) { + ERROR(cdev, "can't configure %s: %d\n", ep->name, err); + return err; + } + + err = usb_ep_enable(ep); + if (err) { + ERROR(cdev, "can't start %s: %d\n", ep->name, err); + return err; + } + + ep->driver_data = midi; + + return 0; +} + +static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_midi *midi = func_to_midi(f); + struct usb_composite_dev *cdev = f->config->cdev; + unsigned i; + int err; + + err = f_midi_start_ep(midi, f, midi->in_ep); + if (err) + return err; + + err = f_midi_start_ep(midi, f, midi->out_ep); + if (err) + return err; + + if (midi->out_ep->driver_data) + usb_ep_disable(midi->out_ep); + + err = config_ep_by_speed(midi->gadget, f, midi->out_ep); + if (err) { + ERROR(cdev, "can't configure %s: %d\n", + midi->out_ep->name, err); + return err; + } + + err = usb_ep_enable(midi->out_ep); + if (err) { + ERROR(cdev, "can't start %s: %d\n", + midi->out_ep->name, err); + return err; + } + + midi->out_ep->driver_data = midi; + + /* allocate a bunch of read buffers and queue them all at once. */ + for (i = 0; i < midi->qlen && err == 0; i++) { + struct usb_request *req = + alloc_ep_req(midi->out_ep, midi->buflen); + if (req == NULL) + return -ENOMEM; + + req->complete = f_midi_complete; + err = usb_ep_queue(midi->out_ep, req, GFP_ATOMIC); + if (err) { + ERROR(midi, "%s queue req: %d\n", + midi->out_ep->name, err); + } + } + + return 0; +} + +static void f_midi_disable(struct usb_function *f) +{ + struct f_midi *midi = func_to_midi(f); + struct usb_composite_dev *cdev = f->config->cdev; + + DBG(cdev, "disable\n"); + + /* + * just disable endpoints, forcing completion of pending i/o. + * all our completion handlers free their requests in this case. + */ + usb_ep_disable(midi->in_ep); + usb_ep_disable(midi->out_ep); +} + +static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = f->config->cdev; + struct f_midi *midi = func_to_midi(f); + struct snd_card *card; + + DBG(cdev, "unbind\n"); + + /* just to be sure */ + f_midi_disable(f); + + card = midi->card; + midi->card = NULL; + if (card) + snd_card_free(card); + + kfree(midi->id); + midi->id = NULL; + + usb_free_descriptors(f->descriptors); + kfree(midi); +} + +static int f_midi_snd_free(struct snd_device *device) +{ + return 0; +} + +static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0, + uint8_t p1, uint8_t p2, uint8_t p3) +{ + unsigned length = req->length; + u8 *buf = (u8 *)req->buf + length; + + buf[0] = p0; + buf[1] = p1; + buf[2] = p2; + buf[3] = p3; + req->length = length + 4; +} + +/* + * Converts MIDI commands to USB MIDI packets. + */ +static void f_midi_transmit_byte(struct usb_request *req, + struct gmidi_in_port *port, uint8_t b) +{ + uint8_t p0 = port->cable; + + if (b >= 0xf8) { + f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0); + } else if (b >= 0xf0) { + switch (b) { + case 0xf0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case 0xf1: + case 0xf3: + port->data[0] = b; + port->state = STATE_1PARAM; + break; + case 0xf2: + port->data[0] = b; + port->state = STATE_2PARAM_1; + break; + case 0xf4: + case 0xf5: + port->state = STATE_UNKNOWN; + break; + case 0xf6: + f_midi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0); + port->state = STATE_UNKNOWN; + break; + case 0xf7: + switch (port->state) { + case STATE_SYSEX_0: + f_midi_transmit_packet(req, + p0 | 0x05, 0xf7, 0, 0); + break; + case STATE_SYSEX_1: + f_midi_transmit_packet(req, + p0 | 0x06, port->data[0], 0xf7, 0); + break; + case STATE_SYSEX_2: + f_midi_transmit_packet(req, + p0 | 0x07, port->data[0], + port->data[1], 0xf7); + break; + } + port->state = STATE_UNKNOWN; + break; + } + } else if (b >= 0x80) { + port->data[0] = b; + if (b >= 0xc0 && b <= 0xdf) + port->state = STATE_1PARAM; + else + port->state = STATE_2PARAM_1; + } else { /* b < 0x80 */ + switch (port->state) { + case STATE_1PARAM: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + } else { + p0 |= 0x02; + port->state = STATE_UNKNOWN; + } + f_midi_transmit_packet(req, p0, port->data[0], b, 0); + break; + case STATE_2PARAM_1: + port->data[1] = b; + port->state = STATE_2PARAM_2; + break; + case STATE_2PARAM_2: + if (port->data[0] < 0xf0) { + p0 |= port->data[0] >> 4; + port->state = STATE_2PARAM_1; + } else { + p0 |= 0x03; + port->state = STATE_UNKNOWN; + } + f_midi_transmit_packet(req, + p0, port->data[0], port->data[1], b); + break; + case STATE_SYSEX_0: + port->data[0] = b; + port->state = STATE_SYSEX_1; + break; + case STATE_SYSEX_1: + port->data[1] = b; + port->state = STATE_SYSEX_2; + break; + case STATE_SYSEX_2: + f_midi_transmit_packet(req, + p0 | 0x04, port->data[0], port->data[1], b); + port->state = STATE_SYSEX_0; + break; + } + } +} + +static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) +{ + struct usb_ep *ep = midi->in_ep; + struct gmidi_in_port *port = &midi->in_port; + + if (!ep) + return; + + if (!req) + req = alloc_ep_req(ep, midi->buflen); + + if (!req) { + ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n"); + return; + } + req->length = 0; + req->complete = f_midi_complete; + + if (port->active) { + while (req->length + 3 < midi->buflen) { + uint8_t b; + if (snd_rawmidi_transmit(midi->in_substream, &b, 1) != 1) { + port->active = 0; + break; + } + f_midi_transmit_byte(req, port, b); + } + } + + if (req->length > 0) + usb_ep_queue(ep, req, GFP_ATOMIC); + else + free_ep_req(ep, req); +} + +static void f_midi_in_tasklet(unsigned long data) +{ + struct f_midi *midi = (struct f_midi *) data; + f_midi_transmit(midi, NULL); +} + +static int f_midi_in_open(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + midi->in_substream = substream; + midi->in_port.state = STATE_UNKNOWN; + return 0; +} + +static int f_midi_in_close(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + return 0; +} + +static void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s() %d\n", __func__, up); + midi->in_port.active = up; + if (up) + tasklet_hi_schedule(&midi->tasklet); +} + +static int f_midi_out_open(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + midi->out_substream = substream; + return 0; +} + +static int f_midi_out_close(struct snd_rawmidi_substream *substream) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + return 0; +} + +static void f_midi_out_trigger(struct snd_rawmidi_substream *substream, int up) +{ + struct f_midi *midi = substream->rmidi->private_data; + + VDBG(midi, "%s()\n", __func__); + + if (up) + set_bit(substream->number, &midi->out_triggered); + else + clear_bit(substream->number, &midi->out_triggered); +} + +static struct snd_rawmidi_ops gmidi_in_ops = { + .open = f_midi_in_open, + .close = f_midi_in_close, + .trigger = f_midi_in_trigger, +}; + +static struct snd_rawmidi_ops gmidi_out_ops = { + .open = f_midi_out_open, + .close = f_midi_out_close, + .trigger = f_midi_out_trigger +}; + +/* register as a sound "card" */ +static int f_midi_register_card(struct f_midi *midi) +{ + struct snd_card *card; + struct snd_rawmidi *rmidi; + int err; + int out_ports = 1; + int in_ports = 1; + static struct snd_device_ops ops = { + .dev_free = f_midi_snd_free, + }; + + err = snd_card_create(midi->index, midi->id, THIS_MODULE, 0, &card); + if (err < 0) { + ERROR(midi, "snd_card_create() failed\n"); + goto fail; + } + midi->card = card; + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, midi, &ops); + if (err < 0) { + ERROR(midi, "snd_device_new() failed: error %d\n", err); + goto fail; + } + + strcpy(card->driver, f_midi_longname); + strcpy(card->longname, f_midi_longname); + strcpy(card->shortname, f_midi_shortname); + + /* Set up rawmidi */ + midi->in_port.midi = midi; + midi->in_port.active = 0; + snd_component_add(card, "MIDI"); + err = snd_rawmidi_new(card, card->longname, 0, + out_ports, in_ports, &rmidi); + if (err < 0) { + ERROR(midi, "snd_rawmidi_new() failed: error %d\n", err); + goto fail; + } + midi->rmidi = rmidi; + strcpy(rmidi->name, card->shortname); + rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | + SNDRV_RAWMIDI_INFO_INPUT | + SNDRV_RAWMIDI_INFO_DUPLEX; + rmidi->private_data = midi; + + /* + * Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT. + * It's an upside-down world being a gadget. + */ + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops); + snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops); + + snd_card_set_dev(card, &midi->gadget->dev); + + /* register it - we're ready to go */ + err = snd_card_register(card); + if (err < 0) { + ERROR(midi, "snd_card_register() failed\n"); + goto fail; + } + + VDBG(midi, "%s() finished ok\n", __func__); + return 0; + +fail: + if (midi->card) { + snd_card_free(midi->card); + midi->card = NULL; + } + return err; +} + +/* MIDI function driver setup/binding */ + +static int __init +f_midi_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_midi *midi = func_to_midi(f); + int status; + + /* maybe allocate device-global string ID */ + if (midi_string_defs[0].id == 0) { + status = usb_string_id(c->cdev); + if (status < 0) + goto fail; + midi_string_defs[0].id = status; + } + + /* We have two interfaces, AudioControl and MIDIStreaming */ + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ac_interface_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + ms_interface_desc.bInterfaceNumber = status; + ac_header_desc.baInterfaceNr[0] = status; + + status = -ENODEV; + + /* allocate instance-specific endpoints */ + midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc); + if (!midi->in_ep) + goto fail; + midi->in_ep->driver_data = cdev; /* claim */ + + midi->out_ep = usb_ep_autoconfig(cdev->gadget, &bulk_out_desc); + if (!midi->out_ep) + goto fail; + midi->out_ep->driver_data = cdev; /* claim */ + + /* + * support all relevant hardware speeds... we expect that when + * hardware is dual speed, all bulk-capable endpoints work at + * both speeds + */ + /* copy descriptors, and track endpoint copies */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + c->highspeed = true; + bulk_in_desc.wMaxPacketSize = cpu_to_le16(512); + bulk_out_desc.wMaxPacketSize = cpu_to_le16(512); + f->hs_descriptors = usb_copy_descriptors(midi_function); + } else { + f->descriptors = usb_copy_descriptors(midi_function); + } + + return 0; + +fail: + /* we might as well release our claims on endpoints */ + if (midi->out_ep) + midi->out_ep->driver_data = NULL; + if (midi->in_ep) + midi->in_ep->driver_data = NULL; + + ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); + + return status; +} + +/** + * f_midi_bind_config - add USB MIDI function to a configuration + * @c: the configuration to supcard the USB audio function + * @index: the soundcard index to use for the ALSA device creation + * @id: the soundcard id to use for the ALSA device creation + * @buflen: the buffer length to use + * @qlen the number of read requests to pre-allocate + * Context: single threaded during gadget setup + * + * Returns zero on success, else negative errno. + */ +int __init f_midi_bind_config(struct usb_configuration *c, + int index, char *id, + unsigned int buflen, + unsigned int qlen) +{ + struct f_midi *midi; + int status; + + /* allocate and initialize one new instance */ + midi = kzalloc(sizeof *midi, GFP_KERNEL); + if (!midi) { + status = -ENOMEM; + goto fail; + } + + midi->gadget = c->cdev->gadget; + tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); + + /* set up ALSA midi devices */ + status = f_midi_register_card(midi); + if (status < 0) + goto setup_fail; + + midi->func.name = "gmidi function"; + midi->func.strings = midi_strings; + midi->func.bind = f_midi_bind; + midi->func.unbind = f_midi_unbind; + midi->func.set_alt = f_midi_set_alt; + midi->func.disable = f_midi_disable; + + midi->id = kstrdup(id, GFP_KERNEL); + midi->index = index; + midi->buflen = buflen; + midi->qlen = qlen; + + status = usb_add_function(c, &midi->func); + if (status) + goto setup_fail; + + return 0; + +setup_fail: + kfree(midi); +fail: + return status; +} + -- cgit v0.10.2 From 2672eea5a38b35e6d28f851e990e110e217271eb Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 28 Sep 2011 16:41:33 +0200 Subject: USB: gadget: midi: refactor the device code Make use of the newly added MIDI function in f_midi.c and strip down the MIDI gadget code radically. Also use the generic framework function to avoid code duplication and rename some symbols to bring them in sync with other code in the gadget framework. [ balbi@ti.com : fix Section mismatch warnings. rebased on top of usb_speed_string() patch to avoid conflicts. ] Signed-off-by: Daniel Mack Cc: Greg Kroah-Hartman Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c index 5064ea4..84cbf85 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/f_midi.c @@ -225,7 +225,7 @@ static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = { } }; -static struct usb_descriptor_header *midi_function[] = { +static struct usb_descriptor_header *midi_function[] __initdata = { (struct usb_descriptor_header *)&ac_interface_desc, (struct usb_descriptor_header *)&ac_header_desc, (struct usb_descriptor_header *)&ms_interface_desc, diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index 893b967..e7c8c39 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -36,134 +36,35 @@ #include "gadget_chips.h" - -/* - * Kbuild is not very cooperative with respect to linking separately - * compiled library objects into one module. So for now we won't use - * separate compilation ... ensuring init/exit sections work to shrink - * the runtime footprint, and giving us at least some parts of what - * a "gcc --combine ... part1.c part2.c part3.c ... " build would. - */ +#include "composite.c" #include "usbstring.c" #include "config.c" #include "epautoconf.c" +#include "f_midi.c" /*-------------------------------------------------------------------------*/ - MODULE_AUTHOR("Ben Williamson"); MODULE_LICENSE("GPL v2"); -#define DRIVER_VERSION "25 Jul 2006" - static const char shortname[] = "g_midi"; static const char longname[] = "MIDI Gadget"; static int index = SNDRV_DEFAULT_IDX1; -static char *id = SNDRV_DEFAULT_STR1; - -module_param(index, int, 0444); +module_param(index, int, S_IRUGO); MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter."); -module_param(id, charp, 0444); -MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); - -/* Some systems will want different product identifiers published in the - * device descriptor, either numbers or strings or both. These string - * parameters are in UTF-8 (superset of ASCII's 7 bit characters). - */ - -static ushort idVendor; -module_param(idVendor, ushort, S_IRUGO); -MODULE_PARM_DESC(idVendor, "USB Vendor ID"); - -static ushort idProduct; -module_param(idProduct, ushort, S_IRUGO); -MODULE_PARM_DESC(idProduct, "USB Product ID"); - -static ushort bcdDevice; -module_param(bcdDevice, ushort, S_IRUGO); -MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); - -static char *iManufacturer; -module_param(iManufacturer, charp, S_IRUGO); -MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); - -static char *iProduct; -module_param(iProduct, charp, S_IRUGO); -MODULE_PARM_DESC(iProduct, "USB Product string"); - -static char *iSerialNumber; -module_param(iSerialNumber, charp, S_IRUGO); -MODULE_PARM_DESC(iSerialNumber, "SerialNumber"); - -/* - * this version autoconfigures as much as possible, - * which is reasonable for most "bulk-only" drivers. - */ -static const char *EP_IN_NAME; -static const char *EP_OUT_NAME; +static char *id = SNDRV_DEFAULT_STR1; +module_param(id, charp, S_IRUGO); +MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter."); -/* big enough to hold our biggest descriptor */ -#define USB_BUFSIZ 256 - - -/* This is a gadget, and the IN/OUT naming is from the host's perspective. - USB -> OUT endpoint -> rawmidi - USB <- IN endpoint <- rawmidi */ -struct gmidi_in_port { - struct gmidi_device* dev; - int active; - uint8_t cable; /* cable number << 4 */ - uint8_t state; -#define STATE_UNKNOWN 0 -#define STATE_1PARAM 1 -#define STATE_2PARAM_1 2 -#define STATE_2PARAM_2 3 -#define STATE_SYSEX_0 4 -#define STATE_SYSEX_1 5 -#define STATE_SYSEX_2 6 - uint8_t data[2]; -}; - -struct gmidi_device { - spinlock_t lock; - struct usb_gadget *gadget; - struct usb_request *req; /* for control responses */ - u8 config; - struct usb_ep *in_ep, *out_ep; - struct snd_card *card; - struct snd_rawmidi *rmidi; - struct snd_rawmidi_substream *in_substream; - struct snd_rawmidi_substream *out_substream; - - /* For the moment we only support one port in - each direction, but in_port is kept as a - separate struct so we can have more later. */ - struct gmidi_in_port in_port; - unsigned long out_triggered; - struct tasklet_struct tasklet; -}; - -static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req); - - -#define DBG(d, fmt, args...) \ - dev_dbg(&(d)->gadget->dev , fmt , ## args) -#define VDBG(d, fmt, args...) \ - dev_vdbg(&(d)->gadget->dev , fmt , ## args) -#define ERROR(d, fmt, args...) \ - dev_err(&(d)->gadget->dev , fmt , ## args) -#define INFO(d, fmt, args...) \ - dev_info(&(d)->gadget->dev , fmt , ## args) - - -static unsigned buflen = 256; -static unsigned qlen = 32; - +static unsigned int buflen = 256; module_param(buflen, uint, S_IRUGO); -module_param(qlen, uint, S_IRUGO); +MODULE_PARM_DESC(buflen, "MIDI buffer length"); +static unsigned int qlen = 32; +module_param(qlen, uint, S_IRUGO); +MODULE_PARM_DESC(qlen, "USB read request queue length"); /* Thanks to Grey Innovation for donating this product ID. * @@ -173,1140 +74,122 @@ module_param(qlen, uint, S_IRUGO); #define DRIVER_VENDOR_NUM 0x17b3 /* Grey Innovation */ #define DRIVER_PRODUCT_NUM 0x0004 /* Linux-USB "MIDI Gadget" */ +/* string IDs are assigned dynamically */ -/* - * DESCRIPTORS ... most are static, but strings and (full) - * configuration descriptors are built on demand. - */ - -#define STRING_MANUFACTURER 25 -#define STRING_PRODUCT 42 -#define STRING_SERIAL 101 -#define STRING_MIDI_GADGET 250 - -/* We only have the one configuration, it's number 1. */ -#define GMIDI_CONFIG 1 - -/* We have two interfaces- AudioControl and MIDIStreaming */ -#define GMIDI_AC_INTERFACE 0 -#define GMIDI_MS_INTERFACE 1 -#define GMIDI_NUM_INTERFACES 2 +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_DESCRIPTION_IDX 2 -DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); -DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); -DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1); - -/* B.1 Device Descriptor */ static struct usb_device_descriptor device_desc = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = cpu_to_le16(0x0200), + .bcdUSB = __constant_cpu_to_le16(0x0200), .bDeviceClass = USB_CLASS_PER_INTERFACE, - .idVendor = cpu_to_le16(DRIVER_VENDOR_NUM), - .idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM), - .iManufacturer = STRING_MANUFACTURER, - .iProduct = STRING_PRODUCT, + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM), + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ .bNumConfigurations = 1, }; -/* B.2 Configuration Descriptor */ -static struct usb_config_descriptor config_desc = { - .bLength = USB_DT_CONFIG_SIZE, - .bDescriptorType = USB_DT_CONFIG, - /* compute wTotalLength on the fly */ - .bNumInterfaces = GMIDI_NUM_INTERFACES, - .bConfigurationValue = GMIDI_CONFIG, - .iConfiguration = STRING_MIDI_GADGET, - /* - * FIXME: When embedding this driver in a device, - * these need to be set to reflect the actual - * power properties of the device. Is it selfpowered? - */ - .bmAttributes = USB_CONFIG_ATT_ONE, - .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, -}; - -/* B.3.1 Standard AC Interface Descriptor */ -static const struct usb_interface_descriptor ac_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GMIDI_AC_INTERFACE, - .bNumEndpoints = 0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - .iInterface = STRING_MIDI_GADGET, -}; - -/* B.3.2 Class-Specific AC Interface Descriptor */ -static const struct uac1_ac_header_descriptor_1 ac_header_desc = { - .bLength = UAC_DT_AC_HEADER_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_HEADER, - .bcdADC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(UAC_DT_AC_HEADER_SIZE(1)), - .bInCollection = 1, - .baInterfaceNr = { - [0] = GMIDI_MS_INTERFACE, - } -}; - -/* B.4.1 Standard MS Interface Descriptor */ -static const struct usb_interface_descriptor ms_interface_desc = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = GMIDI_MS_INTERFACE, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING, - .iInterface = STRING_MIDI_GADGET, -}; - -/* B.4.2 Class-Specific MS Interface Descriptor */ -static const struct usb_ms_header_descriptor ms_header_desc = { - .bLength = USB_DT_MS_HEADER_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_HEADER, - .bcdMSC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(USB_DT_MS_HEADER_SIZE - + 2*USB_DT_MIDI_IN_SIZE - + 2*USB_DT_MIDI_OUT_SIZE(1)), -}; - -#define JACK_IN_EMB 1 -#define JACK_IN_EXT 2 -#define JACK_OUT_EMB 3 -#define JACK_OUT_EXT 4 - -/* B.4.3 MIDI IN Jack Descriptors */ -static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = { - .bLength = USB_DT_MIDI_IN_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EMBEDDED, - .bJackID = JACK_IN_EMB, -}; - -static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = { - .bLength = USB_DT_MIDI_IN_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = JACK_IN_EXT, -}; - -/* B.4.4 MIDI OUT Jack Descriptors */ -static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = { - .bLength = USB_DT_MIDI_OUT_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, - .bJackType = USB_MS_EMBEDDED, - .bJackID = JACK_OUT_EMB, - .bNrInputPins = 1, - .pins = { - [0] = { - .baSourceID = JACK_IN_EXT, - .baSourcePin = 1, - } - } -}; - -static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = { - .bLength = USB_DT_MIDI_OUT_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = JACK_OUT_EXT, - .bNrInputPins = 1, - .pins = { - [0] = { - .baSourceID = JACK_IN_EMB, - .baSourcePin = 1, - } - } -}; - -/* B.5.1 Standard Bulk OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor bulk_out_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */ -static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = { - .bLength = USB_DT_MS_ENDPOINT_SIZE(1), - .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = USB_MS_GENERAL, - .bNumEmbMIDIJack = 1, - .baAssocJackID = { - [0] = JACK_IN_EMB, - } -}; - -/* B.6.1 Standard Bulk IN Endpoint Descriptor */ -static struct usb_endpoint_descriptor bulk_in_desc = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_IN, - .bmAttributes = USB_ENDPOINT_XFER_BULK, -}; - -/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ -static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = { - .bLength = USB_DT_MS_ENDPOINT_SIZE(1), - .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = USB_MS_GENERAL, - .bNumEmbMIDIJack = 1, - .baAssocJackID = { - [0] = JACK_OUT_EMB, - } -}; - -static const struct usb_descriptor_header *gmidi_function [] = { - (struct usb_descriptor_header *)&ac_interface_desc, - (struct usb_descriptor_header *)&ac_header_desc, - (struct usb_descriptor_header *)&ms_interface_desc, - - (struct usb_descriptor_header *)&ms_header_desc, - (struct usb_descriptor_header *)&jack_in_emb_desc, - (struct usb_descriptor_header *)&jack_in_ext_desc, - (struct usb_descriptor_header *)&jack_out_emb_desc, - (struct usb_descriptor_header *)&jack_out_ext_desc, - /* If you add more jacks, update ms_header_desc.wTotalLength */ - - (struct usb_descriptor_header *)&bulk_out_desc, - (struct usb_descriptor_header *)&ms_out_desc, - (struct usb_descriptor_header *)&bulk_in_desc, - (struct usb_descriptor_header *)&ms_in_desc, - NULL, -}; - -static char manufacturer[50]; -static char product_desc[40] = "MIDI Gadget"; -static char serial_number[20]; - -/* static strings, in UTF-8 */ -static struct usb_string strings [] = { - { STRING_MANUFACTURER, manufacturer, }, - { STRING_PRODUCT, product_desc, }, - { STRING_SERIAL, serial_number, }, - { STRING_MIDI_GADGET, longname, }, - { } /* end of list */ +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = "Grey Innovation", + [STRING_PRODUCT_IDX].s = "MIDI Gadget", + [STRING_DESCRIPTION_IDX].s = "MIDI", + { } /* end of list */ }; -static struct usb_gadget_strings stringtab = { +static struct usb_gadget_strings stringtab_dev = { .language = 0x0409, /* en-us */ - .strings = strings, + .strings = strings_dev, }; -static int config_buf(struct usb_gadget *gadget, - u8 *buf, u8 type, unsigned index) -{ - int len; - - /* only one configuration */ - if (index != 0) { - return -EINVAL; - } - len = usb_gadget_config_buf(&config_desc, - buf, USB_BUFSIZ, gmidi_function); - if (len < 0) { - return len; - } - ((struct usb_config_descriptor *)buf)->bDescriptorType = type; - return len; -} - -static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) -{ - struct usb_request *req; - - req = usb_ep_alloc_request(ep, GFP_ATOMIC); - if (req) { - req->length = length; - req->buf = kmalloc(length, GFP_ATOMIC); - if (!req->buf) { - usb_ep_free_request(ep, req); - req = NULL; - } - } - return req; -} - -static void free_ep_req(struct usb_ep *ep, struct usb_request *req) -{ - kfree(req->buf); - usb_ep_free_request(ep, req); -} - -static const uint8_t gmidi_cin_length[] = { - 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, }; -/* - * Receives a chunk of MIDI data. - */ -static void gmidi_read_data(struct usb_ep *ep, int cable, - uint8_t *data, int length) -{ - struct gmidi_device *dev = ep->driver_data; - /* cable is ignored, because for now we only have one. */ - - if (!dev->out_substream) { - /* Nobody is listening - throw it on the floor. */ - return; - } - if (!test_bit(dev->out_substream->number, &dev->out_triggered)) { - return; - } - snd_rawmidi_receive(dev->out_substream, data, length); -} - -static void gmidi_handle_out_data(struct usb_ep *ep, struct usb_request *req) -{ - unsigned i; - u8 *buf = req->buf; - - for (i = 0; i + 3 < req->actual; i += 4) { - if (buf[i] != 0) { - int cable = buf[i] >> 4; - int length = gmidi_cin_length[buf[i] & 0x0f]; - gmidi_read_data(ep, cable, &buf[i + 1], length); - } - } -} - -static void gmidi_complete(struct usb_ep *ep, struct usb_request *req) -{ - struct gmidi_device *dev = ep->driver_data; - int status = req->status; - - switch (status) { - case 0: /* normal completion */ - if (ep == dev->out_ep) { - /* we received stuff. - req is queued again, below */ - gmidi_handle_out_data(ep, req); - } else if (ep == dev->in_ep) { - /* our transmit completed. - see if there's more to go. - gmidi_transmit eats req, don't queue it again. */ - gmidi_transmit(dev, req); - return; - } - break; - - /* this endpoint is normally active while we're configured */ - case -ECONNABORTED: /* hardware forced ep reset */ - case -ECONNRESET: /* request dequeued */ - case -ESHUTDOWN: /* disconnect from host */ - VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status, - req->actual, req->length); - if (ep == dev->out_ep) { - gmidi_handle_out_data(ep, req); - } - free_ep_req(ep, req); - return; - - case -EOVERFLOW: /* buffer overrun on read means that - * we didn't provide a big enough - * buffer. - */ - default: - DBG(dev, "%s complete --> %d, %d/%d\n", ep->name, - status, req->actual, req->length); - break; - case -EREMOTEIO: /* short read */ - break; - } - - status = usb_ep_queue(ep, req, GFP_ATOMIC); - if (status) { - ERROR(dev, "kill %s: resubmit %d bytes --> %d\n", - ep->name, req->length, status); - usb_ep_set_halt(ep); - /* FIXME recover later ... somehow */ - } -} - -static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags) -{ - int err = 0; - struct usb_request *req; - struct usb_ep *ep; - unsigned i; - - dev->in_ep->desc = &bulk_in_desc; - err = usb_ep_enable(dev->in_ep); - if (err) { - ERROR(dev, "can't start %s: %d\n", dev->in_ep->name, err); - goto fail; - } - dev->in_ep->driver_data = dev; - - dev->out_ep->desc = &bulk_out_desc; - err = usb_ep_enable(dev->out_ep); - if (err) { - ERROR(dev, "can't start %s: %d\n", dev->out_ep->name, err); - goto fail; - } - dev->out_ep->driver_data = dev; - - /* allocate a bunch of read buffers and queue them all at once. */ - ep = dev->out_ep; - for (i = 0; i < qlen && err == 0; i++) { - req = alloc_ep_req(ep, buflen); - if (req) { - req->complete = gmidi_complete; - err = usb_ep_queue(ep, req, GFP_ATOMIC); - if (err) { - DBG(dev, "%s queue req: %d\n", ep->name, err); - } - } else { - err = -ENOMEM; - } - } -fail: - /* caller is responsible for cleanup on error */ - return err; -} - - -static void gmidi_reset_config(struct gmidi_device *dev) -{ - if (dev->config == 0) { - return; - } - - DBG(dev, "reset config\n"); - - /* just disable endpoints, forcing completion of pending i/o. - * all our completion handlers free their requests in this case. - */ - usb_ep_disable(dev->in_ep); - usb_ep_disable(dev->out_ep); - dev->config = 0; -} - -/* change our operational config. this code must agree with the code - * that returns config descriptors, and altsetting code. - * - * it's also responsible for power management interactions. some - * configurations might not work with our current power sources. - * - * note that some device controller hardware will constrain what this - * code can do, perhaps by disallowing more than one configuration or - * by limiting configuration choices (like the pxa2xx). - */ -static int -gmidi_set_config(struct gmidi_device *dev, unsigned number, gfp_t gfp_flags) -{ - int result = 0; - struct usb_gadget *gadget = dev->gadget; - -#if 0 - /* FIXME */ - /* Hacking this bit out fixes a bug where on receipt of two - USB_REQ_SET_CONFIGURATION messages, we end up with no - buffered OUT requests waiting for data. This is clearly - hiding a bug elsewhere, because if the config didn't - change then we really shouldn't do anything. */ - /* Having said that, when we do "change" from config 1 - to config 1, we at least gmidi_reset_config() which - clears out any requests on endpoints, so it's not like - we leak or anything. */ - if (number == dev->config) { - return 0; - } -#endif - - gmidi_reset_config(dev); - - switch (number) { - case GMIDI_CONFIG: - result = set_gmidi_config(dev, gfp_flags); - break; - default: - result = -EINVAL; - /* FALL THROUGH */ - case 0: - return result; - } - - if (!result && (!dev->in_ep || !dev->out_ep)) { - result = -ENODEV; - } - if (result) { - gmidi_reset_config(dev); - } else { - dev->config = number; - INFO(dev, "%s speed\n", usb_speed_string(gadget->speed)); - } - return result; -} - - -static void gmidi_setup_complete(struct usb_ep *ep, struct usb_request *req) +static int __exit midi_unbind(struct usb_composite_dev *dev) { - if (req->status || req->actual != req->length) { - DBG((struct gmidi_device *) ep->driver_data, - "setup complete --> %d, %d/%d\n", - req->status, req->actual, req->length); - } -} - -/* - * The setup() callback implements all the ep0 functionality that's - * not handled lower down, in hardware or the hardware driver (like - * device and endpoint feature flags, and their status). It's all - * housekeeping for the gadget function we're implementing. Most of - * the work is in config-specific setup. - */ -static int gmidi_setup(struct usb_gadget *gadget, - const struct usb_ctrlrequest *ctrl) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - struct usb_request *req = dev->req; - int value = -EOPNOTSUPP; - u16 w_index = le16_to_cpu(ctrl->wIndex); - u16 w_value = le16_to_cpu(ctrl->wValue); - u16 w_length = le16_to_cpu(ctrl->wLength); - - /* usually this stores reply data in the pre-allocated ep0 buffer, - * but config change events will reconfigure hardware. - */ - req->zero = 0; - switch (ctrl->bRequest) { - - case USB_REQ_GET_DESCRIPTOR: - if (ctrl->bRequestType != USB_DIR_IN) { - goto unknown; - } - switch (w_value >> 8) { - - case USB_DT_DEVICE: - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; - value = min(w_length, (u16) sizeof(device_desc)); - memcpy(req->buf, &device_desc, value); - break; - case USB_DT_CONFIG: - value = config_buf(gadget, req->buf, - w_value >> 8, - w_value & 0xff); - if (value >= 0) { - value = min(w_length, (u16)value); - } - break; - - case USB_DT_STRING: - /* wIndex == language code. - * this driver only handles one language, you can - * add string tables for other languages, using - * any UTF-8 characters - */ - value = usb_gadget_get_string(&stringtab, - w_value & 0xff, req->buf); - if (value >= 0) { - value = min(w_length, (u16)value); - } - break; - } - break; - - /* currently two configs, two speeds */ - case USB_REQ_SET_CONFIGURATION: - if (ctrl->bRequestType != 0) { - goto unknown; - } - if (gadget->a_hnp_support) { - DBG(dev, "HNP available\n"); - } else if (gadget->a_alt_hnp_support) { - DBG(dev, "HNP needs a different root port\n"); - } else { - VDBG(dev, "HNP inactive\n"); - } - spin_lock(&dev->lock); - value = gmidi_set_config(dev, w_value, GFP_ATOMIC); - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_CONFIGURATION: - if (ctrl->bRequestType != USB_DIR_IN) { - goto unknown; - } - *(u8 *)req->buf = dev->config; - value = min(w_length, (u16)1); - break; - - /* until we add altsetting support, or other interfaces, - * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) - * and already killed pending endpoint I/O. - */ - case USB_REQ_SET_INTERFACE: - if (ctrl->bRequestType != USB_RECIP_INTERFACE) { - goto unknown; - } - spin_lock(&dev->lock); - if (dev->config && w_index < GMIDI_NUM_INTERFACES - && w_value == 0) - { - u8 config = dev->config; - - /* resets interface configuration, forgets about - * previous transaction state (queued bufs, etc) - * and re-inits endpoint state (toggle etc) - * no response queued, just zero status == success. - * if we had more than one interface we couldn't - * use this "reset the config" shortcut. - */ - gmidi_reset_config(dev); - gmidi_set_config(dev, config, GFP_ATOMIC); - value = 0; - } - spin_unlock(&dev->lock); - break; - case USB_REQ_GET_INTERFACE: - if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) { - goto unknown; - } - if (!dev->config) { - break; - } - if (w_index >= GMIDI_NUM_INTERFACES) { - value = -EDOM; - break; - } - *(u8 *)req->buf = 0; - value = min(w_length, (u16)1); - break; - - default: -unknown: - VDBG(dev, "unknown control req%02x.%02x v%04x i%04x l%d\n", - ctrl->bRequestType, ctrl->bRequest, - w_value, w_index, w_length); - } - - /* respond with data transfer before status phase? */ - if (value >= 0) { - req->length = value; - req->zero = value < w_length; - value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); - if (value < 0) { - DBG(dev, "ep_queue --> %d\n", value); - req->status = 0; - gmidi_setup_complete(gadget->ep0, req); - } - } - - /* device either stalls (value < 0) or reports success */ - return value; -} - -static void gmidi_disconnect(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - unsigned long flags; - - spin_lock_irqsave(&dev->lock, flags); - gmidi_reset_config(dev); - - /* a more significant application might have some non-usb - * activities to quiesce here, saving resources like power - * or pushing the notification up a network stack. - */ - spin_unlock_irqrestore(&dev->lock, flags); - - /* next we may get setup() calls to enumerate new connections; - * or an unbind() during shutdown (including removing module). - */ -} - -static void /* __init_or_exit */ gmidi_unbind(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - struct snd_card *card; - - DBG(dev, "unbind\n"); - - card = dev->card; - dev->card = NULL; - if (card) { - snd_card_free(card); - } - - /* we've already been disconnected ... no i/o is active */ - if (dev->req) { - dev->req->length = USB_BUFSIZ; - free_ep_req(gadget->ep0, dev->req); - } - kfree(dev); - set_gadget_data(gadget, NULL); -} - -static int gmidi_snd_free(struct snd_device *device) -{ - return 0; -} - -static void gmidi_transmit_packet(struct usb_request *req, uint8_t p0, - uint8_t p1, uint8_t p2, uint8_t p3) -{ - unsigned length = req->length; - u8 *buf = (u8 *)req->buf + length; - - buf[0] = p0; - buf[1] = p1; - buf[2] = p2; - buf[3] = p3; - req->length = length + 4; -} - -/* - * Converts MIDI commands to USB MIDI packets. - */ -static void gmidi_transmit_byte(struct usb_request *req, - struct gmidi_in_port *port, uint8_t b) -{ - uint8_t p0 = port->cable; - - if (b >= 0xf8) { - gmidi_transmit_packet(req, p0 | 0x0f, b, 0, 0); - } else if (b >= 0xf0) { - switch (b) { - case 0xf0: - port->data[0] = b; - port->state = STATE_SYSEX_1; - break; - case 0xf1: - case 0xf3: - port->data[0] = b; - port->state = STATE_1PARAM; - break; - case 0xf2: - port->data[0] = b; - port->state = STATE_2PARAM_1; - break; - case 0xf4: - case 0xf5: - port->state = STATE_UNKNOWN; - break; - case 0xf6: - gmidi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0); - port->state = STATE_UNKNOWN; - break; - case 0xf7: - switch (port->state) { - case STATE_SYSEX_0: - gmidi_transmit_packet(req, - p0 | 0x05, 0xf7, 0, 0); - break; - case STATE_SYSEX_1: - gmidi_transmit_packet(req, - p0 | 0x06, port->data[0], 0xf7, 0); - break; - case STATE_SYSEX_2: - gmidi_transmit_packet(req, - p0 | 0x07, port->data[0], - port->data[1], 0xf7); - break; - } - port->state = STATE_UNKNOWN; - break; - } - } else if (b >= 0x80) { - port->data[0] = b; - if (b >= 0xc0 && b <= 0xdf) - port->state = STATE_1PARAM; - else - port->state = STATE_2PARAM_1; - } else { /* b < 0x80 */ - switch (port->state) { - case STATE_1PARAM: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - } else { - p0 |= 0x02; - port->state = STATE_UNKNOWN; - } - gmidi_transmit_packet(req, p0, port->data[0], b, 0); - break; - case STATE_2PARAM_1: - port->data[1] = b; - port->state = STATE_2PARAM_2; - break; - case STATE_2PARAM_2: - if (port->data[0] < 0xf0) { - p0 |= port->data[0] >> 4; - port->state = STATE_2PARAM_1; - } else { - p0 |= 0x03; - port->state = STATE_UNKNOWN; - } - gmidi_transmit_packet(req, - p0, port->data[0], port->data[1], b); - break; - case STATE_SYSEX_0: - port->data[0] = b; - port->state = STATE_SYSEX_1; - break; - case STATE_SYSEX_1: - port->data[1] = b; - port->state = STATE_SYSEX_2; - break; - case STATE_SYSEX_2: - gmidi_transmit_packet(req, - p0 | 0x04, port->data[0], port->data[1], b); - port->state = STATE_SYSEX_0; - break; - } - } -} - -static void gmidi_transmit(struct gmidi_device *dev, struct usb_request *req) -{ - struct usb_ep *ep = dev->in_ep; - struct gmidi_in_port *port = &dev->in_port; - - if (!ep) { - return; - } - if (!req) { - req = alloc_ep_req(ep, buflen); - } - if (!req) { - ERROR(dev, "gmidi_transmit: alloc_ep_request failed\n"); - return; - } - req->length = 0; - req->complete = gmidi_complete; - - if (port->active) { - while (req->length + 3 < buflen) { - uint8_t b; - if (snd_rawmidi_transmit(dev->in_substream, &b, 1) - != 1) - { - port->active = 0; - break; - } - gmidi_transmit_byte(req, port, b); - } - } - if (req->length > 0) { - usb_ep_queue(ep, req, GFP_ATOMIC); - } else { - free_ep_req(ep, req); - } -} - -static void gmidi_in_tasklet(unsigned long data) -{ - struct gmidi_device *dev = (struct gmidi_device *)data; - - gmidi_transmit(dev, NULL); -} - -static int gmidi_in_open(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_open\n"); - dev->in_substream = substream; - dev->in_port.state = STATE_UNKNOWN; - return 0; -} - -static int gmidi_in_close(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_close\n"); - return 0; -} - -static void gmidi_in_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_in_trigger %d\n", up); - dev->in_port.active = up; - if (up) { - tasklet_hi_schedule(&dev->tasklet); - } -} - -static int gmidi_out_open(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_open\n"); - dev->out_substream = substream; return 0; } -static int gmidi_out_close(struct snd_rawmidi_substream *substream) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_close\n"); - return 0; -} - -static void gmidi_out_trigger(struct snd_rawmidi_substream *substream, int up) -{ - struct gmidi_device *dev = substream->rmidi->private_data; - - VDBG(dev, "gmidi_out_trigger %d\n", up); - if (up) { - set_bit(substream->number, &dev->out_triggered); - } else { - clear_bit(substream->number, &dev->out_triggered); - } -} - -static struct snd_rawmidi_ops gmidi_in_ops = { - .open = gmidi_in_open, - .close = gmidi_in_close, - .trigger = gmidi_in_trigger, +static struct usb_configuration midi_config = { + .label = "MIDI Gadget", + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_ONE, + .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, }; -static struct snd_rawmidi_ops gmidi_out_ops = { - .open = gmidi_out_open, - .close = gmidi_out_close, - .trigger = gmidi_out_trigger -}; - -/* register as a sound "card" */ -static int gmidi_register_card(struct gmidi_device *dev) +static int __init midi_bind_config(struct usb_configuration *c) { - struct snd_card *card; - struct snd_rawmidi *rmidi; - int err; - int out_ports = 1; - int in_ports = 1; - static struct snd_device_ops ops = { - .dev_free = gmidi_snd_free, - }; - - err = snd_card_create(index, id, THIS_MODULE, 0, &card); - if (err < 0) { - ERROR(dev, "snd_card_create failed\n"); - goto fail; - } - dev->card = card; - - err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, dev, &ops); - if (err < 0) { - ERROR(dev, "snd_device_new failed: error %d\n", err); - goto fail; - } - - strcpy(card->driver, longname); - strcpy(card->longname, longname); - strcpy(card->shortname, shortname); - - /* Set up rawmidi */ - dev->in_port.dev = dev; - dev->in_port.active = 0; - snd_component_add(card, "MIDI"); - err = snd_rawmidi_new(card, "USB MIDI Gadget", 0, - out_ports, in_ports, &rmidi); - if (err < 0) { - ERROR(dev, "snd_rawmidi_new failed: error %d\n", err); - goto fail; - } - dev->rmidi = rmidi; - strcpy(rmidi->name, card->shortname); - rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | - SNDRV_RAWMIDI_INFO_DUPLEX; - rmidi->private_data = dev; - - /* Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT. - It's an upside-down world being a gadget. */ - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops); - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops); - - snd_card_set_dev(card, &dev->gadget->dev); - - /* register it - we're ready to go */ - err = snd_card_register(card); - if (err < 0) { - ERROR(dev, "snd_card_register failed\n"); - goto fail; - } - - VDBG(dev, "gmidi_register_card finished ok\n"); - return 0; - -fail: - if (dev->card) { - snd_card_free(dev->card); - dev->card = NULL; - } - return err; + return f_midi_bind_config(c, index, id, buflen, qlen); } -/* - * Creates an output endpoint, and initializes output ports. - */ -static int __init gmidi_bind(struct usb_gadget *gadget) +static int __init midi_bind(struct usb_composite_dev *cdev) { - struct gmidi_device *dev; - struct usb_ep *in_ep, *out_ep; - int gcnum, err = 0; + struct usb_gadget *gadget = cdev->gadget; + int gcnum, status; - /* support optional vendor/distro customization */ - if (idVendor) { - if (!idProduct) { - pr_err("idVendor needs idProduct!\n"); - return -ENODEV; - } - device_desc.idVendor = cpu_to_le16(idVendor); - device_desc.idProduct = cpu_to_le16(idProduct); - if (bcdDevice) { - device_desc.bcdDevice = cpu_to_le16(bcdDevice); - } - } - if (iManufacturer) { - strlcpy(manufacturer, iManufacturer, sizeof(manufacturer)); - } else { - snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s", - init_utsname()->sysname, init_utsname()->release, - gadget->name); - } - if (iProduct) { - strlcpy(product_desc, iProduct, sizeof(product_desc)); - } - if (iSerialNumber) { - device_desc.iSerialNumber = STRING_SERIAL, - strlcpy(serial_number, iSerialNumber, sizeof(serial_number)); - } + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; - /* Bulk-only drivers like this one SHOULD be able to - * autoconfigure on any sane usb controller driver, - * but there may also be important quirks to address. - */ - usb_ep_autoconfig_reset(gadget); - in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc); - if (!in_ep) { -autoconf_fail: - pr_err("%s: can't autoconfigure on %s\n", - shortname, gadget->name); - return -ENODEV; - } - EP_IN_NAME = in_ep->name; - in_ep->driver_data = in_ep; /* claim */ + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; - out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc); - if (!out_ep) { - goto autoconf_fail; - } - EP_OUT_NAME = out_ep->name; - out_ep->driver_data = out_ep; /* claim */ + /* config description */ + status = usb_string_id(cdev); + if (status < 0) + return status; + strings_dev[STRING_DESCRIPTION_IDX].id = status; + + midi_config.iConfiguration = status; gcnum = usb_gadget_controller_number(gadget); - if (gcnum >= 0) { - device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); - } else { + if (gcnum < 0) { /* gmidi is so simple (no altsettings) that * it SHOULD NOT have problems with bulk-capable hardware. * so warn about unrecognized controllers, don't panic. */ pr_warning("%s: controller '%s' not recognized\n", - shortname, gadget->name); + __func__, gadget->name); device_desc.bcdDevice = cpu_to_le16(0x9999); + } else { + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); } + status = usb_add_config(cdev, &midi_config, midi_bind_config); + if (status < 0) + return status; - /* ok, we made sense of the hardware ... */ - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - return -ENOMEM; - } - spin_lock_init(&dev->lock); - dev->gadget = gadget; - dev->in_ep = in_ep; - dev->out_ep = out_ep; - set_gadget_data(gadget, dev); - tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev); - - /* preallocate control response and buffer */ - dev->req = alloc_ep_req(gadget->ep0, USB_BUFSIZ); - if (!dev->req) { - err = -ENOMEM; - goto fail; - } - - dev->req->complete = gmidi_setup_complete; - - gadget->ep0->driver_data = dev; - - INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname); - INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, - EP_OUT_NAME, EP_IN_NAME); - - /* register as an ALSA sound card */ - err = gmidi_register_card(dev); - if (err < 0) { - goto fail; - } - - VDBG(dev, "gmidi_bind finished ok\n"); + pr_info("%s\n", longname); return 0; - -fail: - gmidi_unbind(gadget); - return err; -} - - -static void gmidi_suspend(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - - if (gadget->speed == USB_SPEED_UNKNOWN) { - return; - } - - DBG(dev, "suspend\n"); -} - -static void gmidi_resume(struct usb_gadget *gadget) -{ - struct gmidi_device *dev = get_gadget_data(gadget); - - DBG(dev, "resume\n"); } - -static struct usb_gadget_driver gmidi_driver = { - .speed = USB_SPEED_FULL, - .function = (char *)longname, - .unbind = gmidi_unbind, - - .setup = gmidi_setup, - .disconnect = gmidi_disconnect, - - .suspend = gmidi_suspend, - .resume = gmidi_resume, - - .driver = { - .name = (char *)shortname, - .owner = THIS_MODULE, - }, +static struct usb_composite_driver midi_driver = { + .name = (char *) longname, + .dev = &device_desc, + .strings = dev_strings, + .max_speed = USB_SPEED_HIGH, + .unbind = __exit_p(midi_unbind), }; -static int __init gmidi_init(void) +static int __init midi_init(void) { - return usb_gadget_probe_driver(&gmidi_driver, gmidi_bind); + return usb_composite_probe(&midi_driver, midi_bind); } -module_init(gmidi_init); +module_init(midi_init); -static void __exit gmidi_cleanup(void) +static void __exit midi_cleanup(void) { - usb_gadget_unregister_driver(&gmidi_driver); + usb_composite_unregister(&midi_driver); } -module_exit(gmidi_cleanup); +module_exit(midi_cleanup); -- cgit v0.10.2 From c8933c3f79568263c90a46f06cf80419e6c63c97 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 28 Sep 2011 16:41:34 +0200 Subject: USB: gadget: f_midi: allow a dynamic number of input and output ports The code in the MIDI gadget was already sort of prepared for multi-port configuration, so the streaming logic itself didn't need much tweaking. However, the descriptors change when the number of ports do, and so some rework of the the preparation algorithms were necessary. Successfully tested on Linux and Max OS X hosts for both input and output streams. Signed-off-by: Daniel Mack Cc: Greg Kroah-Hartman Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c index 84cbf85..44858dc 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/f_midi.c @@ -40,6 +40,13 @@ static const char f_midi_shortname[] = "f_midi"; static const char f_midi_longname[] = "MIDI Gadget"; /* + * We can only handle 16 cables on one single endpoint, as cable numbers are + * stored in 4-bit fields. And as the interface currently only holds one + * single endpoint, this is the maximum number of ports we can allow. + */ +#define MAX_PORTS 16 + +/* * This is a gadget, and the IN/OUT naming is from the host's perspective. * USB -> OUT endpoint -> rawmidi * USB <- IN endpoint <- rawmidi @@ -47,7 +54,7 @@ static const char f_midi_longname[] = "MIDI Gadget"; struct gmidi_in_port { struct f_midi *midi; int active; - uint8_t cable; /* cable number << 4 */ + uint8_t cable; uint8_t state; #define STATE_UNKNOWN 0 #define STATE_1PARAM 1 @@ -65,15 +72,15 @@ struct f_midi { struct usb_ep *in_ep, *out_ep; struct snd_card *card; struct snd_rawmidi *rmidi; - struct snd_rawmidi_substream *in_substream; - struct snd_rawmidi_substream *out_substream; - /* For the moment we only support one port in - each direction, but in_port is kept as a - separate struct so we can have more later. */ - struct gmidi_in_port in_port; + struct snd_rawmidi_substream *in_substream[MAX_PORTS]; + struct snd_rawmidi_substream *out_substream[MAX_PORTS]; + struct gmidi_in_port *in_port[MAX_PORTS]; + unsigned long out_triggered; struct tasklet_struct tasklet; + unsigned int in_ports; + unsigned int out_ports; int index; char *id; unsigned int buflen, qlen; @@ -88,7 +95,8 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req); DECLARE_UAC_AC_HEADER_DESCRIPTOR(1); DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1); -DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1); +DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(16); +DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16); /* B.3.1 Standard AC Interface Descriptor */ static struct usb_interface_descriptor ac_interface_desc __initdata = { @@ -129,62 +137,27 @@ static struct usb_ms_header_descriptor ms_header_desc __initdata = { .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_HEADER, .bcdMSC = cpu_to_le16(0x0100), - .wTotalLength = cpu_to_le16(USB_DT_MS_HEADER_SIZE - + 2*USB_DT_MIDI_IN_SIZE - + 2*USB_DT_MIDI_OUT_SIZE(1)), -}; - -#define JACK_IN_EMB 1 -#define JACK_IN_EXT 2 -#define JACK_OUT_EMB 3 -#define JACK_OUT_EXT 4 - -/* B.4.3 MIDI IN Jack Descriptors */ -static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = { - .bLength = USB_DT_MIDI_IN_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EMBEDDED, - .bJackID = JACK_IN_EMB, + /* .wTotalLength = DYNAMIC */ }; -static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = { - .bLength = USB_DT_MIDI_IN_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = JACK_IN_EXT, +/* B.4.3 Embedded MIDI IN Jack Descriptor */ +static struct usb_midi_in_jack_descriptor jack_in_emb_desc = { + .bLength = USB_DT_MIDI_IN_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = USB_MS_MIDI_IN_JACK, + .bJackType = USB_MS_EMBEDDED, + /* .bJackID = DYNAMIC */ }; -/* B.4.4 MIDI OUT Jack Descriptors */ -static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = { - .bLength = USB_DT_MIDI_OUT_SIZE(1), +/* B.4.4 Embedded MIDI OUT Jack Descriptor */ +static struct usb_midi_out_jack_descriptor_16 jack_out_emb_desc = { + /* .bLength = DYNAMIC */ .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, .bJackType = USB_MS_EMBEDDED, - .bJackID = JACK_OUT_EMB, - .bNrInputPins = 1, - .pins = { - [0] = { - .baSourceID = JACK_IN_EXT, - .baSourcePin = 1, - } - } -}; - -static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = { - .bLength = USB_DT_MIDI_OUT_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK, - .bJackType = USB_MS_EXTERNAL, - .bJackID = JACK_OUT_EXT, - .bNrInputPins = 1, - .pins = { - [0] = { - .baSourceID = JACK_IN_EMB, - .baSourcePin = 1, - } - } + /* .bJackID = DYNAMIC */ + /* .bNrInputPins = DYNAMIC */ + /* .pins = DYNAMIC */ }; /* B.5.1 Standard Bulk OUT Endpoint Descriptor */ @@ -196,14 +169,12 @@ static struct usb_endpoint_descriptor bulk_out_desc = { }; /* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */ -static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = { - .bLength = USB_DT_MS_ENDPOINT_SIZE(1), +static struct usb_ms_endpoint_descriptor_16 ms_out_desc = { + /* .bLength = DYNAMIC */ .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = USB_MS_GENERAL, - .bNumEmbMIDIJack = 1, - .baAssocJackID = { - [0] = JACK_IN_EMB, - } + /* .bNumEmbMIDIJack = DYNAMIC */ + /* .baAssocJackID = DYNAMIC */ }; /* B.6.1 Standard Bulk IN Endpoint Descriptor */ @@ -215,33 +186,12 @@ static struct usb_endpoint_descriptor bulk_in_desc = { }; /* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */ -static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = { - .bLength = USB_DT_MS_ENDPOINT_SIZE(1), +static struct usb_ms_endpoint_descriptor_16 ms_in_desc = { + /* .bLength = DYNAMIC */ .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = USB_MS_GENERAL, - .bNumEmbMIDIJack = 1, - .baAssocJackID = { - [0] = JACK_OUT_EMB, - } -}; - -static struct usb_descriptor_header *midi_function[] __initdata = { - (struct usb_descriptor_header *)&ac_interface_desc, - (struct usb_descriptor_header *)&ac_header_desc, - (struct usb_descriptor_header *)&ms_interface_desc, - - (struct usb_descriptor_header *)&ms_header_desc, - (struct usb_descriptor_header *)&jack_in_emb_desc, - (struct usb_descriptor_header *)&jack_in_ext_desc, - (struct usb_descriptor_header *)&jack_out_emb_desc, - (struct usb_descriptor_header *)&jack_out_ext_desc, - /* If you add more jacks, update ms_header_desc.wTotalLength */ - - (struct usb_descriptor_header *)&bulk_out_desc, - (struct usb_descriptor_header *)&ms_out_desc, - (struct usb_descriptor_header *)&bulk_in_desc, - (struct usb_descriptor_header *)&ms_in_desc, - NULL, + /* .bNumEmbMIDIJack = DYNAMIC */ + /* .baAssocJackID = DYNAMIC */ }; /* string IDs are assigned dynamically */ @@ -296,16 +246,16 @@ static void f_midi_read_data(struct usb_ep *ep, int cable, uint8_t *data, int length) { struct f_midi *midi = ep->driver_data; - /* cable is ignored, because for now we only have one. */ + struct snd_rawmidi_substream *substream = midi->out_substream[cable]; - if (!midi->out_substream) + if (!substream) /* Nobody is listening - throw it on the floor. */ return; - if (!test_bit(midi->out_substream->number, &midi->out_triggered)) + if (!test_bit(cable, &midi->out_triggered)) return; - snd_rawmidi_receive(midi->out_substream, data, length); + snd_rawmidi_receive(substream, data, length); } static void f_midi_handle_out_data(struct usb_ep *ep, struct usb_request *req) @@ -514,7 +464,7 @@ static void f_midi_transmit_packet(struct usb_request *req, uint8_t p0, static void f_midi_transmit_byte(struct usb_request *req, struct gmidi_in_port *port, uint8_t b) { - uint8_t p0 = port->cable; + uint8_t p0 = port->cable << 4; if (b >= 0xf8) { f_midi_transmit_packet(req, p0 | 0x0f, b, 0, 0); @@ -612,7 +562,7 @@ static void f_midi_transmit_byte(struct usb_request *req, static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) { struct usb_ep *ep = midi->in_ep; - struct gmidi_in_port *port = &midi->in_port; + int i; if (!ep) return; @@ -627,10 +577,16 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) req->length = 0; req->complete = f_midi_complete; - if (port->active) { + for (i = 0; i < MAX_PORTS; i++) { + struct gmidi_in_port *port = midi->in_port[i]; + struct snd_rawmidi_substream *substream = midi->in_substream[i]; + + if (!port || !port->active || !substream) + continue; + while (req->length + 3 < midi->buflen) { uint8_t b; - if (snd_rawmidi_transmit(midi->in_substream, &b, 1) != 1) { + if (snd_rawmidi_transmit(substream, &b, 1) != 1) { port->active = 0; break; } @@ -654,9 +610,12 @@ static int f_midi_in_open(struct snd_rawmidi_substream *substream) { struct f_midi *midi = substream->rmidi->private_data; + if (!midi->in_port[substream->number]) + return -EINVAL; + VDBG(midi, "%s()\n", __func__); - midi->in_substream = substream; - midi->in_port.state = STATE_UNKNOWN; + midi->in_substream[substream->number] = substream; + midi->in_port[substream->number]->state = STATE_UNKNOWN; return 0; } @@ -672,8 +631,11 @@ static void f_midi_in_trigger(struct snd_rawmidi_substream *substream, int up) { struct f_midi *midi = substream->rmidi->private_data; + if (!midi->in_port[substream->number]) + return; + VDBG(midi, "%s() %d\n", __func__, up); - midi->in_port.active = up; + midi->in_port[substream->number]->active = up; if (up) tasklet_hi_schedule(&midi->tasklet); } @@ -682,8 +644,11 @@ static int f_midi_out_open(struct snd_rawmidi_substream *substream) { struct f_midi *midi = substream->rmidi->private_data; + if (!substream->number >= MAX_PORTS) + return -EINVAL; + VDBG(midi, "%s()\n", __func__); - midi->out_substream = substream; + midi->out_substream[substream->number] = substream; return 0; } @@ -725,8 +690,6 @@ static int f_midi_register_card(struct f_midi *midi) struct snd_card *card; struct snd_rawmidi *rmidi; int err; - int out_ports = 1; - int in_ports = 1; static struct snd_device_ops ops = { .dev_free = f_midi_snd_free, }; @@ -749,11 +712,9 @@ static int f_midi_register_card(struct f_midi *midi) strcpy(card->shortname, f_midi_shortname); /* Set up rawmidi */ - midi->in_port.midi = midi; - midi->in_port.active = 0; snd_component_add(card, "MIDI"); err = snd_rawmidi_new(card, card->longname, 0, - out_ports, in_ports, &rmidi); + midi->out_ports, midi->in_ports, &rmidi); if (err < 0) { ERROR(midi, "snd_rawmidi_new() failed: error %d\n", err); goto fail; @@ -797,9 +758,12 @@ fail: static int __init f_midi_bind(struct usb_configuration *c, struct usb_function *f) { + struct usb_descriptor_header *midi_function[(MAX_PORTS * 2) + 12]; + struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS]; + struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc[MAX_PORTS]; struct usb_composite_dev *cdev = c->cdev; struct f_midi *midi = func_to_midi(f); - int status; + int status, n, jack = 1, i = 0; /* maybe allocate device-global string ID */ if (midi_string_defs[0].id == 0) { @@ -835,6 +799,94 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f) midi->out_ep->driver_data = cdev; /* claim */ /* + * construct the function's descriptor set. As the number of + * input and output MIDI ports is configurable, we have to do + * it that way. + */ + + /* add the headers - these are always the same */ + midi_function[i++] = (struct usb_descriptor_header *) &ac_interface_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ac_header_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_interface_desc; + + /* calculate the header's wTotalLength */ + n = USB_DT_MS_HEADER_SIZE + + (1 + midi->in_ports) * USB_DT_MIDI_IN_SIZE + + (1 + midi->out_ports) * USB_DT_MIDI_OUT_SIZE(1); + ms_header_desc.wTotalLength = cpu_to_le16(n); + + midi_function[i++] = (struct usb_descriptor_header *) &ms_header_desc; + + /* we have one embedded IN jack */ + jack_in_emb_desc.bJackID = jack++; + midi_function[i++] = (struct usb_descriptor_header *) &jack_in_emb_desc; + + /* and a dynamic amount of external IN jacks */ + for (n = 0; n < midi->in_ports; n++) { + struct usb_midi_in_jack_descriptor *ext = &jack_in_ext_desc[n]; + + ext->bLength = USB_DT_MIDI_IN_SIZE; + ext->bDescriptorType = USB_DT_CS_INTERFACE; + ext->bDescriptorSubtype = USB_MS_MIDI_IN_JACK; + ext->bJackType = USB_MS_EXTERNAL; + ext->bJackID = jack++; + ext->iJack = 0; + + midi_function[i++] = (struct usb_descriptor_header *) ext; + } + + /* one embedded OUT jack ... */ + jack_out_emb_desc.bLength = USB_DT_MIDI_OUT_SIZE(midi->in_ports); + jack_out_emb_desc.bJackID = jack++; + jack_out_emb_desc.bNrInputPins = midi->in_ports; + /* ... which referencess all external IN jacks */ + for (n = 0; n < midi->in_ports; n++) { + jack_out_emb_desc.pins[n].baSourceID = jack_in_ext_desc[n].bJackID; + jack_out_emb_desc.pins[n].baSourcePin = 1; + } + + midi_function[i++] = (struct usb_descriptor_header *) &jack_out_emb_desc; + + /* and multiple external OUT jacks ... */ + for (n = 0; n < midi->out_ports; n++) { + struct usb_midi_out_jack_descriptor_1 *ext = &jack_out_ext_desc[n]; + int m; + + ext->bLength = USB_DT_MIDI_OUT_SIZE(1); + ext->bDescriptorType = USB_DT_CS_INTERFACE; + ext->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK; + ext->bJackType = USB_MS_EXTERNAL; + ext->bJackID = jack++; + ext->bNrInputPins = 1; + ext->iJack = 0; + /* ... which all reference the same embedded IN jack */ + for (m = 0; m < midi->out_ports; m++) { + ext->pins[m].baSourceID = jack_in_emb_desc.bJackID; + ext->pins[m].baSourcePin = 1; + } + + midi_function[i++] = (struct usb_descriptor_header *) ext; + } + + /* configure the endpoint descriptors ... */ + ms_out_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->in_ports); + ms_out_desc.bNumEmbMIDIJack = midi->in_ports; + for (n = 0; n < midi->in_ports; n++) + ms_out_desc.baAssocJackID[n] = jack_in_emb_desc.bJackID; + + ms_in_desc.bLength = USB_DT_MS_ENDPOINT_SIZE(midi->out_ports); + ms_in_desc.bNumEmbMIDIJack = midi->out_ports; + for (n = 0; n < midi->out_ports; n++) + ms_in_desc.baAssocJackID[n] = jack_out_emb_desc.bJackID; + + /* ... and add them to the list */ + midi_function[i++] = (struct usb_descriptor_header *) &bulk_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_out_desc; + midi_function[i++] = (struct usb_descriptor_header *) &bulk_in_desc; + midi_function[i++] = (struct usb_descriptor_header *) &ms_in_desc; + midi_function[i++] = NULL; + + /* * support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds @@ -876,11 +928,17 @@ fail: */ int __init f_midi_bind_config(struct usb_configuration *c, int index, char *id, + unsigned int in_ports, + unsigned int out_ports, unsigned int buflen, unsigned int qlen) { struct f_midi *midi; - int status; + int status, i; + + /* sanity check */ + if (in_ports > MAX_PORTS || out_ports > MAX_PORTS) + return -EINVAL; /* allocate and initialize one new instance */ midi = kzalloc(sizeof *midi, GFP_KERNEL); @@ -889,10 +947,25 @@ int __init f_midi_bind_config(struct usb_configuration *c, goto fail; } + for (i = 0; i < in_ports; i++) { + struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL); + if (!port) { + status = -ENOMEM; + goto fail; + } + + port->midi = midi; + port->active = 0; + port->cable = i; + midi->in_port[i] = port; + } + midi->gadget = c->cdev->gadget; tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi); /* set up ALSA midi devices */ + midi->in_ports = in_ports; + midi->out_ports = out_ports; status = f_midi_register_card(midi); if (status < 0) goto setup_fail; diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c index e7c8c39..8fcde37 100644 --- a/drivers/usb/gadget/gmidi.c +++ b/drivers/usb/gadget/gmidi.c @@ -66,6 +66,14 @@ static unsigned int qlen = 32; module_param(qlen, uint, S_IRUGO); MODULE_PARM_DESC(qlen, "USB read request queue length"); +static unsigned int in_ports = 1; +module_param(in_ports, uint, S_IRUGO); +MODULE_PARM_DESC(in_ports, "Number of MIDI input ports"); + +static unsigned int out_ports = 1; +module_param(out_ports, uint, S_IRUGO); +MODULE_PARM_DESC(out_ports, "Number of MIDI output ports"); + /* Thanks to Grey Innovation for donating this product ID. * * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! @@ -124,7 +132,9 @@ static struct usb_configuration midi_config = { static int __init midi_bind_config(struct usb_configuration *c) { - return f_midi_bind_config(c, index, id, buflen, qlen); + return f_midi_bind_config(c, index, id, + in_ports, out_ports, + buflen, qlen); } static int __init midi_bind(struct usb_composite_dev *cdev) -- cgit v0.10.2 From fe69676530f182bbb1fe0edd027e18751a1a2595 Mon Sep 17 00:00:00 2001 From: Paul Zimmerman Date: Fri, 30 Sep 2011 15:26:06 -0700 Subject: usb: gadget: storage: fix mass storage gadgets to work with Synopsys UDC The Synopsys USB device controller requires all OUT transfer request lengths to be aligned to max packet size. The mass storage gadgets do not meet this requirement for Super Speed. The gadgets already have a function which performs this alignment for CBW packets, so use it for data packets too. The alternative would be to implement bounce buffers in the DWC3 driver, but that could have a significant impact on performance. This version is based upon a more-correct patch written by Alan Stern. Signed-off-by: Paul Zimmerman Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 7569414..927ee88 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -958,9 +958,7 @@ static int do_write(struct fsg_common *common) * equal to the buffer size, which is divisible by * the bulk-out maxpacket size. */ - bh->outreq->length = amount; - bh->bulk_out_intended_length = amount; - bh->outreq->short_not_ok = 1; + set_bulk_out_req_length(common, bh, amount); if (!start_out_transfer(common, bh)) /* Dunno what to do if common->fsg is NULL */ return -EIO; @@ -995,6 +993,11 @@ static int do_write(struct fsg_common *common) amount = curlun->file_length - file_offset; } + /* Don't accept excess data. The spec doesn't say + * what to do in this case. We'll ignore the error. + */ + amount = min(amount, bh->bulk_out_intended_length); + /* Don't write a partial block */ amount = round_down(amount, curlun->blksize); if (amount == 0) @@ -1034,7 +1037,7 @@ static int do_write(struct fsg_common *common) empty_write: /* Did the host decide to stop early? */ - if (bh->outreq->actual != bh->outreq->length) { + if (bh->outreq->actual < bh->bulk_out_intended_length) { common->short_packet_received = 1; break; } @@ -1592,7 +1595,7 @@ static int throw_away_data(struct fsg_common *common) common->next_buffhd_to_drain = bh->next; /* A short packet or an error ends everything */ - if (bh->outreq->actual != bh->outreq->length || + if (bh->outreq->actual < bh->bulk_out_intended_length || bh->outreq->status != 0) { raise_exception(common, FSG_STATE_ABORT_BULK_OUT); @@ -1612,9 +1615,7 @@ static int throw_away_data(struct fsg_common *common) * equal to the buffer size, which is divisible by * the bulk-out maxpacket size. */ - bh->outreq->length = amount; - bh->bulk_out_intended_length = amount; - bh->outreq->short_not_ok = 1; + set_bulk_out_req_length(common, bh, amount); if (!start_out_transfer(common, bh)) /* Dunno what to do if common->fsg is NULL */ return -EIO; @@ -2289,7 +2290,6 @@ static int get_next_command(struct fsg_common *common) /* Queue a request to read a Bulk-only CBW */ set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); - bh->outreq->short_not_ok = 1; if (!start_out_transfer(common, bh)) /* Don't know what to do if common->fsg is NULL */ return -EIO; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 12ac30b..a230009 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -1335,9 +1335,7 @@ static int do_write(struct fsg_dev *fsg) * equal to the buffer size, which is divisible by * the bulk-out maxpacket size. */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; - bh->outreq->short_not_ok = 1; + set_bulk_out_req_length(fsg, bh, amount); start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); fsg->next_buffhd_to_fill = bh->next; @@ -1370,6 +1368,11 @@ static int do_write(struct fsg_dev *fsg) amount = curlun->file_length - file_offset; } + /* Don't accept excess data. The spec doesn't say + * what to do in this case. We'll ignore the error. + */ + amount = min(amount, bh->bulk_out_intended_length); + /* Don't write a partial block */ amount = round_down(amount, curlun->blksize); if (amount == 0) @@ -1409,7 +1412,7 @@ static int do_write(struct fsg_dev *fsg) empty_write: /* Did the host decide to stop early? */ - if (bh->outreq->actual != bh->outreq->length) { + if (bh->outreq->actual < bh->bulk_out_intended_length) { fsg->short_packet_received = 1; break; } @@ -1953,7 +1956,7 @@ static int throw_away_data(struct fsg_dev *fsg) fsg->next_buffhd_to_drain = bh->next; /* A short packet or an error ends everything */ - if (bh->outreq->actual != bh->outreq->length || + if (bh->outreq->actual < bh->bulk_out_intended_length || bh->outreq->status != 0) { raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); return -EINTR; @@ -1971,9 +1974,7 @@ static int throw_away_data(struct fsg_dev *fsg) * equal to the buffer size, which is divisible by * the bulk-out maxpacket size. */ - bh->outreq->length = bh->bulk_out_intended_length = - amount; - bh->outreq->short_not_ok = 1; + set_bulk_out_req_length(fsg, bh, amount); start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); fsg->next_buffhd_to_fill = bh->next; @@ -2652,7 +2653,6 @@ static int get_next_command(struct fsg_dev *fsg) /* Queue a request to read a Bulk-only CBW */ set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); - bh->outreq->short_not_ok = 1; start_transfer(fsg, fsg->bulk_out, bh->outreq, &bh->outreq_busy, &bh->state); -- cgit v0.10.2 From d5546b380583f009bc18504e7f946fa16d861381 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 1 Oct 2011 22:03:33 +0200 Subject: usb: gadget: pxa25x: is_vbus_present is gone A recent commit obsoleted the is_vbus_present function, so we must not use it any more. Signed-off-by: Arnd Bergmann Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h index 4634cd3..8eaf4e4 100644 --- a/drivers/usb/gadget/pxa25x_udc.h +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -152,8 +152,6 @@ static struct pxa25x_udc *the_controller; #ifdef DEBUG -static int is_vbus_present(void); - static const char *state_name[] = { "EP0_IDLE", "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", @@ -205,8 +203,7 @@ dump_state(struct pxa25x_udc *dev) u32 tmp; unsigned i; - DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", - is_vbus_present() ? "host " : "disconnected", + DMSG("%s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", state_name[dev->ep0state], UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); dump_udccr("udccr"); @@ -223,9 +220,6 @@ dump_state(struct pxa25x_udc *dev) } else DMSG("ep0 driver '%s'\n", dev->driver->driver.name); - if (!is_vbus_present()) - return; - dump_udccs0 ("udccs0"); DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", dev->stats.write.bytes, dev->stats.write.ops, -- cgit v0.10.2 From 1a8060d9e5c0d9488f7f1aa830ed37adcaba6adb Mon Sep 17 00:00:00 2001 From: Robert Schwebel Date: Thu, 6 Oct 2011 21:36:26 +0200 Subject: usb: gadget: at91_udc: change french comments to english While being there, change C++ style comments to /* */. Signed-off-by: Robert Schwebel Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index e10cf3c..8efe0fa 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -450,7 +450,7 @@ static void nuke(struct at91_ep *ep, int status) { struct at91_request *req; - // terminer chaque requete dans la queue + /* terminate any request in the queue */ ep->stopped = 1; if (list_empty(&ep->queue)) return; @@ -778,7 +778,7 @@ static const struct usb_ep_ops at91_ep_ops = { .queue = at91_ep_queue, .dequeue = at91_ep_dequeue, .set_halt = at91_ep_set_halt, - // there's only imprecise fifo status reporting + /* there's only imprecise fifo status reporting */ }; /*-------------------------------------------------------------------------*/ @@ -836,7 +836,7 @@ static void udc_reinit(struct at91_udc *udc) ep->fifo_bank = 0; ep->ep.maxpacket = ep->maxpacket; ep->creg = (void __iomem *) udc->udp_baseaddr + AT91_UDP_CSR(i); - // initialiser une queue par endpoint + /* initialize one queue per endpoint */ INIT_LIST_HEAD(&ep->queue); } } @@ -942,7 +942,7 @@ static int at91_vbus_session(struct usb_gadget *gadget, int is_active) struct at91_udc *udc = to_udc(gadget); unsigned long flags; - // VDBG("vbus %s\n", is_active ? "on" : "off"); + /* VDBG("vbus %s\n", is_active ? "on" : "off"); */ spin_lock_irqsave(&udc->lock, flags); udc->vbus = (is_active != 0); if (udc->driver) @@ -993,7 +993,7 @@ static const struct usb_gadget_ops at91_udc_ops = { * VBUS-powered devices may also also want to support bigger * power budgets after an appropriate SET_CONFIGURATION. */ - // .vbus_power = at91_vbus_power, + /* .vbus_power = at91_vbus_power, */ }; /*-------------------------------------------------------------------------*/ @@ -1062,7 +1062,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr) ep->is_in = 0; } } else { - // REVISIT this happens sometimes under load; why?? + /* REVISIT this happens sometimes under load; why?? */ ERR("SETUP len %d, csr %08x\n", rxcount, csr); status = -EINVAL; } @@ -1441,7 +1441,7 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXSUSP); at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXSUSP); - // VDBG("bus suspend\n"); + /* VDBG("bus suspend\n"); */ if (udc->suspended) continue; udc->suspended = 1; @@ -1463,7 +1463,7 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc) at91_udp_write(udc, AT91_UDP_IDR, AT91_UDP_RXRSM); at91_udp_write(udc, AT91_UDP_IER, AT91_UDP_RXSUSP); at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_RXRSM); - // VDBG("bus resume\n"); + /* VDBG("bus resume\n"); */ if (!udc->suspended) continue; udc->suspended = 0; -- cgit v0.10.2 From 5bc639fc55b51f828b20747eb1c48a669053b1e5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 8 Oct 2011 13:08:01 +0300 Subject: usb: gadget: langwell: don't use the_controller when it's not needed in most cases, we can fetch our structure by calling dev_get_drvdata(). Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index cb728d3..444bd11 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -1551,7 +1551,7 @@ static void stop_activity(struct langwell_udc *dev, static ssize_t show_function(struct device *_dev, struct device_attribute *attr, char *buf) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); if (!dev->driver || !dev->driver->function || strlen(dev->driver->function) > PAGE_SIZE) @@ -1580,7 +1580,7 @@ static inline enum usb_device_speed lpm_device_speed(u32 reg) static ssize_t show_langwell_udc(struct device *_dev, struct device_attribute *attr, char *buf) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); struct langwell_request *req; struct langwell_ep *ep = NULL; char *next; @@ -1812,7 +1812,7 @@ static DEVICE_ATTR(langwell_udc, S_IRUGO, show_langwell_udc, NULL); static ssize_t store_remote_wakeup(struct device *_dev, struct device_attribute *attr, const char *buf, size_t count) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); unsigned long flags; ssize_t rc = count; @@ -2973,7 +2973,7 @@ static irqreturn_t langwell_irq(int irq, void *_dev) /* release device structure */ static void gadget_release(struct device *_dev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = dev_get_drvdata(_dev); dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -3031,7 +3031,7 @@ static void sram_deinit(struct langwell_udc *dev) /* tear down the binding between this driver and the pci device */ static void langwell_udc_remove(struct pci_dev *pdev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); DECLARE_COMPLETION(done); @@ -3378,7 +3378,7 @@ error: /* device controller suspend */ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -3426,7 +3426,7 @@ static int langwell_udc_suspend(struct pci_dev *pdev, pm_message_t state) /* device controller resume */ static int langwell_udc_resume(struct pci_dev *pdev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); size_t size; dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); @@ -3508,7 +3508,7 @@ static int langwell_udc_resume(struct pci_dev *pdev) /* pci driver shutdown */ static void langwell_udc_shutdown(struct pci_dev *pdev) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = pci_get_drvdata(pdev); u32 usbmode; dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); -- cgit v0.10.2 From 2c7f098916e3eb5473724d0653eca9f446997915 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Sat, 8 Oct 2011 13:17:34 +0300 Subject: usb: gadget: langwell: convert to new style with this patch we can finally remove the global "the_controller" pointer. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/langwell_udc.c b/drivers/usb/gadget/langwell_udc.c index 444bd11..7a13363 100644 --- a/drivers/usb/gadget/langwell_udc.c +++ b/drivers/usb/gadget/langwell_udc.c @@ -50,9 +50,6 @@ static const char driver_name[] = "langwell_udc"; static const char driver_desc[] = DRIVER_DESC; -/* controller device global variable */ -static struct langwell_udc *the_controller; - /* for endpoint 0 operations */ static const struct usb_endpoint_descriptor langwell_ep0_desc = { @@ -1311,9 +1308,12 @@ static int langwell_pullup(struct usb_gadget *_gadget, int is_on) return 0; } -static int langwell_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); -static int langwell_stop(struct usb_gadget_driver *driver); +static int langwell_start(struct usb_gadget *g, + struct usb_gadget_driver *driver); + +static int langwell_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver); + /* device controller usb_gadget_ops structure */ static const struct usb_gadget_ops langwell_ops = { @@ -1335,8 +1335,8 @@ static const struct usb_gadget_ops langwell_ops = { /* D+ pullup, software-controlled connect/disconnect to USB host */ .pullup = langwell_pullup, - .start = langwell_start, - .stop = langwell_stop, + .udc_start = langwell_start, + .udc_stop = langwell_stop, }; @@ -1848,21 +1848,15 @@ static DEVICE_ATTR(remote_wakeup, S_IWUSR, NULL, store_remote_wakeup); * the driver might get unbound. */ -static int langwell_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int langwell_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = gadget_to_langwell(g); unsigned long flags; int retval; - if (!dev) - return -ENODEV; - dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); - if (dev->driver) - return -EBUSY; - spin_lock_irqsave(&dev->lock, flags); /* hook up the driver ... */ @@ -1872,18 +1866,9 @@ static int langwell_start(struct usb_gadget_driver *driver, spin_unlock_irqrestore(&dev->lock, flags); - retval = bind(&dev->gadget); - if (retval) { - dev_dbg(&dev->pdev->dev, "bind to driver %s --> %d\n", - driver->driver.name, retval); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } - retval = device_create_file(&dev->pdev->dev, &dev_attr_function); if (retval) - goto err_unbind; + goto err; dev->usb_state = USB_STATE_ATTACHED; dev->ep0_state = WAIT_FOR_SETUP; @@ -1900,31 +1885,27 @@ static int langwell_start(struct usb_gadget_driver *driver, dev_info(&dev->pdev->dev, "register driver: %s\n", driver->driver.name); dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); + return 0; -err_unbind: - driver->unbind(&dev->gadget); +err: dev->gadget.dev.driver = NULL; dev->driver = NULL; dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); + return retval; } /* unregister gadget driver */ -static int langwell_stop(struct usb_gadget_driver *driver) +static int langwell_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) { - struct langwell_udc *dev = the_controller; + struct langwell_udc *dev = gadget_to_langwell(g); unsigned long flags; - if (!dev) - return -ENODEV; - dev_dbg(&dev->pdev->dev, "---> %s()\n", __func__); - if (unlikely(!driver || !driver->unbind)) - return -EINVAL; - /* exit PHY low power suspend */ if (dev->pdev->device != 0x0829) langwell_phy_low_power(dev, 0); @@ -1947,8 +1928,6 @@ static int langwell_stop(struct usb_gadget_driver *driver) stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); - /* unbind gadget driver */ - driver->unbind(&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; @@ -1957,6 +1936,7 @@ static int langwell_stop(struct usb_gadget_driver *driver) dev_info(&dev->pdev->dev, "unregistered driver '%s'\n", driver->driver.name); dev_dbg(&dev->pdev->dev, "<--- %s()\n", __func__); + return 0; } @@ -3098,8 +3078,6 @@ static void langwell_udc_remove(struct pci_dev *pdev) /* free dev, wait for the release() finished */ wait_for_completion(&done); - - the_controller = NULL; } @@ -3118,11 +3096,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, size_t size; int retval; - if (the_controller) { - dev_warn(&pdev->dev, "ignoring\n"); - return -EBUSY; - } - /* alloc, and start init */ dev = kzalloc(sizeof *dev, GFP_KERNEL); if (dev == NULL) { @@ -3342,8 +3315,6 @@ static int langwell_udc_probe(struct pci_dev *pdev, "After langwell_udc_probe(), print all registers:\n"); print_all_registers(dev); - the_controller = dev; - retval = device_register(&dev->gadget.dev); if (retval) goto error; diff --git a/drivers/usb/gadget/langwell_udc.h b/drivers/usb/gadget/langwell_udc.h index 1bec66f..ef79e24 100644 --- a/drivers/usb/gadget/langwell_udc.h +++ b/drivers/usb/gadget/langwell_udc.h @@ -221,3 +221,5 @@ struct langwell_udc { u16 dev_status; }; +#define gadget_to_langwell(g) container_of((g), struct langwell_udc, gadget) + -- cgit v0.10.2 From 96f8db6a77e3490608e5b5b3f57e7201f8c85496 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 10 Oct 2011 10:33:47 +0300 Subject: usb: gadget: net2272: convert to new style with this patch we can finally remove the global "the controller" pointer. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c index 08a4a36..d1b7636 100644 --- a/drivers/usb/gadget/net2272.c +++ b/drivers/usb/gadget/net2272.c @@ -1172,17 +1172,18 @@ net2272_pullup(struct usb_gadget *_gadget, int is_on) return 0; } -static int net2272_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); -static int net2272_stop(struct usb_gadget_driver *driver); +static int net2272_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); +static int net2272_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops net2272_ops = { - .get_frame = net2272_get_frame, - .wakeup = net2272_wakeup, + .get_frame = net2272_get_frame, + .wakeup = net2272_wakeup, .set_selfpowered = net2272_set_selfpowered, - .pullup = net2272_pullup, - .start = net2272_start, - .stop = net2272_stop, + .pullup = net2272_pullup, + .udc_start = net2272_start, + .udc_stop = net2272_stop, }; /*---------------------------------------------------------------------------*/ @@ -1356,8 +1357,6 @@ net2272_set_fifo_mode(struct net2272 *dev, int mode) /*---------------------------------------------------------------------------*/ -static struct net2272 *the_controller; - static void net2272_usb_reset(struct net2272 *dev) { @@ -1453,20 +1452,17 @@ net2272_ep0_start(struct net2272 *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -static int net2272_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int net2272_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2272 *dev = the_controller; - int ret; + struct net2272 *dev; unsigned i; - if (!driver || !bind || !driver->unbind || !driver->setup || + if (!driver || !driver->unbind || !driver->setup || driver->speed != USB_SPEED_HIGH) return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; + + dev = container_of(_gadget, struct net2272, gadget); for (i = 0; i < 4; ++i) dev->ep[i].irqs = 0; @@ -1475,14 +1471,6 @@ static int net2272_start(struct usb_gadget_driver *driver, driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; - ret = bind(&dev->gadget); - if (ret) { - dev_dbg(dev->dev, "bind to driver %s --> %d\n", - driver->driver.name, ret); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return ret; - } /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. @@ -1510,33 +1498,21 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver) for (i = 0; i < 4; ++i) net2272_dequeue_all(&dev->ep[i]); - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock(&dev->lock); - driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - - } net2272_usb_reinit(dev); } -static int net2272_stop(struct usb_gadget_driver *driver) +static int net2272_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2272 *dev = the_controller; + struct net2272 *dev; unsigned long flags; - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver) - return -EINVAL; + dev = container_of(_gadget, struct net2272, gadget); spin_lock_irqsave(&dev->lock, flags); stop_activity(dev, driver); spin_unlock_irqrestore(&dev->lock, flags); - net2272_pullup(&dev->gadget, 0); - - driver->unbind(&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; @@ -2238,7 +2214,6 @@ net2272_remove(struct net2272 *dev) device_remove_file(dev->dev, &dev_attr_registers); dev_info(dev->dev, "unbind\n"); - the_controller = NULL; } static struct net2272 * __devinit @@ -2246,11 +2221,6 @@ net2272_probe_init(struct device *dev, unsigned int irq) { struct net2272 *ret; - if (the_controller) { - dev_warn(dev, "ignoring\n"); - return ERR_PTR(-EBUSY); - } - if (!irq) { dev_dbg(dev, "No IRQ!\n"); return ERR_PTR(-ENODEV); @@ -2307,8 +2277,6 @@ net2272_probe_fin(struct net2272 *dev, unsigned int irqflags) dma_mode_string()); dev_info(dev->dev, "version: %s\n", driver_vers); - the_controller = dev; - ret = device_register(&dev->gadget.dev); if (ret) goto err_irq; @@ -2684,8 +2652,6 @@ net2272_plat_probe(struct platform_device *pdev) dev_info(&pdev->dev, "running in 16-bit, %sbyte swap local bus mode\n", (net2272_read(dev, LOCCTL) & (1 << BYTE_SWAP)) ? "" : "no "); - the_controller = dev; - return 0; err_io: -- cgit v0.10.2 From 4cf5e00b055ba5e4f3852e477a2a4346730ea283 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 10 Oct 2011 10:37:17 +0300 Subject: usb: gadget: net2280: convert to new style with this patch we can finally remove the global "the_controller" pointer. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index db508d0..2680fcc 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -1401,17 +1401,18 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on) return 0; } -static int net2280_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)); -static int net2280_stop(struct usb_gadget_driver *driver); +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver); static const struct usb_gadget_ops net2280_ops = { .get_frame = net2280_get_frame, .wakeup = net2280_wakeup, .set_selfpowered = net2280_set_selfpowered, .pullup = net2280_pullup, - .start = net2280_start, - .stop = net2280_stop, + .udc_start = net2280_start, + .udc_stop = net2280_stop, }; /*-------------------------------------------------------------------------*/ @@ -1744,8 +1745,6 @@ static void set_fifo_mode (struct net2280 *dev, int mode) * perhaps to bind specific drivers to specific devices. */ -static struct net2280 *the_controller; - static void usb_reset (struct net2280 *dev) { u32 tmp; @@ -1871,10 +1870,10 @@ static void ep0_start (struct net2280 *dev) * disconnect is reported. then a host may connect again, or * the driver might get unbound. */ -static int net2280_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int net2280_start(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2280 *dev = the_controller; + struct net2280 *dev; int retval; unsigned i; @@ -1882,14 +1881,11 @@ static int net2280_start(struct usb_gadget_driver *driver, * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) * "must not be used in normal operation" */ - if (!driver - || driver->speed != USB_SPEED_HIGH - || !bind || !driver->setup) + if (!driver || driver->speed != USB_SPEED_HIGH + || !driver->setup) return -EINVAL; - if (!dev) - return -ENODEV; - if (dev->driver) - return -EBUSY; + + dev = container_of (_gadget, struct net2280, gadget); for (i = 0; i < 7; i++) dev->ep [i].irqs = 0; @@ -1899,14 +1895,6 @@ static int net2280_start(struct usb_gadget_driver *driver, driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; - retval = bind(&dev->gadget); - if (retval) { - DEBUG (dev, "bind to driver %s --> %d\n", - driver->driver.name, retval); - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; - } retval = device_create_file (&dev->pdev->dev, &dev_attr_function); if (retval) goto err_unbind; @@ -1952,33 +1940,21 @@ stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) for (i = 0; i < 7; i++) nuke (&dev->ep [i]); - /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock (&dev->lock); - driver->disconnect (&dev->gadget); - spin_lock (&dev->lock); - } - usb_reinit (dev); } -static int net2280_stop(struct usb_gadget_driver *driver) +static int net2280_stop(struct usb_gadget *_gadget, + struct usb_gadget_driver *driver) { - struct net2280 *dev = the_controller; + struct net2280 *dev; unsigned long flags; - if (!dev) - return -ENODEV; - if (!driver || driver != dev->driver || !driver->unbind) - return -EINVAL; + dev = container_of (_gadget, struct net2280, gadget); spin_lock_irqsave (&dev->lock, flags); stop_activity (dev, driver); spin_unlock_irqrestore (&dev->lock, flags); - net2280_pullup (&dev->gadget, 0); - - driver->unbind (&dev->gadget); dev->gadget.dev.driver = NULL; dev->driver = NULL; @@ -2698,8 +2674,6 @@ static void net2280_remove (struct pci_dev *pdev) pci_set_drvdata (pdev, NULL); INFO (dev, "unbind\n"); - - the_controller = NULL; } /* wrap this driver around the specified device, but @@ -2713,14 +2687,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) void __iomem *base = NULL; int retval, i; - /* if you want to support more than one controller in a system, - * usb_gadget_driver_{register,unregister}() must change. - */ - if (the_controller) { - dev_warn (&pdev->dev, "ignoring\n"); - return -EBUSY; - } - /* alloc, and start init */ dev = kzalloc (sizeof *dev, GFP_KERNEL); if (dev == NULL){ @@ -2847,8 +2813,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) use_dma ? (use_dma_chaining ? "chaining" : "enabled") : "disabled"); - the_controller = dev; - retval = device_register (&dev->gadget.dev); if (retval) goto done; retval = device_create_file (&pdev->dev, &dev_attr_registers); -- cgit v0.10.2 From af1d7056a5c1e5eaaf807ddd1423101db84668d0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 10 Oct 2011 17:11:20 +0300 Subject: usb: gadget: renesas: convert to new style using udc_start()/udc_stop() as we should. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index cb2d451..c5c9ee5 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -730,10 +730,6 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) usbhsg_pipe_disable(dcp); - if (gpriv->driver && - gpriv->driver->disconnect) - gpriv->driver->disconnect(&gpriv->gadget); - dev_dbg(dev, "stop gadget\n"); return 0; @@ -744,31 +740,19 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) * linux usb function * */ -static int usbhsg_gadget_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int usbhsg_gadget_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct usbhsg_gpriv *gpriv; + struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); struct usbhs_priv *priv; struct device *dev; int ret; - if (!bind || - !driver || + if (!driver || !driver->setup || driver->speed != USB_SPEED_HIGH) return -EINVAL; - /* - * find unused controller - */ - usbhsg_for_each_controller(gpriv) { - if (!gpriv->driver) - goto find_unused_controller; - } - return -ENODEV; - -find_unused_controller: - dev = usbhsg_gpriv_to_dev(gpriv); priv = usbhsg_gpriv_to_priv(gpriv); @@ -782,19 +766,8 @@ find_unused_controller: goto add_fail; } - ret = bind(&gpriv->gadget); - if (ret) { - dev_err(dev, "bind to driver %s error %d\n", - driver->driver.name, ret); - goto bind_fail; - } - - dev_dbg(dev, "bind %s\n", driver->driver.name); - return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD); -bind_fail: - device_del(&gpriv->gadget.dev); add_fail: gpriv->driver = NULL; gpriv->gadget.dev.driver = NULL; @@ -802,9 +775,10 @@ add_fail: return ret; } -static int usbhsg_gadget_stop(struct usb_gadget_driver *driver) +static int usbhsg_gadget_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct usbhsg_gpriv *gpriv; + struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget); struct usbhs_priv *priv; struct device *dev; @@ -812,17 +786,6 @@ static int usbhsg_gadget_stop(struct usb_gadget_driver *driver) !driver->unbind) return -EINVAL; - /* - * find controller - */ - usbhsg_for_each_controller(gpriv) { - if (gpriv->driver == driver) - goto find_matching_controller; - } - return -ENODEV; - -find_matching_controller: - dev = usbhsg_gpriv_to_dev(gpriv); priv = usbhsg_gpriv_to_priv(gpriv); @@ -830,12 +793,6 @@ find_matching_controller: device_del(&gpriv->gadget.dev); gpriv->driver = NULL; - if (driver->disconnect) - driver->disconnect(&gpriv->gadget); - - driver->unbind(&gpriv->gadget); - dev_dbg(dev, "unbind %s\n", driver->driver.name); - return 0; } @@ -852,8 +809,8 @@ static int usbhsg_get_frame(struct usb_gadget *gadget) static struct usb_gadget_ops usbhsg_gadget_ops = { .get_frame = usbhsg_get_frame, - .start = usbhsg_gadget_start, - .stop = usbhsg_gadget_stop, + .udc_start = usbhsg_gadget_start, + .udc_stop = usbhsg_gadget_stop, }; static int usbhsg_start(struct usbhs_priv *priv) -- cgit v0.10.2 From 478ff25adef3d0ec394c8870fcf317c26839b482 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 10 Oct 2011 17:18:51 +0300 Subject: usb: gadget: r8a66597: convert to new style convert to new style UDC registration and remove the global 'the_controller' pointer. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 035879b..68a826a 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -1738,23 +1738,18 @@ static struct usb_ep_ops r8a66597_ep_ops = { }; /*-------------------------------------------------------------------------*/ -static struct r8a66597 *the_controller; - -static int r8a66597_start(struct usb_gadget_driver *driver, - int (*bind)(struct usb_gadget *)) +static int r8a66597_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct r8a66597 *r8a66597 = the_controller; + struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); int retval; if (!driver || driver->speed != USB_SPEED_HIGH - || !bind || !driver->setup) return -EINVAL; if (!r8a66597) return -ENODEV; - if (r8a66597->driver) - return -EBUSY; /* hook up the driver */ driver->driver.bus = NULL; @@ -1768,14 +1763,6 @@ static int r8a66597_start(struct usb_gadget_driver *driver, goto error; } - retval = bind(&r8a66597->gadget); - if (retval) { - dev_err(r8a66597_to_dev(r8a66597), - "bind to driver error (%d)\n", retval); - device_del(&r8a66597->gadget.dev); - goto error; - } - init_controller(r8a66597); r8a66597_bset(r8a66597, VBSE, INTENB0); if (r8a66597_read(r8a66597, INTSTS0) & VBSTS) { @@ -1796,23 +1783,17 @@ error: return retval; } -static int r8a66597_stop(struct usb_gadget_driver *driver) +static int r8a66597_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) { - struct r8a66597 *r8a66597 = the_controller; + struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget); unsigned long flags; - if (driver != r8a66597->driver || !driver->unbind) - return -EINVAL; - spin_lock_irqsave(&r8a66597->lock, flags); - if (r8a66597->gadget.speed != USB_SPEED_UNKNOWN) - r8a66597_usb_disconnect(r8a66597); r8a66597_bclr(r8a66597, VBSE, INTENB0); disable_controller(r8a66597); spin_unlock_irqrestore(&r8a66597->lock, flags); - driver->unbind(&r8a66597->gadget); - device_del(&r8a66597->gadget.dev); r8a66597->driver = NULL; return 0; @@ -1842,8 +1823,8 @@ static int r8a66597_pullup(struct usb_gadget *gadget, int is_on) static struct usb_gadget_ops r8a66597_gadget_ops = { .get_frame = r8a66597_get_frame, - .start = r8a66597_start, - .stop = r8a66597_stop, + .udc_start = r8a66597_start, + .udc_stop = r8a66597_stop, .pullup = r8a66597_pullup, }; @@ -2010,8 +1991,6 @@ static int __init r8a66597_probe(struct platform_device *pdev) r8a66597->pipenum2ep[0] = &r8a66597->ep[0]; r8a66597->epaddr2ep[0] = &r8a66597->ep[0]; - the_controller = r8a66597; - r8a66597->ep0_req = r8a66597_alloc_request(&r8a66597->ep[0].ep, GFP_KERNEL); if (r8a66597->ep0_req == NULL) -- cgit v0.10.2 From 089b837a39552ee49a4ea4c188e8c3517473f10c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 10 Oct 2011 09:43:44 +0300 Subject: usb: gadget: fix typo for default U1/U2 exit latencies s/DEFULT/DEFAULT/, no functional changes. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index e74fd55..8a5529d 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -542,9 +542,9 @@ static int bos_desc(struct usb_composite_dev *cdev) if (cdev->gadget->ops->get_config_params) cdev->gadget->ops->get_config_params(&dcd_config_params); else { - dcd_config_params.bU1devExitLat = USB_DEFULT_U1_DEV_EXIT_LAT; + dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT; dcd_config_params.bU2DevExitLat = - cpu_to_le16(USB_DEFULT_U2_DEV_EXIT_LAT); + cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); } ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 087f4b9..1d3a675 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -437,9 +437,9 @@ static inline void usb_ep_fifo_flush(struct usb_ep *ep) struct usb_dcd_config_params { __u8 bU1devExitLat; /* U1 Device exit Latency */ -#define USB_DEFULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */ +#define USB_DEFAULT_U1_DEV_EXIT_LAT 0x01 /* Less then 1 microsec */ __le16 bU2DevExitLat; /* U2 Device exit Latency */ -#define USB_DEFULT_U2_DEV_EXIT_LAT 0x1F4 /* Less then 500 microsec */ +#define USB_DEFAULT_U2_DEV_EXIT_LAT 0x1F4 /* Less then 500 microsec */ }; -- cgit v0.10.2 From 4bb99b7c82bac1488a0228d2363db1f68d90f6f3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 3 Aug 2011 14:33:27 +0300 Subject: usb: gadget: storage: add superspeed support this patch adds superspeed descriptors for the storage gadgets. Acked-by: Michal Nazarewicz Acked-by: Alan Stern Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 927ee88..52583a2 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -3023,6 +3023,28 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) } } + if (gadget_is_superspeed(gadget)) { + unsigned max_burst; + + /* Calculate bMaxBurst, we know packet size is 1024 */ + max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15); + + fsg_ss_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; + + fsg_ss_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; + + f->ss_descriptors = usb_copy_descriptors(fsg_ss_function); + if (unlikely(!f->ss_descriptors)) { + usb_free_descriptors(f->hs_descriptors); + usb_free_descriptors(f->descriptors); + return -ENOMEM; + } + } + return 0; autoconf_fail: diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index a230009..5779549 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -586,7 +586,19 @@ dev_qualifier = { .bNumConfigurations = 1, }; +static int populate_bos(struct fsg_dev *fsg, u8 *buf) +{ + memcpy(buf, &fsg_bos_desc, USB_DT_BOS_SIZE); + buf += USB_DT_BOS_SIZE; + + memcpy(buf, &fsg_ext_cap_desc, USB_DT_USB_EXT_CAP_SIZE); + buf += USB_DT_USB_EXT_CAP_SIZE; + memcpy(buf, &fsg_ss_cap_desc, USB_DT_USB_SS_CAP_SIZE); + + return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE + + USB_DT_USB_EXT_CAP_SIZE; +} /* * Config descriptors must agree with the code that sets configurations @@ -935,7 +947,8 @@ static int standard_setup_req(struct fsg_dev *fsg, break; case USB_DT_DEVICE_QUALIFIER: VDBG(fsg, "get device qualifier\n"); - if (!gadget_is_dualspeed(fsg->gadget)) + if (!gadget_is_dualspeed(fsg->gadget) || + fsg->gadget->speed == USB_SPEED_SUPER) break; /* * Assume ep0 uses the same maxpacket value for both @@ -948,7 +961,8 @@ static int standard_setup_req(struct fsg_dev *fsg, case USB_DT_OTHER_SPEED_CONFIG: VDBG(fsg, "get other-speed config descriptor\n"); - if (!gadget_is_dualspeed(fsg->gadget)) + if (!gadget_is_dualspeed(fsg->gadget) || + fsg->gadget->speed == USB_SPEED_SUPER) break; goto get_config; case USB_DT_CONFIG: @@ -967,7 +981,15 @@ get_config: value = usb_gadget_get_string(&fsg_stringtab, w_value & 0xff, req->buf); break; + + case USB_DT_BOS: + VDBG(fsg, "get bos descriptor\n"); + + if (gadget_is_superspeed(fsg->gadget)) + value = populate_bos(fsg, req->buf); + break; } + break; /* One config, two speeds */ @@ -2777,13 +2799,15 @@ reset: /* Enable the endpoints */ d = fsg_ep_desc(fsg->gadget, - &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); + &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc, + &fsg_ss_bulk_in_desc); if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) goto reset; fsg->bulk_in_enabled = 1; d = fsg_ep_desc(fsg->gadget, - &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); + &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc, + &fsg_ss_bulk_out_desc); if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) goto reset; fsg->bulk_out_enabled = 1; @@ -2792,7 +2816,8 @@ reset: if (transport_is_cbi()) { d = fsg_ep_desc(fsg->gadget, - &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc); + &fsg_fs_intr_in_desc, &fsg_hs_intr_in_desc, + &fsg_ss_intr_in_desc); if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0) goto reset; fsg->intr_in_enabled = 1; @@ -3424,6 +3449,24 @@ static int __init fsg_bind(struct usb_gadget *gadget) fsg_fs_intr_in_desc.bEndpointAddress; } + if (gadget_is_superspeed(gadget)) { + unsigned max_burst; + + fsg_ss_function[i + FSG_SS_FUNCTION_PRE_EP_ENTRIES] = NULL; + + /* Calculate bMaxBurst, we know packet size is 1024 */ + max_burst = min_t(unsigned, mod_data.buflen / 1024, 15); + + /* Assume endpoint addresses are the same for both speeds */ + fsg_ss_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; + + fsg_ss_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; + } + if (gadget_is_otg(gadget)) fsg_otg_desc.bmAttributes |= USB_OTG_HNP; @@ -3540,11 +3583,7 @@ static void fsg_resume(struct usb_gadget *gadget) /*-------------------------------------------------------------------------*/ static struct usb_gadget_driver fsg_driver = { -#ifdef CONFIG_USB_GADGET_DUALSPEED - .speed = USB_SPEED_HIGH, -#else - .speed = USB_SPEED_FULL, -#endif + .speed = USB_SPEED_SUPER, .function = (char *) fsg_string_product, .unbind = fsg_unbind, .disconnect = fsg_disconnect, diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index d2a9dcb..e24f72f 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -160,7 +160,7 @@ static struct usb_composite_driver msg_driver = { .name = "g_mass_storage", .dev = &msg_device_desc, .iProduct = DRIVER_DESC, - .max_speed = USB_SPEED_HIGH, + .max_speed = USB_SPEED_SUPER, .needs_serial = 1, }; diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index 5236262..c7f291a 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -515,12 +515,128 @@ static struct usb_descriptor_header *fsg_hs_function[] = { NULL, }; +static struct usb_endpoint_descriptor +fsg_ss_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +static struct usb_endpoint_descriptor +fsg_ss_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_ss_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */ +}; + +static struct usb_ss_ep_comp_descriptor fsg_ss_intr_in_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + .wBytesPerInterval = cpu_to_le16(2), +}; + +#ifndef FSG_NO_OTG +# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 2 +#else +# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 1 +#endif + +#endif + +static __maybe_unused struct usb_ext_cap_descriptor fsg_ext_cap_desc = { + .bLength = USB_DT_USB_EXT_CAP_SIZE, + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_CAP_TYPE_EXT, + + .bmAttributes = cpu_to_le32(USB_LPM_SUPPORT), +}; + +static __maybe_unused struct usb_ss_cap_descriptor fsg_ss_cap_desc = { + .bLength = USB_DT_USB_SS_CAP_SIZE, + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_SS_CAP_TYPE, + + /* .bmAttributes = LTM is not supported yet */ + + .wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION + | USB_FULL_SPEED_OPERATION + | USB_HIGH_SPEED_OPERATION + | USB_5GBPS_OPERATION), + .bFunctionalitySupport = USB_LOW_SPEED_OPERATION, + .bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT, + .bU2DevExitLat = USB_DEFAULT_U2_DEV_EXIT_LAT, +}; + +static __maybe_unused struct usb_bos_descriptor fsg_bos_desc = { + .bLength = USB_DT_BOS_SIZE, + .bDescriptorType = USB_DT_BOS, + + .wTotalLength = USB_DT_BOS_SIZE + + USB_DT_USB_EXT_CAP_SIZE + + USB_DT_USB_SS_CAP_SIZE, + + .bNumDeviceCaps = 2, +}; + +static struct usb_descriptor_header *fsg_ss_function[] = { +#ifndef FSG_NO_OTG + (struct usb_descriptor_header *) &fsg_otg_desc, +#endif + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, +#ifndef FSG_NO_INTR_EP + (struct usb_descriptor_header *) &fsg_ss_intr_in_desc, + (struct usb_descriptor_header *) &fsg_ss_intr_in_comp_desc, +#endif + NULL, +}; + /* Maxpacket and other transfer characteristics vary by speed. */ static __maybe_unused struct usb_endpoint_descriptor * fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, - struct usb_endpoint_descriptor *hs) + struct usb_endpoint_descriptor *hs, + struct usb_endpoint_descriptor *ss) { - if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER) + return ss; + else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) return hs; return fs; } -- cgit v0.10.2 From ca8a282a5373f96d0ea002d97a168211448e8526 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 21:58:19 -0700 Subject: usb: gadget: renesas_usbhs: add suspend/resume support Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index d8239e5..319ed47 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -203,13 +203,10 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) } /* - * notify hotplug + * hotplug */ -static void usbhsc_notify_hotplug(struct work_struct *work) +static void usbhsc_hotplug(struct usbhs_priv *priv) { - struct usbhs_priv *priv = container_of(work, - struct usbhs_priv, - notify_hotplug_work.work); struct platform_device *pdev = usbhs_priv_to_pdev(priv); struct usbhs_mod *mod = usbhs_mod_get_current(priv); int id; @@ -257,6 +254,17 @@ static void usbhsc_notify_hotplug(struct work_struct *work) } } +/* + * notify hotplug + */ +static void usbhsc_notify_hotplug(struct work_struct *work) +{ + struct usbhs_priv *priv = container_of(work, + struct usbhs_priv, + notify_hotplug_work.work); + usbhsc_hotplug(priv); +} + int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); @@ -443,9 +451,60 @@ static int __devexit usbhs_remove(struct platform_device *pdev) return 0; } +static int usbhsc_suspend(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + + if (mod) { + usbhs_mod_call(priv, stop, priv); + usbhs_mod_change(priv, -1); + } + + if (mod || !usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_ctrl(priv, 0); + + return 0; +} + +static int usbhsc_resume(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + struct platform_device *pdev = usbhs_priv_to_pdev(priv); + + usbhs_platform_call(priv, phy_reset, pdev); + + if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_ctrl(priv, 1); + + usbhsc_hotplug(priv); + + return 0; +} + +static int usbhsc_runtime_nop(struct device *dev) +{ + /* Runtime PM callback shared between ->runtime_suspend() + * and ->runtime_resume(). Simply returns success. + * + * This driver re-initializes all registers after + * pm_runtime_get_sync() anyway so there is no need + * to save and restore registers here. + */ + return 0; +} + +static const struct dev_pm_ops usbhsc_pm_ops = { + .suspend = usbhsc_suspend, + .resume = usbhsc_resume, + .runtime_suspend = usbhsc_runtime_nop, + .runtime_resume = usbhsc_runtime_nop, +}; + static struct platform_driver renesas_usbhs_driver = { .driver = { .name = "renesas_usbhs", + .pm = &usbhsc_pm_ops, }, .probe = usbhs_probe, .remove = __devexit_p(usbhs_remove), -- cgit v0.10.2 From 51b8a0218b885b5e527da776e20022273a838893 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 21:58:45 -0700 Subject: usb: gadget: renesas_usbhs: move USBHSF_PKT_xxx to pipe.c There is no longer necessity that USBHSF_PKT_xxx are in fifo.h. it are used in only fifo.c now. This patch move it. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index a34430f..7eacacd 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -118,7 +118,13 @@ struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt) return pkt; } -int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type) +enum { + USBHSF_PKT_PREPARE, + USBHSF_PKT_TRY_RUN, + USBHSF_PKT_DMA_DONE, +}; + +static int usbhsf_pkt_handler(struct usbhs_pipe *pipe, int type) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); @@ -168,6 +174,11 @@ __usbhs_pkt_handler_end: return ret; } +void usbhs_pkt_start(struct usbhs_pipe *pipe) +{ + usbhsf_pkt_handler(pipe, USBHSF_PKT_PREPARE); +} + /* * irq enable/disable function */ @@ -884,7 +895,7 @@ static int usbhsf_irq_empty(struct usbhs_priv *priv, if (!(irq_state->bempsts & (1 << i))) continue; - ret = usbhs_pkt_run(pipe); + ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN); if (ret < 0) dev_err(dev, "irq_empty run_error %d : %d\n", i, ret); } @@ -914,7 +925,7 @@ static int usbhsf_irq_ready(struct usbhs_priv *priv, if (!(irq_state->brdysts & (1 << i))) continue; - ret = usbhs_pkt_run(pipe); + ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN); if (ret < 0) dev_err(dev, "irq_ready run_error %d : %d\n", i, ret); } @@ -929,7 +940,7 @@ static void usbhsf_dma_complete(void *arg) struct device *dev = usbhs_priv_to_dev(priv); int ret; - ret = usbhs_pkt_dmadone(pipe); + ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_DMA_DONE); if (ret < 0) dev_err(dev, "dma_complete run_error %d : %d\n", usbhs_pipe_number(pipe), ret); diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index ed6d8e5..7a78440 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -76,12 +76,6 @@ void usbhs_fifo_quit(struct usbhs_priv *priv); /* * packet info */ -enum { - USBHSF_PKT_PREPARE, - USBHSF_PKT_TRY_RUN, - USBHSF_PKT_DMA_DONE, -}; - extern struct usbhs_pkt_handle usbhs_fifo_pio_push_handler; extern struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler; extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler; @@ -95,10 +89,6 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, struct usbhs_pkt_handle *handler, void *buf, int len, int zero); struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt); -int __usbhs_pkt_handler(struct usbhs_pipe *pipe, int type); - -#define usbhs_pkt_start(p) __usbhs_pkt_handler(p, USBHSF_PKT_PREPARE) -#define usbhs_pkt_run(p) __usbhs_pkt_handler(p, USBHSF_PKT_TRY_RUN) -#define usbhs_pkt_dmadone(p) __usbhs_pkt_handler(p, USBHSF_PKT_DMA_DONE) +void usbhs_pkt_start(struct usbhs_pipe *pipe); #endif /* RENESAS_USB_FIFO_H */ -- cgit v0.10.2 From f5aa889f725b56934171f9845cf00a17de9cc76c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 21:59:46 -0700 Subject: usb: gadget: renesas_usbhs: remove desc from usbhs_pipe_malloc Current usbhs_pipe_malloc() used usb_endpoint_descriptor to get necessary information. It was very good for mod_gadget which allocate pipe in runtime, but is not good for mod_host which allocate pipe in initial timing. This patch remove usb_endpoint_descriptor from usbhs_pipe_malloc() Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index c5c9ee5..5a697b7 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -483,11 +483,18 @@ static int usbhsg_ep_enable(struct usb_ep *ep, return 0; } - pipe = usbhs_pipe_malloc(priv, desc); + pipe = usbhs_pipe_malloc(priv, + usb_endpoint_type(desc), + usb_endpoint_dir_in(desc)); if (pipe) { uep->pipe = pipe; pipe->mod_private = uep; + /* set epnum / maxp */ + usbhs_pipe_config_update(pipe, + usb_endpoint_num(desc), + usb_endpoint_maxp(desc)); + /* * usbhs_fifo_dma_push/pop_handler try to * use dmaengine if possible. @@ -667,6 +674,7 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) /* dcp init */ dcp->pipe = usbhs_dcp_malloc(priv); dcp->pipe->mod_private = dcp; + usbhs_pipe_config_update(dcp->pipe, 0, 64); /* * system config enble diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 3bbbbb4..1af1905 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -311,8 +311,8 @@ static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe) } static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, - const struct usb_endpoint_descriptor *desc, - int is_host) + int is_host, + int dir_in) { u16 type = 0; u16 bfre = 0; @@ -358,11 +358,11 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, cntmd = 0; /* FIXME */ /* DIR */ - if (usb_endpoint_dir_in(desc)) + if (dir_in) usbhsp_flags_set(pipe, IS_DIR_HOST); - if ((is_host && usb_endpoint_dir_out(desc)) || - (!is_host && usb_endpoint_dir_in(desc))) + if ((is_host && !dir_in) || + (!is_host && dir_in)) dir |= DIR_OUT; if (!dir) @@ -374,7 +374,7 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, shtnak = SHTNAK; /* EPNUM */ - epnum = 0xF & usb_endpoint_num(desc); + epnum = 0; /* see usbhs_pipe_config_update() */ return type | bfre | @@ -385,19 +385,7 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, epnum; } -static u16 usbhsp_setup_pipemaxp(struct usbhs_pipe *pipe, - const struct usb_endpoint_descriptor *desc, - int is_host) -{ - /* host should set DEVSEL */ - - /* reutn MXPS */ - return PIPE_MAXP_MASK & usb_endpoint_maxp(desc); -} - -static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, - const struct usb_endpoint_descriptor *desc, - int is_host) +static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); @@ -473,6 +461,17 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe, (0xff & bufnmb) << 0; } +void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 epnum, u16 maxp) +{ + usbhsp_pipe_barrier(pipe); + + usbhsp_pipe_select(pipe); + usbhsp_pipe_maxp_set(pipe, 0xFFFF, maxp); + + if (!usbhs_pipe_is_dcp(pipe)) + usbhsp_pipe_cfg_set(pipe, 0x000F, epnum); +} + /* * pipe control */ @@ -582,19 +581,20 @@ void usbhs_pipe_init(struct usbhs_priv *priv, } struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, - const struct usb_endpoint_descriptor *desc) + int endpoint_type, + int dir_in) { struct device *dev = usbhs_priv_to_dev(priv); struct usbhs_mod *mod = usbhs_mod_get_current(priv); struct usbhs_pipe *pipe; int is_host = usbhs_mod_is_host(priv, mod); int ret; - u16 pipecfg, pipebuf, pipemaxp; + u16 pipecfg, pipebuf; - pipe = usbhsp_get_pipe(priv, usb_endpoint_type(desc)); + pipe = usbhsp_get_pipe(priv, endpoint_type); if (!pipe) { dev_err(dev, "can't get pipe (%s)\n", - usbhsp_pipe_name[usb_endpoint_type(desc)]); + usbhsp_pipe_name[endpoint_type]); return NULL; } @@ -609,22 +609,25 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, return NULL; } - pipecfg = usbhsp_setup_pipecfg(pipe, desc, is_host); - pipebuf = usbhsp_setup_pipebuff(pipe, desc, is_host); - pipemaxp = usbhsp_setup_pipemaxp(pipe, desc, is_host); + pipecfg = usbhsp_setup_pipecfg(pipe, is_host, dir_in); + pipebuf = usbhsp_setup_pipebuff(pipe); usbhsp_pipe_select(pipe); usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg); usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf); - usbhsp_pipe_maxp_set(pipe, 0xFFFF, pipemaxp); usbhs_pipe_clear_sequence(pipe); dev_dbg(dev, "enable pipe %d : %s (%s)\n", usbhs_pipe_number(pipe), - usbhsp_pipe_name[usb_endpoint_type(desc)], + usbhsp_pipe_name[endpoint_type], usbhs_pipe_is_dir_in(pipe) ? "in" : "out"); + /* + * epnum / maxp are still not set to this pipe. + * call usbhs_pipe_config_update() after this function !! + */ + return pipe; } @@ -651,16 +654,12 @@ struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv) if (!pipe) return NULL; + INIT_LIST_HEAD(&pipe->list); + /* - * dcpcfg : default - * dcpmaxp : default - * pipebuf : nothing to do + * call usbhs_pipe_config_update() after this function !! */ - usbhsp_pipe_select(pipe); - usbhs_pipe_clear_sequence(pipe); - INIT_LIST_HEAD(&pipe->list); - return pipe; } diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 41534cb..46707b5 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -76,8 +76,7 @@ void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); * pipe control */ struct usbhs_pipe -*usbhs_pipe_malloc(struct usbhs_priv *priv, - const struct usb_endpoint_descriptor *desc); +*usbhs_pipe_malloc(struct usbhs_priv *priv, int endpoint_type, int dir_in); int usbhs_pipe_probe(struct usbhs_priv *priv); void usbhs_pipe_remove(struct usbhs_priv *priv); int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); @@ -93,6 +92,7 @@ void usbhs_pipe_enable(struct usbhs_pipe *pipe); void usbhs_pipe_disable(struct usbhs_pipe *pipe); void usbhs_pipe_stall(struct usbhs_pipe *pipe); void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); +void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 epnum, u16 maxp); #define usbhs_pipe_to_priv(p) ((p)->priv) #define usbhs_pipe_number(p) (int)((p) - (p)->priv->pipe_info.pipe) -- cgit v0.10.2 From 7fd097e727466cda1b22beca6cb11096b8be88d2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:00:42 -0700 Subject: usb: gadget: renesas_usbhs: each pipe hold maxpacket size Current renesas_usbhs pipe accessed DCPMAXP/PIPEMAXP register to get own maxpacket size every time. But maxpacket size isn't changed after pipe start, and register access is too slow. This patch adds new maxp variable to keep own maxpacket. And un-used function are removed. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 1af1905..1f86bed 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -106,17 +106,6 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe, usbhs_bset(priv, pipe_reg, mask, val); } -static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe, - u16 dcp_reg, u16 pipe_reg) -{ - struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); - - if (usbhs_pipe_is_dcp(pipe)) - return usbhs_read(priv, dcp_reg); - else - return usbhs_read(priv, pipe_reg); -} - /* * DCPCFG/PIPECFG functions */ @@ -144,11 +133,6 @@ static void usbhsp_pipe_maxp_set(struct usbhs_pipe *pipe, u16 mask, u16 val) __usbhsp_pipe_xxx_set(pipe, DCPMAXP, PIPEMAXP, mask, val); } -static u16 usbhsp_pipe_maxp_get(struct usbhs_pipe *pipe) -{ - return __usbhsp_pipe_xxx_get(pipe, DCPMAXP, PIPEMAXP); -} - /* * pipe control functions */ @@ -465,6 +449,8 @@ void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 epnum, u16 maxp) { usbhsp_pipe_barrier(pipe); + pipe->maxp = maxp; + usbhsp_pipe_select(pipe); usbhsp_pipe_maxp_set(pipe, 0xFFFF, maxp); @@ -477,11 +463,12 @@ void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 epnum, u16 maxp) */ int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe) { - u16 mask = usbhs_pipe_is_dcp(pipe) ? DCP_MAXP_MASK : PIPE_MAXP_MASK; - - usbhsp_pipe_select(pipe); - - return (int)(usbhsp_pipe_maxp_get(pipe) & mask); + /* + * see + * usbhs_pipe_config_update() + * usbhs_dcp_malloc() + */ + return pipe->maxp; } int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe) diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 46707b5..1baa199 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -30,6 +30,8 @@ struct usbhs_pipe { struct usbhs_fifo *fifo; struct list_head list; + int maxp; + u32 flags; #define USBHS_PIPE_FLAGS_IS_USED (1 << 0) #define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1) -- cgit v0.10.2 From 0c6ef985ced753b60ecdc091fa49c3f5fa4a4f79 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:00:59 -0700 Subject: usb: gadget: renesas_usbhs: struct usbhs_pipe hold handler packet handler had moved to struct usbhs_pipe from struct usbhsg_uep. it is preparation of mod_host support Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 7eacacd..328238c 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -54,7 +54,6 @@ static struct usbhs_pkt_handle usbhsf_null_handler = { }; void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, - struct usbhs_pkt_handle *handler, void *buf, int len, int zero) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); @@ -64,17 +63,22 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, /******************** spin lock ********************/ usbhs_lock(priv, flags); - if (!handler) { + if (!pipe->handler) { dev_err(dev, "no handler function\n"); - handler = &usbhsf_null_handler; + pipe->handler = &usbhsf_null_handler; } list_del_init(&pkt->node); list_add_tail(&pkt->node, &pipe->list); + /* + * each pkt must hold own handler. + * because handler might be changed by its situation. + * dma handler -> pio handler. + */ pkt->pipe = pipe; pkt->buf = buf; - pkt->handler = handler; + pkt->handler = pipe->handler; pkt->length = len; pkt->zero = zero; pkt->actual = 0; diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 7a78440..60aa20f 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -86,7 +86,6 @@ extern struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler; void usbhs_pkt_init(struct usbhs_pkt *pkt); void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, - struct usbhs_pkt_handle *handler, void *buf, int len, int zero); struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt); void usbhs_pkt_start(struct usbhs_pipe *pipe); diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 5a697b7..babd90c 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -39,7 +39,6 @@ struct usbhsg_uep { char ep_name[EP_NAME_SIZE]; struct usbhsg_gpriv *gpriv; - struct usbhs_pkt_handle *handler; }; struct usbhsg_gpriv { @@ -139,8 +138,7 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep, req->actual = 0; req->status = -EINPROGRESS; - usbhs_pkt_push(pipe, pkt, uep->handler, - req->buf, req->length, req->zero); + usbhs_pkt_push(pipe, pkt, req->buf, req->length, req->zero); dev_dbg(dev, "pipe %d : queue push (%d)\n", usbhs_pipe_number(pipe), @@ -389,13 +387,13 @@ static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv, switch (stage) { case READ_DATA_STAGE: - dcp->handler = &usbhs_fifo_pio_push_handler; + pipe->handler = &usbhs_fifo_pio_push_handler; break; case WRITE_DATA_STAGE: - dcp->handler = &usbhs_fifo_pio_pop_handler; + pipe->handler = &usbhs_fifo_pio_pop_handler; break; case NODATA_STATUS_STAGE: - dcp->handler = &usbhs_ctrl_stage_end_handler; + pipe->handler = &usbhs_ctrl_stage_end_handler; break; default: return ret; @@ -501,9 +499,9 @@ static int usbhsg_ep_enable(struct usb_ep *ep, * It will use pio handler if impossible. */ if (usb_endpoint_dir_in(desc)) - uep->handler = &usbhs_fifo_dma_push_handler; + pipe->handler = &usbhs_fifo_dma_push_handler; else - uep->handler = &usbhs_fifo_dma_pop_handler; + pipe->handler = &usbhs_fifo_dma_pop_handler; ret = 0; } diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 1baa199..6760dff 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -37,6 +37,8 @@ struct usbhs_pipe { #define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1) #define USBHS_PIPE_FLAGS_IS_DIR_HOST (1 << 2) + struct usbhs_pkt_handle *handler; + void *mod_private; }; -- cgit v0.10.2 From 651f5e49dd3d369711bd622f4bc72c2fa30a3cf9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:01:15 -0700 Subject: usb: gadget: renesas_usbhs: tidyup INTENB0 method This patch tidyup Interrupt Enable Register 0 (INTENB0) which has relationship with picky BEMPENB/BRDYENB. - add explain comment - ignore nonsense 0 write case Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 621f6cc..74ef49e 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -292,6 +292,14 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) u16 intenb0 = 0; struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); + /* + * BEMPENB/BRDYENB are picky. + * below method is required + * + * - clear INTSTS0 + * - update BEMPENB/BRDYENB + * - update INTSTS0 + */ usbhs_write(priv, INTENB0, 0); usbhs_write(priv, BEMPENB, 0); @@ -324,5 +332,6 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) } } - usbhs_write(priv, INTENB0, intenb0); + if (intenb0) + usbhs_write(priv, INTENB0, intenb0); } -- cgit v0.10.2 From 11935de5579a5d01b3c89d69b4fc7a38b4dd8eae Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:01:28 -0700 Subject: usb: gadget: renesas_usbhs: change usbhsc_bus_ctrl() to usbsc_set_buswait() renesas_usbhs will have register DVSTCTR control function for HOST support. This patch changes usbhsc_bus_ctrl() to usbsc_set_buswait(), to remove DVSTCTR access from it, Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 319ed47..fd43fdd 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -149,17 +149,13 @@ int usbhs_frame_get_num(struct usbhs_priv *priv) /* * local functions */ -static void usbhsc_bus_ctrl(struct usbhs_priv *priv, int enable) +static void usbhsc_set_buswait(struct usbhs_priv *priv) { int wait = usbhs_get_dparam(priv, buswait_bwait); - u16 data = 0; - if (enable) { - /* set bus wait if platform have */ - if (wait) - usbhs_bset(priv, BUSWAIT, 0x000F, wait); - } - usbhs_write(priv, DVSTCTR, data); + /* set bus wait if platform have */ + if (wait) + usbhs_bset(priv, BUSWAIT, 0x000F, wait); } /* @@ -191,10 +187,9 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) /* USB on */ usbhs_sys_clock_ctrl(priv, enable); - usbhsc_bus_ctrl(priv, enable); + usbhsc_set_buswait(priv); } else { /* USB off */ - usbhsc_bus_ctrl(priv, enable); usbhs_sys_clock_ctrl(priv, enable); /* disable PM */ diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 8977431..959af02 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -101,6 +101,8 @@ struct renesas_usbhs_driver_param { * option: * * for BUSWAIT :: BWAIT + * see + * renesas_usbhs/common.c :: usbhsc_set_buswait() * */ int buswait_bwait; -- cgit v0.10.2 From 258485d9904703c4cb3a2b3ee38fe2a0cbf01f48 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:01:40 -0700 Subject: usb: gadget: renesas_usbhs: add bus control functions this patch add DVSTCTR control function for HOST support Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index fd43fdd..243512d 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -147,6 +147,33 @@ int usbhs_frame_get_num(struct usbhs_priv *priv) } /* + * bus/vbus functions + */ +void usbhs_bus_send_sof_enable(struct usbhs_priv *priv) +{ + usbhs_bset(priv, DVSTCTR, (USBRST | UACT), UACT); +} + +void usbhs_bus_send_reset(struct usbhs_priv *priv) +{ + usbhs_bset(priv, DVSTCTR, (USBRST | UACT), USBRST); +} + +int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable) +{ + struct platform_device *pdev = usbhs_priv_to_pdev(priv); + + return usbhs_platform_call(priv, set_vbus, pdev, enable); +} + +static void usbhsc_bus_init(struct usbhs_priv *priv) +{ + usbhs_write(priv, DVSTCTR, 0); + + usbhs_vbus_ctrl(priv, 0); +} + +/* * local functions */ static void usbhsc_set_buswait(struct usbhs_priv *priv) @@ -187,7 +214,6 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) /* USB on */ usbhs_sys_clock_ctrl(priv, enable); - usbhsc_set_buswait(priv); } else { /* USB off */ usbhs_sys_clock_ctrl(priv, enable); @@ -229,6 +255,10 @@ static void usbhsc_hotplug(struct usbhs_priv *priv) if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) usbhsc_power_ctrl(priv, enable); + /* bus init */ + usbhsc_set_buswait(priv); + usbhsc_bus_init(priv); + /* module start */ usbhs_mod_call(priv, start, priv); @@ -238,6 +268,9 @@ static void usbhsc_hotplug(struct usbhs_priv *priv) /* module stop */ usbhs_mod_call(priv, stop, priv); + /* bus init */ + usbhsc_bus_init(priv); + /* power off */ if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) usbhsc_power_ctrl(priv, enable); diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index b410463..54b5924 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -102,6 +102,8 @@ struct usbhs_priv; /* DVSTCTR */ #define EXTLP (1 << 10) /* Controls the EXTLP pin output state */ #define PWEN (1 << 9) /* Controls the PWEN pin output state */ +#define USBRST (1 << 6) /* Bus Reset Output */ +#define UACT (1 << 4) /* USB Bus Enable */ #define RHST (0x7) /* Reset Handshake */ #define RHST_LOW_SPEED 1 /* Low-speed connection */ #define RHST_FULL_SPEED 2 /* Full-speed connection */ @@ -258,6 +260,13 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable); void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable); /* + * bus + */ +void usbhs_bus_send_sof_enable(struct usbhs_priv *priv); +void usbhs_bus_send_reset(struct usbhs_priv *priv); +int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable); + +/* * frame */ int usbhs_frame_get_num(struct usbhs_priv *priv); diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 959af02..040d8bb 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -82,6 +82,13 @@ struct renesas_usbhs_platform_callback { * get VBUS status function. */ int (*get_vbus)(struct platform_device *pdev); + + /* + * option: + * + * VBUS control is needed for Host + */ + int (*set_vbus)(struct platform_device *pdev, int enable); }; /* -- cgit v0.10.2 From 75587f52c7b0d6c319515138a495a619b552a670 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:01:51 -0700 Subject: usb: gadget: renesas_usbhs: add usbhs_bus_get_speed() current mod_gadget had got usb speed on usbhsg_irq_dev_state() which is status change interrupt callback function. And the usb speed data was included in its parameter. But this style works for mod_gadget, but doesn't work for mod_host which isn't interrupted when device status was changed. This patch add usbhs_bus_get_speed() to solve this issue. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 243512d..17abdfe 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -159,6 +159,22 @@ void usbhs_bus_send_reset(struct usbhs_priv *priv) usbhs_bset(priv, DVSTCTR, (USBRST | UACT), USBRST); } +int usbhs_bus_get_speed(struct usbhs_priv *priv) +{ + u16 dvstctr = usbhs_read(priv, DVSTCTR); + + switch (RHST & dvstctr) { + case RHST_LOW_SPEED: + return USB_SPEED_LOW; + case RHST_FULL_SPEED: + return USB_SPEED_FULL; + case RHST_HIGH_SPEED: + return USB_SPEED_HIGH; + } + + return USB_SPEED_UNKNOWN; +} + int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable) { struct platform_device *pdev = usbhs_priv_to_pdev(priv); diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 54b5924..a5cef81 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -264,6 +264,7 @@ void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable); */ void usbhs_bus_send_sof_enable(struct usbhs_priv *priv); void usbhs_bus_send_reset(struct usbhs_priv *priv); +int usbhs_bus_get_speed(struct usbhs_priv *priv); int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable); /* diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 74ef49e..ab12030 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -168,20 +168,6 @@ void usbhs_mod_remove(struct usbhs_priv *priv) /* * status functions */ -int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state) -{ - switch (irq_state->dvstctr & RHST) { - case RHST_LOW_SPEED: - return USB_SPEED_LOW; - case RHST_FULL_SPEED: - return USB_SPEED_FULL; - case RHST_HIGH_SPEED: - return USB_SPEED_HIGH; - } - - return USB_SPEED_UNKNOWN; -} - int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state) { int state = irq_state->intsts0 & DVSQ_MASK; @@ -221,8 +207,6 @@ static void usbhs_status_get_each_irq(struct usbhs_priv *priv, state->intsts0 = usbhs_read(priv, INTSTS0); state->intsts1 = usbhs_read(priv, INTSTS1); - state->dvstctr = usbhs_read(priv, DVSTCTR); - /* mask */ if (mod) { state->brdysts = usbhs_read(priv, BRDYSTS); diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index 5c845a2..93edb1f 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h @@ -30,7 +30,6 @@ struct usbhs_irq_state { u16 brdysts; u16 nrdysts; u16 bempsts; - u16 dvstctr; }; struct usbhs_mod { @@ -99,7 +98,6 @@ void usbhs_mod_autonomy_mode(struct usbhs_priv *priv); /* * status functions */ -int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state); int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state); int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state); diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index babd90c..ae7d816 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -353,7 +353,7 @@ static int usbhsg_irq_dev_state(struct usbhs_priv *priv, struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); struct device *dev = usbhsg_gpriv_to_dev(gpriv); - gpriv->gadget.speed = usbhs_status_get_usb_speed(irq_state); + gpriv->gadget.speed = usbhs_bus_get_speed(priv); dev_dbg(dev, "state = %x : speed : %d\n", usbhs_status_get_device_state(irq_state), -- cgit v0.10.2 From 356db7edadb98edbc60abf46b86a1816bfee6278 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:02:21 -0700 Subject: usb: gadget: renesas_usbhs: move usbhsp_type() to usbhs_pipe_type() Pipe type check macro will be used in other files. This patch move local macro to global macro. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 1f86bed..1810133 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -29,9 +29,6 @@ #define usbhsp_flags_has(p, f) ((p)->flags & USBHS_PIPE_FLAGS_##f) #define usbhsp_flags_init(p) do {(p)->flags = 0; } while (0) -#define usbhsp_type(p) ((p)->pipe_type) -#define usbhsp_type_is(p, t) ((p)->pipe_type == t) - /* * for debug */ @@ -287,8 +284,8 @@ static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe) /* * only ISO / BULK pipe can use double buffer */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) || - usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) || + usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC)) return 1; return 0; @@ -325,20 +322,20 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, */ /* TYPE */ - type = type_array[usbhsp_type(pipe)]; + type = type_array[usbhs_pipe_type(pipe)]; /* BFRE */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || - usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || + usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) bfre = 0; /* FIXME */ /* DBLB */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || - usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) || + usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) dblb = (is_double) ? DBLB : 0; /* CNTMD */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK)) cntmd = 0; /* FIXME */ /* DIR */ @@ -353,7 +350,7 @@ static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, usbhsp_flags_set(pipe, IS_DIR_IN); /* SHTNAK */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) && + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) && !dir) shtnak = SHTNAK; @@ -413,9 +410,9 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe) * INT : 64 byte * ISOC: 512 byte */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_CONTROL)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_CONTROL)) buff_size = 256; - else if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) + else if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) buff_size = 64; else buff_size = 512; @@ -425,7 +422,7 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe) /* BUFNMB has been reserved for INT pipe * see above */ - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) { + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) { bufnmb = pipe_num - 2; } else { bufnmb = info->bufnmb_last; @@ -502,7 +499,7 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) */ pipe = NULL; usbhs_for_each_pipe_with_dcp(pos, priv, i) { - if (!usbhsp_type_is(pos, type)) + if (!usbhs_pipe_type_is(pos, type)) continue; if (usbhsp_flags_has(pos, IS_USED)) continue; @@ -551,7 +548,7 @@ void usbhs_pipe_init(struct usbhs_priv *priv, */ info->bufnmb_last = 4; usbhs_for_each_pipe_with_dcp(pipe, priv, i) { - if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) + if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_INT)) info->bufnmb_last++; usbhsp_flags_init(pipe); @@ -689,7 +686,9 @@ int usbhs_pipe_probe(struct usbhs_priv *priv) */ usbhs_for_each_pipe_with_dcp(pipe, priv, i) { pipe->priv = priv; - usbhsp_type(pipe) = pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK; + + usbhs_pipe_type(pipe) = + pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK; dev_dbg(dev, "pipe %x\t: %s\n", i, usbhsp_pipe_name[pipe_type[i]]); diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 6760dff..fc776ac 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -104,6 +104,9 @@ void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 epnum, u16 maxp); #define usbhs_pipe_to_fifo(p) ((p)->fifo) #define usbhs_pipe_is_busy(p) usbhs_pipe_to_fifo(p) +#define usbhs_pipe_type(p) ((p)->pipe_type) +#define usbhs_pipe_type_is(p, t) ((p)->pipe_type == t) + /* * dcp control */ -- cgit v0.10.2 From ef8bedb9048c293dfa85ac36482a1970646a8272 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:02:33 -0700 Subject: usb: gadget: renesas_usbhs: move usbhs_usbreq_get/set_val() to common.c usbhs_usbreq_get/set_val() functions were in pipe.c file, but it is irrelevant to pipe. this patch move it to common.c Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 17abdfe..b327458 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -147,6 +147,32 @@ int usbhs_frame_get_num(struct usbhs_priv *priv) } /* + * usb request functions + */ +void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) +{ + u16 val; + + val = usbhs_read(priv, USBREQ); + req->bRequest = (val >> 8) & 0xFF; + req->bRequestType = (val >> 0) & 0xFF; + + req->wValue = usbhs_read(priv, USBVAL); + req->wIndex = usbhs_read(priv, USBINDX); + req->wLength = usbhs_read(priv, USBLENG); +} + +void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) +{ + usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType); + usbhs_write(priv, USBVAL, req->wValue); + usbhs_write(priv, USBINDX, req->wIndex); + usbhs_write(priv, USBLENG, req->wLength); + + usbhs_bset(priv, DCPCTR, SUREQ, SUREQ); +} + +/* * bus/vbus functions */ void usbhs_bus_send_sof_enable(struct usbhs_priv *priv) diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index a5cef81..3b23380 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -185,6 +185,7 @@ struct usbhs_priv; /* PIPEnCTR */ /* DCPCTR */ #define BSTS (1 << 15) /* Buffer Status */ +#define SUREQ (1 << 14) /* Sending SETUP Token */ #define CSSTS (1 << 12) /* CSSTS Status */ #define SQCLR (1 << 8) /* Toggle Bit Clear */ #define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */ @@ -260,6 +261,12 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable); void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable); /* + * usb request + */ +void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); +void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); + +/* * bus */ void usbhs_bus_send_sof_enable(struct usbhs_priv *priv); diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 1810133..6aaa436 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -40,30 +40,6 @@ static char *usbhsp_pipe_name[] = { }; /* - * usb request functions - */ -void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) -{ - u16 val; - - val = usbhs_read(priv, USBREQ); - req->bRequest = (val >> 8) & 0xFF; - req->bRequestType = (val >> 0) & 0xFF; - - req->wValue = usbhs_read(priv, USBVAL); - req->wIndex = usbhs_read(priv, USBINDX); - req->wLength = usbhs_read(priv, USBLENG); -} - -void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) -{ - usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType); - usbhs_write(priv, USBVAL, req->wValue); - usbhs_write(priv, USBINDX, req->wIndex); - usbhs_write(priv, USBLENG, req->wLength); -} - -/* * DCPCTR/PIPEnCTR functions */ static void usbhsp_pipectrl_set(struct usbhs_pipe *pipe, u16 mask, u16 val) diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index fc776ac..8120fad 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -71,12 +71,6 @@ struct usbhs_pipe_info { #define usbhs_priv_to_pipeinfo(pr) (&(pr)->pipe_info) /* - * usb request - */ -void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); -void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req); - -/* * pipe control */ struct usbhs_pipe -- cgit v0.10.2 From 0deb3e77025688e0650e1af672d3e42e15cd8916 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:02:45 -0700 Subject: usb: gadget: renesas_usbhs: remove unneeded parameter from usbhs_mod_is_host() it was possible to get usbhs_mod from usbhs_priv. this patch remove unneeded parameter. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index ab12030..0340fb9 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -93,8 +93,9 @@ struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id) return ret; } -int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod) +int usbhs_mod_is_host(struct usbhs_priv *priv) { + struct usbhs_mod *mod = usbhs_mod_get_current(priv); struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); if (!mod) diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index 93edb1f..0e2c5b6 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h @@ -88,7 +88,7 @@ struct usbhs_mod_info { struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id); struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv); void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *usb, int id); -int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod); +int usbhs_mod_is_host(struct usbhs_priv *priv); int usbhs_mod_change(struct usbhs_priv *priv, int id); int usbhs_mod_probe(struct usbhs_priv *priv); void usbhs_mod_remove(struct usbhs_priv *priv); diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 6aaa436..ff97a8e 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -545,9 +545,8 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, int dir_in) { struct device *dev = usbhs_priv_to_dev(priv); - struct usbhs_mod *mod = usbhs_mod_get_current(priv); struct usbhs_pipe *pipe; - int is_host = usbhs_mod_is_host(priv, mod); + int is_host = usbhs_mod_is_host(priv); int ret; u16 pipecfg, pipebuf; -- cgit v0.10.2 From 923520715b5a698271f8763e97eee65e21d5561e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:02:57 -0700 Subject: usb: gadget: renesas_usbhs: add usbhs_dcp_dir_for_host() renesas_usbhs device needs special bit settings if it was mod_host and dcp pipe. This patch support it. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 328238c..b9ab8aef 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -291,9 +291,13 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe, usbhsf_fifo_is_busy(fifo)) return -EBUSY; - if (usbhs_pipe_is_dcp(pipe)) + if (usbhs_pipe_is_dcp(pipe)) { base |= (1 == write) << 5; /* ISEL */ + if (usbhs_mod_is_host(priv)) + usbhs_dcp_dir_for_host(pipe, write); + } + /* "base" will be used below */ usbhs_write(priv, fifo->sel, base | MBW_32); diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index ff97a8e..5238287 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -630,6 +630,12 @@ void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe) usbhsp_pipectrl_set(pipe, CCPL, CCPL); } +void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out) +{ + usbhsp_pipe_cfg_set(pipe, DIR_OUT, + dir_out ? DIR_OUT : 0); +} + /* * pipe module function */ diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 8120fad..6073a5c 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -106,5 +106,6 @@ void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 epnum, u16 maxp); */ struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv); void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe); +void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out); #endif /* RENESAS_USB_PIPE_H */ -- cgit v0.10.2 From 2cc971978c0cabcd7760f4431270088cda72d8df Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:03:39 -0700 Subject: usb: gadget: renesas_usbhs: add struct usbhs_priv to packet done function There was no method to get struct usbhs_priv when packet transfer done function was called. This patch allow that callback function receive it. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index b9ab8aef..9bf3a43 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -171,7 +171,7 @@ __usbhs_pkt_handler_end: /******************** spin unlock ******************/ if (is_done) { - info->done(pkt); + info->done(priv, pkt); usbhs_pkt_start(pipe); } diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index ae7d816..dd4ca37 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -159,7 +159,7 @@ static void usbhsg_queue_pop(struct usbhsg_uep *uep, ureq->req.complete(&uep->ep, &ureq->req); } -static void usbhsg_queue_done(struct usbhs_pkt *pkt) +static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) { struct usbhs_pipe *pipe = pkt->pipe; struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe); diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 5238287..08d11b0 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -497,7 +497,8 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) } void usbhs_pipe_init(struct usbhs_priv *priv, - void (*done)(struct usbhs_pkt *pkt), + void (*done)(struct usbhs_priv *priv, + struct usbhs_pkt *pkt), int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)) { struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 6073a5c..05b706a 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -47,7 +47,7 @@ struct usbhs_pipe_info { int size; /* array size of "pipe" */ int bufnmb_last; /* FIXME : driver needs good allocator */ - void (*done)(struct usbhs_pkt *pkt); + void (*done)(struct usbhs_priv *priv, struct usbhs_pkt *pkt); int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map); }; @@ -80,7 +80,8 @@ void usbhs_pipe_remove(struct usbhs_priv *priv); int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe); void usbhs_pipe_init(struct usbhs_priv *priv, - void (*done)(struct usbhs_pkt *pkt), + void (*done)(struct usbhs_priv *priv, + struct usbhs_pkt *pkt), int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)); int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe); -- cgit v0.10.2 From bc6fbf59df75c4e3533d0daf7873f783c835bd53 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:04:00 -0700 Subject: usb: gadget: renesas_usbhs: add device select support in usbhs_pipe_config_update() device select method will be used on mod_host Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index dd4ca37..d5f80c4 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -489,7 +489,7 @@ static int usbhsg_ep_enable(struct usb_ep *ep, pipe->mod_private = uep; /* set epnum / maxp */ - usbhs_pipe_config_update(pipe, + usbhs_pipe_config_update(pipe, 0, usb_endpoint_num(desc), usb_endpoint_maxp(desc)); @@ -672,7 +672,7 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) /* dcp init */ dcp->pipe = usbhs_dcp_malloc(priv); dcp->pipe->mod_private = dcp; - usbhs_pipe_config_update(dcp->pipe, 0, 64); + usbhs_pipe_config_update(dcp->pipe, 0, 0, 64); /* * system config enble diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 08d11b0..4235a2e 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -418,14 +418,26 @@ static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe) (0xff & bufnmb) << 0; } -void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 epnum, u16 maxp) +void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, + u16 epnum, u16 maxp) { + if (devsel > 0xA) { + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_err(dev, "devsel error %d\n", devsel); + + devsel = 0; + } + usbhsp_pipe_barrier(pipe); pipe->maxp = maxp; usbhsp_pipe_select(pipe); - usbhsp_pipe_maxp_set(pipe, 0xFFFF, maxp); + usbhsp_pipe_maxp_set(pipe, 0xFFFF, + (devsel << 12) | + maxp); if (!usbhs_pipe_is_dcp(pipe)) usbhsp_pipe_cfg_set(pipe, 0x000F, epnum); diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 05b706a..aff8bfa 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -91,7 +91,8 @@ void usbhs_pipe_enable(struct usbhs_pipe *pipe); void usbhs_pipe_disable(struct usbhs_pipe *pipe); void usbhs_pipe_stall(struct usbhs_pipe *pipe); void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); -void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 epnum, u16 maxp); +void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, + u16 epnum, u16 maxp); #define usbhs_pipe_to_priv(p) ((p)->priv) #define usbhs_pipe_number(p) (int)((p) - (p)->priv->pipe_info.pipe) -- cgit v0.10.2 From 3cf8ed1284799406c506029207004e97d5b2c738 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:04:23 -0700 Subject: usb: gadget: renesas_usbhs: add usbhs_pipe_name() pipe name is usefull function for mod_xxx Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 4235a2e..7636f23 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -39,6 +39,11 @@ static char *usbhsp_pipe_name[] = { [USB_ENDPOINT_XFER_ISOC] = "ISO", }; +char *usbhs_pipe_name(struct usbhs_pipe *pipe) +{ + return usbhsp_pipe_name[usbhs_pipe_type(pipe)]; +} + /* * DCPCTR/PIPEnCTR functions */ @@ -592,7 +597,7 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, dev_dbg(dev, "enable pipe %d : %s (%s)\n", usbhs_pipe_number(pipe), - usbhsp_pipe_name[endpoint_type], + usbhs_pipe_name(pipe), usbhs_pipe_is_dir_in(pipe) ? "in" : "out"); /* diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index aff8bfa..3701835 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -73,6 +73,7 @@ struct usbhs_pipe_info { /* * pipe control */ +char *usbhs_pipe_name(struct usbhs_pipe *pipe); struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, int endpoint_type, int dir_in); int usbhs_pipe_probe(struct usbhs_priv *priv); -- cgit v0.10.2 From b331872b85c2ab388129af3343474e02e89498af Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:04:41 -0700 Subject: usb: gadget: renesas_usbhs: move done callback to struct usbhs_pkt transfer done function was registered in struct struct usbhs_pipe_info. It was good for mod_gadget, but not good for mod_host. This function move it to struct usbhs_pkt. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 9bf3a43..8b40726 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -54,6 +54,8 @@ static struct usbhs_pkt_handle usbhsf_null_handler = { }; void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, + void (*done)(struct usbhs_priv *priv, + struct usbhs_pkt *pkt), void *buf, int len, int zero) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); @@ -63,6 +65,11 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, /******************** spin lock ********************/ usbhs_lock(priv, flags); + if (!done) { + dev_err(dev, "no done function\n"); + return; + } + if (!pipe->handler) { dev_err(dev, "no handler function\n"); pipe->handler = &usbhsf_null_handler; @@ -82,6 +89,7 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, pkt->length = len; pkt->zero = zero; pkt->actual = 0; + pkt->done = done; usbhs_unlock(priv, flags); /******************** spin unlock ******************/ @@ -131,7 +139,6 @@ enum { static int usbhsf_pkt_handler(struct usbhs_pipe *pipe, int type) { struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); - struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); struct usbhs_pkt *pkt; struct device *dev = usbhs_priv_to_dev(priv); int (*func)(struct usbhs_pkt *pkt, int *is_done); @@ -171,7 +178,7 @@ __usbhs_pkt_handler_end: /******************** spin unlock ******************/ if (is_done) { - info->done(priv, pkt); + pkt->done(priv, pkt); usbhs_pkt_start(pipe); } diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 60aa20f..0e82d67 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -51,6 +51,8 @@ struct usbhs_pkt { struct list_head node; struct usbhs_pipe *pipe; struct usbhs_pkt_handle *handler; + void (*done)(struct usbhs_priv *priv, + struct usbhs_pkt *pkt); dma_addr_t dma; void *buf; int length; @@ -86,6 +88,8 @@ extern struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler; void usbhs_pkt_init(struct usbhs_pkt *pkt); void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, + void (*done)(struct usbhs_priv *priv, + struct usbhs_pkt *pkt), void *buf, int len, int zero); struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt); void usbhs_pkt_start(struct usbhs_pipe *pipe); diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index d5f80c4..5631285 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -127,24 +127,6 @@ LIST_HEAD(the_controller_link); /* * queue push/pop */ -static void usbhsg_queue_push(struct usbhsg_uep *uep, - struct usbhsg_request *ureq) -{ - struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); - struct device *dev = usbhsg_gpriv_to_dev(gpriv); - struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); - struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); - struct usb_request *req = &ureq->req; - - req->actual = 0; - req->status = -EINPROGRESS; - usbhs_pkt_push(pipe, pkt, req->buf, req->length, req->zero); - - dev_dbg(dev, "pipe %d : queue push (%d)\n", - usbhs_pipe_number(pipe), - req->length); -} - static void usbhsg_queue_pop(struct usbhsg_uep *uep, struct usbhsg_request *ureq, int status) @@ -170,6 +152,25 @@ static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) usbhsg_queue_pop(uep, ureq, 0); } +static void usbhsg_queue_push(struct usbhsg_uep *uep, + struct usbhsg_request *ureq) +{ + struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); + struct device *dev = usbhsg_gpriv_to_dev(gpriv); + struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq); + struct usb_request *req = &ureq->req; + + req->actual = 0; + req->status = -EINPROGRESS; + usbhs_pkt_push(pipe, pkt, usbhsg_queue_done, + req->buf, req->length, req->zero); + + dev_dbg(dev, "pipe %d : queue push (%d)\n", + usbhs_pipe_number(pipe), + req->length); +} + /* * dma map/unmap */ @@ -664,7 +665,6 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status) * pipe initialize and enable DCP */ usbhs_pipe_init(priv, - usbhsg_queue_done, usbhsg_dma_map_ctrl); usbhs_fifo_init(priv); usbhsg_uep_init(gpriv); diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 7636f23..6bc9e33 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -514,20 +514,12 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type) } void usbhs_pipe_init(struct usbhs_priv *priv, - void (*done)(struct usbhs_priv *priv, - struct usbhs_pkt *pkt), int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)) { struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv); - struct device *dev = usbhs_priv_to_dev(priv); struct usbhs_pipe *pipe; int i; - if (!done) { - dev_err(dev, "no done function\n"); - return; - } - /* * FIXME * @@ -554,7 +546,6 @@ void usbhs_pipe_init(struct usbhs_priv *priv, usbhs_pipe_clear(pipe); } - info->done = done; info->dma_map_ctrl = dma_map_ctrl; } diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 3701835..ddbd319 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -47,7 +47,6 @@ struct usbhs_pipe_info { int size; /* array size of "pipe" */ int bufnmb_last; /* FIXME : driver needs good allocator */ - void (*done)(struct usbhs_priv *priv, struct usbhs_pkt *pkt); int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map); }; @@ -81,8 +80,6 @@ void usbhs_pipe_remove(struct usbhs_priv *priv); int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe); int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe); void usbhs_pipe_init(struct usbhs_priv *priv, - void (*done)(struct usbhs_priv *priv, - struct usbhs_pkt *pkt), int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)); int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe); -- cgit v0.10.2 From 654c35ab5e643aa7ff34fbc11315f4a6e3a3e1f5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:04:53 -0700 Subject: usb: gadget: renesas_usbhs: disable auto paket start on usbhs_pkt_push() Automatically packet start by usbhs_pkt_push() was useful. But the pushed packet will be called twice if new packet was pushed on usbhs_pkt :: done callback. (1st is called by usbhs_pkt_push(), 2nd is called by usbhsf_pkt_handler()) This patch disables automatic packet start, and clarified packet start timing. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 8b40726..87f56b6 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -93,8 +93,6 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, usbhs_unlock(priv, flags); /******************** spin unlock ******************/ - - usbhs_pkt_start(pipe); } static void __usbhsf_pkt_del(struct usbhs_pkt *pkt) diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 5631285..d3d833b 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -165,6 +165,7 @@ static void usbhsg_queue_push(struct usbhsg_uep *uep, req->status = -EINPROGRESS; usbhs_pkt_push(pipe, pkt, usbhsg_queue_done, req->buf, req->length, req->zero); + usbhs_pkt_start(pipe); dev_dbg(dev, "pipe %d : queue push (%d)\n", usbhs_pipe_number(pipe), -- cgit v0.10.2 From 6e6db82ba9bf2d5912897f77ccf6902cb8543372 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:05:30 -0700 Subject: usb: gadget: renesas_usbhs: modify pipe sequence settings renesas_usbhs can manually set DATA0/DATA1. This patch is prepare for mod_host support Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 3b23380..7822b0b 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -187,8 +187,9 @@ struct usbhs_priv; #define BSTS (1 << 15) /* Buffer Status */ #define SUREQ (1 << 14) /* Sending SETUP Token */ #define CSSTS (1 << 12) /* CSSTS Status */ -#define SQCLR (1 << 8) /* Toggle Bit Clear */ #define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */ +#define SQCLR (1 << 8) /* Toggle Bit Clear */ +#define SQSET (1 << 7) /* Toggle Bit Set */ #define PBUSY (1 << 5) /* Pipe Busy */ #define PID_MASK (0x3) /* Response PID */ #define PID_NAK 0 diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index d3d833b..4cc7ee0 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -265,7 +265,7 @@ static int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv, if (!usbhsg_status_has(gpriv, USBHSG_STATUS_WEDGE)) { usbhs_pipe_disable(pipe); - usbhs_pipe_clear_sequence(pipe); + usbhs_pipe_sequence_data0(pipe); usbhs_pipe_enable(pipe); } @@ -479,7 +479,7 @@ static int usbhsg_ep_enable(struct usb_ep *ep, */ if (uep->pipe) { usbhs_pipe_clear(uep->pipe); - usbhs_pipe_clear_sequence(uep->pipe); + usbhs_pipe_sequence_data0(uep->pipe); return 0; } diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 6bc9e33..1acbe29 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -471,9 +471,12 @@ int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe) return usbhsp_flags_has(pipe, IS_DIR_HOST); } -void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe) +void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data) { - usbhsp_pipectrl_set(pipe, SQCLR, SQCLR); + u16 mask = (SQCLR | SQSET); + u16 val = (data) ? SQSET : SQCLR; + + usbhsp_pipectrl_set(pipe, mask, val); } void usbhs_pipe_clear(struct usbhs_pipe *pipe) @@ -584,7 +587,7 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv, usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg); usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf); - usbhs_pipe_clear_sequence(pipe); + usbhs_pipe_sequence_data0(pipe); dev_dbg(dev, "enable pipe %d : %s (%s)\n", usbhs_pipe_number(pipe), diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index ddbd319..6334fc6 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -82,7 +82,6 @@ int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe); void usbhs_pipe_init(struct usbhs_priv *priv, int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map)); int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe); -void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe); void usbhs_pipe_clear(struct usbhs_pipe *pipe); int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe); void usbhs_pipe_enable(struct usbhs_pipe *pipe); @@ -92,6 +91,10 @@ void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo); void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel, u16 epnum, u16 maxp); +#define usbhs_pipe_sequence_data0(pipe) usbhs_pipe_data_sequence(pipe, 0) +#define usbhs_pipe_sequence_data1(pipe) usbhs_pipe_data_sequence(pipe, 1) +void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data); + #define usbhs_pipe_to_priv(p) ((p)->priv) #define usbhs_pipe_number(p) (int)((p) - (p)->priv->pipe_info.pipe) #define usbhs_pipe_is_dcp(p) ((p)->priv->pipe_info.pipe == (p)) -- cgit v0.10.2 From 6ff5d09bd2fd5943f5b16cb1e3453446a1f575e2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:05:51 -0700 Subject: usb: gadget: renesas_usbhs: disable pipe on top of interrupt When data read interrupt happened, the pipe is BUF which means "enable". then, next un-necessary interrupt/token might be issued again when all data were popped from fifo. It will cause un-understandable bug. This patch decides pipe disable on top of read interrupt. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 87f56b6..1a345c2 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -476,6 +476,20 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done) total_len = len; /* + * update actual length first here to decide disable pipe. + * if this pipe keeps BUF status and all data were popped, + * then, next interrupt/token will be issued again + */ + pkt->actual += total_len; + + if ((pkt->actual == pkt->length) || /* receive all data */ + (total_len < maxp)) { /* short packet */ + *is_done = 1; + usbhsf_rx_irq_ctrl(pipe, 0); + usbhs_pipe_disable(pipe); /* disable pipe first */ + } + + /* * Buffer clear if Zero-Length packet * * see @@ -505,16 +519,7 @@ static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done) buf[i] = (data >> ((i & 0x03) * 8)) & 0xff; } - pkt->actual += total_len; - usbhs_fifo_read_end: - if ((pkt->actual == pkt->length) || /* receive all data */ - (total_len < maxp)) { /* short packet */ - *is_done = 1; - usbhsf_rx_irq_ctrl(pipe, 0); - usbhs_pipe_disable(pipe); - } - dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n", usbhs_pipe_number(pipe), pkt->length, pkt->actual, *is_done, pkt->zero); -- cgit v0.10.2 From f427eb64f4c5433a91da5eb139970dd5cbad9082 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:06:12 -0700 Subject: usb: gadget: renesas_usbhs: support otg pin control some renesas_usbhs device is supporting OTG external device interface. In that device, it is necessary to control PWEN/EXTLP on DVSTCTR. This patch support it. But renesas_usbhs driver doesn't have OTG support for now. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index 0ea71f8..c20612e 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -808,6 +808,7 @@ static struct usbhs_private usbhs1_private = { }, .driver_param = { .buswait_bwait = 4, + .has_otg = 1, .pipe_type = usbhs1_pipe_cfg, .pipe_size = ARRAY_SIZE(usbhs1_pipe_cfg), }, diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index b327458..1161d78 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -114,6 +114,10 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable) { u16 mask = DCFM | DRPD | DPRPU; u16 val = DCFM | DRPD; + int has_otg = usbhs_get_dparam(priv, has_otg); + + if (has_otg) + usbhs_bset(priv, DVSTCTR, (EXTLP | PWEN), (EXTLP | PWEN)); /* * if enable diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 040d8bb..e5a40c3 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -136,6 +136,11 @@ struct renesas_usbhs_driver_param { * pio <--> dma border. */ int pio_dma_border; /* default is 64byte */ + + /* + * option: + */ + u32 has_otg:1; /* for controlling PWEN/EXTLP */ }; /* -- cgit v0.10.2 From a9be4a45627c0eaa5f3f16987243530f4df6b514 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:06:35 -0700 Subject: usb: gadget: renesas_usbhs: make sure SOF packet sending-out control It is enabled to set SOF packet output bit when USBRST bit was set. And USBRST bit should be set 0 when SOF packet was output. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 1161d78..895eb44 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -181,6 +181,13 @@ void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) */ void usbhs_bus_send_sof_enable(struct usbhs_priv *priv) { + u16 status = usbhs_read(priv, DVSTCTR) & (USBRST | UACT); + + if (status != USBRST) { + struct device *dev = usbhs_priv_to_dev(priv); + dev_err(dev, "usbhs should be reset\n"); + } + usbhs_bset(priv, DVSTCTR, (USBRST | UACT), UACT); } -- cgit v0.10.2 From eb05191f6a9aba8344a0d5f28642b5ecf44d49a4 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:06:46 -0700 Subject: usb: gadget: renesas_usbhs: add usbhs_set_device_speed() support for host mod_host needs device speed setup function Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 895eb44..c3aef40 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -227,6 +227,48 @@ static void usbhsc_bus_init(struct usbhs_priv *priv) } /* + * device configuration + */ +int usbhs_set_device_speed(struct usbhs_priv *priv, int devnum, + u16 upphub, u16 hubport, u16 speed) +{ + struct device *dev = usbhs_priv_to_dev(priv); + u16 usbspd = 0; + u32 reg = DEVADD0 + (2 * devnum); + + if (devnum > 10) { + dev_err(dev, "cannot set speed to unknown device %d\n", devnum); + return -EIO; + } + + if (upphub > 0xA) { + dev_err(dev, "unsupported hub number %d\n", upphub); + return -EIO; + } + + switch (speed) { + case USB_SPEED_LOW: + usbspd = USBSPD_SPEED_LOW; + break; + case USB_SPEED_FULL: + usbspd = USBSPD_SPEED_FULL; + break; + case USB_SPEED_HIGH: + usbspd = USBSPD_SPEED_HIGH; + break; + default: + dev_err(dev, "unsupported speed %d\n", speed); + return -EIO; + } + + usbhs_write(priv, reg, UPPHUB(upphub) | + HUBPORT(hubport)| + USBSPD(usbspd)); + + return 0; +} + +/* * local functions */ static void usbhsc_set_buswait(struct usbhs_priv *priv) diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 7822b0b..0e867a3 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -90,6 +90,17 @@ struct usbhs_priv; #define PIPE9TRN 0x00BA #define PIPEATRE 0x00BC #define PIPEATRN 0x00BE +#define DEVADD0 0x00D0 /* Device address n configuration */ +#define DEVADD1 0x00D2 +#define DEVADD2 0x00D4 +#define DEVADD3 0x00D6 +#define DEVADD4 0x00D8 +#define DEVADD5 0x00DA +#define DEVADD6 0x00DC +#define DEVADD7 0x00DE +#define DEVADD8 0x00E0 +#define DEVADD9 0x00E2 +#define DEVADDA 0x00E4 /* SYSCFG */ #define SCKE (1 << 10) /* USB Module Clock Enable */ @@ -206,6 +217,14 @@ struct usbhs_priv; /* FRMNUM */ #define FRNM_MASK (0x7FF) +/* DEVADDn */ +#define UPPHUB(x) (((x) & 0xF) << 11) /* HUB Register */ +#define HUBPORT(x) (((x) & 0x7) << 8) /* HUB Port for Target Device */ +#define USBSPD(x) (((x) & 0x3) << 6) /* Device Transfer Rate */ +#define USBSPD_SPEED_LOW 0x1 +#define USBSPD_SPEED_FULL 0x2 +#define USBSPD_SPEED_HIGH 0x3 + /* * struct */ @@ -281,6 +300,12 @@ int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable); int usbhs_frame_get_num(struct usbhs_priv *priv); /* + * device config + */ +int usbhs_set_device_speed(struct usbhs_priv *priv, int devnum, u16 upphub, + u16 hubport, u16 speed); + +/* * data */ struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev); -- cgit v0.10.2 From 89c1d2e7b5993db33805b42e3675289920812f6f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:06:57 -0700 Subject: usb: gadget: renesas_usbhs: add INTSTS1 interrupt support mod_host use INTSTS1 interrupt Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 0e867a3..dc9490d 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -172,6 +172,15 @@ struct usbhs_priv; #define NODATA_STATUS_STAGE 5 /* Control write NoData status stage */ #define SEQUENCE_ERROR 6 /* Control transfer sequence error */ +/* INTSTS1 */ +#define OVRCR (1 << 15) /* OVRCR Interrupt Status */ +#define BCHG (1 << 14) /* USB Bus Change Interrupt Status */ +#define DTCH (1 << 12) /* USB Disconnection Detect Interrupt Status */ +#define ATTCH (1 << 11) /* ATTCH Interrupt Status */ +#define EOFERR (1 << 6) /* EOF Error Detect Interrupt Status */ +#define SIGN (1 << 5) /* Setup Transaction Error Interrupt Status */ +#define SACK (1 << 4) /* Setup Transaction ACK Response Interrupt Status */ + /* PIPECFG */ /* DCPCFG */ #define TYPE_NONE (0 << 14) /* Transfer Type */ diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 0340fb9..993c2ca 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -254,6 +254,8 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) * see also * usbhs_irq_setting_update */ + + /* INTSTS0 */ if (irq_state.intsts0 & VBINT) usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state); @@ -269,12 +271,26 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) if (irq_state.intsts0 & BRDY) usbhs_mod_call(priv, irq_ready, priv, &irq_state); + /* INTSTS1 */ + if (irq_state.intsts1 & ATTCH) + usbhs_mod_call(priv, irq_attch, priv, &irq_state); + + if (irq_state.intsts1 & DTCH) + usbhs_mod_call(priv, irq_dtch, priv, &irq_state); + + if (irq_state.intsts1 & SIGN) + usbhs_mod_call(priv, irq_sign, priv, &irq_state); + + if (irq_state.intsts1 & SACK) + usbhs_mod_call(priv, irq_sack, priv, &irq_state); + return IRQ_HANDLED; } void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) { u16 intenb0 = 0; + u16 intenb1 = 0; struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); /* @@ -286,6 +302,7 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) * - update INTSTS0 */ usbhs_write(priv, INTENB0, 0); + usbhs_write(priv, INTENB1, 0); usbhs_write(priv, BEMPENB, 0); usbhs_write(priv, BRDYENB, 0); @@ -303,6 +320,9 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) intenb0 |= VBSE; if (mod) { + /* + * INTSTS0 + */ if (mod->irq_ctrl_stage) intenb0 |= CTRE; @@ -315,8 +335,26 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) usbhs_write(priv, BRDYENB, mod->irq_brdysts); intenb0 |= BRDYE; } + + /* + * INTSTS1 + */ + if (mod->irq_attch) + intenb1 |= ATTCHE; + + if (mod->irq_attch) + intenb1 |= DTCHE; + + if (mod->irq_sign) + intenb1 |= SIGNE; + + if (mod->irq_sack) + intenb1 |= SACKE; } if (intenb0) usbhs_write(priv, INTENB0, intenb0); + + if (intenb1) + usbhs_write(priv, INTENB1, intenb1); } diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index 0e2c5b6..df4b61d 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h @@ -41,26 +41,48 @@ struct usbhs_mod { int (*start)(struct usbhs_priv *priv); int (*stop)(struct usbhs_priv *priv); - /* INTSTS0 :: DVST (DVSQ) */ + /* + * INTSTS0 + */ + + /* DVST (DVSQ) */ int (*irq_dev_state)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); - /* INTSTS0 :: CTRT (CTSQ) */ + /* CTRT (CTSQ) */ int (*irq_ctrl_stage)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); - /* INTSTS0 :: BEMP */ - /* BEMPSTS */ + /* BEMP / BEMPSTS */ int (*irq_empty)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); u16 irq_bempsts; - /* INTSTS0 :: BRDY */ - /* BRDYSTS */ + /* BRDY / BRDYSTS */ int (*irq_ready)(struct usbhs_priv *priv, struct usbhs_irq_state *irq_state); u16 irq_brdysts; + /* + * INTSTS1 + */ + + /* ATTCHE */ + int (*irq_attch)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + + /* DTCHE */ + int (*irq_dtch)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + + /* SIGN */ + int (*irq_sign)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + + /* SACK */ + int (*irq_sack)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); + struct usbhs_priv *priv; }; -- cgit v0.10.2 From 9e74d601de8adb0fe96e100e459ac52f2ddd47f3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:07:08 -0700 Subject: usb: gadget: renesas_usbhs: add data/status stage handler mod_host needs data/status stage handler Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 1a345c2..16b12b0 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -321,6 +321,151 @@ static int usbhsf_fifo_select(struct usbhs_pipe *pipe, } /* + * DCP status stage + */ +static int usbhs_dcp_dir_switch_to_write(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */ + struct device *dev = usbhs_priv_to_dev(priv); + int ret; + + usbhs_pipe_disable(pipe); + + ret = usbhsf_fifo_select(pipe, fifo, 1); + if (ret < 0) { + dev_err(dev, "%s() faile\n", __func__); + return ret; + } + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + + usbhsf_fifo_clear(pipe, fifo); + usbhsf_send_terminator(pipe, fifo); + + usbhsf_fifo_unselect(pipe, fifo); + + usbhsf_tx_irq_ctrl(pipe, 1); + usbhs_pipe_enable(pipe); + + return ret; +} + +static int usbhs_dcp_dir_switch_to_read(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */ + struct device *dev = usbhs_priv_to_dev(priv); + int ret; + + usbhs_pipe_disable(pipe); + + ret = usbhsf_fifo_select(pipe, fifo, 0); + if (ret < 0) { + dev_err(dev, "%s() fail\n", __func__); + return ret; + } + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + usbhsf_fifo_clear(pipe, fifo); + + usbhsf_fifo_unselect(pipe, fifo); + + usbhsf_rx_irq_ctrl(pipe, 1); + usbhs_pipe_enable(pipe); + + return ret; + +} + +static int usbhs_dcp_dir_switch_done(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + + if (pkt->handler == &usbhs_dcp_status_stage_in_handler) + usbhsf_tx_irq_ctrl(pipe, 0); + else + usbhsf_rx_irq_ctrl(pipe, 0); + + pkt->actual = pkt->length; + *is_done = 1; + + return 0; +} + +struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler = { + .prepare = usbhs_dcp_dir_switch_to_write, + .try_run = usbhs_dcp_dir_switch_done, +}; + +struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler = { + .prepare = usbhs_dcp_dir_switch_to_read, + .try_run = usbhs_dcp_dir_switch_done, +}; + +/* + * DCP data stage (push) + */ +static int usbhsf_dcp_data_stage_try_push(struct usbhs_pkt *pkt, int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + + /* + * change handler to PIO push + */ + pkt->handler = &usbhs_fifo_pio_push_handler; + + return pkt->handler->prepare(pkt, is_done); +} + +struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler = { + .prepare = usbhsf_dcp_data_stage_try_push, +}; + +/* + * DCP data stage (pop) + */ +static int usbhsf_dcp_data_stage_prepare_pop(struct usbhs_pkt *pkt, + int *is_done) +{ + struct usbhs_pipe *pipe = pkt->pipe; + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); + + if (usbhs_pipe_is_busy(pipe)) + return 0; + + /* + * prepare pop for DCP should + * - change DCP direction, + * - clear fifo + * - DATA1 + */ + usbhs_pipe_disable(pipe); + + usbhs_pipe_sequence_data1(pipe); /* DATA1 */ + + usbhsf_fifo_select(pipe, fifo, 0); + usbhsf_fifo_clear(pipe, fifo); + usbhsf_fifo_unselect(pipe, fifo); + + /* + * change handler to PIO pop + */ + pkt->handler = &usbhs_fifo_pio_pop_handler; + + return pkt->handler->prepare(pkt, is_done); +} + +struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler = { + .prepare = usbhsf_dcp_data_stage_prepare_pop, +}; + +/* * PIO push handler */ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done) diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 0e82d67..32a7b24 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -85,6 +85,11 @@ extern struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler; extern struct usbhs_pkt_handle usbhs_fifo_dma_push_handler; extern struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler; +extern struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler; +extern struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler; + +extern struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler; +extern struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler; void usbhs_pkt_init(struct usbhs_pkt *pkt); void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, -- cgit v0.10.2 From e2eddc6103c7f00a2a1a0dfe5fac494d039b099a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:07:20 -0700 Subject: usb: gadget: renesas_usbhs: usbhs_dcp_control_transfer_done() cares mod_host CCPL setting is needed on only mod_gadget. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index 1acbe29..c74389c 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -636,10 +636,14 @@ struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv) void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe) { + struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); + WARN_ON(!usbhs_pipe_is_dcp(pipe)); usbhs_pipe_enable(pipe); - usbhsp_pipectrl_set(pipe, CCPL, CCPL); + + if (!usbhs_mod_is_host(priv)) /* funconly */ + usbhsp_pipectrl_set(pipe, CCPL, CCPL); } void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out) -- cgit v0.10.2 From 034d7c13a79c67d3b52dd782d68e6c324613878a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Oct 2011 22:07:40 -0700 Subject: usb: gadget: renesas_usbhs: add mod_host support This is mod_host prototype support for renesas_usbhs driver. It doesn't support USB-Hub, and USB-DMAC for now. Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 3e5b235..060e0e2 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -515,6 +515,19 @@ config USB_R8A66597_HCD To compile this driver as a module, choose M here: the module will be called r8a66597-hcd. +config USB_RENESAS_USBHS_HCD + tristate "Renesas USBHS HCD support" + depends on USB + depends on USB_RENESAS_USBHS + help + The Renesas USBHS is a USB 2.0 host and peripheral controller. + + Enable this option if your board has this chip, and you want + to use it as a host controller. If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called renesas-usbhs. + config USB_WHCI_HCD tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)" depends on EXPERIMENTAL diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index ce08345..e44984d 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -6,4 +6,5 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o renesas_usbhs-y := common.o mod.o pipe.o fifo.o +renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_HCD) += mod_host.o renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_UDC) += mod_gadget.o diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 993c2ca..2d3b09d 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -140,10 +140,14 @@ int usbhs_mod_probe(struct usbhs_priv *priv) /* * install host/gadget driver */ - ret = usbhs_mod_gadget_probe(priv); + ret = usbhs_mod_host_probe(priv); if (ret < 0) return ret; + ret = usbhs_mod_gadget_probe(priv); + if (ret < 0) + goto mod_init_host_err; + /* irq settings */ ret = request_irq(priv->irq, usbhs_interrupt, 0, dev_name(dev), priv); @@ -156,12 +160,15 @@ int usbhs_mod_probe(struct usbhs_priv *priv) mod_init_gadget_err: usbhs_mod_gadget_remove(priv); +mod_init_host_err: + usbhs_mod_host_remove(priv); return ret; } void usbhs_mod_remove(struct usbhs_priv *priv) { + usbhs_mod_host_remove(priv); usbhs_mod_gadget_remove(priv); free_irq(priv->irq, priv); } diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index df4b61d..1f28ed2 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h @@ -139,8 +139,21 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod); }) /* - * gadget control + * host / gadget control */ +#ifdef CONFIG_USB_RENESAS_USBHS_HCD +extern int __devinit usbhs_mod_host_probe(struct usbhs_priv *priv); +extern int __devexit usbhs_mod_host_remove(struct usbhs_priv *priv); +#else +static inline int usbhs_mod_host_probe(struct usbhs_priv *priv) +{ + return 0; +} +static inline void usbhs_mod_host_remove(struct usbhs_priv *priv) +{ +} +#endif + #ifdef CONFIG_USB_RENESAS_USBHS_UDC extern int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv); extern void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv); diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c new file mode 100644 index 0000000..1c60331 --- /dev/null +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -0,0 +1,1313 @@ +/* + * Renesas USB driver + * + * Copyright (C) 2011 Renesas Solutions Corp. + * Kuninori Morimoto + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +/* + *** HARDWARE LIMITATION *** + * + * 1) renesas_usbhs has a limited number of controllable devices. + * it can control only 9 devices in generally. + * see DEVADDn / DCPMAXP / PIPEMAXP. + * + * 2) renesas_usbhs pipe number is limited. + * the pipe will be re-used for each devices. + * so, software should control DATA0/1 sequence of each devices. + */ + + +/* + * image of mod_host + * + * +--------+ + * | udev 0 | --> it is used when set address + * +--------+ + * + * +--------+ pipes are reused for each uep. + * | udev 1 |-+- [uep 0 (dcp) ] --+ pipe will be switched when + * +--------+ | | target device was changed + * +- [uep 1 (bulk)] --|---+ +--------------+ + * | +--------------> | pipe0 (dcp) | + * +- [uep 2 (bulk)] --|---|---+ +--------------+ + * | | | | pipe1 (isoc) | + * +--------+ | | | +--------------+ + * | udev 2 |-+- [uep 0 (dcp) ] --+ +-- |------> | pipe2 (bulk) | + * +--------+ | | | | +--------------+ + * +- [uep 1 (int) ] --|-+ | +------> | pipe3 (bulk) | + * | | | | +--------------+ + * +--------+ | +-|---|------> | pipe4 (int) | + * | udev 3 |-+- [uep 0 (dcp) ] --+ | | +--------------+ + * +--------+ | | | | .... | + * +- [uep 1 (bulk)] ------+ | | .... | + * | | + * +- [uep 2 (bulk)]-----------+ + */ + + +/* + * struct + */ +struct usbhsh_pipe_info { + unsigned int usr_cnt; /* see usbhsh_endpoint_alloc() */ +}; + +struct usbhsh_request { + struct urb *urb; + struct usbhs_pkt pkt; + struct list_head ureq_link; /* see hpriv :: ureq_link_xxx */ +}; + +struct usbhsh_device { + struct usb_device *usbv; + struct list_head ep_list_head; /* list of usbhsh_ep */ +}; + +struct usbhsh_ep { + struct usbhs_pipe *pipe; + struct usbhsh_device *udev; /* attached udev */ + struct list_head ep_list; /* list to usbhsh_device */ + + int maxp; +}; + +#define USBHSH_DEVICE_MAX 10 /* see DEVADDn / DCPMAXP / PIPEMAXP */ +#define USBHSH_PORT_MAX 7 /* see DEVADDn :: HUBPORT */ +struct usbhsh_hpriv { + struct usbhs_mod mod; + struct usbhs_pipe *dcp; + + struct usbhsh_device udev[USBHSH_DEVICE_MAX]; + + struct usbhsh_pipe_info *pipe_info; + int pipe_size; + + u32 port_stat; /* USB_PORT_STAT_xxx */ + + struct completion *done; + + /* see usbhsh_req_alloc/free */ + struct list_head ureq_link_active; + struct list_head ureq_link_free; +}; + + +static const char usbhsh_hcd_name[] = "renesas_usbhs host"; + +/* + * macro + */ +#define usbhsh_priv_to_hpriv(priv) \ + container_of(usbhs_mod_get(priv, USBHS_HOST), struct usbhsh_hpriv, mod) + +#define __usbhsh_for_each_hpipe(start, pos, h, i) \ + for (i = start, pos = (h)->hpipe + i; \ + i < (h)->hpipe_size; \ + i++, pos = (h)->hpipe + i) + +#define usbhsh_for_each_hpipe(pos, hpriv, i) \ + __usbhsh_for_each_hpipe(1, pos, hpriv, i) + +#define usbhsh_for_each_hpipe_with_dcp(pos, hpriv, i) \ + __usbhsh_for_each_hpipe(0, pos, hpriv, i) + +#define __usbhsh_for_each_udev(start, pos, h, i) \ + for (i = start, pos = (h)->udev + i; \ + i < USBHSH_DEVICE_MAX; \ + i++, pos = (h)->udev + i) + +#define usbhsh_for_each_udev(pos, hpriv, i) \ + __usbhsh_for_each_udev(1, pos, hpriv, i) + +#define usbhsh_for_each_udev_with_dev0(pos, hpriv, i) \ + __usbhsh_for_each_udev(0, pos, hpriv, i) + +#define usbhsh_hcd_to_hpriv(h) (struct usbhsh_hpriv *)((h)->hcd_priv) +#define usbhsh_hcd_to_dev(h) ((h)->self.controller) + +#define usbhsh_hpriv_to_priv(h) ((h)->mod.priv) +#define usbhsh_hpriv_to_dcp(h) ((h)->dcp) +#define usbhsh_hpriv_to_hcd(h) \ + container_of((void *)h, struct usb_hcd, hcd_priv) + +#define usbhsh_ep_to_uep(u) ((u)->hcpriv) +#define usbhsh_uep_to_pipe(u) ((u)->pipe) +#define usbhsh_uep_to_udev(u) ((u)->udev) +#define usbhsh_urb_to_ureq(u) ((u)->hcpriv) +#define usbhsh_urb_to_usbv(u) ((u)->dev) + +#define usbhsh_usbv_to_udev(d) dev_get_drvdata(&(d)->dev) + +#define usbhsh_udev_to_usbv(h) ((h)->usbv) + +#define usbhsh_pipe_info(p) ((p)->mod_private) + +#define usbhsh_device_number(h, d) ((int)((d) - (h)->udev)) +#define usbhsh_device_nth(h, d) ((h)->udev + d) +#define usbhsh_device0(h) usbhsh_device_nth(h, 0) + +#define usbhsh_port_stat_init(h) ((h)->port_stat = 0) +#define usbhsh_port_stat_set(h, s) ((h)->port_stat |= (s)) +#define usbhsh_port_stat_clear(h, s) ((h)->port_stat &= ~(s)) +#define usbhsh_port_stat_get(h) ((h)->port_stat) + +#define usbhsh_pkt_to_req(p) \ + container_of((void *)p, struct usbhsh_request, pkt) + +/* + * req alloc/free + */ +static void usbhsh_req_list_init(struct usbhsh_hpriv *hpriv) +{ + INIT_LIST_HEAD(&hpriv->ureq_link_active); + INIT_LIST_HEAD(&hpriv->ureq_link_free); +} + +static void usbhsh_req_list_quit(struct usbhsh_hpriv *hpriv) +{ + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usbhsh_request *ureq, *next; + + /* kfree all active ureq */ + list_for_each_entry_safe(ureq, next, + &hpriv->ureq_link_active, + ureq_link) { + dev_err(dev, "active ureq (%p) is force freed\n", ureq); + kfree(ureq); + } + + /* kfree all free ureq */ + list_for_each_entry_safe(ureq, next, &hpriv->ureq_link_free, ureq_link) + kfree(ureq); +} + +static struct usbhsh_request *usbhsh_req_alloc(struct usbhsh_hpriv *hpriv, + struct urb *urb, + gfp_t mem_flags) +{ + struct usbhsh_request *ureq; + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + + if (list_empty(&hpriv->ureq_link_free)) { + /* + * create new one if there is no free ureq + */ + ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags); + if (ureq) + INIT_LIST_HEAD(&ureq->ureq_link); + } else { + /* + * reuse "free" ureq if exist + */ + ureq = list_entry(hpriv->ureq_link_free.next, + struct usbhsh_request, + ureq_link); + if (ureq) + list_del_init(&ureq->ureq_link); + } + + if (!ureq) { + dev_err(dev, "ureq alloc fail\n"); + return NULL; + } + + usbhs_pkt_init(&ureq->pkt); + + /* + * push it to "active" list + */ + list_add_tail(&ureq->ureq_link, &hpriv->ureq_link_active); + ureq->urb = urb; + + return ureq; +} + +static void usbhsh_req_free(struct usbhsh_hpriv *hpriv, + struct usbhsh_request *ureq) +{ + struct usbhs_pkt *pkt = &ureq->pkt; + + usbhs_pkt_init(pkt); + + /* + * removed from "active" list, + * and push it to "free" list + */ + ureq->urb = NULL; + list_del_init(&ureq->ureq_link); + list_add_tail(&ureq->ureq_link, &hpriv->ureq_link_free); +} + +/* + * device control + */ + +static int usbhsh_device_has_endpoint(struct usbhsh_device *udev) +{ + return !list_empty(&udev->ep_list_head); +} + +static struct usbhsh_device *usbhsh_device_alloc(struct usbhsh_hpriv *hpriv, + struct urb *urb) +{ + struct usbhsh_device *udev = NULL; + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usb_device *usbv = usbhsh_urb_to_usbv(urb); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + int i; + + /* + * device 0 + */ + if (0 == usb_pipedevice(urb->pipe)) { + udev = usbhsh_device0(hpriv); + goto usbhsh_device_find; + } + + /* + * find unused device + */ + usbhsh_for_each_udev(udev, hpriv, i) { + if (usbhsh_udev_to_usbv(udev)) + continue; + goto usbhsh_device_find; + } + + dev_err(dev, "no free usbhsh_device\n"); + + return NULL; + +usbhsh_device_find: + if (usbhsh_device_has_endpoint(udev)) + dev_warn(dev, "udev have old endpoint\n"); + + /* uep will be attached */ + INIT_LIST_HEAD(&udev->ep_list_head); + + /* + * usbhsh_usbv_to_udev() + * usbhsh_udev_to_usbv() + * will be enable + */ + dev_set_drvdata(&usbv->dev, udev); + udev->usbv = usbv; + + /* set device config */ + usbhs_set_device_speed(priv, + usbhsh_device_number(hpriv, udev), + usbhsh_device_number(hpriv, udev), + 0, /* FIXME no parent */ + usbv->speed); + + dev_dbg(dev, "%s [%d](%p)\n", __func__, + usbhsh_device_number(hpriv, udev), udev); + + return udev; +} + +static void usbhsh_device_free(struct usbhsh_hpriv *hpriv, + struct usbhsh_device *udev) +{ + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usb_device *usbv = usbhsh_udev_to_usbv(udev); + + dev_dbg(dev, "%s [%d](%p)\n", __func__, + usbhsh_device_number(hpriv, udev), udev); + + if (usbhsh_device_has_endpoint(udev)) + dev_warn(dev, "udev still have endpoint\n"); + + /* + * usbhsh_usbv_to_udev() + * usbhsh_udev_to_usbv() + * will be disable + */ + dev_set_drvdata(&usbv->dev, NULL); + udev->usbv = NULL; +} + +/* + * end-point control + */ +struct usbhsh_ep *usbhsh_endpoint_alloc(struct usbhsh_hpriv *hpriv, + struct usbhsh_device *udev, + struct usb_host_endpoint *ep, + gfp_t mem_flags) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct usbhsh_ep *uep; + struct usbhsh_pipe_info *info; + struct usbhs_pipe *pipe, *best_pipe; + struct device *dev = usbhsh_hcd_to_dev(hcd); + struct usb_endpoint_descriptor *desc = &ep->desc; + int type, i; + unsigned int min_usr; + + uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags); + if (!uep) { + dev_err(dev, "usbhsh_ep alloc fail\n"); + return NULL; + } + type = usb_endpoint_type(desc); + + /* + * find best pipe for endpoint + * see + * HARDWARE LIMITATION + */ + min_usr = ~0; + best_pipe = NULL; + usbhs_for_each_pipe_with_dcp(pipe, priv, i) { + if (!usbhs_pipe_type_is(pipe, type)) + continue; + + info = usbhsh_pipe_info(pipe); + + if (min_usr > info->usr_cnt) { + min_usr = info->usr_cnt; + best_pipe = pipe; + } + } + + if (unlikely(!best_pipe)) { + dev_err(dev, "couldn't find best pipe\n"); + kfree(uep); + return NULL; + } + + /* + * init uep + */ + uep->pipe = best_pipe; + uep->maxp = usb_endpoint_maxp(desc); + usbhsh_uep_to_udev(uep) = udev; + usbhsh_ep_to_uep(ep) = uep; + + /* + * update pipe user count + */ + info = usbhsh_pipe_info(best_pipe); + info->usr_cnt++; + + /* init this endpoint, and attach it to udev */ + INIT_LIST_HEAD(&uep->ep_list); + list_add_tail(&uep->ep_list, &udev->ep_list_head); + + /* + * usbhs_pipe_config_update() should be called after + * usbhs_device_config() + * see + * DCPMAXP/PIPEMAXP + */ + usbhs_pipe_config_update(uep->pipe, + usbhsh_device_number(hpriv, udev), + usb_endpoint_num(desc), + uep->maxp); + + dev_dbg(dev, "%s [%d-%s](%p)\n", __func__, + usbhsh_device_number(hpriv, udev), + usbhs_pipe_name(pipe), uep); + + return uep; +} + +void usbhsh_endpoint_free(struct usbhsh_hpriv *hpriv, + struct usb_host_endpoint *ep) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep); + struct usbhsh_device *udev = usbhsh_uep_to_udev(uep); + struct usbhsh_pipe_info *info; + + if (!uep) + return; + + dev_dbg(dev, "%s [%d-%s](%p)\n", __func__, + usbhsh_device_number(hpriv, udev), + usbhs_pipe_name(uep->pipe), uep); + + info = usbhsh_pipe_info(uep->pipe); + info->usr_cnt--; + + /* remove this endpoint from udev */ + list_del_init(&uep->ep_list); + + usbhsh_uep_to_udev(uep) = NULL; + usbhsh_ep_to_uep(ep) = NULL; + + kfree(uep); +} + +/* + * queue push/pop + */ +static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt) +{ + struct usbhsh_request *ureq = usbhsh_pkt_to_req(pkt); + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct urb *urb = ureq->urb; + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "%s\n", __func__); + + if (!urb) { + dev_warn(dev, "pkt doesn't have urb\n"); + return; + } + + urb->actual_length = pkt->actual; + usbhsh_req_free(hpriv, ureq); + usbhsh_urb_to_ureq(urb) = NULL; + + usb_hcd_unlink_urb_from_ep(hcd, urb); + usb_hcd_giveback_urb(hcd, urb, 0); +} + +static int usbhsh_queue_push(struct usb_hcd *hcd, + struct usbhs_pipe *pipe, + struct urb *urb) +{ + struct usbhsh_request *ureq = usbhsh_urb_to_ureq(urb); + struct usbhs_pkt *pkt = &ureq->pkt; + struct device *dev = usbhsh_hcd_to_dev(hcd); + void *buf; + int len; + + if (usb_pipeisoc(urb->pipe)) { + dev_err(dev, "pipe iso is not supported now\n"); + return -EIO; + } + + if (usb_pipein(urb->pipe)) + pipe->handler = &usbhs_fifo_pio_pop_handler; + else + pipe->handler = &usbhs_fifo_pio_push_handler; + + buf = (void *)(urb->transfer_buffer + urb->actual_length); + len = urb->transfer_buffer_length - urb->actual_length; + + dev_dbg(dev, "%s\n", __func__); + usbhs_pkt_push(pipe, pkt, usbhsh_queue_done, + buf, len, (urb->transfer_flags & URB_ZERO_PACKET)); + usbhs_pkt_start(pipe); + + return 0; +} + +/* + * DCP setup stage + */ +static int usbhsh_is_request_address(struct urb *urb) +{ + struct usb_ctrlrequest *cmd; + + cmd = (struct usb_ctrlrequest *)urb->setup_packet; + + if ((DeviceOutRequest == cmd->bRequestType << 8) && + (USB_REQ_SET_ADDRESS == cmd->bRequest)) + return 1; + else + return 0; +} + +static void usbhsh_setup_stage_packet_push(struct usbhsh_hpriv *hpriv, + struct urb *urb, + struct usbhs_pipe *pipe) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct usb_ctrlrequest req; + struct device *dev = usbhs_priv_to_dev(priv); + + /* + * wait setup packet ACK + * see + * usbhsh_irq_setup_ack() + * usbhsh_irq_setup_err() + */ + DECLARE_COMPLETION(done); + hpriv->done = &done; + + /* copy original request */ + memcpy(&req, urb->setup_packet, sizeof(struct usb_ctrlrequest)); + + /* + * renesas_usbhs can not use original usb address. + * see HARDWARE LIMITATION. + * modify usb address here. + */ + if (usbhsh_is_request_address(urb)) { + /* FIXME */ + req.wValue = 1; + dev_dbg(dev, "create new address - %d\n", req.wValue); + } + + /* set request */ + usbhs_usbreq_set_val(priv, &req); + + /* + * wait setup packet ACK + */ + wait_for_completion(&done); + hpriv->done = NULL; + + dev_dbg(dev, "%s done\n", __func__); +} + +/* + * DCP data stage + */ +static void usbhsh_data_stage_packet_done(struct usbhs_priv *priv, + struct usbhs_pkt *pkt) +{ + struct usbhsh_request *ureq = usbhsh_pkt_to_req(pkt); + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct urb *urb = ureq->urb; + + /* this ureq was connected to urb when usbhsh_urb_enqueue() */ + + usbhsh_req_free(hpriv, ureq); + usbhsh_urb_to_ureq(urb) = NULL; +} + +static void usbhsh_data_stage_packet_push(struct usbhsh_hpriv *hpriv, + struct urb *urb, + struct usbhs_pipe *pipe) +{ + struct usbhsh_request *ureq; + struct usbhs_pkt *pkt; + + /* + * FIXME + * + * data stage uses ureq which is connected to urb + * see usbhsh_urb_enqueue() :: alloc new request. + * it will be freed in usbhsh_data_stage_packet_done() + */ + ureq = usbhsh_urb_to_ureq(urb); + pkt = &ureq->pkt; + + if (usb_pipein(urb->pipe)) + pipe->handler = &usbhs_dcp_data_stage_in_handler; + else + pipe->handler = &usbhs_dcp_data_stage_out_handler; + + usbhs_pkt_push(pipe, pkt, + usbhsh_data_stage_packet_done, + urb->transfer_buffer, + urb->transfer_buffer_length, + (urb->transfer_flags & URB_ZERO_PACKET)); +} + +/* + * DCP status stage + */ +static void usbhsh_status_stage_packet_push(struct usbhsh_hpriv *hpriv, + struct urb *urb, + struct usbhs_pipe *pipe) +{ + struct usbhsh_request *ureq; + struct usbhs_pkt *pkt; + + /* + * FIXME + * + * status stage uses allocated ureq. + * it will be freed on usbhsh_queue_done() + */ + ureq = usbhsh_req_alloc(hpriv, urb, GFP_KERNEL); + pkt = &ureq->pkt; + + if (usb_pipein(urb->pipe)) + pipe->handler = &usbhs_dcp_status_stage_in_handler; + else + pipe->handler = &usbhs_dcp_status_stage_out_handler; + + usbhs_pkt_push(pipe, pkt, + usbhsh_queue_done, + NULL, + urb->transfer_buffer_length, + 0); +} + +static int usbhsh_dcp_queue_push(struct usb_hcd *hcd, + struct usbhsh_hpriv *hpriv, + struct usbhs_pipe *pipe, + struct urb *urb) +{ + struct device *dev = usbhsh_hcd_to_dev(hcd); + + dev_dbg(dev, "%s\n", __func__); + + /* + * setup stage + * + * usbhsh_send_setup_stage_packet() wait SACK/SIGN + */ + usbhsh_setup_stage_packet_push(hpriv, urb, pipe); + + /* + * data stage + * + * It is pushed only when urb has buffer. + */ + if (urb->transfer_buffer_length) + usbhsh_data_stage_packet_push(hpriv, urb, pipe); + + /* + * status stage + */ + usbhsh_status_stage_packet_push(hpriv, urb, pipe); + + /* + * start pushed packets + */ + usbhs_pkt_start(pipe); + + return 0; +} + +/* + * dma map functions + */ +static int usbhsh_dma_map_ctrl(struct usbhs_pkt *pkt, int map) +{ + return 0; +} + +/* + * for hc_driver + */ +static int usbhsh_host_start(struct usb_hcd *hcd) +{ + return 0; +} + +static void usbhsh_host_stop(struct usb_hcd *hcd) +{ +} + +static int usbhsh_urb_enqueue(struct usb_hcd *hcd, + struct urb *urb, + gfp_t mem_flags) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + struct usb_device *usbv = usbhsh_urb_to_usbv(urb); + struct usb_host_endpoint *ep = urb->ep; + struct usbhsh_request *ureq; + struct usbhsh_device *udev, *new_udev = NULL; + struct usbhs_pipe *pipe; + struct usbhsh_ep *uep; + + int ret; + + dev_dbg(dev, "%s (%s)\n", + __func__, usb_pipein(urb->pipe) ? "in" : "out"); + + ret = usb_hcd_link_urb_to_ep(hcd, urb); + if (ret) + goto usbhsh_urb_enqueue_error_not_linked; + + /* + * get udev + */ + udev = usbhsh_usbv_to_udev(usbv); + if (!udev) { + new_udev = usbhsh_device_alloc(hpriv, urb); + if (!new_udev) + goto usbhsh_urb_enqueue_error_not_linked; + + udev = new_udev; + } + + /* + * get uep + */ + uep = usbhsh_ep_to_uep(ep); + if (!uep) { + uep = usbhsh_endpoint_alloc(hpriv, udev, ep, mem_flags); + if (!uep) + goto usbhsh_urb_enqueue_error_free_device; + } + pipe = usbhsh_uep_to_pipe(uep); + + /* + * alloc new request + */ + ureq = usbhsh_req_alloc(hpriv, urb, mem_flags); + if (unlikely(!ureq)) { + ret = -ENOMEM; + goto usbhsh_urb_enqueue_error_free_endpoint; + } + usbhsh_urb_to_ureq(urb) = ureq; + + /* + * push packet + */ + if (usb_pipecontrol(urb->pipe)) + usbhsh_dcp_queue_push(hcd, hpriv, pipe, urb); + else + usbhsh_queue_push(hcd, pipe, urb); + + return 0; + +usbhsh_urb_enqueue_error_free_endpoint: + usbhsh_endpoint_free(hpriv, ep); +usbhsh_urb_enqueue_error_free_device: + if (new_udev) + usbhsh_device_free(hpriv, new_udev); +usbhsh_urb_enqueue_error_not_linked: + + dev_dbg(dev, "%s error\n", __func__); + + return ret; +} + +static int usbhsh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhsh_request *ureq = usbhsh_urb_to_ureq(urb); + + if (ureq) { + usbhsh_req_free(hpriv, ureq); + usbhsh_urb_to_ureq(urb) = NULL; + } + + return 0; +} + +static void usbhsh_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep); + struct usbhsh_device *udev; + struct usbhsh_hpriv *hpriv; + + /* + * this function might be called manytimes by same hcd/ep + * in-endpoitn == out-endpoint if ep == dcp. + */ + if (!uep) + return; + + udev = usbhsh_uep_to_udev(uep); + hpriv = usbhsh_hcd_to_hpriv(hcd); + + usbhsh_endpoint_free(hpriv, ep); + ep->hcpriv = NULL; + + /* + * if there is no endpoint, + * free device + */ + if (!usbhsh_device_has_endpoint(udev)) + usbhsh_device_free(hpriv, udev); +} + +static int usbhsh_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + int roothub_id = 1; /* only 1 root hub */ + + /* + * does port stat was changed ? + * check USB_PORT_STAT_C_xxx << 16 + */ + if (usbhsh_port_stat_get(hpriv) & 0xFFFF0000) + *buf = (1 << roothub_id); + else + *buf = 0; + + dev_dbg(dev, "%s (%02x)\n", __func__, *buf); + + return !!(*buf); +} + +static int __usbhsh_hub_hub_feature(struct usbhsh_hpriv *hpriv, + u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + dev_dbg(dev, "%s :: C_HUB_xx\n", __func__); + return 0; + } + + return -EPIPE; +} + +static int __usbhsh_hub_port_feature(struct usbhsh_hpriv *hpriv, + u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + int enable = (typeReq == SetPortFeature); + int speed, i, timeout = 128; + int roothub_id = 1; /* only 1 root hub */ + + /* common error */ + if (wIndex > roothub_id || wLength != 0) + return -EPIPE; + + /* check wValue */ + switch (wValue) { + case USB_PORT_FEAT_POWER: + usbhs_vbus_ctrl(priv, enable); + dev_dbg(dev, "%s :: USB_PORT_FEAT_POWER\n", __func__); + break; + + case USB_PORT_FEAT_ENABLE: + case USB_PORT_FEAT_SUSPEND: + case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_SUSPEND: + case USB_PORT_FEAT_C_CONNECTION: + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_RESET: + dev_dbg(dev, "%s :: USB_PORT_FEAT_xxx\n", __func__); + break; + + case USB_PORT_FEAT_RESET: + if (!enable) + break; + + usbhsh_port_stat_clear(hpriv, + USB_PORT_STAT_HIGH_SPEED | + USB_PORT_STAT_LOW_SPEED); + + usbhs_bus_send_reset(priv); + msleep(20); + usbhs_bus_send_sof_enable(priv); + + for (i = 0; i < timeout ; i++) { + switch (usbhs_bus_get_speed(priv)) { + case USB_SPEED_LOW: + speed = USB_PORT_STAT_LOW_SPEED; + goto got_usb_bus_speed; + case USB_SPEED_HIGH: + speed = USB_PORT_STAT_HIGH_SPEED; + goto got_usb_bus_speed; + case USB_SPEED_FULL: + speed = 0; + goto got_usb_bus_speed; + } + + msleep(20); + } + return -EPIPE; + +got_usb_bus_speed: + usbhsh_port_stat_set(hpriv, speed); + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_ENABLE); + + dev_dbg(dev, "%s :: USB_PORT_FEAT_RESET (speed = %d)\n", + __func__, speed); + + /* status change is not needed */ + return 0; + + default: + return -EPIPE; + } + + /* set/clear status */ + if (enable) + usbhsh_port_stat_set(hpriv, (1 << wValue)); + else + usbhsh_port_stat_clear(hpriv, (1 << wValue)); + + return 0; +} + +static int __usbhsh_hub_get_status(struct usbhsh_hpriv *hpriv, + u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)buf; + struct device *dev = usbhs_priv_to_dev(priv); + int roothub_id = 1; /* only 1 root hub */ + + switch (typeReq) { + case GetHubStatus: + dev_dbg(dev, "%s :: GetHubStatus\n", __func__); + + *buf = 0x00; + break; + + case GetPortStatus: + if (wIndex != roothub_id) + return -EPIPE; + + dev_dbg(dev, "%s :: GetPortStatus\n", __func__); + *(__le32 *)buf = cpu_to_le32(usbhsh_port_stat_get(hpriv)); + break; + + case GetHubDescriptor: + desc->bDescriptorType = 0x29; + desc->bHubContrCurrent = 0; + desc->bNbrPorts = roothub_id; + desc->bDescLength = 9; + desc->bPwrOn2PwrGood = 0; + desc->wHubCharacteristics = cpu_to_le16(0x0011); + desc->u.hs.DeviceRemovable[0] = (roothub_id << 1); + desc->u.hs.DeviceRemovable[1] = ~0; + dev_dbg(dev, "%s :: GetHubDescriptor\n", __func__); + break; + } + + return 0; +} + +static int usbhsh_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd); + struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + int ret = -EPIPE; + + switch (typeReq) { + + /* Hub Feature */ + case ClearHubFeature: + case SetHubFeature: + ret = __usbhsh_hub_hub_feature(hpriv, typeReq, + wValue, wIndex, buf, wLength); + break; + + /* Port Feature */ + case SetPortFeature: + case ClearPortFeature: + ret = __usbhsh_hub_port_feature(hpriv, typeReq, + wValue, wIndex, buf, wLength); + break; + + /* Get status */ + case GetHubStatus: + case GetPortStatus: + case GetHubDescriptor: + ret = __usbhsh_hub_get_status(hpriv, typeReq, + wValue, wIndex, buf, wLength); + break; + } + + dev_dbg(dev, "typeReq = %x, ret = %d, port_stat = %x\n", + typeReq, ret, usbhsh_port_stat_get(hpriv)); + + return ret; +} + +static struct hc_driver usbhsh_driver = { + .description = usbhsh_hcd_name, + .hcd_priv_size = sizeof(struct usbhsh_hpriv), + + /* + * generic hardware linkage + */ + .flags = HCD_USB2, + + .start = usbhsh_host_start, + .stop = usbhsh_host_stop, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = usbhsh_urb_enqueue, + .urb_dequeue = usbhsh_urb_dequeue, + .endpoint_disable = usbhsh_endpoint_disable, + + /* + * root hub + */ + .hub_status_data = usbhsh_hub_status_data, + .hub_control = usbhsh_hub_control, +}; + +/* + * interrupt functions + */ +static int usbhsh_irq_attch(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "device attached\n"); + + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_CONNECTION); + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16); + + return 0; +} + +static int usbhsh_irq_dtch(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "device detached\n"); + + usbhsh_port_stat_clear(hpriv, USB_PORT_STAT_CONNECTION); + usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16); + + return 0; +} + +static int usbhsh_irq_setup_ack(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "setup packet OK\n"); + + if (unlikely(!hpriv->done)) + dev_err(dev, "setup ack happen without necessary data\n"); + else + complete(hpriv->done); /* see usbhsh_urb_enqueue() */ + + return 0; +} + +static int usbhsh_irq_setup_err(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct device *dev = usbhs_priv_to_dev(priv); + + dev_dbg(dev, "setup packet Err\n"); + + if (unlikely(!hpriv->done)) + dev_err(dev, "setup err happen without necessary data\n"); + else + complete(hpriv->done); /* see usbhsh_urb_enqueue() */ + + return 0; +} + +/* + * module start/stop + */ +static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usbhsh_pipe_info *pipe_info = hpriv->pipe_info; + struct usbhs_pipe *pipe; + u32 *pipe_type = usbhs_get_dparam(priv, pipe_type); + int pipe_size = usbhs_get_dparam(priv, pipe_size); + int old_type, dir_in, i; + + /* init all pipe */ + old_type = USB_ENDPOINT_XFER_CONTROL; + for (i = 0; i < pipe_size; i++) { + pipe_info[i].usr_cnt = 0; + + /* + * data "output" will be finished as soon as possible, + * but there is no guaranty at data "input" case. + * + * "input" needs "standby" pipe. + * So, "input" direction pipe > "output" direction pipe + * is good idea. + * + * 1st USB_ENDPOINT_XFER_xxx will be output direction, + * and the other will be input direction here. + * + * ex) + * ... + * USB_ENDPOINT_XFER_ISOC -> dir out + * USB_ENDPOINT_XFER_ISOC -> dir in + * USB_ENDPOINT_XFER_BULK -> dir out + * USB_ENDPOINT_XFER_BULK -> dir in + * USB_ENDPOINT_XFER_BULK -> dir in + * ... + */ + dir_in = (pipe_type[i] == old_type); + old_type = pipe_type[i]; + + if (USB_ENDPOINT_XFER_CONTROL == pipe_type[i]) { + pipe = usbhs_dcp_malloc(priv); + usbhsh_hpriv_to_dcp(hpriv) = pipe; + } else { + pipe = usbhs_pipe_malloc(priv, + pipe_type[i], + dir_in); + } + + pipe->mod_private = pipe_info + i; + } +} + +static int usbhsh_start(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct usbhs_mod *mod = usbhs_mod_get_current(priv); + struct device *dev = usbhs_priv_to_dev(priv); + int ret; + + /* add hcd */ + ret = usb_add_hcd(hcd, 0, 0); + if (ret < 0) + return 0; + + /* + * pipe initialize and enable DCP + */ + usbhs_pipe_init(priv, + usbhsh_dma_map_ctrl); + usbhs_fifo_init(priv); + usbhsh_pipe_init_for_host(priv); + + /* + * system config enble + * - HI speed + * - host + * - usb module + */ + usbhs_sys_hispeed_ctrl(priv, 1); + usbhs_sys_host_ctrl(priv, 1); + usbhs_sys_usb_ctrl(priv, 1); + + /* + * enable irq callback + */ + mod->irq_attch = usbhsh_irq_attch; + mod->irq_dtch = usbhsh_irq_dtch; + mod->irq_sack = usbhsh_irq_setup_ack; + mod->irq_sign = usbhsh_irq_setup_err; + usbhs_irq_callback_update(priv, mod); + + dev_dbg(dev, "start host\n"); + + return ret; +} + +static int usbhsh_stop(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + struct device *dev = usbhs_priv_to_dev(priv); + + usb_remove_hcd(hcd); + + /* disable sys */ + usbhs_sys_hispeed_ctrl(priv, 0); + usbhs_sys_host_ctrl(priv, 0); + usbhs_sys_usb_ctrl(priv, 0); + + dev_dbg(dev, "quit host\n"); + + return 0; +} + +int __devinit usbhs_mod_host_probe(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv; + struct usb_hcd *hcd; + struct usbhsh_pipe_info *pipe_info; + struct usbhsh_device *udev; + struct device *dev = usbhs_priv_to_dev(priv); + int pipe_size = usbhs_get_dparam(priv, pipe_size); + int i; + + /* initialize hcd */ + hcd = usb_create_hcd(&usbhsh_driver, dev, usbhsh_hcd_name); + if (!hcd) { + dev_err(dev, "Failed to create hcd\n"); + return -ENOMEM; + } + + pipe_info = kzalloc(sizeof(*pipe_info) * pipe_size, GFP_KERNEL); + if (!pipe_info) { + dev_err(dev, "Could not allocate pipe_info\n"); + goto usbhs_mod_host_probe_err; + } + + /* + * CAUTION + * + * There is no guarantee that it is possible to access usb module here. + * Don't accesses to it. + * The accesse will be enable after "usbhsh_start" + */ + + hpriv = usbhsh_hcd_to_hpriv(hcd); + + /* + * register itself + */ + usbhs_mod_register(priv, &hpriv->mod, USBHS_HOST); + + /* init hpriv */ + hpriv->mod.name = "host"; + hpriv->mod.start = usbhsh_start; + hpriv->mod.stop = usbhsh_stop; + hpriv->pipe_info = pipe_info; + hpriv->pipe_size = pipe_size; + hpriv->done = NULL; + usbhsh_req_list_init(hpriv); + usbhsh_port_stat_init(hpriv); + + /* init all device */ + usbhsh_for_each_udev_with_dev0(udev, hpriv, i) { + udev->usbv = NULL; + INIT_LIST_HEAD(&udev->ep_list_head); + } + + dev_info(dev, "host probed\n"); + + return 0; + +usbhs_mod_host_probe_err: + usb_put_hcd(hcd); + + return -ENOMEM; +} + +int __devexit usbhs_mod_host_remove(struct usbhs_priv *priv) +{ + struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv); + struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv); + + usbhsh_req_list_quit(hpriv); + + usb_put_hcd(hcd); + + return 0; +} -- cgit v0.10.2 From 482982062f1bc25ffb5383ab724d73d1a7af07cf Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 12 Oct 2011 21:02:22 -0700 Subject: usb: gadget: renesas_usbhs: bugfix: don't modify platform data renesas_usbhs has default callback functions and settings. And it tried overwrite to platform private data if platform doesn't have them. So, if renesas_usbhs was compiled as module, it will be hung-up on 2nd insmod. This patch fixup it. Special thanks to Bastian Reported-by: Bastian Hecht Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index c3aef40..d2e2efa 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -61,8 +61,8 @@ */ #define usbhs_platform_call(priv, func, args...)\ (!(priv) ? -ENODEV : \ - !((priv)->pfunc->func) ? 0 : \ - (priv)->pfunc->func(args)) + !((priv)->pfunc.func) ? 0 : \ + (priv)->pfunc.func(args)) /* * common functions @@ -446,24 +446,28 @@ static int __devinit usbhs_probe(struct platform_device *pdev) /* * care platform info */ - priv->pfunc = &info->platform_callback; - priv->dparam = &info->driver_param; + memcpy(&priv->pfunc, + &info->platform_callback, + sizeof(struct renesas_usbhs_platform_callback)); + memcpy(&priv->dparam, + &info->driver_param, + sizeof(struct renesas_usbhs_driver_param)); /* set driver callback functions for platform */ dfunc = &info->driver_callback; dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug; /* set default param if platform doesn't have */ - if (!priv->dparam->pipe_type) { - priv->dparam->pipe_type = usbhsc_default_pipe_type; - priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); + if (!priv->dparam.pipe_type) { + priv->dparam.pipe_type = usbhsc_default_pipe_type; + priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); } - if (!priv->dparam->pio_dma_border) - priv->dparam->pio_dma_border = 64; /* 64byte */ + if (!priv->dparam.pio_dma_border) + priv->dparam.pio_dma_border = 64; /* 64byte */ /* FIXME */ /* runtime power control ? */ - if (priv->pfunc->get_vbus) + if (priv->pfunc.get_vbus) usbhsc_flags_set(priv, USBHSF_RUNTIME_PWCTRL); /* diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index dc9490d..8729da5 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -242,8 +242,8 @@ struct usbhs_priv { void __iomem *base; unsigned int irq; - struct renesas_usbhs_platform_callback *pfunc; - struct renesas_usbhs_driver_param *dparam; + struct renesas_usbhs_platform_callback pfunc; + struct renesas_usbhs_driver_param dparam; struct delayed_work notify_hotplug_work; struct platform_device *pdev; @@ -318,7 +318,7 @@ int usbhs_set_device_speed(struct usbhs_priv *priv, int devnum, u16 upphub, * data */ struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev); -#define usbhs_get_dparam(priv, param) (priv->dparam->param) +#define usbhs_get_dparam(priv, param) (priv->dparam.param) #define usbhs_priv_to_pdev(priv) (priv->pdev) #define usbhs_priv_to_dev(priv) (&priv->pdev->dev) #define usbhs_priv_to_lock(priv) (&priv->lock) diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 2d3b09d..053f86d 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -58,7 +58,7 @@ void usbhs_mod_autonomy_mode(struct usbhs_priv *priv) struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); info->irq_vbus = usbhsm_autonomy_irq_vbus; - priv->pfunc->get_vbus = usbhsm_autonomy_get_vbus; + priv->pfunc.get_vbus = usbhsm_autonomy_get_vbus; usbhs_irq_callback_update(priv, NULL); } -- cgit v0.10.2 From 951fe829c73b89a2e4a5b1aaae4c0a43ffa7c68e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 12 Oct 2011 21:02:44 -0700 Subject: usb: gadget: renesas_usbhs: fixup driver compile style In current renesas-usbhs, there was inconsistency about the style of kernel module or built-in. This patch solve it. [ balbi@ti.com : fix compile issue when building modules ] Signed-off-by: Kuninori Morimoto Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig index 286cbf1..97a4401 100644 --- a/drivers/usb/renesas_usbhs/Kconfig +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -5,6 +5,7 @@ config USB_RENESAS_USBHS tristate 'Renesas USBHS controller' depends on SUPERH || ARCH_SHMOBILE + depends on USB && USB_GADGET default n help Renesas USBHS is a discrete USB host and peripheral controller chip diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile index e44984d..bc8aef4 100644 --- a/drivers/usb/renesas_usbhs/Makefile +++ b/drivers/usb/renesas_usbhs/Makefile @@ -6,5 +6,10 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o renesas_usbhs-y := common.o mod.o pipe.o fifo.o -renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_HCD) += mod_host.o -renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_UDC) += mod_gadget.o +ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),) + renesas_usbhs-y += mod_host.o +endif + +ifneq ($(CONFIG_USB_RENESAS_USBHS_UDC),) + renesas_usbhs-y += mod_gadget.o +endif diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index 1f28ed2..8ae3733 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h @@ -141,7 +141,8 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod); /* * host / gadget control */ -#ifdef CONFIG_USB_RENESAS_USBHS_HCD +#if defined(CONFIG_USB_RENESAS_USBHS_HCD) || \ + defined(CONFIG_USB_RENESAS_USBHS_HCD_MODULE) extern int __devinit usbhs_mod_host_probe(struct usbhs_priv *priv); extern int __devexit usbhs_mod_host_remove(struct usbhs_priv *priv); #else @@ -154,7 +155,8 @@ static inline void usbhs_mod_host_remove(struct usbhs_priv *priv) } #endif -#ifdef CONFIG_USB_RENESAS_USBHS_UDC +#if defined(CONFIG_USB_RENESAS_USBHS_UDC) || \ + defined(CONFIG_USB_RENESAS_USBHS_UDC_MODULE) extern int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv); extern void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv); #else -- cgit v0.10.2 From d242c110c43b55aaf3ebb3ceac1eeab0d452a177 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Oct 2011 19:45:17 +0300 Subject: usb: gadget: renesas_usbhs: drop dependency that driver can compile cleanly on x86 without any issues. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig index 97a4401..6f4afa4 100644 --- a/drivers/usb/renesas_usbhs/Kconfig +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -4,7 +4,6 @@ config USB_RENESAS_USBHS tristate 'Renesas USBHS controller' - depends on SUPERH || ARCH_SHMOBILE depends on USB && USB_GADGET default n help -- cgit v0.10.2 From 8a9775ab71218690ac34bed9e237e2b968857d3a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Oct 2011 20:00:03 +0300 Subject: usb: gadget: renesas_usbhs: fix compile warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit | drivers/usb/renesas_usbhs/fifo.c: In function ‘usbhsf_dma_prepare_push’: | drivers/usb/renesas_usbhs/fifo.c:823:7: warning: cast from pointer \ to integer of different size [-Wpointer-to-int-cast] | drivers/usb/renesas_usbhs/fifo.c: In function ‘usbhsf_dma_try_pop’: | drivers/usb/renesas_usbhs/fifo.c:900:7: warning: cast from pointer \ to integer of different size [-Wpointer-to-int-cast] Signed-off-by: Felipe Balbi diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 16b12b0..762b742 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -820,7 +820,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done) if (len % 4) /* 32bit alignment */ goto usbhsf_pio_prepare_push; - if (((u32)pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ + if ((*(u32 *) pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ goto usbhsf_pio_prepare_push; /* get enable DMA fifo */ @@ -897,7 +897,7 @@ static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done) if (!fifo) goto usbhsf_pio_prepare_pop; - if (((u32)pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ + if ((*(u32 *) pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */ goto usbhsf_pio_prepare_pop; ret = usbhsf_fifo_select(pipe, fifo, 0); -- cgit v0.10.2 From dde34cc5019b51088c18ca789d4b1a20cf9bc617 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:24 +0800 Subject: usb: gadget: mv_udc: refine the driver structure This patch do the following things: 1. Add header and Copyright for marvell usb driver. 2. Add mv_usb.h in include/linux/platform_data, make the driver fits all the marvell platform using the same ChipIdea usb ip. 3. Some SOC may has mutiple clock sources, so let me define it in mv_usb_platform_data and give two helper functions named udc_clock_enable/udc_clock_disable to deal with the clocks. 4. Different SOCs will have some difference in PHY initialization, so we will remove file mv_udc_phy.c and add two funtions in mv_usb_platform_data, let the platform relative driver to realize it. 5. Rewrite probe function according to the modification list above. Find it will kernel panic when probe failed. The root cause is as follows: When probe failed, the error handle may call device_unregister() which in return will call gadget_release.In current code, gadget_release have two issues: 1: the_controller is a NULL pointer. 2: if we free udc here, then the following code in probe will access NULL pointer. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 9ba725a..c36da63 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -28,7 +28,7 @@ obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o obj-$(CONFIG_USB_EG20T) += pch_udc.o obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o -mv_udc-y := mv_udc_core.o mv_udc_phy.o +mv_udc-y := mv_udc_core.o obj-$(CONFIG_USB_CI13XXX_MSM) += ci13xxx_msm.o obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h index 65f1f7c..d3d2645 100644 --- a/drivers/usb/gadget/mv_udc.h +++ b/drivers/usb/gadget/mv_udc.h @@ -1,3 +1,11 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ #ifndef __MV_UDC_H #define __MV_UDC_H @@ -201,7 +209,12 @@ struct mv_udc { remote_wakeup:1, softconnected:1, force_fs:1; - struct clk *clk; + + struct mv_usb_platform_data *pdata; + + /* some SOC has mutiple clock sources for USB*/ + unsigned int clknum; + struct clk *clk[0]; }; /* endpoint data structure */ @@ -289,6 +302,4 @@ struct mv_dtd { struct mv_dtd *next_dtd_virt; }; -extern int mv_udc_phy_init(unsigned int base); - #endif diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 0d0e9e3..40a25e7 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1,3 +1,14 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * Author: Chao Xie + * Neil Zhang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + #include #include #include @@ -22,6 +33,7 @@ #include #include #include +#include #include #include @@ -45,6 +57,8 @@ #define LOOPS_USEC (1 << LOOPS_USEC_SHIFT) #define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT) +static DECLARE_COMPLETION(release_done); + static const char driver_name[] = "mv_udc"; static const char driver_desc[] = DRIVER_DESC; @@ -987,6 +1001,22 @@ static struct usb_ep_ops mv_ep_ops = { .fifo_flush = mv_ep_fifo_flush, /* flush fifo */ }; +static void udc_clock_enable(struct mv_udc *udc) +{ + unsigned int i; + + for (i = 0; i < udc->clknum; i++) + clk_enable(udc->clk[i]); +} + +static void udc_clock_disable(struct mv_udc *udc) +{ + unsigned int i; + + for (i = 0; i < udc->clknum; i++) + clk_disable(udc->clk[i]); +} + static void udc_stop(struct mv_udc *udc) { u32 tmp; @@ -1877,18 +1907,15 @@ static void gadget_release(struct device *_dev) struct mv_udc *udc = the_controller; complete(udc->done); - kfree(udc); } static int mv_udc_remove(struct platform_device *dev) { struct mv_udc *udc = the_controller; - DECLARE_COMPLETION(done); + int clk_i; usb_del_gadget_udc(&udc->gadget); - udc->done = &done; - /* free memory allocated in probe */ if (udc->dtd_pool) dma_pool_destroy(udc->dtd_pool); @@ -1915,10 +1942,14 @@ static int mv_udc_remove(struct platform_device *dev) kfree(udc->status_req); } + for (clk_i = 0; clk_i <= udc->clknum; clk_i++) + clk_put(udc->clk[clk_i]); + device_unregister(&udc->gadget.dev); /* free dev, wait for the release() finished */ - wait_for_completion(&done); + wait_for_completion(udc->done); + kfree(udc); the_controller = NULL; @@ -1927,33 +1958,46 @@ static int mv_udc_remove(struct platform_device *dev) int mv_udc_probe(struct platform_device *dev) { + struct mv_usb_platform_data *pdata = dev->dev.platform_data; struct mv_udc *udc; int retval = 0; + int clk_i = 0; struct resource *r; size_t size; - udc = kzalloc(sizeof *udc, GFP_KERNEL); + if (pdata == NULL) { + dev_err(&dev->dev, "missing platform_data\n"); + return -ENODEV; + } + + size = sizeof(*udc) + sizeof(struct clk *) * pdata->clknum; + udc = kzalloc(size, GFP_KERNEL); if (udc == NULL) { dev_err(&dev->dev, "failed to allocate memory for udc\n"); - retval = -ENOMEM; - goto error; + return -ENOMEM; } + the_controller = udc; + udc->done = &release_done; + udc->pdata = dev->dev.platform_data; spin_lock_init(&udc->lock); udc->dev = dev; - udc->clk = clk_get(&dev->dev, "U2OCLK"); - if (IS_ERR(udc->clk)) { - retval = PTR_ERR(udc->clk); - goto error; + udc->clknum = pdata->clknum; + for (clk_i = 0; clk_i < udc->clknum; clk_i++) { + udc->clk[clk_i] = clk_get(&dev->dev, pdata->clkname[clk_i]); + if (IS_ERR(udc->clk[clk_i])) { + retval = PTR_ERR(udc->clk[clk_i]); + goto err_put_clk; + } } - r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2o"); + r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "capregs"); if (r == NULL) { dev_err(&dev->dev, "no I/O memory resource defined\n"); retval = -ENODEV; - goto error; + goto err_put_clk; } udc->cap_regs = (struct mv_cap_regs __iomem *) @@ -1961,29 +2005,31 @@ int mv_udc_probe(struct platform_device *dev) if (udc->cap_regs == NULL) { dev_err(&dev->dev, "failed to map I/O memory\n"); retval = -EBUSY; - goto error; + goto err_put_clk; } - r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "u2ophy"); + r = platform_get_resource_byname(udc->dev, IORESOURCE_MEM, "phyregs"); if (r == NULL) { dev_err(&dev->dev, "no phy I/O memory resource defined\n"); retval = -ENODEV; - goto error; + goto err_iounmap_capreg; } udc->phy_regs = (unsigned int)ioremap(r->start, resource_size(r)); if (udc->phy_regs == 0) { dev_err(&dev->dev, "failed to map phy I/O memory\n"); retval = -EBUSY; - goto error; + goto err_iounmap_capreg; } /* we will acces controller register, so enable the clk */ - clk_enable(udc->clk); - retval = mv_udc_phy_init(udc->phy_regs); - if (retval) { - dev_err(&dev->dev, "phy initialization error %d\n", retval); - goto error; + udc_clock_enable(udc); + if (pdata->phy_init) { + retval = pdata->phy_init(udc->phy_regs); + if (retval) { + dev_err(&dev->dev, "phy init error %d\n", retval); + goto err_iounmap_phyreg; + } } udc->op_regs = (struct mv_op_regs __iomem *)((u32)udc->cap_regs @@ -1999,7 +2045,7 @@ int mv_udc_probe(struct platform_device *dev) if (udc->ep_dqh == NULL) { dev_err(&dev->dev, "allocate dQH memory failed\n"); retval = -ENOMEM; - goto error; + goto err_disable_clock; } udc->ep_dqh_size = size; @@ -2012,7 +2058,7 @@ int mv_udc_probe(struct platform_device *dev) if (!udc->dtd_pool) { retval = -ENOMEM; - goto error; + goto err_free_dma; } size = udc->max_eps * sizeof(struct mv_ep) *2; @@ -2020,7 +2066,7 @@ int mv_udc_probe(struct platform_device *dev) if (udc->eps == NULL) { dev_err(&dev->dev, "allocate ep memory failed\n"); retval = -ENOMEM; - goto error; + goto err_destroy_dma; } /* initialize ep0 status request structure */ @@ -2028,7 +2074,7 @@ int mv_udc_probe(struct platform_device *dev) if (!udc->status_req) { dev_err(&dev->dev, "allocate status_req memory failed\n"); retval = -ENOMEM; - goto error; + goto err_free_eps; } INIT_LIST_HEAD(&udc->status_req->queue); @@ -2045,7 +2091,7 @@ int mv_udc_probe(struct platform_device *dev) if (r == NULL) { dev_err(&dev->dev, "no IRQ resource defined\n"); retval = -ENODEV; - goto error; + goto err_free_status_req; } udc->irq = r->start; if (request_irq(udc->irq, mv_udc_irq, @@ -2053,7 +2099,7 @@ int mv_udc_probe(struct platform_device *dev) dev_err(&dev->dev, "Request irq %d for UDC failed\n", udc->irq); retval = -ENODEV; - goto error; + goto err_free_status_req; } /* initialize gadget structure */ @@ -2072,18 +2118,43 @@ int mv_udc_probe(struct platform_device *dev) retval = device_register(&udc->gadget.dev); if (retval) - goto error; + goto err_free_irq; eps_init(udc); - the_controller = udc; - retval = usb_add_gadget_udc(&dev->dev, &udc->gadget); - if (!retval) - return retval; -error: - if (udc) - mv_udc_remove(udc->dev); + if (retval) + goto err_unregister; + + return 0; + +err_unregister: + device_unregister(&udc->gadget.dev); +err_free_irq: + free_irq(udc->irq, &dev->dev); +err_free_status_req: + kfree(udc->status_req->req.buf); + kfree(udc->status_req); +err_free_eps: + kfree(udc->eps); +err_destroy_dma: + dma_pool_destroy(udc->dtd_pool); +err_free_dma: + dma_free_coherent(&dev->dev, udc->ep_dqh_size, + udc->ep_dqh, udc->ep_dqh_dma); +err_disable_clock: + if (udc->pdata->phy_deinit) + udc->pdata->phy_deinit(udc->phy_regs); + udc_clock_disable(udc); +err_iounmap_phyreg: + iounmap((void *)udc->phy_regs); +err_iounmap_capreg: + iounmap(udc->cap_regs); +err_put_clk: + for (clk_i--; clk_i >= 0; clk_i--) + clk_put(udc->clk[clk_i]); + the_controller = NULL; + kfree(udc); return retval; } @@ -2102,11 +2173,16 @@ static int mv_udc_resume(struct device *_dev) struct mv_udc *udc = the_controller; int retval; - retval = mv_udc_phy_init(udc->phy_regs); - if (retval) { - dev_err(_dev, "phy initialization error %d\n", retval); - return retval; + if (udc->pdata->phy_init) { + retval = udc->pdata->phy_init(udc->phy_regs); + if (retval) { + dev_err(&udc->dev->dev, + "init phy error %d when resume back\n", + retval); + return retval; + } } + udc_reset(udc); ep0_reset(udc); udc_start(udc); diff --git a/drivers/usb/gadget/mv_udc_phy.c b/drivers/usb/gadget/mv_udc_phy.c deleted file mode 100644 index d4dea97..0000000 --- a/drivers/usb/gadget/mv_udc_phy.c +++ /dev/null @@ -1,214 +0,0 @@ -#include -#include -#include -#include - -#include - -#ifdef CONFIG_ARCH_MMP - -#define UTMI_REVISION 0x0 -#define UTMI_CTRL 0x4 -#define UTMI_PLL 0x8 -#define UTMI_TX 0xc -#define UTMI_RX 0x10 -#define UTMI_IVREF 0x14 -#define UTMI_T0 0x18 -#define UTMI_T1 0x1c -#define UTMI_T2 0x20 -#define UTMI_T3 0x24 -#define UTMI_T4 0x28 -#define UTMI_T5 0x2c -#define UTMI_RESERVE 0x30 -#define UTMI_USB_INT 0x34 -#define UTMI_DBG_CTL 0x38 -#define UTMI_OTG_ADDON 0x3c - -/* For UTMICTRL Register */ -#define UTMI_CTRL_USB_CLK_EN (1 << 31) -/* pxa168 */ -#define UTMI_CTRL_SUSPEND_SET1 (1 << 30) -#define UTMI_CTRL_SUSPEND_SET2 (1 << 29) -#define UTMI_CTRL_RXBUF_PDWN (1 << 24) -#define UTMI_CTRL_TXBUF_PDWN (1 << 11) - -#define UTMI_CTRL_INPKT_DELAY_SHIFT 30 -#define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28 -#define UTMI_CTRL_PU_REF_SHIFT 20 -#define UTMI_CTRL_ARC_PULLDN_SHIFT 12 -#define UTMI_CTRL_PLL_PWR_UP_SHIFT 1 -#define UTMI_CTRL_PWR_UP_SHIFT 0 -/* For UTMI_PLL Register */ -#define UTMI_PLL_CLK_BLK_EN_SHIFT 24 -#define UTMI_PLL_FBDIV_SHIFT 4 -#define UTMI_PLL_REFDIV_SHIFT 0 -#define UTMI_PLL_FBDIV_MASK 0x00000FF0 -#define UTMI_PLL_REFDIV_MASK 0x0000000F -#define UTMI_PLL_ICP_MASK 0x00007000 -#define UTMI_PLL_KVCO_MASK 0x00031000 -#define UTMI_PLL_PLLCALI12_SHIFT 29 -#define UTMI_PLL_PLLCALI12_MASK (0x3 << 29) -#define UTMI_PLL_PLLVDD18_SHIFT 27 -#define UTMI_PLL_PLLVDD18_MASK (0x3 << 27) -#define UTMI_PLL_PLLVDD12_SHIFT 25 -#define UTMI_PLL_PLLVDD12_MASK (0x3 << 25) -#define UTMI_PLL_KVCO_SHIFT 15 -#define UTMI_PLL_ICP_SHIFT 12 -/* For UTMI_TX Register */ -#define UTMI_TX_REG_EXT_FS_RCAL_SHIFT 27 -#define UTMI_TX_REG_EXT_FS_RCAL_MASK (0xf << 27) -#define UTMI_TX_REG_EXT_FS_RCAL_EN_MASK 26 -#define UTMI_TX_REG_EXT_FS_RCAL_EN (0x1 << 26) -#define UTMI_TX_LOW_VDD_EN_SHIFT 11 -#define UTMI_TX_IMPCAL_VTH_SHIFT 14 -#define UTMI_TX_IMPCAL_VTH_MASK (0x7 << 14) -#define UTMI_TX_CK60_PHSEL_SHIFT 17 -#define UTMI_TX_CK60_PHSEL_MASK (0xf << 17) -#define UTMI_TX_TXVDD12_SHIFT 22 -#define UTMI_TX_TXVDD12_MASK (0x3 << 22) -#define UTMI_TX_AMP_SHIFT 0 -#define UTMI_TX_AMP_MASK (0x7 << 0) -/* For UTMI_RX Register */ -#define UTMI_RX_SQ_THRESH_SHIFT 4 -#define UTMI_RX_SQ_THRESH_MASK (0xf << 4) -#define UTMI_REG_SQ_LENGTH_SHIFT 15 -#define UTMI_REG_SQ_LENGTH_MASK (0x3 << 15) - -#define REG_RCAL_START 0x00001000 -#define VCOCAL_START 0x00200000 -#define KVCO_EXT 0x00400000 -#define PLL_READY 0x00800000 -#define CLK_BLK_EN 0x01000000 -#endif - -static unsigned int u2o_read(unsigned int base, unsigned int offset) -{ - return readl(base + offset); -} - -static void u2o_set(unsigned int base, unsigned int offset, unsigned int value) -{ - unsigned int reg; - - reg = readl(base + offset); - reg |= value; - writel(reg, base + offset); - readl(base + offset); -} - -static void u2o_clear(unsigned int base, unsigned int offset, - unsigned int value) -{ - unsigned int reg; - - reg = readl(base + offset); - reg &= ~value; - writel(reg, base + offset); - readl(base + offset); -} - -static void u2o_write(unsigned int base, unsigned int offset, - unsigned int value) -{ - writel(value, base + offset); - readl(base + offset); -} - -#ifdef CONFIG_ARCH_MMP -int mv_udc_phy_init(unsigned int base) -{ - unsigned long timeout; - - /* Initialize the USB PHY power */ - if (cpu_is_pxa910()) { - u2o_set(base, UTMI_CTRL, (1 << UTMI_CTRL_INPKT_DELAY_SOF_SHIFT) - | (1 << UTMI_CTRL_PU_REF_SHIFT)); - } - - u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PLL_PWR_UP_SHIFT); - u2o_set(base, UTMI_CTRL, 1 << UTMI_CTRL_PWR_UP_SHIFT); - - /* UTMI_PLL settings */ - u2o_clear(base, UTMI_PLL, UTMI_PLL_PLLVDD18_MASK - | UTMI_PLL_PLLVDD12_MASK | UTMI_PLL_PLLCALI12_MASK - | UTMI_PLL_FBDIV_MASK | UTMI_PLL_REFDIV_MASK - | UTMI_PLL_ICP_MASK | UTMI_PLL_KVCO_MASK); - - u2o_set(base, UTMI_PLL, (0xee << UTMI_PLL_FBDIV_SHIFT) - | (0xb << UTMI_PLL_REFDIV_SHIFT) - | (3 << UTMI_PLL_PLLVDD18_SHIFT) - | (3 << UTMI_PLL_PLLVDD12_SHIFT) - | (3 << UTMI_PLL_PLLCALI12_SHIFT) - | (1 << UTMI_PLL_ICP_SHIFT) | (3 << UTMI_PLL_KVCO_SHIFT)); - - /* UTMI_TX */ - u2o_clear(base, UTMI_TX, UTMI_TX_REG_EXT_FS_RCAL_EN_MASK - | UTMI_TX_TXVDD12_MASK - | UTMI_TX_CK60_PHSEL_MASK | UTMI_TX_IMPCAL_VTH_MASK - | UTMI_TX_REG_EXT_FS_RCAL_MASK | UTMI_TX_AMP_MASK); - u2o_set(base, UTMI_TX, (3 << UTMI_TX_TXVDD12_SHIFT) - | (4 << UTMI_TX_CK60_PHSEL_SHIFT) - | (4 << UTMI_TX_IMPCAL_VTH_SHIFT) - | (8 << UTMI_TX_REG_EXT_FS_RCAL_SHIFT) - | (3 << UTMI_TX_AMP_SHIFT)); - - /* UTMI_RX */ - u2o_clear(base, UTMI_RX, UTMI_RX_SQ_THRESH_MASK - | UTMI_REG_SQ_LENGTH_MASK); - if (cpu_is_pxa168()) - u2o_set(base, UTMI_RX, (7 << UTMI_RX_SQ_THRESH_SHIFT) - | (2 << UTMI_REG_SQ_LENGTH_SHIFT)); - else - u2o_set(base, UTMI_RX, (0x7 << UTMI_RX_SQ_THRESH_SHIFT) - | (2 << UTMI_REG_SQ_LENGTH_SHIFT)); - - /* UTMI_IVREF */ - if (cpu_is_pxa168()) - /* - * fixing Microsoft Altair board interface with NEC hub issue - - * Set UTMI_IVREF from 0x4a3 to 0x4bf - */ - u2o_write(base, UTMI_IVREF, 0x4bf); - - /* calibrate */ - timeout = jiffies + 100; - while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) { - if (time_after(jiffies, timeout)) - return -ETIME; - cpu_relax(); - } - - /* toggle VCOCAL_START bit of UTMI_PLL */ - udelay(200); - u2o_set(base, UTMI_PLL, VCOCAL_START); - udelay(40); - u2o_clear(base, UTMI_PLL, VCOCAL_START); - - /* toggle REG_RCAL_START bit of UTMI_TX */ - udelay(200); - u2o_set(base, UTMI_TX, REG_RCAL_START); - udelay(40); - u2o_clear(base, UTMI_TX, REG_RCAL_START); - udelay(200); - - /* make sure phy is ready */ - timeout = jiffies + 100; - while ((u2o_read(base, UTMI_PLL) & PLL_READY) == 0) { - if (time_after(jiffies, timeout)) - return -ETIME; - cpu_relax(); - } - - if (cpu_is_pxa168()) { - u2o_set(base, UTMI_RESERVE, 1 << 5); - /* Turn on UTMI PHY OTG extension */ - u2o_write(base, UTMI_OTG_ADDON, 1); - } - return 0; -} -#else -int mv_udc_phy_init(unsigned int base) -{ - return 0; -} -#endif diff --git a/include/linux/platform_data/mv_usb.h b/include/linux/platform_data/mv_usb.h new file mode 100644 index 0000000..e9d9149 --- /dev/null +++ b/include/linux/platform_data/mv_usb.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 Marvell International Ltd. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __MV_PLATFORM_USB_H +#define __MV_PLATFORM_USB_H + +enum pxa_ehci_type { + EHCI_UNDEFINED = 0, + PXA_U2OEHCI, /* pxa 168, 9xx */ + PXA_SPH, /* pxa 168, 9xx SPH */ + MMP3_HSIC, /* mmp3 hsic */ + MMP3_FSIC, /* mmp3 fsic */ +}; + +enum { + MV_USB_MODE_OTG, + MV_USB_MODE_HOST, +}; + +enum { + VBUS_LOW = 0, + VBUS_HIGH = 1 << 0, +}; + +struct mv_usb_addon_irq { + unsigned int irq; + int (*poll)(void); +}; + +struct mv_usb_platform_data { + unsigned int clknum; + char **clkname; + struct mv_usb_addon_irq *id; /* Only valid for OTG. ID pin change*/ + struct mv_usb_addon_irq *vbus; /* valid for OTG/UDC. VBUS change*/ + + /* only valid for HCD. OTG or Host only*/ + unsigned int mode; + + int (*phy_init)(unsigned int regbase); + void (*phy_deinit)(unsigned int regbase); + int (*set_vbus)(unsigned int vbus); +}; + +#endif -- cgit v0.10.2 From 5d0b8d0f3af46c5364bc36f814cf2c407012dd1b Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:25 +0800 Subject: usb: gadget: mv_udc: add section information Tag the probe function as __devinit. Tag the remove function as __devexit. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 40a25e7..cd4de00 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1909,7 +1909,7 @@ static void gadget_release(struct device *_dev) complete(udc->done); } -static int mv_udc_remove(struct platform_device *dev) +static int __devexit mv_udc_remove(struct platform_device *dev) { struct mv_udc *udc = the_controller; int clk_i; @@ -1956,7 +1956,7 @@ static int mv_udc_remove(struct platform_device *dev) return 0; } -int mv_udc_probe(struct platform_device *dev) +static int __devinit mv_udc_probe(struct platform_device *dev) { struct mv_usb_platform_data *pdata = dev->dev.platform_data; struct mv_udc *udc; -- cgit v0.10.2 From 4540a9ab319f1a55ded5f635da66da60d3c93e69 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:26 +0800 Subject: usb: gadget: mv_udc: fix kernel panic on some platforms Some platforms will use usb to download images, the controller may not be stopped correctly when start kernel. In some cases, it may have some pending interrupts, and they will be triggered immediately when we finish requesting irq in function probe. But we haven't finished the device initialization at this time. So let's stop udc here to avoid this case occurred. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index cd4de00..8465528 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2037,6 +2037,13 @@ static int __devinit mv_udc_probe(struct platform_device *dev) & CAPLENGTH_MASK)); udc->max_eps = readl(&udc->cap_regs->dccparams) & DCCPARAMS_DEN_MASK; + /* + * some platform will use usb to download image, it may not disconnect + * usb gadget before loading kernel. So first stop udc here. + */ + udc_stop(udc); + writel(0xFFFFFFFF, &udc->op_regs->usbsts); + size = udc->max_eps * sizeof(struct mv_dqh) *2; size = (size + DQH_ALIGNMENT - 1) & ~(DQH_ALIGNMENT - 1); udc->ep_dqh = dma_alloc_coherent(&dev->dev, size, -- cgit v0.10.2 From bedcff970e16d36324ddbfa61de33e4640c9d619 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:27 +0800 Subject: usb: gadget: mv_udc: fix a clerical error The max size of data payload is in bit0 - bit10, so we need use 0x7ff as the bitmask to fetch from usb_endpoint_descriptor.wMaxPacketSize. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 8465528..e2b302f 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -546,7 +546,7 @@ static int mv_ep_enable(struct usb_ep *_ep, case USB_ENDPOINT_XFER_ISOC: /* Calculate transactions needed for high bandwidth iso */ mult = (unsigned char)(1 + ((max >> 11) & 0x03)); - max = max & 0x8ff; /* bit 0~10 */ + max = max & 0x7ff; /* bit 0~10 */ /* 3 transactions at most */ if (mult > 3) goto en_done; -- cgit v0.10.2 From 43ad9f3faea18d7b3e1183753d3e6372a7037edf Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:28 +0800 Subject: usb: gadget: mv_udc: ep0 needn't set toggle flag According to ChipIdea datasheet, there is no toggle flag for ep0. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index e2b302f..80168d1 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -98,12 +98,12 @@ static void ep0_reset(struct mv_udc *udc) epctrlx = readl(&udc->op_regs->epctrlx[0]); if (i) { /* TX */ - epctrlx |= EPCTRL_TX_ENABLE | EPCTRL_TX_DATA_TOGGLE_RST + epctrlx |= EPCTRL_TX_ENABLE | (USB_ENDPOINT_XFER_CONTROL << EPCTRL_TX_EP_TYPE_SHIFT); } else { /* RX */ - epctrlx |= EPCTRL_RX_ENABLE | EPCTRL_RX_DATA_TOGGLE_RST + epctrlx |= EPCTRL_RX_ENABLE | (USB_ENDPOINT_XFER_CONTROL << EPCTRL_RX_EP_TYPE_SHIFT); } -- cgit v0.10.2 From fbebe1f070a73c108415bb9ee0483570650e2712 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:29 +0800 Subject: usb: gadget: mv_udc: init next dtd ptr for dqh when init ep0 Set next dtd ptr to EP_QUEUE_HEAD_NEXT_TERMINATE for dqh when init ep0. It means the dQH is empty. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 80168d1..8dab8c4 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -96,6 +96,8 @@ static void ep0_reset(struct mv_udc *udc) (EP0_MAX_PKT_SIZE << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | EP_QUEUE_HEAD_IOS; + ep->dqh->next_dtd_ptr = EP_QUEUE_HEAD_NEXT_TERMINATE; + epctrlx = readl(&udc->op_regs->epctrlx[0]); if (i) { /* TX */ epctrlx |= EPCTRL_TX_ENABLE -- cgit v0.10.2 From 96c2bbb09d0742148a305d7afbdf7c5803fd78c1 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:30 +0800 Subject: usb: gadget: mv_udc: fix bug when handle setup package. For the code doesn't restrict controller ep must be ep0, so we will go through all the eps and check if there is a setup package received. And also we just need to acknowledge the corresponding bit in ENDPTSETUPSTAT register. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 8dab8c4..fcb980d 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1631,8 +1631,7 @@ static void get_setup_data(struct mv_udc *udc, u8 ep_num, u8 *buffer_ptr) dqh = &udc->ep_dqh[ep_num * 2 + EP_DIR_OUT]; /* Clear bit in ENDPTSETUPSTAT */ - temp = readl(&udc->op_regs->epsetupstat); - writel(temp | (1 << ep_num), &udc->op_regs->epsetupstat); + writel((1 << ep_num), &udc->op_regs->epsetupstat); /* while a hazard exists when setup package arrives */ do { -- cgit v0.10.2 From 27cec2b2f7a4d2394af63a3dc7928975f4c072f4 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:31 +0800 Subject: usb: gadget: mv_udc: add missing spinlock in ep enable/disable The ep enable / disable functions can be called from interrupt context, and they are not race safe on SMP systems. The critical data can be modified in more than one routing. Make them race safe by using IRQ-safe spinlock functions. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index fcb980d..501b05a 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -497,6 +497,7 @@ static int mv_ep_enable(struct usb_ep *_ep, u16 max = 0; u32 bit_pos, epctrlx, direction; unsigned char zlt = 0, ios = 0, mult = 0; + unsigned long flags; ep = container_of(_ep, struct mv_ep, ep); udc = ep->udc; @@ -517,9 +518,6 @@ static int mv_ep_enable(struct usb_ep *_ep, */ zlt = 1; - /* Get the endpoint queue head address */ - dqh = (struct mv_dqh *)ep->dqh; - bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); /* Check if the Endpoint is Primed */ @@ -556,6 +554,10 @@ static int mv_ep_enable(struct usb_ep *_ep, default: goto en_done; } + + spin_lock_irqsave(&udc->lock, flags); + /* Get the endpoint queue head address */ + dqh = ep->dqh; dqh->max_packet_length = (max << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) | (mult << EP_QUEUE_HEAD_MULT_POS) | (zlt ? EP_QUEUE_HEAD_ZLT_SEL : 0) @@ -600,6 +602,8 @@ static int mv_ep_enable(struct usb_ep *_ep, writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); } + spin_unlock_irqrestore(&udc->lock, flags); + return 0; en_done: return -EINVAL; @@ -611,6 +615,7 @@ static int mv_ep_disable(struct usb_ep *_ep) struct mv_ep *ep; struct mv_dqh *dqh; u32 bit_pos, epctrlx, direction; + unsigned long flags; ep = container_of(_ep, struct mv_ep, ep); if ((_ep == NULL) || !ep->desc) @@ -621,6 +626,8 @@ static int mv_ep_disable(struct usb_ep *_ep) /* Get the endpoint queue head address */ dqh = ep->dqh; + spin_lock_irqsave(&udc->lock, flags); + direction = ep_dir(ep); bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); @@ -639,6 +646,9 @@ static int mv_ep_disable(struct usb_ep *_ep) ep->desc = NULL; ep->stopped = 1; + + spin_unlock_irqrestore(&udc->lock, flags); + return 0; } -- cgit v0.10.2 From 615268b05f6c719f5151c351022aa79ab73a0898 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:32 +0800 Subject: usb: gadget: mv_udc: set unused endpoint with right type According to the comment right above the code, we should use USB_ENDPOINT_XFER_BULK instead. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 501b05a..16bfcb9 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -590,14 +590,14 @@ static int mv_ep_enable(struct usb_ep *_ep, */ epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); if ((epctrlx & EPCTRL_RX_ENABLE) == 0) { - epctrlx |= ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + epctrlx |= (USB_ENDPOINT_XFER_BULK << EPCTRL_RX_EP_TYPE_SHIFT); writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); } epctrlx = readl(&udc->op_regs->epctrlx[ep->ep_num]); if ((epctrlx & EPCTRL_TX_ENABLE) == 0) { - epctrlx |= ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + epctrlx |= (USB_ENDPOINT_XFER_BULK << EPCTRL_TX_EP_TYPE_SHIFT); writel(epctrlx, &udc->op_regs->epctrlx[ep->ep_num]); } -- cgit v0.10.2 From 0c70840b22d9f3b762f21a28bface1a42c0c5ba2 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:33 +0800 Subject: usb: gadget: mv_udc: rewrite fifo flush 1: Add parameter check. 2: For controller endpoint, we need to flush in and out directions. 3: delete redundant code, make it more readable. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 16bfcb9..843a479 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -681,37 +681,28 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep) { struct mv_udc *udc; u32 bit_pos, direction; - struct mv_ep *ep = container_of(_ep, struct mv_ep, ep); + struct mv_ep *ep; unsigned int loops; + if (!_ep) + return; + + ep = container_of(_ep, struct mv_ep, ep); + if (!ep->desc) + return; + udc = ep->udc; direction = ep_dir(ep); - bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); - /* - * Flushing will halt the pipe - * Write 1 to the Flush register - */ - writel(bit_pos, &udc->op_regs->epflush); - /* Wait until flushing completed */ - loops = LOOPS(FLUSH_TIMEOUT); - while (readl(&udc->op_regs->epflush) & bit_pos) { - /* - * ENDPTFLUSH bit should be cleared to indicate this - * operation is complete - */ - if (loops == 0) { - dev_err(&udc->dev->dev, - "TIMEOUT for ENDPTFLUSH=0x%x, bit_pos=0x%x\n", - (unsigned)readl(&udc->op_regs->epflush), - (unsigned)bit_pos); - return; - } - loops--; - udelay(LOOPS_USEC); - } + if (ep->ep_num == 0) + bit_pos = (1 << 16) | 1; + else if (direction == EP_DIR_OUT) + bit_pos = 1 << ep->ep_num; + else + bit_pos = 1 << (16 + ep->ep_num); + loops = LOOPS(EPSTATUS_TIMEOUT); - while (readl(&udc->op_regs->epstatus) & bit_pos) { + do { unsigned int inter_loops; if (loops == 0) { @@ -726,7 +717,7 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep) /* Wait until flushing completed */ inter_loops = LOOPS(FLUSH_TIMEOUT); - while (readl(&udc->op_regs->epflush) & bit_pos) { + while (readl(&udc->op_regs->epflush)) { /* * ENDPTFLUSH bit should be cleared to indicate this * operation is complete @@ -743,7 +734,7 @@ static void mv_ep_fifo_flush(struct usb_ep *_ep) udelay(LOOPS_USEC); } loops--; - } + } while (readl(&udc->op_regs->epstatus) & bit_pos); } /* queues (submits) an I/O request to an endpoint */ -- cgit v0.10.2 From 366162245e619d59c9d615774ab3aa639deb7725 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:34 +0800 Subject: usb: gadget: mv_udc: correct ep0 state This patch is going to correct the ep0 state, and the unexpected ep0 package warning can be removed. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 843a479..f8dc5b3 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1343,6 +1343,7 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) ep = &udc->eps[0]; udc->ep0_dir = direction; + udc->ep0_state = WAIT_FOR_OUT_STATUS; req = udc->status_req; @@ -1421,6 +1422,8 @@ static void ch9getstatus(struct mv_udc *udc, u8 ep_num, retval = udc_prime_status(udc, EP_DIR_IN, status, false); if (retval) ep0_stall(udc); + else + udc->ep0_state = DATA_STATE_XMIT; } static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) @@ -1466,8 +1469,6 @@ static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) if (udc_prime_status(udc, EP_DIR_IN, 0, true)) ep0_stall(udc); - else - udc->ep0_state = DATA_STATE_XMIT; out: return; } -- cgit v0.10.2 From 046b07ac0458fba8c5ca8d9ee04658c02066ee03 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:35 +0800 Subject: usb: gadget: mv_udc: add shutdown function for it put the device in idle when shutdown. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index f8dc5b3..ffa6f30 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -2206,9 +2206,21 @@ static const struct dev_pm_ops mv_udc_pm_ops = { }; #endif +static void mv_udc_shutdown(struct platform_device *dev) +{ + struct mv_udc *udc = the_controller; + u32 mode; + + /* reset controller mode to IDLE */ + mode = readl(&udc->op_regs->usbmode); + mode &= ~3; + writel(mode, &udc->op_regs->usbmode); +} + static struct platform_driver udc_driver = { .probe = mv_udc_probe, .remove = __exit_p(mv_udc_remove), + .shutdown = mv_udc_shutdown, .driver = { .owner = THIS_MODULE, .name = "pxa-u2o", -- cgit v0.10.2 From daec765da767e4a6a30e1298862b28f2cae9a73f Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:36 +0800 Subject: usb: gadget: mv_udc: fix dtd dma confusion The controller will prime failure sometimes when do the iperf test. Add delay to wait controller release dtd dma before we free it. Then the issue is gone. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index ffa6f30..9d960f1 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -138,6 +138,7 @@ static int process_ep_req(struct mv_udc *udc, int index, int i, direction; int retval = 0; u32 errors; + u32 bit_pos; curr_dqh = &udc->ep_dqh[index]; direction = index % 2; @@ -155,10 +156,20 @@ static int process_ep_req(struct mv_udc *udc, int index, errors = curr_dtd->size_ioc_sts & DTD_ERROR_MASK; if (!errors) { - remaining_length += + remaining_length = (curr_dtd->size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS; actual -= remaining_length; + + if (remaining_length) { + if (direction) { + dev_dbg(&udc->dev->dev, + "TX dTD remains data\n"); + retval = -EPROTO; + break; + } else + break; + } } else { dev_info(&udc->dev->dev, "complete_tr error: ep=%d %s: error = 0x%x\n", @@ -180,6 +191,20 @@ static int process_ep_req(struct mv_udc *udc, int index, if (retval) return retval; + if (direction == EP_DIR_OUT) + bit_pos = 1 << curr_req->ep->ep_num; + else + bit_pos = 1 << (16 + curr_req->ep->ep_num); + + while ((curr_dqh->curr_dtd_ptr == curr_dtd->td_dma)) { + if (curr_dtd->dtd_next == EP_QUEUE_HEAD_NEXT_TERMINATE) { + while (readl(&udc->op_regs->epstatus) & bit_pos) + udelay(1); + break; + } + udelay(1); + } + curr_req->req.actual = actual; return 0; -- cgit v0.10.2 From 46e172dfb38c9dad2ea52d8c161834c1f0bd2473 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:37 +0800 Subject: usb: gadget: mv_udc: use DMA API for status_req's dma address use DMA API for status_req's dma address, it is needed by dtd. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 9d960f1..1f47d03 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1385,6 +1385,13 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) req->req.complete = NULL; req->dtd_count = 0; + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, + req->req.buf, req->req.length, + ep_dir(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + req->mapped = 1; + } + /* prime the data phase */ if (!req_to_dtd(req)) retval = queue_dtd(ep, req); @@ -2115,7 +2122,7 @@ static int __devinit mv_udc_probe(struct platform_device *dev) /* allocate a small amount of memory to get valid address */ udc->status_req->req.buf = kzalloc(8, GFP_KERNEL); - udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf); + udc->status_req->req.dma = DMA_ADDR_INVALID; udc->resume_state = USB_STATE_NOTATTACHED; udc->usb_state = USB_STATE_POWERED; -- cgit v0.10.2 From fb22cbac8242e92d643e5d5cb81bc6307fa6fc9c Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:38 +0800 Subject: usb: gadget: mv_udc: add test mode support Add test mode support for marvell udc driver. Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h index d3d2645..3e5e6ea 100644 --- a/drivers/usb/gadget/mv_udc.h +++ b/drivers/usb/gadget/mv_udc.h @@ -202,6 +202,7 @@ struct mv_udc { unsigned int ep0_dir; unsigned int dev_addr; + unsigned int test_mode; int errors; unsigned softconnect:1, @@ -238,6 +239,7 @@ struct mv_req { struct mv_dtd *dtd, *head, *tail; struct mv_ep *ep; struct list_head queue; + unsigned int test_mode; unsigned dtd_count; unsigned mapped:1; }; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 1f47d03..333b85b 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1204,11 +1204,6 @@ static const struct usb_gadget_ops mv_ops = { .stop = mv_udc_stop, }; -static void mv_udc_testmode(struct mv_udc *udc, u16 index, bool enter) -{ - dev_info(&udc->dev->dev, "Test Mode is not support yet\n"); -} - static int eps_init(struct mv_udc *udc) { struct mv_ep *ep; @@ -1359,6 +1354,31 @@ static int mv_udc_stop(struct usb_gadget_driver *driver) return 0; } +static void mv_set_ptc(struct mv_udc *udc, u32 mode) +{ + u32 portsc; + + portsc = readl(&udc->op_regs->portsc[0]); + portsc |= mode << 16; + writel(portsc, &udc->op_regs->portsc[0]); +} + +static void prime_status_complete(struct usb_ep *ep, struct usb_request *_req) +{ + struct mv_udc *udc = the_controller; + struct mv_req *req = container_of(_req, struct mv_req, req); + unsigned long flags; + + dev_info(&udc->dev->dev, "switch to test mode %d\n", req->test_mode); + + spin_lock_irqsave(&udc->lock, flags); + if (req->test_mode) { + mv_set_ptc(udc, req->test_mode); + req->test_mode = 0; + } + spin_unlock_irqrestore(&udc->lock, flags); +} + static int udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) { @@ -1382,7 +1402,12 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty) req->ep = ep; req->req.status = -EINPROGRESS; req->req.actual = 0; - req->req.complete = NULL; + if (udc->test_mode) { + req->req.complete = prime_status_complete; + req->test_mode = udc->test_mode; + udc->test_mode = 0; + } else + req->req.complete = NULL; req->dtd_count = 0; if (req->req.dma == DMA_ADDR_INVALID) { @@ -1412,6 +1437,17 @@ out: return retval; } +static void mv_udc_testmode(struct mv_udc *udc, u16 index) +{ + if (index <= TEST_FORCE_EN) { + udc->test_mode = index; + if (udc_prime_status(udc, EP_DIR_IN, 0, true)) + ep0_stall(udc); + } else + dev_err(&udc->dev->dev, + "This test mode(%d) is not supported\n", index); +} + static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup) { udc->dev_addr = (u8)setup->wValue; @@ -1470,9 +1506,6 @@ static void ch9clearfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) case USB_DEVICE_REMOTE_WAKEUP: udc->remote_wakeup = 0; break; - case USB_DEVICE_TEST_MODE: - mv_udc_testmode(udc, 0, false); - break; default: goto out; } @@ -1518,16 +1551,16 @@ static void ch9setfeature(struct mv_udc *udc, struct usb_ctrlrequest *setup) break; case USB_DEVICE_TEST_MODE: if (setup->wIndex & 0xFF - && udc->gadget.speed != USB_SPEED_HIGH) - goto out; - if (udc->usb_state == USB_STATE_CONFIGURED - || udc->usb_state == USB_STATE_ADDRESS - || udc->usb_state == USB_STATE_DEFAULT) - mv_udc_testmode(udc, - setup->wIndex & 0xFF00, true); - else - goto out; - break; + || udc->gadget.speed != USB_SPEED_HIGH) + ep0_stall(udc); + + if (udc->usb_state != USB_STATE_CONFIGURED + && udc->usb_state != USB_STATE_ADDRESS + && udc->usb_state != USB_STATE_DEFAULT) + ep0_stall(udc); + + mv_udc_testmode(udc, (setup->wIndex >> 8)); + goto out; default: goto out; } -- cgit v0.10.2 From 1aec033b955ba358dbf365aa7d0bbd49079c8559 Mon Sep 17 00:00:00 2001 From: Neil Zhang Date: Wed, 12 Oct 2011 16:49:39 +0800 Subject: usb: gadget: mv_udc: add clock gating support This patch is going to support clock gating when vbus detection is posible. Clock and phy will be on only when usb gadget is used(vbus valid). Signed-off-by: Neil Zhang Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/mv_udc.h index 3e5e6ea..daa75c1 100644 --- a/drivers/usb/gadget/mv_udc.h +++ b/drivers/usb/gadget/mv_udc.h @@ -209,7 +209,12 @@ struct mv_udc { vbus_active:1, remote_wakeup:1, softconnected:1, - force_fs:1; + force_fs:1, + clock_gating:1, + active:1; + + struct work_struct vbus_work; + struct workqueue_struct *qwork; struct mv_usb_platform_data *pdata; diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 333b85b..046ab12 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -67,6 +67,7 @@ static struct mv_udc *the_controller; int mv_usb_otgsc; static void nuke(struct mv_ep *ep, int status); +static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver); /* for endpoint 0 operations */ static const struct usb_endpoint_descriptor mv_ep0_desc = { @@ -1133,6 +1134,40 @@ static int udc_reset(struct mv_udc *udc) return 0; } +static int mv_udc_enable(struct mv_udc *udc) +{ + int retval; + + if (udc->clock_gating == 0 || udc->active) + return 0; + + dev_dbg(&udc->dev->dev, "enable udc\n"); + udc_clock_enable(udc); + if (udc->pdata->phy_init) { + retval = udc->pdata->phy_init(udc->phy_regs); + if (retval) { + dev_err(&udc->dev->dev, + "init phy error %d\n", retval); + udc_clock_disable(udc); + return retval; + } + } + udc->active = 1; + + return 0; +} + +static void mv_udc_disable(struct mv_udc *udc) +{ + if (udc->clock_gating && udc->active) { + dev_dbg(&udc->dev->dev, "disable udc\n"); + if (udc->pdata->phy_deinit) + udc->pdata->phy_deinit(udc->phy_regs); + udc_clock_disable(udc); + udc->active = 0; + } +} + static int mv_udc_get_frame(struct usb_gadget *gadget) { struct mv_udc *udc; @@ -1168,22 +1203,68 @@ static int mv_udc_wakeup(struct usb_gadget *gadget) return 0; } +static int mv_udc_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct mv_udc *udc; + unsigned long flags; + int retval = 0; + + udc = container_of(gadget, struct mv_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + + dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, udc->softconnect, udc->vbus_active); + + udc->vbus_active = (is_active != 0); + if (udc->driver && udc->softconnect && udc->vbus_active) { + retval = mv_udc_enable(udc); + if (retval == 0) { + /* Clock is disabled, need re-init registers */ + udc_reset(udc); + ep0_reset(udc); + udc_start(udc); + } + } else if (udc->driver && udc->softconnect) { + /* stop all the transfer in queue*/ + stop_activity(udc, udc->driver); + udc_stop(udc); + mv_udc_disable(udc); + } + + spin_unlock_irqrestore(&udc->lock, flags); + return retval; +} + static int mv_udc_pullup(struct usb_gadget *gadget, int is_on) { struct mv_udc *udc; unsigned long flags; + int retval = 0; udc = container_of(gadget, struct mv_udc, gadget); spin_lock_irqsave(&udc->lock, flags); + dev_dbg(&udc->dev->dev, "%s: softconnect %d, vbus_active %d\n", + __func__, udc->softconnect, udc->vbus_active); + udc->softconnect = (is_on != 0); - if (udc->driver && udc->softconnect) - udc_start(udc); - else + if (udc->driver && udc->softconnect && udc->vbus_active) { + retval = mv_udc_enable(udc); + if (retval == 0) { + /* Clock is disabled, need re-init registers */ + udc_reset(udc); + ep0_reset(udc); + udc_start(udc); + } + } else if (udc->driver && udc->vbus_active) { + /* stop all the transfer in queue*/ + stop_activity(udc, udc->driver); udc_stop(udc); + mv_udc_disable(udc); + } spin_unlock_irqrestore(&udc->lock, flags); - return 0; + return retval; } static int mv_udc_start(struct usb_gadget_driver *driver, @@ -1198,6 +1279,9 @@ static const struct usb_gadget_ops mv_ops = { /* tries to wake up the host connected to this gadget */ .wakeup = mv_udc_wakeup, + /* notify controller that VBUS is powered or not */ + .vbus_session = mv_udc_vbus_session, + /* D+ pullup, software-controlled connect/disconnect to USB host */ .pullup = mv_udc_pullup, .start = mv_udc_start, @@ -1310,7 +1394,7 @@ static int mv_udc_start(struct usb_gadget_driver *driver, udc->usb_state = USB_STATE_ATTACHED; udc->ep0_state = WAIT_FOR_SETUP; - udc->ep0_dir = USB_DIR_OUT; + udc->ep0_dir = EP_DIR_OUT; spin_unlock_irqrestore(&udc->lock, flags); @@ -1322,9 +1406,13 @@ static int mv_udc_start(struct usb_gadget_driver *driver, udc->gadget.dev.driver = NULL; return retval; } - udc_reset(udc); - ep0_reset(udc); - udc_start(udc); + + /* pullup is always on */ + mv_udc_pullup(&udc->gadget, 1); + + /* When boot with cable attached, there will be no vbus irq occurred */ + if (udc->qwork) + queue_work(udc->qwork, &udc->vbus_work); return 0; } @@ -1337,13 +1425,16 @@ static int mv_udc_stop(struct usb_gadget_driver *driver) if (!udc) return -ENODEV; - udc_stop(udc); - spin_lock_irqsave(&udc->lock, flags); + mv_udc_enable(udc); + udc_stop(udc); + /* stop all usb activities */ udc->gadget.speed = USB_SPEED_UNKNOWN; stop_activity(udc, driver); + mv_udc_disable(udc); + spin_unlock_irqrestore(&udc->lock, flags); /* unbind gadget driver */ @@ -1969,6 +2060,35 @@ static irqreturn_t mv_udc_irq(int irq, void *dev) return IRQ_HANDLED; } +static irqreturn_t mv_udc_vbus_irq(int irq, void *dev) +{ + struct mv_udc *udc = (struct mv_udc *)dev; + + /* polling VBUS and init phy may cause too much time*/ + if (udc->qwork) + queue_work(udc->qwork, &udc->vbus_work); + + return IRQ_HANDLED; +} + +static void mv_udc_vbus_work(struct work_struct *work) +{ + struct mv_udc *udc; + unsigned int vbus; + + udc = container_of(work, struct mv_udc, vbus_work); + if (!udc->pdata->vbus) + return; + + vbus = udc->pdata->vbus->poll(); + dev_info(&udc->dev->dev, "vbus is %d\n", vbus); + + if (vbus == VBUS_HIGH) + mv_udc_vbus_session(&udc->gadget, 1); + else if (vbus == VBUS_LOW) + mv_udc_vbus_session(&udc->gadget, 0); +} + /* release device structure */ static void gadget_release(struct device *_dev) { @@ -1984,6 +2104,14 @@ static int __devexit mv_udc_remove(struct platform_device *dev) usb_del_gadget_udc(&udc->gadget); + if (udc->qwork) { + flush_workqueue(udc->qwork); + destroy_workqueue(udc->qwork); + } + + if (udc->pdata && udc->pdata->vbus && udc->clock_gating) + free_irq(udc->pdata->vbus->irq, &dev->dev); + /* free memory allocated in probe */ if (udc->dtd_pool) dma_pool_destroy(udc->dtd_pool); @@ -1997,6 +2125,8 @@ static int __devexit mv_udc_remove(struct platform_device *dev) if (udc->irq) free_irq(udc->irq, &dev->dev); + mv_udc_disable(udc); + if (udc->cap_regs) iounmap(udc->cap_regs); udc->cap_regs = NULL; @@ -2197,13 +2327,52 @@ static int __devinit mv_udc_probe(struct platform_device *dev) eps_init(udc); + /* VBUS detect: we can disable/enable clock on demand.*/ + if (pdata->vbus) { + udc->clock_gating = 1; + retval = request_threaded_irq(pdata->vbus->irq, NULL, + mv_udc_vbus_irq, IRQF_ONESHOT, "vbus", udc); + if (retval) { + dev_info(&dev->dev, + "Can not request irq for VBUS, " + "disable clock gating\n"); + udc->clock_gating = 0; + } + + udc->qwork = create_singlethread_workqueue("mv_udc_queue"); + if (!udc->qwork) { + dev_err(&dev->dev, "cannot create workqueue\n"); + retval = -ENOMEM; + goto err_unregister; + } + + INIT_WORK(&udc->vbus_work, mv_udc_vbus_work); + } + + /* + * When clock gating is supported, we can disable clk and phy. + * If not, it means that VBUS detection is not supported, we + * have to enable vbus active all the time to let controller work. + */ + if (udc->clock_gating) { + if (udc->pdata->phy_deinit) + udc->pdata->phy_deinit(udc->phy_regs); + udc_clock_disable(udc); + } else + udc->vbus_active = 1; + retval = usb_add_gadget_udc(&dev->dev, &udc->gadget); if (retval) goto err_unregister; + dev_info(&dev->dev, "successful probe UDC device %s clock gating.\n", + udc->clock_gating ? "with" : "without"); + return 0; err_unregister: + if (udc->pdata && udc->pdata->vbus && udc->clock_gating) + free_irq(pdata->vbus->irq, &dev->dev); device_unregister(&udc->gadget.dev); err_free_irq: free_irq(udc->irq, &dev->dev); -- cgit v0.10.2 From 431879a762277ddb9df959bba80216f3ef3d8e17 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 13 Oct 2011 20:27:54 +0300 Subject: usb: gadget: mv_udc: fix compile warning | drivers/usb/gadget/mv_udc_core.c: In function 'handle_setup_packet': | drivers/usb/gadget/mv_udc_core.c:1556:6: warning: 'status' may be \ used uninitialized in this function Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c index 046ab12..fbed5d0 100644 --- a/drivers/usb/gadget/mv_udc_core.c +++ b/drivers/usb/gadget/mv_udc_core.c @@ -1553,7 +1553,7 @@ static void ch9setaddress(struct mv_udc *udc, struct usb_ctrlrequest *setup) static void ch9getstatus(struct mv_udc *udc, u8 ep_num, struct usb_ctrlrequest *setup) { - u16 status; + u16 status = 0; int retval; if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) -- cgit v0.10.2 From fa3ae0c158c70e6cf227b3a194659ee7fed8c588 Mon Sep 17 00:00:00 2001 From: Klaus Schwarzkopf Date: Mon, 10 Oct 2011 10:32:23 +0200 Subject: usb: gadget: add new usb gadget for ACM and mass storage This driver provides two functions in one configuration: a mass storage, and a ACM (serial port) link. Heavily based on multi.c and cdc2.c Signed-off-by: Klaus Schwarzkopf Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index a60b472..b21cd37 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -873,6 +873,16 @@ config USB_G_NOKIA It's only really useful for N900 hardware. If you're building a kernel for N900, say Y or M here. If unsure, say N. +config USB_G_ACM_MS + tristate "CDC Composite Device (ACM and mass storage)" + depends on BLOCK + help + This driver provides two functions in one configuration: + a mass storage, and a CDC ACM (serial port) link. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_acm_ms". + config USB_G_MULTI tristate "Multifunction Composite Gadget (EXPERIMENTAL)" depends on BLOCK && NET diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index c36da63..b54ac61 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -51,6 +51,7 @@ g_dbgp-y := dbgp.o g_nokia-y := nokia.o g_webcam-y := webcam.o g_ncm-y := ncm.o +g_acm_ms-y := acm_ms.o obj-$(CONFIG_USB_ZERO) += g_zero.o obj-$(CONFIG_USB_AUDIO) += g_audio.o @@ -69,3 +70,4 @@ obj-$(CONFIG_USB_G_MULTI) += g_multi.o obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o obj-$(CONFIG_USB_G_NCM) += g_ncm.o +obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c new file mode 100644 index 0000000..fdb7aec --- /dev/null +++ b/drivers/usb/gadget/acm_ms.c @@ -0,0 +1,256 @@ +/* + * acm_ms.c -- Composite driver, with ACM and mass storage support + * + * Copyright (C) 2008 David Brownell + * Copyright (C) 2008 Nokia Corporation + * Author: David Brownell + * Modified: Klaus Schwarzkopf + * + * Heavily based on multi.c and cdc2.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include "u_serial.h" + +#define DRIVER_DESC "Composite Gadget (ACM + MS)" +#define DRIVER_VERSION "2011/10/10" + +/*-------------------------------------------------------------------------*/ + +/* + * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. + */ +#define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */ +#define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/ + +/*-------------------------------------------------------------------------*/ + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ + +#include "composite.c" +#include "usbstring.c" +#include "config.c" +#include "epautoconf.c" +#include "u_serial.c" +#include "f_acm.c" +#include "f_mass_storage.c" + +/*-------------------------------------------------------------------------*/ + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + + .bDeviceClass = USB_CLASS_MISC /* 0xEF */, + .bDeviceSubClass = 2, + .bDeviceProtocol = 1, + + /* .bMaxPacketSize0 = f(hardware) */ + + /* Vendor and product id can be overridden by module parameters. */ + .idVendor = cpu_to_le16(ACM_MS_VENDOR_NUM), + .idProduct = cpu_to_le16(ACM_MS_PRODUCT_NUM), + /* .bcdDevice = f(hardware) */ + /* .iManufacturer = DYNAMIC */ + /* .iProduct = DYNAMIC */ + /* NO SERIAL NUMBER */ + /*.bNumConfigurations = DYNAMIC*/ +}; + +static struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* + * REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +static const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; + + +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 + +static char manufacturer[50]; + +static struct usb_string strings_dev[] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = DRIVER_DESC, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +/****************************** Configurations ******************************/ + +static struct fsg_module_parameters fsg_mod_data = { .stall = 1 }; +FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); + +static struct fsg_common fsg_common; + +/*-------------------------------------------------------------------------*/ + +/* + * We _always_ have both ACM and mass storage functions. + */ +static int __init acm_ms_do_config(struct usb_configuration *c) +{ + int status; + + if (gadget_is_otg(c->cdev->gadget)) { + c->descriptors = otg_desc; + c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + + status = acm_bind_config(c, 0); + if (status < 0) + return status; + + status = fsg_bind_config(c->cdev, c, &fsg_common); + if (status < 0) + return status; + + return 0; +} + +static struct usb_configuration acm_ms_config_driver = { + .label = DRIVER_DESC, + .bConfigurationValue = 1, + /* .iConfiguration = DYNAMIC */ + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init acm_ms_bind(struct usb_composite_dev *cdev) +{ + int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int status; + void *retp; + + /* set up serial link layer */ + status = gserial_setup(cdev->gadget, 1); + if (status < 0) + return status; + + /* set up mass storage function */ + retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data); + if (IS_ERR(retp)) { + status = PTR_ERR(retp); + goto fail0; + } + + /* set bcdDevice */ + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) { + device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum); + } else { + WARNING(cdev, "controller '%s' not recognized; trying %s\n", + gadget->name, + acm_ms_config_driver.label); + device_desc.bcdDevice = + cpu_to_le16(0x0300 | 0x0099); + } + + /* + * Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + + /* device descriptor strings: manufacturer, product */ + snprintf(manufacturer, sizeof manufacturer, "%s %s with %s", + init_utsname()->sysname, init_utsname()->release, + gadget->name); + status = usb_string_id(cdev); + if (status < 0) + goto fail1; + strings_dev[STRING_MANUFACTURER_IDX].id = status; + device_desc.iManufacturer = status; + + status = usb_string_id(cdev); + if (status < 0) + goto fail1; + strings_dev[STRING_PRODUCT_IDX].id = status; + device_desc.iProduct = status; + + /* register our configuration */ + status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config); + if (status < 0) + goto fail1; + + dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n", + DRIVER_DESC); + fsg_common_put(&fsg_common); + return 0; + + /* error recovery */ +fail1: + fsg_common_put(&fsg_common); +fail0: + gserial_cleanup(); + return status; +} + +static int __exit acm_ms_unbind(struct usb_composite_dev *cdev) +{ + gserial_cleanup(); + + return 0; +} + +static struct usb_composite_driver acm_ms_driver = { + .name = "g_acm_ms", + .dev = &device_desc, + .strings = dev_strings, + .unbind = __exit_p(acm_ms_unbind), +}; + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Klaus Schwarzkopf "); +MODULE_LICENSE("GPL v2"); + +static int __init init(void) +{ + return usb_composite_probe(&acm_ms_driver, acm_ms_bind); +} +module_init(init); + +static void __exit cleanup(void) +{ + usb_composite_unregister(&acm_ms_driver); +} +module_exit(cleanup); -- cgit v0.10.2 From 48e8236114c12c5366e032fc517e1bd376369a56 Mon Sep 17 00:00:00 2001 From: Sarah Sharp Date: Thu, 6 Oct 2011 11:54:23 -0700 Subject: xHCI/USB: Make xHCI driver have a BOS descriptor. To add USB 3.0 link power management (LPM), we need to know what the U1 and U2 exit latencies are for the xHCI host controller. External USB 3.0 hubs report these values through the SuperSpeed Capabilities descriptor in the BOS descriptor. Make the USB 3.0 roothub for the xHCI host behave like an external hub and return the BOS descriptors. The U1 and U2 exit latencies will vary across each host controller, so we need to dynamically fill those values in by reading the exit latencies out of the xHC registers. Make the roothub code in the USB core handle hub_control() returning the length of the data copied. Signed-off-by: Sarah Sharp Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 100d4b0..b3b7d06 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -442,7 +442,11 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) struct usb_ctrlrequest *cmd; u16 typeReq, wValue, wIndex, wLength; u8 *ubuf = urb->transfer_buffer; - u8 tbuf [sizeof (struct usb_hub_descriptor)] + /* + * tbuf should be as big as the BOS descriptor and + * the USB hub descriptor. + */ + u8 tbuf[USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE] __attribute__((aligned(4))); const u8 *bufp = tbuf; unsigned len = 0; @@ -562,6 +566,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) else /* unsupported IDs --> "protocol stall" */ goto error; break; + case USB_DT_BOS << 8: + goto nongeneric; default: goto error; } @@ -596,6 +602,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) /* CLASS REQUESTS (and errors) */ default: +nongeneric: /* non-generic request */ switch (typeReq) { case GetHubStatus: @@ -605,6 +612,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) case GetHubDescriptor: len = sizeof (struct usb_hub_descriptor); break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + /* len is returned by hub_control */ + break; } status = hcd->driver->hub_control (hcd, typeReq, wValue, wIndex, @@ -615,7 +625,7 @@ error: status = -EPIPE; } - if (status) { + if (status < 0) { len = 0; if (status != -EPIPE) { dev_dbg (hcd->self.controller, @@ -624,6 +634,10 @@ error: typeReq, wValue, wIndex, wLength, status); } + } else if (status > 0) { + /* hub_control may return the length of data copied. */ + len = status; + status = 0; } if (len) { if (urb->transfer_buffer_length < len) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 9f844d4..2f74edd 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -28,6 +28,25 @@ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ PORT_RC | PORT_PLC | PORT_PE) +/* usb 1.1 root hub device descriptor */ +static u8 usb_bos_descriptor [] = { + USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */ + USB_DT_BOS, /* __u8 bDescriptorType */ + 0x0F, 0x00, /* __le16 wTotalLength, 15 bytes */ + 0x1, /* __u8 bNumDeviceCaps */ + /* First device capability */ + USB_DT_USB_SS_CAP_SIZE, /* __u8 bLength, 10 bytes */ + USB_DT_DEVICE_CAPABILITY, /* Device Capability */ + USB_SS_CAP_TYPE, /* bDevCapabilityType, SUPERSPEED_USB */ + 0x00, /* bmAttributes, LTM off by default */ + USB_5GBPS_OPERATION, 0x00, /* wSpeedsSupported, 5Gbps only */ + 0x03, /* bFunctionalitySupport, + USB 3.0 speed only */ + 0x00, /* bU1DevExitLat, set later. */ + 0x00, 0x00 /* __le16 bU2DevExitLat, set later. */ +}; + + static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, struct usb_hub_descriptor *desc, int ports) { @@ -455,6 +474,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_hub_descriptor(hcd, xhci, (struct usb_hub_descriptor *) buf); break; + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + if ((wValue & 0xff00) != (USB_DT_BOS << 8)) + goto error; + + if (hcd->speed != HCD_USB3) + goto error; + + memcpy(buf, &usb_bos_descriptor, + USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); + temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); + buf[12] = HCS_U1_LATENCY(temp); + put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + + spin_unlock_irqrestore(&xhci->lock, flags); + return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; case GetPortStatus: if (!wIndex || wIndex > max_ports) goto error; -- cgit v0.10.2 From a8b43c00ef06aec49b9fe0a5bad8a6a320e4d27b Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 6 Oct 2011 15:35:43 -0400 Subject: USB: Fix runtime wakeup on OHCI At least some OHCI hardware (such as the MCP89) fails to flag any change in the host status register or the port status registers when receiving a remote wakeup while in D3 state. This results in the controller being resumed but no device state change being noticed, at which point the controller is put back to sleep again. Since there doesn't seem to be any reliable way to identify the state change, just unconditionally resume the hub. It'll be put back to sleep in the near future anyway if there are no active devices attached to it. Signed-off-by: Matthew Garrett Cc: stable Cc: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 9154615..2f00040 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -356,10 +356,7 @@ static void ohci_finish_controller_resume(struct usb_hcd *hcd) msleep(20); } - /* Does the root hub have a port wakeup pending? */ - if (ohci_readl(ohci, &ohci->regs->intrstatus) & - (OHCI_INTR_RD | OHCI_INTR_RHSC)) - usb_hcd_resume_root_hub(hcd); + usb_hcd_resume_root_hub(hcd); } /* Carry out polling-, autostop-, and autoresume-related state changes */ -- cgit v0.10.2 From 3687f641307eeff6f7fe31a88dc39db88e89238b Mon Sep 17 00:00:00 2001 From: Peter Stuge Date: Mon, 10 Oct 2011 03:34:54 +0200 Subject: USB: ftdi_sio: Support TI/Luminary Micro Stellaris BD-ICDI Board Some Stellaris evaluation kits have the JTAG/SWD FTDI chip onboard, and some, like EK-LM3S9B90, come with a separate In-Circuit Debugger Interface Board. The ICDI board can also be used stand-alone, for other boards and chips than the kit it came with. The ICDI has both old style 20-pin JTAG connector and new style JTAG/SWD 10-pin 1.27mm pitch connector. Tested with EK-LM3S9B90, where the BD-ICDI board is included. Signed-off-by: Peter Stuge Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f971c98..effc436 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -747,6 +747,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, LMI_LM3S_ICDI_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) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 4d231ee..571fa96 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -54,6 +54,7 @@ /* FTDI 2332C Dual channel device, side A=245 FIFO (JTAG), Side B=RS232 UART */ #define LMI_LM3S_DEVEL_BOARD_PID 0xbcd8 #define LMI_LM3S_EVAL_BOARD_PID 0xbcd9 +#define LMI_LM3S_ICDI_BOARD_PID 0xbcda #define FTDI_TURTELIZER_PID 0xBDC8 /* JTAG/RS-232 adapter by egnite GmbH */ -- cgit v0.10.2 From 8f5d621543cb064d2989fc223d3c2bc61a43981e Mon Sep 17 00:00:00 2001 From: Joachim Foerster Date: Mon, 10 Oct 2011 18:06:54 +0200 Subject: usb/isp1760: Let OF bindings depend on general CONFIG_OF instead of PPC_OF . To be able to use the driver on other OF-aware architectures, too. And add necessary OF related #includes to fix compilation error. Signed-off-by: Joachim Foerster Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index d2f6b2d..791792d 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -17,16 +17,18 @@ #include "isp1760-hcd.h" -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF #include #include +#include +#include #endif #ifdef CONFIG_PCI #include #endif -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF static int of_isp1760_probe(struct platform_device *dev) { struct usb_hcd *hcd; @@ -396,7 +398,7 @@ static int __init isp1760_init(void) ret = platform_driver_register(&isp1760_plat_driver); if (!ret) any_ret = 0; -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF ret = platform_driver_register(&isp1760_of_driver); if (!ret) any_ret = 0; @@ -416,7 +418,7 @@ module_init(isp1760_init); static void __exit isp1760_exit(void) { platform_driver_unregister(&isp1760_plat_driver); -#ifdef CONFIG_PPC_OF +#ifdef CONFIG_OF platform_driver_unregister(&isp1760_of_driver); #endif #ifdef CONFIG_PCI -- cgit v0.10.2 From c75ccd481258d923216337955930ea49ea1a9a1a Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 10 Oct 2011 17:24:08 +0200 Subject: USB: ftdi_sio.c: Basic icount infrastructure for ftdi_sio Signed-off-by: Uwe Bonnes Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index effc436..4cfc4cd 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -73,6 +73,7 @@ struct ftdi_private { */ int flags; /* some ASYNC_xxxx flags are supported */ unsigned long last_dtr_rts; /* saved modem control outputs */ + struct async_icount icount; wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */ char prev_status, diff_status; /* Used for TIOCMIWAIT */ char transmit_empty; /* If transmitter is empty or not */ @@ -889,6 +890,8 @@ static void ftdi_set_termios(struct tty_struct *tty, static int ftdi_tiocmget(struct tty_struct *tty); static int ftdi_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int ftdi_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount); static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); static void ftdi_break_ctl(struct tty_struct *tty, int break_state); @@ -923,6 +926,7 @@ static struct usb_serial_driver ftdi_sio_device = { .prepare_write_buffer = ftdi_prepare_write_buffer, .tiocmget = ftdi_tiocmget, .tiocmset = ftdi_tiocmset, + .get_icount = ftdi_get_icount, .ioctl = ftdi_ioctl, .set_termios = ftdi_set_termios, .break_ctl = ftdi_break_ctl, @@ -1650,6 +1654,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port) kref_init(&priv->kref); mutex_init(&priv->cfg_lock); + memset(&priv->icount, 0x00, sizeof(priv->icount)); init_waitqueue_head(&priv->delta_msr_wait); priv->flags = ASYNC_LOW_LATENCY; @@ -2281,6 +2286,27 @@ static int ftdi_tiocmset(struct tty_struct *tty, return update_mctrl(port, set, clear); } +static int ftdi_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + struct usb_serial_port *port = tty->driver_data; + struct ftdi_private *priv = usb_get_serial_port_data(port); + struct async_icount *ic = &priv->icount; + + icount->cts = ic->cts; + icount->dsr = ic->dsr; + icount->rng = ic->rng; + icount->dcd = ic->dcd; + icount->tx = ic->tx; + icount->rx = ic->rx; + icount->frame = ic->frame; + icount->parity = ic->parity; + icount->overrun = ic->overrun; + icount->brk = ic->brk; + icount->buf_overrun = ic->buf_overrun; + return 0; +} + static int ftdi_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { -- cgit v0.10.2 From c9222ec92e6ac60a4624234b61cf848c237e8e0e Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 10 Oct 2011 17:24:16 +0200 Subject: USB: ftdi_sio.c: Fill the RX field of the ftdi async_icount structure Signed-off-by: Uwe Bonnes Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 4cfc4cd..cb0ecd5 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1984,6 +1984,7 @@ static int ftdi_process_packet(struct tty_struct *tty, len -= 2; if (!len) return 0; /* status only */ + priv->icount.rx += len; ch = packet + 2; if (port->port.console && port->sysrq) { -- cgit v0.10.2 From da7fbb6dd061fc4e2945d63cf04f843063dd51e5 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 10 Oct 2011 17:24:27 +0200 Subject: USB: ftdi_sio.c:Fill TX field of the ftdi async_icount structure Signed-off-by: Uwe Bonnes Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index cb0ecd5..a865df81 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1918,6 +1918,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port, c = kfifo_out(&port->write_fifo, &buffer[i + 1], len); if (!c) break; + priv->icount.tx += c; buffer[i] = (c << 2) + 1; count += c + 1; } @@ -1925,6 +1926,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port, } else { count = kfifo_out_locked(&port->write_fifo, dest, size, &port->lock); + priv->icount.tx += count; } return count; -- cgit v0.10.2 From ac295f39249896f100ab1c49e2cbc269d176bf48 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 10 Oct 2011 17:24:34 +0200 Subject: USB: ftdi_sio.c: Fill LSR fields of the ftdi async_icount structure Signed-off-by: Uwe Bonnes Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index a865df81..372ae5b 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1954,6 +1954,14 @@ static int ftdi_process_packet(struct tty_struct *tty, N.B. packet may be processed more than once, but differences are only processed once. */ status = packet[0] & FTDI_STATUS_B0_MASK; + if (status & FTDI_RS0_CTS) + priv->icount.cts++; + if (status & FTDI_RS0_DSR) + priv->icount.dsr++; + if (status & FTDI_RS0_RI) + priv->icount.rng++; + if (status & FTDI_RS0_RLSD) + priv->icount.dcd++; if (status != priv->prev_status) { priv->diff_status |= status ^ priv->prev_status; wake_up_interruptible(&priv->delta_msr_wait); -- cgit v0.10.2 From 005b3cded789c2358b71cb7d2d410ad0df00e366 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 10 Oct 2011 17:24:46 +0200 Subject: USB: ftdi_sio.c:Fill MSR fields of the ftdi async_icount structure Signed-off-by: Uwe Bonnes Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 372ae5b..9699580 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1974,15 +1974,20 @@ static int ftdi_process_packet(struct tty_struct *tty, * over framing errors */ if (packet[1] & FTDI_RS_BI) { flag = TTY_BREAK; + priv->icount.brk++; usb_serial_handle_break(port); } else if (packet[1] & FTDI_RS_PE) { flag = TTY_PARITY; + priv->icount.parity++; } else if (packet[1] & FTDI_RS_FE) { flag = TTY_FRAME; + priv->icount.frame++; } /* Overrun is special, not associated with a char */ - if (packet[1] & FTDI_RS_OE) + if (packet[1] & FTDI_RS_OE) { + priv->icount.overrun++; tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } } /* save if the transmitter is empty or not */ -- cgit v0.10.2 From 1d749f9afa657f6ee9336b2bc1fcd750a647d157 Mon Sep 17 00:00:00 2001 From: Uwe Bonnes Date: Mon, 10 Oct 2011 17:24:51 +0200 Subject: USB: ftdi_sio.c: Use ftdi async_icount structure for TIOCMIWAIT, as in other drivers Signed-off-by: Uwe Bonnes Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 9699580..8fe034d 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2328,6 +2328,8 @@ static int ftdi_ioctl(struct tty_struct *tty, { struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); + struct async_icount cnow; + struct async_icount cprev; dbg("%s cmd 0x%04x", __func__, cmd); @@ -2347,41 +2349,30 @@ static int ftdi_ioctl(struct tty_struct *tty, * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was. - * (except that the driver doesn't support it !) * * This code is borrowed from linux/drivers/char/serial.c */ case TIOCMIWAIT: - while (priv != NULL) { + cprev = priv->icount; + while (1) { interruptible_sleep_on(&priv->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; - else { - char diff = priv->diff_status; - - if (diff == 0) - return -EIO; /* no change => error */ - - /* Consume all events */ - priv->diff_status = 0; - - /* Return 0 if caller wanted to know about - these bits */ - if (((arg & TIOCM_RNG) && (diff & FTDI_RS0_RI)) || - ((arg & TIOCM_DSR) && (diff & FTDI_RS0_DSR)) || - ((arg & TIOCM_CD) && (diff & FTDI_RS0_RLSD)) || - ((arg & TIOCM_CTS) && (diff & FTDI_RS0_CTS))) { - return 0; - } - /* - * Otherwise caller can't care less about what - * happened,and so we continue to wait for more - * events. - */ + cnow = priv->icount; + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { + return 0; } + cprev = cnow; } - return 0; + /* not reached */ + break; case TIOCSERGETLSR: return get_lsr_info(port, (struct serial_struct __user *)arg); break; -- cgit v0.10.2 From 91960c2ef095c4b0744349e80a933921cbdcfd6e Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Thu, 13 Oct 2011 14:19:05 +0300 Subject: usb: gadget: file_storage: fix race on unloading There is a race, reproduced rarely if you unload the module when host finishes mass storage device initialization (reading partition table and so on): fsg_unbind() code first closes lun files then waits for worker thread to finish its work, as the result the thread may operate on already closed device with an oops and backtrace: [ 484.937225] [] (touch_atime+0x4/0x140) from [] (generic_file_aio_read+0x678/0x6f0) [ 484.946563] [] (generic_file_aio_read+0x678/0x6f0) from [] (do_sync_read+0xb0/0xf4) [ 484.955963] [] (do_sync_read+0xb0/0xf4) from [] (vfs_read+0xac/0x144) [ 484.964172] [] (vfs_read+0xac/0x144) from [] (fsg_setup+0x7f4/0x900 [g_file_storage]) [ 484.973785] [] (fsg_setup+0x7f4/0x900 [g_file_storage]) from [] (fsg_main_thread+0x85c/0x175c [g_file_storage]) [ 484.985626] [] (fsg_main_thread+0x85c/0x175c [g_file_storage]) from [] (kthread+0x7c/0x84) [ 484.995666] [] (kthread+0x7c/0x84) from [] (kernel_thread_exit+0x0/0x8) [ 485.004028] Code: eaffffd0 e28dd008 e8bd8df0 e92d40f7 (e591400c) Change the order in unbind: wait for the thread first, then close the files. Signed-off-by: Yauheni Kaliuta Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index 5779549..3ac4f51 100644 --- a/drivers/usb/gadget/file_storage.c +++ b/drivers/usb/gadget/file_storage.c @@ -3153,6 +3153,15 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) DBG(fsg, "unbind\n"); clear_bit(REGISTERED, &fsg->atomic_bitflags); + /* If the thread isn't already dead, tell it to exit now */ + if (fsg->state != FSG_STATE_TERMINATED) { + raise_exception(fsg, FSG_STATE_EXIT); + wait_for_completion(&fsg->thread_notifier); + + /* The cleanup routine waits for this completion also */ + complete(&fsg->thread_notifier); + } + /* Unregister the sysfs attribute files and the LUNs */ for (i = 0; i < fsg->nluns; ++i) { curlun = &fsg->luns[i]; @@ -3166,15 +3175,6 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget) } } - /* If the thread isn't already dead, tell it to exit now */ - if (fsg->state != FSG_STATE_TERMINATED) { - raise_exception(fsg, FSG_STATE_EXIT); - wait_for_completion(&fsg->thread_notifier); - - /* The cleanup routine waits for this completion also */ - complete(&fsg->thread_notifier); - } - /* Free the data buffers */ for (i = 0; i < fsg_num_buffers; ++i) kfree(fsg->buffhds[i].buf); -- cgit v0.10.2 From 68aa95d5d4de31c9348c1628ffa85c805305ebc5 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 12 Oct 2011 10:39:14 -0400 Subject: EHCI: workaround for MosChip controller bug This patch (as1489) works around a hardware bug in MosChip EHCI controllers. Evidently when one of these controllers increments the frame-index register, it changes the three low-order bits (the microframe counter) before changing the higher order bits (the frame counter). If the register is read at just the wrong time, the value obtained is too low by 8. When the appropriate quirk flag is set, we work around this problem by reading the frame-index register a second time if the first value's three low-order bits are all 0. This gives the hardware a chance to finish updating the register, yielding the correct value. Signed-off-by: Alan Stern Tested-by: Jason N Pitt CC: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 9952505d..d6d74d2 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -821,7 +821,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) next += temp; temp = scnprintf (next, size, "uframe %04x\n", - ehci_readl(ehci, &ehci->regs->frame_index)); + ehci_read_frame_index(ehci)); size -= temp; next += temp; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 05abbcd..59e8161 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1195,8 +1195,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) static int ehci_get_frame (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - return (ehci_readl(ehci, &ehci->regs->frame_index) >> 3) % - ehci->periodic_size; + return (ehci_read_frame_index(ehci) >> 3) % ehci->periodic_size; } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 8311de7..f4b627d 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -224,6 +224,11 @@ static int ehci_pci_setup(struct usb_hcd *hcd) pci_dev_put(p_smbus); } break; + case PCI_VENDOR_ID_NETMOS: + /* MosChip frame-index-register bug */ + ehci_info(ehci, "applying MosChip frame-index workaround\n"); + ehci->frame_index_bug = 1; + break; } /* optional debug port, normally in the first BAR */ diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 488151b..2e829fa 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -36,6 +36,27 @@ static int ehci_get_frame (struct usb_hcd *hcd); +#ifdef CONFIG_PCI + +static unsigned ehci_read_frame_index(struct ehci_hcd *ehci) +{ + unsigned uf; + + /* + * The MosChip MCS9990 controller updates its microframe counter + * a little before the frame counter, and occasionally we will read + * the invalid intermediate value. Avoid problems by checking the + * microframe number (the low-order 3 bits); if they are 0 then + * re-read the register to get the correct value. + */ + uf = ehci_readl(ehci, &ehci->regs->frame_index); + if (unlikely(ehci->frame_index_bug && ((uf & 7) == 0))) + uf = ehci_readl(ehci, &ehci->regs->frame_index); + return uf; +} + +#endif + /*-------------------------------------------------------------------------*/ /* @@ -481,7 +502,7 @@ static int enable_periodic (struct ehci_hcd *ehci) /* posted write ... PSS happens later */ /* make sure ehci_work scans these */ - ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index) + ehci->next_uframe = ehci_read_frame_index(ehci) % (ehci->periodic_size << 3); if (unlikely(ehci->broken_periodic)) ehci->last_periodic_enable = ktime_get_real(); @@ -1408,7 +1429,7 @@ iso_stream_schedule ( goto fail; } - now = ehci_readl(ehci, &ehci->regs->frame_index) & (mod - 1); + now = ehci_read_frame_index(ehci) & (mod - 1); /* Typical case: reuse current schedule, stream is still active. * Hopefully there are no gaps from the host falling behind @@ -2275,7 +2296,7 @@ scan_periodic (struct ehci_hcd *ehci) */ now_uframe = ehci->next_uframe; if (ehci->rh_state == EHCI_RH_RUNNING) { - clock = ehci_readl(ehci, &ehci->regs->frame_index); + clock = ehci_read_frame_index(ehci); clock_frame = (clock >> 3) & (ehci->periodic_size - 1); } else { clock = now_uframe + mod - 1; @@ -2454,8 +2475,7 @@ restart: || ehci->periodic_sched == 0) break; ehci->next_uframe = now_uframe; - now = ehci_readl(ehci, &ehci->regs->frame_index) & - (mod - 1); + now = ehci_read_frame_index(ehci) & (mod - 1); if (now_uframe == now) break; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index c161d97..0a5fda7 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -146,6 +146,7 @@ struct ehci_hcd { /* one per controller */ unsigned fs_i_thresh:1; /* Intel iso scheduling */ unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ unsigned has_synopsys_hc_bug:1; /* Synopsys HC */ + unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */ /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) @@ -747,6 +748,22 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x) /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_PCI + +/* For working around the MosChip frame-index-register bug */ +static unsigned ehci_read_frame_index(struct ehci_hcd *ehci); + +#else + +static inline unsigned ehci_read_frame_index(struct ehci_hcd *ehci) +{ + return ehci_readl(ehci, &ehci->regs->frame_index); +} + +#endif + +/*-------------------------------------------------------------------------*/ + #ifndef DEBUG #define STUB_DEBUG_FILES #endif /* DEBUG */ -- cgit v0.10.2 From 3a6e7119a7779866b432a3abf443243b52eaad3d Mon Sep 17 00:00:00 2001 From: Xu lei Date: Fri, 14 Oct 2011 15:35:10 -0500 Subject: USB: Fix USB Kconfig dependency problem on 85xx/QoirQ platforms For FSL PPC SoCs USB_ARCH_HAS_EHCI currently on depends on PPC_83xx. However that excludes support for USB on 85xx & QorIQ devices. Use FSL_SOC insted which will get us 83xx, 85xx, QorIQ, and 5xxx which all have the same USB IP on them. Signed-off-by: Xulei Signed-off-by: Kumar Gala Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 251279e..4ac2750 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -60,7 +60,7 @@ config USB_ARCH_HAS_OHCI # some non-PCI hcds implement EHCI config USB_ARCH_HAS_EHCI boolean - default y if PPC_83xx + default y if FSL_SOC default y if PPC_MPC512x default y if SOC_AU1200 default y if ARCH_IXP4XX -- cgit v0.10.2 From 55b5a624a0cc5aa4b350fd50d78cf3415f795bfe Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 17 Oct 2011 18:04:37 -0700 Subject: usb: renesas_usbhs: tidyup for smatch warnings This patch tidyup below smatch complaint drivers/usb/renesas_usbhs/mod_host.c +447 usbhsh_endpoint_free() warn: variable dereferenced before check 'uep' (see line 444) Special thanks to Dan Reported-by: Dan Carpenter Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index 1c60331..1a7208a 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -441,14 +441,13 @@ void usbhsh_endpoint_free(struct usbhsh_hpriv *hpriv, struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv); struct device *dev = usbhs_priv_to_dev(priv); struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep); - struct usbhsh_device *udev = usbhsh_uep_to_udev(uep); struct usbhsh_pipe_info *info; if (!uep) return; dev_dbg(dev, "%s [%d-%s](%p)\n", __func__, - usbhsh_device_number(hpriv, udev), + usbhsh_device_number(hpriv, usbhsh_uep_to_udev(uep)), usbhs_pipe_name(uep->pipe), uep); info = usbhsh_pipe_info(uep->pipe); -- cgit v0.10.2 From 273d23574f9dacd9c63c80e7d63639a669aad441 Mon Sep 17 00:00:00 2001 From: Jerry Huang Date: Tue, 18 Oct 2011 13:09:48 +0800 Subject: QE/FHCI: fixed the CONTROL bug For USB CONTROL transaction, when the data length is zero, the IN package is needed to finish this transaction in status stage. Signed-off-by: Jerry Huang Cc: stable Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index a42ef38..2df851b 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -1,7 +1,7 @@ /* * Freescale QUICC Engine USB Host Controller Driver * - * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Copyright (c) Freescale Semicondutor, Inc. 2006, 2011. * Shlomi Gridish * Jerry Huang * Copyright (c) Logic Product Development, Inc. 2007 @@ -810,9 +810,11 @@ void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb) ed->dev_addr = usb_pipedevice(urb->pipe); ed->max_pkt_size = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + /* setup stage */ td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP, USB_TD_TOGGLE_DATA0, urb->setup_packet, 8, 0, 0, true); + /* data stage */ if (data_len > 0) { td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, usb_pipeout(urb->pipe) ? FHCI_TA_OUT : @@ -820,9 +822,18 @@ void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb) USB_TD_TOGGLE_DATA1, data, data_len, 0, 0, true); } - td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, - usb_pipeout(urb->pipe) ? FHCI_TA_IN : FHCI_TA_OUT, - USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true); + + /* status stage */ + if (data_len > 0) + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + (usb_pipeout(urb->pipe) ? FHCI_TA_IN : + FHCI_TA_OUT), + USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true); + else + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + FHCI_TA_IN, + USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true); + urb_state = US_CTRL_SETUP; break; case FHCI_TF_ISO: -- cgit v0.10.2 From 0889551267e7b6b4d27dc9f726f3d0cb0d976105 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 18 Oct 2011 09:24:36 +0300 Subject: USB: gadget: midi: fix range check in f_midi_out_open() ! has higher precedence than >= and since neither 0 nor 1 are greater than 8 the condition is always false. Signed-off-by: Dan Carpenter Acked-by: Daniel Mack Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c index 44858dc..ef1bc7a 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/f_midi.c @@ -644,7 +644,7 @@ static int f_midi_out_open(struct snd_rawmidi_substream *substream) { struct f_midi *midi = substream->rmidi->private_data; - if (!substream->number >= MAX_PORTS) + if (substream->number >= MAX_PORTS) return -EINVAL; VDBG(midi, "%s()\n", __func__); -- cgit v0.10.2 From 0f8fd43c42f2cc249943be76a4db0d058c16b6dc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 18 Oct 2011 09:25:34 +0300 Subject: USB: gadget: midi: memory leak in f_midi_bind_config() There is a small memory leak on the error paths. Signed-off-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c index ef1bc7a..67b2229 100644 --- a/drivers/usb/gadget/f_midi.c +++ b/drivers/usb/gadget/f_midi.c @@ -951,7 +951,7 @@ int __init f_midi_bind_config(struct usb_configuration *c, struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL); if (!port) { status = -ENOMEM; - goto fail; + goto setup_fail; } port->midi = midi; @@ -989,6 +989,8 @@ int __init f_midi_bind_config(struct usb_configuration *c, return 0; setup_fail: + for (--i; i >= 0; i--) + kfree(midi->in_port[i]); kfree(midi); fail: return status; -- cgit v0.10.2 From 3a7655fcb210b349111251689d0a56b7250885ea Mon Sep 17 00:00:00 2001 From: Joachim Foerster Date: Wed, 19 Oct 2011 14:18:41 +0200 Subject: usb/isp1760: Allow to optionally trigger low-level chip reset via GPIOLIB. Properly triggering the reset wire is necessary with the ISP1761 used on Terasic DE4 Altera-FPGA boards using a NIOS2 processor, for example. This is an optional implementation for the OF binding only. The other bindings just pass an invalid GPIO to the isp1760_register() routine. Example, usage in DTS: gpios = <&pio_isp1761rst_0 0 1>; to point to a GPIO controller from within the ISP1761 node: GPIO 0, active low. Signed-off-by: Joachim Foerster Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 5b08bd7..27dfab8 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "isp1760-hcd.h" @@ -48,6 +49,8 @@ struct isp1760_hcd { unsigned long reset_done; unsigned long next_statechange; unsigned int devflags; + + int rst_gpio; }; static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd) @@ -433,6 +436,18 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) int result; u32 scratch, hwmode; + /* low-level chip reset */ + if (gpio_is_valid(priv->rst_gpio)) { + unsigned int rst_lvl; + + rst_lvl = (priv->devflags & + ISP1760_FLAG_RESET_ACTIVE_HIGH) ? 1 : 0; + + gpio_set_value(priv->rst_gpio, rst_lvl); + mdelay(50); + gpio_set_value(priv->rst_gpio, !rst_lvl); + } + /* Setup HW Mode Control: This assumes a level active-low interrupt */ hwmode = HW_DATA_BUS_32BIT; @@ -2207,6 +2222,7 @@ void deinit_kmem_cache(void) struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, unsigned long irqflags, + int rst_gpio, struct device *dev, const char *busname, unsigned int devflags) { @@ -2226,6 +2242,7 @@ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, priv = hcd_to_priv(hcd); priv->devflags = devflags; + priv->rst_gpio = rst_gpio; init_memory(priv); hcd->regs = ioremap(res_start, res_len); if (!hcd->regs) { diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index fda0f2d..33dc79c 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -4,6 +4,7 @@ /* exports for if */ struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, int irq, unsigned long irqflags, + int rst_gpio, struct device *dev, const char *busname, unsigned int devflags); int init_kmem_once(void); @@ -126,6 +127,7 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh, #define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */ #define ISP1760_FLAG_INTR_POL_HIGH 0x00000080 /* Interrupt polarity active high */ #define ISP1760_FLAG_INTR_EDGE_TRIG 0x00000100 /* Interrupt edge triggered */ +#define ISP1760_FLAG_RESET_ACTIVE_HIGH 0x80000000 /* RESET GPIO active high */ /* chip memory management */ struct memory_chunk { diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index 791792d..2c7fc83 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -18,10 +18,12 @@ #include "isp1760-hcd.h" #ifdef CONFIG_OF +#include #include #include #include #include +#include #endif #ifdef CONFIG_PCI @@ -29,9 +31,14 @@ #endif #ifdef CONFIG_OF +struct isp1760 { + struct usb_hcd *hcd; + int rst_gpio; +}; + static int of_isp1760_probe(struct platform_device *dev) { - struct usb_hcd *hcd; + struct isp1760 *drvdata; struct device_node *dp = dev->dev.of_node; struct resource *res; struct resource memory; @@ -41,6 +48,11 @@ static int of_isp1760_probe(struct platform_device *dev) int ret; const unsigned int *prop; unsigned int devflags = 0; + enum of_gpio_flags gpio_flags; + + drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; ret = of_address_to_resource(dp, 0, &memory); if (ret) @@ -80,32 +92,57 @@ static int of_isp1760_probe(struct platform_device *dev) if (of_get_property(dp, "dreq-polarity", NULL) != NULL) devflags |= ISP1760_FLAG_DREQ_POL_HIGH; - hcd = isp1760_register(memory.start, res_len, virq, - IRQF_SHARED, &dev->dev, dev_name(&dev->dev), - devflags); - if (IS_ERR(hcd)) { - ret = PTR_ERR(hcd); - goto release_reg; + drvdata->rst_gpio = of_get_gpio_flags(dp, 0, &gpio_flags); + if (gpio_is_valid(drvdata->rst_gpio)) { + ret = gpio_request(drvdata->rst_gpio, dev_name(&dev->dev)); + if (!ret) { + if (!(gpio_flags & OF_GPIO_ACTIVE_LOW)) { + devflags |= ISP1760_FLAG_RESET_ACTIVE_HIGH; + gpio_direction_output(drvdata->rst_gpio, 0); + } else { + gpio_direction_output(drvdata->rst_gpio, 1); + } + } else { + drvdata->rst_gpio = ret; + } } - dev_set_drvdata(&dev->dev, hcd); + drvdata->hcd = isp1760_register(memory.start, res_len, virq, + IRQF_SHARED, drvdata->rst_gpio, + &dev->dev, dev_name(&dev->dev), + devflags); + if (IS_ERR(drvdata->hcd)) { + ret = PTR_ERR(drvdata->hcd); + goto free_gpio; + } + + dev_set_drvdata(&dev->dev, drvdata); return ret; +free_gpio: + if (gpio_is_valid(drvdata->rst_gpio)) + gpio_free(drvdata->rst_gpio); release_reg: release_mem_region(memory.start, res_len); + kfree(drvdata); return ret; } static int of_isp1760_remove(struct platform_device *dev) { - struct usb_hcd *hcd = dev_get_drvdata(&dev->dev); + struct isp1760 *drvdata = dev_get_drvdata(&dev->dev); dev_set_drvdata(&dev->dev, NULL); - usb_remove_hcd(hcd); - iounmap(hcd->regs); - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - usb_put_hcd(hcd); + usb_remove_hcd(drvdata->hcd); + iounmap(drvdata->hcd->regs); + release_mem_region(drvdata->hcd->rsrc_start, drvdata->hcd->rsrc_len); + usb_put_hcd(drvdata->hcd); + + if (gpio_is_valid(drvdata->rst_gpio)) + gpio_free(drvdata->rst_gpio); + + kfree(drvdata); return 0; } @@ -242,7 +279,7 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev, dev->dev.dma_mask = NULL; hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq, - IRQF_SHARED, &dev->dev, dev_name(&dev->dev), + IRQF_SHARED, -ENOENT, &dev->dev, dev_name(&dev->dev), devflags); if (IS_ERR(hcd)) { ret_status = -ENODEV; @@ -353,7 +390,8 @@ static int __devinit isp1760_plat_probe(struct platform_device *pdev) } hcd = isp1760_register(mem_res->start, mem_size, irq_res->start, - irqflags, &pdev->dev, dev_name(&pdev->dev), devflags); + irqflags, -ENOENT, + &pdev->dev, dev_name(&pdev->dev), devflags); if (IS_ERR(hcd)) { pr_warning("isp1760: Failed to register the HCD device\n"); ret = -ENODEV; -- cgit v0.10.2 From a2c76b83fdd763c826f38a55127ccf25708099ce Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 18 Oct 2011 20:05:50 -0700 Subject: usb: renesas_usbhs: fixup inconsistent return from usbhs_pkt_push() usbhs_pkt_push() had inconsistent return under spin lock. This patch fix it up. Special thanks to Dan Cc: Dan Carpenter Signed-off-by: Kuninori Morimoto Signed-off-by: Greg Kroah-Hartman diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 762b742..8da685e 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -62,14 +62,14 @@ void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt, struct device *dev = usbhs_priv_to_dev(priv); unsigned long flags; - /******************** spin lock ********************/ - usbhs_lock(priv, flags); - if (!done) { dev_err(dev, "no done function\n"); return; } + /******************** spin lock ********************/ + usbhs_lock(priv, flags); + if (!pipe->handler) { dev_err(dev, "no handler function\n"); pipe->handler = &usbhsf_null_handler; -- cgit v0.10.2