提交 6d879107 编写于 作者: Y Yoshihiro Shimoda 提交者: 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: NYoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 97af0a91
...@@ -46,7 +46,7 @@ MODULE_LICENSE("GPL"); ...@@ -46,7 +46,7 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yoshihiro Shimoda"); MODULE_AUTHOR("Yoshihiro Shimoda");
MODULE_ALIAS("platform:r8a66597_hcd"); MODULE_ALIAS("platform:r8a66597_hcd");
#define DRIVER_VERSION "29 May 2007" #define DRIVER_VERSION "10 Apr 2008"
static const char hcd_name[] = "r8a66597_hcd"; static const char hcd_name[] = "r8a66597_hcd";
...@@ -577,13 +577,9 @@ static void pipe_buffer_setting(struct r8a66597 *r8a66597, ...@@ -577,13 +577,9 @@ static void pipe_buffer_setting(struct r8a66597 *r8a66597,
PIPEBUF); PIPEBUF);
r8a66597_write(r8a66597, make_devsel(info->address) | info->maxpacket, r8a66597_write(r8a66597, make_devsel(info->address) | info->maxpacket,
PIPEMAXP); PIPEMAXP);
if (info->interval)
info->interval--;
r8a66597_write(r8a66597, info->interval, PIPEPERI); r8a66597_write(r8a66597, info->interval, PIPEPERI);
} }
/* this function must be called with interrupt disabled */ /* this function must be called with interrupt disabled */
static void pipe_setting(struct r8a66597 *r8a66597, struct r8a66597_td *td) static void pipe_setting(struct r8a66597 *r8a66597, struct r8a66597_td *td)
{ {
...@@ -825,6 +821,25 @@ static void disable_r8a66597_pipe_all(struct r8a66597 *r8a66597, ...@@ -825,6 +821,25 @@ static void disable_r8a66597_pipe_all(struct r8a66597 *r8a66597,
dev->dma_map = 0; 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 */ /* this function must be called with interrupt disabled */
static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb, static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb,
struct usb_host_endpoint *hep, struct usb_host_endpoint *hep,
...@@ -840,7 +855,16 @@ static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb, ...@@ -840,7 +855,16 @@ static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb,
& USB_ENDPOINT_XFERTYPE_MASK); & USB_ENDPOINT_XFERTYPE_MASK);
info.bufnum = get_bufnum(info.pipenum); info.bufnum = get_bufnum(info.pipenum);
info.buf_bsize = get_buf_bsize(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) if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
info.dir_in = 1; info.dir_in = 1;
else else
...@@ -1582,6 +1606,29 @@ static void r8a66597_root_hub_control(struct r8a66597 *r8a66597, int port) ...@@ -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) static void r8a66597_td_timer(unsigned long _r8a66597)
{ {
struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597; struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597;
...@@ -1763,10 +1810,17 @@ static int r8a66597_urb_enqueue(struct usb_hcd *hcd, ...@@ -1763,10 +1810,17 @@ static int r8a66597_urb_enqueue(struct usb_hcd *hcd,
urb->hcpriv = td; urb->hcpriv = td;
if (request) { if (request) {
ret = start_transfer(r8a66597, td); if (td->pipe->info.timer_interval) {
if (ret < 0) { r8a66597->interval_map |= 1 << td->pipenum;
list_del(&td->queue); mod_timer(&r8a66597->interval_timer[td->pipenum],
kfree(td); 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 } else
set_td_timer(r8a66597, td); set_td_timer(r8a66597, td);
...@@ -2192,6 +2246,9 @@ static int __init r8a66597_probe(struct platform_device *pdev) ...@@ -2192,6 +2246,9 @@ static int __init r8a66597_probe(struct platform_device *pdev)
init_timer(&r8a66597->td_timer[i]); init_timer(&r8a66597->td_timer[i]);
r8a66597->td_timer[i].function = r8a66597_td_timer; r8a66597->td_timer[i].function = r8a66597_td_timer;
r8a66597->td_timer[i].data = (unsigned long)r8a66597; 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); INIT_LIST_HEAD(&r8a66597->child_device);
......
...@@ -404,6 +404,7 @@ ...@@ -404,6 +404,7 @@
#define make_devsel(addr) (addr << 12) #define make_devsel(addr) (addr << 12)
struct r8a66597_pipe_info { struct r8a66597_pipe_info {
unsigned long timer_interval;
u16 pipenum; u16 pipenum;
u16 address; /* R8A66597 HCD usb address */ u16 address; /* R8A66597 HCD usb address */
u16 epnum; u16 epnum;
...@@ -478,9 +479,11 @@ struct r8a66597 { ...@@ -478,9 +479,11 @@ struct r8a66597 {
struct timer_list rh_timer; struct timer_list rh_timer;
struct timer_list td_timer[R8A66597_MAX_NUM_PIPE]; struct timer_list td_timer[R8A66597_MAX_NUM_PIPE];
struct timer_list interval_timer[R8A66597_MAX_NUM_PIPE];
unsigned short address_map; unsigned short address_map;
unsigned short timeout_map; unsigned short timeout_map;
unsigned short interval_map;
unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE]; unsigned char pipe_cnt[R8A66597_MAX_NUM_PIPE];
unsigned char dma_map; unsigned char dma_map;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册