]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/host/r8a66597-hcd.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/steve/gfs2-2.6-nmw
[linux-2.6-omap-h63xx.git] / drivers / usb / host / r8a66597-hcd.c
index 9f80e528557529c9a33f2da13085b546872028ec..16667342b3c324f694300c54d3d3532674c417c5 100644 (file)
@@ -46,15 +46,17 @@ 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";
 
 /* module parameters */
+#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
 static unsigned short clock = XTAL12;
 module_param(clock, ushort, 0644);
 MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0 "
                "(default=0)");
+#endif
 
 static unsigned short vif = LDRV;
 module_param(vif, ushort, 0644);
@@ -106,11 +108,22 @@ static void set_devadd_reg(struct r8a66597 *r8a66597, u8 r8a66597_address,
        r8a66597_write(r8a66597, val, devadd_reg);
 }
 
-static int enable_controller(struct r8a66597 *r8a66597)
+static int r8a66597_clock_enable(struct r8a66597 *r8a66597)
 {
        u16 tmp;
        int i = 0;
 
+#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+       do {
+               r8a66597_write(r8a66597, SCKE, SYSCFG0);
+               tmp = r8a66597_read(r8a66597, SYSCFG0);
+               if (i++ > 1000) {
+                       err("register access fail.");
+                       return -ENXIO;
+               }
+       } while ((tmp & SCKE) != SCKE);
+       r8a66597_write(r8a66597, 0x04, 0x02);
+#else
        do {
                r8a66597_write(r8a66597, USBE, SYSCFG0);
                tmp = r8a66597_read(r8a66597, SYSCFG0);
@@ -132,13 +145,63 @@ static int enable_controller(struct r8a66597 *r8a66597)
                        return -ENXIO;
                }
        } while ((tmp & SCKE) != SCKE);
+#endif /* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */
+
+       return 0;
+}
+
+static void r8a66597_clock_disable(struct r8a66597 *r8a66597)
+{
+       r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
+       udelay(1);
+#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
+       r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
+       r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
+       r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+#endif
+}
+
+static void r8a66597_enable_port(struct r8a66597 *r8a66597, int port)
+{
+       u16 val;
+
+       val = port ? DRPD : DCFM | DRPD;
+       r8a66597_bset(r8a66597, val, get_syscfg_reg(port));
+       r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port));
+
+       r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, get_dmacfg_reg(port));
+       r8a66597_bclr(r8a66597, DTCHE, get_intenb_reg(port));
+       r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port));
+}
 
-       r8a66597_bset(r8a66597, DCFM | DRPD, SYSCFG0);
-       r8a66597_bset(r8a66597, DRPD, SYSCFG1);
+static void r8a66597_disable_port(struct r8a66597 *r8a66597, int port)
+{
+       u16 val, tmp;
+
+       r8a66597_write(r8a66597, 0, get_intenb_reg(port));
+       r8a66597_write(r8a66597, 0, get_intsts_reg(port));
+
+       r8a66597_port_power(r8a66597, port, 0);
+
+       do {
+               tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS;
+               udelay(640);
+       } while (tmp == EDGESTS);
+
+       val = port ? DRPD : DCFM | DRPD;
+       r8a66597_bclr(r8a66597, val, get_syscfg_reg(port));
+       r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port));
+}
+
+static int enable_controller(struct r8a66597 *r8a66597)
+{
+       int ret, port;
+
+       ret = r8a66597_clock_enable(r8a66597);
+       if (ret < 0)
+               return ret;
 
        r8a66597_bset(r8a66597, vif & LDRV, PINCFG);
-       r8a66597_bset(r8a66597, HSE, SYSCFG0);
-       r8a66597_bset(r8a66597, HSE, SYSCFG1);
        r8a66597_bset(r8a66597, USBE, SYSCFG0);
 
        r8a66597_bset(r8a66597, BEMPE | NRDYE | BRDYE, INTENB0);
@@ -146,53 +209,30 @@ static int enable_controller(struct r8a66597 *r8a66597)
        r8a66597_bset(r8a66597, BRDY0, BRDYENB);
        r8a66597_bset(r8a66597, BEMP0, BEMPENB);
 
-       r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, DMA0CFG);
-       r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, DMA1CFG);
-
        r8a66597_bset(r8a66597, endian & BIGEND, CFIFOSEL);
        r8a66597_bset(r8a66597, endian & BIGEND, D0FIFOSEL);
        r8a66597_bset(r8a66597, endian & BIGEND, D1FIFOSEL);
-
        r8a66597_bset(r8a66597, TRNENSEL, SOFCFG);
 
        r8a66597_bset(r8a66597, SIGNE | SACKE, INTENB1);
-       r8a66597_bclr(r8a66597, DTCHE, INTENB1);
-       r8a66597_bset(r8a66597, ATTCHE, INTENB1);
-       r8a66597_bclr(r8a66597, DTCHE, INTENB2);
-       r8a66597_bset(r8a66597, ATTCHE, INTENB2);
+
+       for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++)
+               r8a66597_enable_port(r8a66597, port);
 
        return 0;
 }
 
 static void disable_controller(struct r8a66597 *r8a66597)
 {
-       u16 tmp;
+       int port;
 
        r8a66597_write(r8a66597, 0, INTENB0);
-       r8a66597_write(r8a66597, 0, INTENB1);
-       r8a66597_write(r8a66597, 0, INTENB2);
        r8a66597_write(r8a66597, 0, INTSTS0);
-       r8a66597_write(r8a66597, 0, INTSTS1);
-       r8a66597_write(r8a66597, 0, INTSTS2);
-
-       r8a66597_port_power(r8a66597, 0, 0);
-       r8a66597_port_power(r8a66597, 1, 0);
-
-       do {
-               tmp = r8a66597_read(r8a66597, SOFCFG) & EDGESTS;
-               udelay(640);
-       } while (tmp == EDGESTS);
 
-       r8a66597_bclr(r8a66597, DCFM | DRPD, SYSCFG0);
-       r8a66597_bclr(r8a66597, DRPD, SYSCFG1);
-       r8a66597_bclr(r8a66597, HSE, SYSCFG0);
-       r8a66597_bclr(r8a66597, HSE, SYSCFG1);
+       for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++)
+               r8a66597_disable_port(r8a66597, port);
 
-       r8a66597_bclr(r8a66597, SCKE, SYSCFG0);
-       udelay(1);
-       r8a66597_bclr(r8a66597, PLLC, SYSCFG0);
-       r8a66597_bclr(r8a66597, XCKE, SYSCFG0);
-       r8a66597_bclr(r8a66597, USBE, SYSCFG0);
+       r8a66597_clock_disable(r8a66597);
 }
 
 static int get_parent_r8a66597_address(struct r8a66597 *r8a66597,
@@ -577,13 +617,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)
 {
@@ -715,6 +751,7 @@ static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597,
                                     struct r8a66597_pipe *pipe,
                                     struct urb *urb)
 {
+#if !defined(CONFIG_SUPERH_ON_CHIP_R8A66597)
        int i;
        struct r8a66597_pipe_info *info = &pipe->info;
 
@@ -742,6 +779,7 @@ static void enable_r8a66597_pipe_dma(struct r8a66597 *r8a66597,
                        break;
                }
        }
+#endif /* #if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) */
 }
 
 /* this function must be called with interrupt disabled */
@@ -825,6 +863,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 +897,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
@@ -876,10 +942,19 @@ static void pipe_irq_disable(struct r8a66597 *r8a66597, u16 pipenum)
 }
 
 /* this function must be called with interrupt disabled */
-static void r8a66597_usb_preconnect(struct r8a66597 *r8a66597, int port)
+static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port,
+                                       u16 syssts)
 {
-       r8a66597->root_hub[port].port |= (1 << USB_PORT_FEAT_CONNECTION)
-                                        | (1 << USB_PORT_FEAT_C_CONNECTION);
+       if (syssts == SE0) {
+               r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port));
+               return;
+       }
+
+       if (syssts == FS_JSTS)
+               r8a66597_bset(r8a66597, HSE, get_syscfg_reg(port));
+       else if (syssts == LS_JSTS)
+               r8a66597_bclr(r8a66597, HSE, get_syscfg_reg(port));
+
        r8a66597_write(r8a66597, ~DTCH, get_intsts_reg(port));
        r8a66597_bset(r8a66597, DTCHE, get_intenb_reg(port));
 }
@@ -918,7 +993,7 @@ static void prepare_setup_packet(struct r8a66597 *r8a66597,
                                 struct r8a66597_td *td)
 {
        int i;
-       u16 *p = (u16 *)td->urb->setup_packet;
+       __le16 *p = (__le16 *)td->urb->setup_packet;
        unsigned long setup_addr = USBREQ;
 
        r8a66597_write(r8a66597, make_devsel(td->address) | td->maxpacket,
@@ -926,7 +1001,7 @@ static void prepare_setup_packet(struct r8a66597 *r8a66597,
        r8a66597_write(r8a66597, ~(SIGN | SACK), INTSTS1);
 
        for (i = 0; i < 4; i++) {
-               r8a66597_write(r8a66597, cpu_to_le16(p[i]), setup_addr);
+               r8a66597_write(r8a66597, le16_to_cpu(p[i]), setup_addr);
                setup_addr += 2;
        }
        r8a66597_write(r8a66597, SUREQ, DCPCTR);
@@ -960,9 +1035,9 @@ static void prepare_packet_read(struct r8a66597 *r8a66597,
                                r8a66597_write(r8a66597, TRCLR,
                                                td->pipe->pipetre);
                                r8a66597_write(r8a66597,
-                                               (urb->transfer_buffer_length
-                                               + td->maxpacket - 1)
-                                               / td->maxpacket,
+                                               DIV_ROUND_UP
+                                                 (urb->transfer_buffer_length,
+                                                  td->maxpacket),
                                                td->pipe->pipetrn);
                                r8a66597_bset(r8a66597, TRENB,
                                                td->pipe->pipetre);
@@ -1021,8 +1096,7 @@ static void prepare_status_packet(struct r8a66597 *r8a66597,
                r8a66597_mdfy(r8a66597, ISEL, ISEL | CURPIPE, CFIFOSEL);
                r8a66597_reg_wait(r8a66597, CFIFOSEL, CURPIPE, 0);
                r8a66597_write(r8a66597, ~BEMP0, BEMPSTS);
-               r8a66597_write(r8a66597, BCLR, CFIFOCTR);
-               r8a66597_write(r8a66597, BVAL, CFIFOCTR);
+               r8a66597_write(r8a66597, BCLR | BVAL, CFIFOCTR);
                enable_irq_empty(r8a66597, 0);
        } else {
                r8a66597_bclr(r8a66597, R8A66597_DIR, DCPCFG);
@@ -1454,13 +1528,21 @@ static void irq_pipe_nrdy(struct r8a66597 *r8a66597)
        }
 }
 
+static void r8a66597_root_hub_start_polling(struct r8a66597 *r8a66597)
+{
+       mod_timer(&r8a66597->rh_timer,
+                       jiffies + msecs_to_jiffies(R8A66597_RH_POLL_TIME));
+}
+
 static void start_root_hub_sampling(struct r8a66597 *r8a66597, int port)
 {
        struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
 
        rh->old_syssts = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST;
        rh->scount = R8A66597_MAX_SAMPLING;
-       mod_timer(&r8a66597->rh_timer, jiffies + msecs_to_jiffies(50));
+       r8a66597->root_hub[port].port |= (1 << USB_PORT_FEAT_CONNECTION)
+                                        | (1 << USB_PORT_FEAT_C_CONNECTION);
+       r8a66597_root_hub_start_polling(r8a66597);
 }
 
 static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
@@ -1547,41 +1629,55 @@ static void r8a66597_root_hub_control(struct r8a66597 *r8a66597, int port)
                if ((tmp & USBRST) == USBRST) {
                        r8a66597_mdfy(r8a66597, UACT, USBRST | UACT,
                                      dvstctr_reg);
-                       mod_timer(&r8a66597->rh_timer,
-                                 jiffies + msecs_to_jiffies(50));
+                       r8a66597_root_hub_start_polling(r8a66597);
                } else
                        r8a66597_usb_connect(r8a66597, port);
        }
 
+       if (!(rh->port & (1 << USB_PORT_FEAT_CONNECTION))) {
+               r8a66597_write(r8a66597, ~ATTCH, get_intsts_reg(port));
+               r8a66597_bset(r8a66597, ATTCHE, get_intenb_reg(port));
+       }
+
        if (rh->scount > 0) {
                tmp = r8a66597_read(r8a66597, get_syssts_reg(port)) & LNST;
                if (tmp == rh->old_syssts) {
                        rh->scount--;
-                       if (rh->scount == 0) {
-                               if (tmp == FS_JSTS) {
-                                       r8a66597_bset(r8a66597, HSE,
-                                                     get_syscfg_reg(port));
-                                       r8a66597_usb_preconnect(r8a66597, port);
-                               } else if (tmp == LS_JSTS) {
-                                       r8a66597_bclr(r8a66597, HSE,
-                                                     get_syscfg_reg(port));
-                                       r8a66597_usb_preconnect(r8a66597, port);
-                               } else if (tmp == SE0)
-                                       r8a66597_bset(r8a66597, ATTCHE,
-                                                     get_intenb_reg(port));
-                       } else {
-                               mod_timer(&r8a66597->rh_timer,
-                                         jiffies + msecs_to_jiffies(50));
-                       }
+                       if (rh->scount == 0)
+                               r8a66597_check_syssts(r8a66597, port, tmp);
+                       else
+                               r8a66597_root_hub_start_polling(r8a66597);
                } else {
                        rh->scount = R8A66597_MAX_SAMPLING;
                        rh->old_syssts = tmp;
-                       mod_timer(&r8a66597->rh_timer,
-                                 jiffies + msecs_to_jiffies(50));
+                       r8a66597_root_hub_start_polling(r8a66597);
                }
        }
 }
 
+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 +1859,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);
@@ -2028,7 +2131,7 @@ static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
        case GetPortStatus:
                if (wIndex > R8A66597_MAX_ROOT_HUB)
                        goto error;
-               *(u32 *)buf = cpu_to_le32(rh->port);
+               *(__le32 *)buf = cpu_to_le32(rh->port);
                break;
        case SetPortFeature:
                if (wIndex > R8A66597_MAX_ROOT_HUB)
@@ -2107,13 +2210,11 @@ static struct hc_driver r8a66597_hc_driver = {
 #if defined(CONFIG_PM)
 static int r8a66597_suspend(struct platform_device *pdev, pm_message_t state)
 {
-       pdev->dev.power.power_state = state;
        return 0;
 }
 
 static int r8a66597_resume(struct platform_device *pdev)
 {
-       pdev->dev.power.power_state = PMSG_ON;
        return 0;
 }
 #else  /* if defined(CONFIG_PM) */
@@ -2194,6 +2295,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);