Commit 6d879107 authored by Yoshihiro Shimoda's avatar Yoshihiro Shimoda Committed by Greg Kroah-Hartman

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: default avatarYoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 97af0a91
......@@ -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);
......
......@@ -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 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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment