USB: r8a66597-hcd: fix interrupt transfer interval
authorYoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
Thu, 10 Apr 2008 12:05:47 +0000 (21:05 +0900)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 25 Apr 2008 04:16:49 +0000 (21:16 -0700)
This driver ignored the value of bInterval and revised the problem
that performed interrupt transfer.

ASIX USB Ethernet adapter comes to work with this host controller
by applying this patch.

Signed-off-by: Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/host/r8a66597-hcd.c
drivers/usb/host/r8a66597.h

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);
 
index 57388252b693cb7ca6e79fa75c251b1abc64f708..a01461017ad692a6e8ba27a5d1b2c9395b8fa922 100644 (file)
 #define make_devsel(addr)              (addr << 12)
 
 struct r8a66597_pipe_info {
+       unsigned long timer_interval;
        u16 pipenum;
        u16 address;    /* R8A66597 HCD usb address */
        u16 epnum;
@@ -478,9 +479,11 @@ struct r8a66597 {
 
        struct timer_list rh_timer;
        struct timer_list td_timer[R8A66597_MAX_NUM_PIPE];
+       struct timer_list interval_timer[R8A66597_MAX_NUM_PIPE];
 
        unsigned short address_map;
        unsigned short timeout_map;
+       unsigned short interval_map;
        unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
        unsigned char dma_map;