Skip to content

Commit

Permalink
USB: r8a66597-hcd: fix interrupt transfer interval
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Yoshihiro Shimoda authored and Greg Kroah-Hartman committed Apr 25, 2008
1 parent 97af0a9 commit 6d87910
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 10 deletions.
77 changes: 67 additions & 10 deletions drivers/usb/host/r8a66597-hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions drivers/usb/host/r8a66597.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit 6d87910

Please sign in to comment.