From 6d8791076c7742c65dd796ae0ac260ab22e85517 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Thu, 10 Apr 2008 21:05:47 +0900 Subject: 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 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 77 +++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 10 deletions(-) (limited to 'drivers/usb/host/r8a66597-hcd.c') diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index b46ff9a80b66..dac2f7dd69b9 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -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); -- cgit v1.2.3