]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/host/r8a66597-hcd.c
USB: r8a66597-hcd: fix interrupt transfer interval
[linux-2.6-omap-h63xx.git] / drivers / usb / host / r8a66597-hcd.c
index b46ff9a80b6618ce650066ca4b6d9a3aa57bb8a6..dac2f7dd69b9bdf3d6febca9b176057b5bf104c2 100644 (file)
@@ -46,7 +46,7 @@ MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Yoshihiro Shimoda");
 MODULE_ALIAS("platform:r8a66597_hcd");
 
-#define DRIVER_VERSION "29 May 2007"
+#define DRIVER_VERSION "10 Apr 2008"
 
 static const char hcd_name[] = "r8a66597_hcd";
 
@@ -577,13 +577,9 @@ static void pipe_buffer_setting(struct r8a66597 *r8a66597,
                       PIPEBUF);
        r8a66597_write(r8a66597, make_devsel(info->address) | info->maxpacket,
                       PIPEMAXP);
-       if (info->interval)
-               info->interval--;
        r8a66597_write(r8a66597, info->interval, PIPEPERI);
 }
 
-
-
 /* this function must be called with interrupt disabled */
 static void pipe_setting(struct r8a66597 *r8a66597, struct r8a66597_td *td)
 {
@@ -825,6 +821,25 @@ static void disable_r8a66597_pipe_all(struct r8a66597 *r8a66597,
        dev->dma_map = 0;
 }
 
+static unsigned long get_timer_interval(struct urb *urb, __u8 interval)
+{
+       __u8 i;
+       unsigned long time = 1;
+
+       if (usb_pipeisoc(urb->pipe))
+               return 0;
+
+       if (get_r8a66597_usb_speed(urb->dev->speed) == HSMODE) {
+               for (i = 0; i < (interval - 1); i++)
+                       time *= 2;
+               time = time * 125 / 1000;       /* uSOF -> msec */
+       } else {
+               time = interval;
+       }
+
+       return time;
+}
+
 /* this function must be called with interrupt disabled */
 static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb,
                           struct usb_host_endpoint *hep,
@@ -840,7 +855,16 @@ static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb,
                                      & USB_ENDPOINT_XFERTYPE_MASK);
        info.bufnum = get_bufnum(info.pipenum);
        info.buf_bsize = get_buf_bsize(info.pipenum);
-       info.interval = ep->bInterval;
+       if (info.type == R8A66597_BULK) {
+               info.interval = 0;
+               info.timer_interval = 0;
+       } else {
+               if (ep->bInterval > IITV)
+                       info.interval = IITV;
+               else
+                       info.interval = ep->bInterval ? ep->bInterval - 1 : 0;
+               info.timer_interval = get_timer_interval(urb, ep->bInterval);
+       }
        if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
                info.dir_in = 1;
        else
@@ -1582,6 +1606,29 @@ static void r8a66597_root_hub_control(struct r8a66597 *r8a66597, int port)
        }
 }
 
+static void r8a66597_interval_timer(unsigned long _r8a66597)
+{
+       struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;
+       unsigned long flags;
+       u16 pipenum;
+       struct r8a66597_td *td;
+
+       spin_lock_irqsave(&r8a66597->lock, flags);
+
+       for (pipenum = 0; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) {
+               if (!(r8a66597->interval_map & (1 << pipenum)))
+                       continue;
+               if (timer_pending(&r8a66597->interval_timer[pipenum]))
+                       continue;
+
+               td = r8a66597_get_td(r8a66597, pipenum);
+               if (td)
+                       start_transfer(r8a66597, td);
+       }
+
+       spin_unlock_irqrestore(&r8a66597->lock, flags);
+}
+
 static void r8a66597_td_timer(unsigned long _r8a66597)
 {
        struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;
@@ -1763,10 +1810,17 @@ static int r8a66597_urb_enqueue(struct usb_hcd *hcd,
        urb->hcpriv = td;
 
        if (request) {
-               ret = start_transfer(r8a66597, td);
-               if (ret < 0) {
-                       list_del(&td->queue);
-                       kfree(td);
+               if (td->pipe->info.timer_interval) {
+                       r8a66597->interval_map |= 1 << td->pipenum;
+                       mod_timer(&r8a66597->interval_timer[td->pipenum],
+                                 jiffies + msecs_to_jiffies(
+                                       td->pipe->info.timer_interval));
+               } else {
+                       ret = start_transfer(r8a66597, td);
+                       if (ret < 0) {
+                               list_del(&td->queue);
+                               kfree(td);
+                       }
                }
        } else
                set_td_timer(r8a66597, td);
@@ -2192,6 +2246,9 @@ static int __init r8a66597_probe(struct platform_device *pdev)
                init_timer(&r8a66597->td_timer[i]);
                r8a66597->td_timer[i].function = r8a66597_td_timer;
                r8a66597->td_timer[i].data = (unsigned long)r8a66597;
+               setup_timer(&r8a66597->interval_timer[i],
+                               r8a66597_interval_timer,
+                               (unsigned long)r8a66597);
        }
        INIT_LIST_HEAD(&r8a66597->child_device);