USB: r8a66597-hcd: fix interrupt transfer interval

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>

diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index b46ff9a..dac2f7d 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -46,7 +46,7 @@
 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 @@
 		       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 @@
 	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 @@
 				      & 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_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 @@
 	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 @@
 		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);
 
diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h
index 5738825..a014610 100644
--- a/drivers/usb/host/r8a66597.h
+++ b/drivers/usb/host/r8a66597.h
@@ -404,6 +404,7 @@
 #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 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;