vserver 2.0 rc7
[linux-2.6.git] / drivers / usb / host / sl811-hcd.c
index c932c53..99d43f7 100644 (file)
@@ -2,8 +2,8 @@
  * SL811HS HCD (Host Controller Driver) for USB.
  *
  * Copyright (C) 2004 Psion Teklogix (for NetBook PRO)
- * Copyright (C) 2004 David Brownell
- * 
+ * Copyright (C) 2004-2005 David Brownell
+ *
  * Periodic scheduling is based on Roman's OHCI code
  *     Copyright (C) 1999 Roman Weissgaerber
  *
@@ -15,7 +15,7 @@
  * For documentation, see the SL811HS spec and the "SL811HS Embedded Host"
  * document (providing significant pieces missing from that spec); plus
  * the SL811S spec if you want peripheral side info.
- */ 
+ */
 
 /*
  * Status:  Passed basic stress testing, works with hubs, mice, keyboards,
@@ -67,7 +67,7 @@
 MODULE_DESCRIPTION("SL811HS USB Host Controller Driver");
 MODULE_LICENSE("GPL");
 
-#define DRIVER_VERSION "15 Dec 2004"
+#define DRIVER_VERSION "19 May 2005"
 
 
 #ifndef DEBUG
@@ -90,8 +90,6 @@ static const char hcd_name[] = "sl811-hcd";
 
 /*-------------------------------------------------------------------------*/
 
-static irqreturn_t sl811h_irq(int irq, void *_hcd, struct pt_regs *regs);
-
 static void port_power(struct sl811 *sl811, int is_on)
 {
        struct usb_hcd  *hcd = sl811_to_hcd(sl811);
@@ -103,12 +101,12 @@ static void port_power(struct sl811 *sl811, int is_on)
 
                sl811->port1 = (1 << USB_PORT_FEAT_POWER);
                sl811->irq_enable = SL11H_INTMASK_INSRMV;
-               hcd->self.controller->power.power_state = PM_SUSPEND_ON;
+               hcd->self.controller->power.power_state = PMSG_ON;
        } else {
                sl811->port1 = 0;
                sl811->irq_enable = 0;
-               hcd->state = USB_STATE_HALT;
-               hcd->self.controller->power.power_state = PM_SUSPEND_DISK;
+               hcd->state = HC_STATE_HALT;
+               hcd->self.controller->power.power_state = PMSG_SUSPEND;
        }
        sl811->ctrl1 = 0;
        sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
@@ -123,6 +121,10 @@ static void port_power(struct sl811 *sl811, int is_on)
        /* reset as thoroughly as we can */
        if (sl811->board && sl811->board->reset)
                sl811->board->reset(hcd->self.controller);
+       else {
+               sl811_write(sl811, SL11H_CTLREG1, SL11H_CTL1MASK_SE0);
+               mdelay(20);
+       }
 
        sl811_write(sl811, SL11H_IRQ_ENABLE, 0);
        sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1);
@@ -136,7 +138,7 @@ static void port_power(struct sl811 *sl811, int is_on)
 
 /* This is a PIO-only HCD.  Queueing appends URBs to the endpoint's queue,
  * and may start I/O.  Endpoint queues are scanned during completion irq
- * handlers (one per packet: ACK, NAK, faults, etc) and urb cancelation.
+ * handlers (one per packet: ACK, NAK, faults, etc) and urb cancellation.
  *
  * Using an external DMA engine to copy a packet at a time could work,
  * though setup/teardown costs may be too big to make it worthwhile.
@@ -445,6 +447,7 @@ static void finish_request(
        spin_lock(&urb->lock);
        if (urb->status == -EINPROGRESS)
                urb->status = status;
+       urb->hcpriv = NULL;
        spin_unlock(&urb->lock);
 
        spin_unlock(&sl811->lock);
@@ -474,7 +477,7 @@ static void finish_request(
                if (*prev)
                        *prev = ep->next;
                sl811->load[i] -= ep->load;
-       }       
+       }
        ep->branch = PERIODIC_SIZE;
        sl811->periodic_count--;
        sl811_to_hcd(sl811)->self.bandwidth_allocated
@@ -645,9 +648,8 @@ static inline u8 checkdone(struct sl811 *sl811)
        return irqstat;
 }
 
-static irqreturn_t sl811h_irq(int irq, void *_hcd, struct pt_regs *regs)
+static irqreturn_t sl811h_irq(struct usb_hcd *hcd, struct pt_regs *regs)
 {
-       struct usb_hcd  *hcd = _hcd;
        struct sl811    *sl811 = hcd_to_sl811(hcd);
        u8              irqstat;
        irqreturn_t     ret = IRQ_NONE;
@@ -664,9 +666,9 @@ retry:
 
 #ifdef QUIRK2
        /* this may no longer be necessary ... */
-       if (irqstat == 0 && ret == IRQ_NONE) {
+       if (irqstat == 0) {
                irqstat = checkdone(sl811);
-               if (irqstat && irq != ~0)
+               if (irqstat)
                        sl811->stat_lost++;
        }
 #endif
@@ -725,7 +727,8 @@ retry:
                if (sl811->active_a) {
                        sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0);
                        finish_request(sl811, sl811->active_a,
-                               container_of(sl811->active_a->hep->urb_list.next,
+                               container_of(sl811->active_a
+                                               ->hep->urb_list.next,
                                        struct urb, urb_list),
                                NULL, -ESHUTDOWN);
                        sl811->active_a = NULL;
@@ -734,14 +737,15 @@ retry:
                if (sl811->active_b) {
                        sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0);
                        finish_request(sl811, sl811->active_b,
-                               container_of(sl811->active_b->hep->urb_list.next,
+                               container_of(sl811->active_b
+                                               ->hep->urb_list.next,
                                        struct urb, urb_list),
                                NULL, -ESHUTDOWN);
                        sl811->active_b = NULL;
                }
 #endif
 
-               /* port status seems wierd until after reset, so
+               /* port status seems weird until after reset, so
                 * force the reset and make khubd clean up later.
                 */
                sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION)
@@ -760,12 +764,11 @@ retry:
                if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
                        start_transfer(sl811);
                ret = IRQ_HANDLED;
-               hcd->saw_irq = 1;
                if (retries--)
                        goto retry;
        }
 
-       if (sl811->periodic_count == 0 && list_empty(&sl811->async)) 
+       if (sl811->periodic_count == 0 && list_empty(&sl811->async))
                sofirq_off(sl811);
        sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable);
 
@@ -800,7 +803,7 @@ static int balance(struct sl811 *sl811, u16 period, u16 load)
                        }
                        if (j < PERIODIC_SIZE)
                                continue;
-                       branch = i; 
+                       branch = i;
                }
        }
        return branch;
@@ -838,7 +841,7 @@ static int sl811h_urb_enqueue(
 
        /* don't submit to a dead or disabled port */
        if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))
-                       || !HCD_IS_RUNNING(hcd->state)) {
+                       || !HC_IS_RUNNING(hcd->state)) {
                retval = -ENODEV;
                goto fail;
        }
@@ -894,6 +897,7 @@ static int sl811h_urb_enqueue(
                        break;
                }
 
+               ep->hep = hep;
                hep->hcpriv = ep;
        }
 
@@ -965,15 +969,16 @@ fail:
 static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
 {
        struct sl811            *sl811 = hcd_to_sl811(hcd);
-       struct usb_host_endpoint *hep = urb->hcpriv;
+       struct usb_host_endpoint *hep;
        unsigned long           flags;
        struct sl811h_ep        *ep;
        int                     retval = 0;
 
+       spin_lock_irqsave(&sl811->lock, flags);
+       hep = urb->hcpriv;
        if (!hep)
-               return -EINVAL;
+               goto fail;
 
-       spin_lock_irqsave(&sl811->lock, flags);
        ep = hep->hcpriv;
        if (ep) {
                /* finish right away if this urb can't be active ...
@@ -1021,6 +1026,7 @@ static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
                        VDBG("dequeue, urb %p active %s; wait4irq\n", urb,
                                (sl811->active_a == ep) ? "A" : "B");
        } else
+fail:
                retval = -EINVAL;
        spin_unlock_irqrestore(&sl811->lock, flags);
        return retval;
@@ -1073,7 +1079,7 @@ sl811h_hub_status_data(struct usb_hcd *hcd, char *buf)
         */
        local_irq_save(flags);
        if (!timer_pending(&sl811->timer)) {
-               if (sl811h_irq(~0, sl811, NULL) != IRQ_NONE)
+               if (sl811h_irq( /* ~0, */ hcd, NULL) != IRQ_NONE)
                        sl811->stat_lost++;
        }
        local_irq_restore(flags);
@@ -1566,12 +1572,12 @@ sl811h_start(struct usb_hcd *hcd)
                return -ENOMEM;
 
        udev->speed = USB_SPEED_FULL;
-       hcd->state = USB_STATE_RUNNING;
+       hcd->state = HC_STATE_RUNNING;
 
        if (sl811->board)
                hcd->can_wakeup = sl811->board->can_wakeup;
 
-       if (hcd_register_root(udev, hcd) != 0) {
+       if (usb_hcd_register_root_hub(udev, hcd) != 0) {
                usb_put_dev(udev);
                sl811h_stop(hcd);
                return -ENODEV;
@@ -1580,6 +1586,9 @@ sl811h_start(struct usb_hcd *hcd)
        if (sl811->board && sl811->board->power)
                hub_set_power_budget(udev, sl811->board->power * 2);
 
+       /* enable power and interupts */
+       port_power(sl811, 1);
+
        return 0;
 }
 
@@ -1592,7 +1601,12 @@ static struct hc_driver sl811h_hc_driver = {
        /*
         * generic hardware linkage
         */
-       .flags =                HCD_USB11,
+       .irq =                  sl811h_irq,
+       .flags =                HCD_USB11 | HCD_MEMORY,
+
+       /* Basic lifecycle operations */
+       .start =                sl811h_start,
+       .stop =                 sl811h_stop,
 
        /*
         * managing i/o requests and associated device resources
@@ -1617,42 +1631,33 @@ static struct hc_driver sl811h_hc_driver = {
 
 /*-------------------------------------------------------------------------*/
 
-static int __init_or_module
+static int __devexit
 sl811h_remove(struct device *dev)
 {
-       struct sl811            *sl811 = dev_get_drvdata(dev);
-       struct usb_hcd          *hcd = sl811_to_hcd(sl811);
+       struct usb_hcd          *hcd = dev_get_drvdata(dev);
+       struct sl811            *sl811 = hcd_to_sl811(hcd);
        struct platform_device  *pdev;
        struct resource         *res;
 
        pdev = container_of(dev, struct platform_device, dev);
 
-       if (HCD_IS_RUNNING(hcd->state))
-               hcd->state = USB_STATE_QUIESCING;
-
-       usb_disconnect(&hcd->self.root_hub);
        remove_debug_file(sl811);
-       sl811h_stop(hcd);
+       usb_remove_hcd(hcd);
 
-       usb_deregister_bus(&hcd->self);
-
-       free_irq(hcd->irq, hcd);
-
-       iounmap(sl811->data_reg);
+       /* some platforms may use IORESOURCE_IO */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       release_mem_region(res->start, 1);
+       if (res)
+               iounmap(sl811->data_reg);
 
-       iounmap(sl811->addr_reg);
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(res->start, 1);
+       if (res)
+               iounmap(sl811->addr_reg);
 
        usb_put_hcd(hcd);
        return 0;
 }
 
-#define resource_len(r) (((r)->end - (r)->start) + 1)
-
-static int __init
+static int __devinit
 sl811h_probe(struct device *dev)
 {
        struct usb_hcd          *hcd;
@@ -1663,7 +1668,7 @@ sl811h_probe(struct device *dev)
        void __iomem            *addr_reg;
        void __iomem            *data_reg;
        int                     retval;
-       u8                      tmp;
+       u8                      tmp, ioaddr = 0;
 
        /* basic sanity checks first.  board-specific init logic should
         * have initialized these three resources and probably board
@@ -1671,13 +1676,8 @@ sl811h_probe(struct device *dev)
         * minimal sanity checking.
         */
        pdev = container_of(dev, struct platform_device, dev);
-       if (pdev->num_resources < 3)
-               return -ENODEV;
-
-       addr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       data = platform_get_resource(pdev, IORESOURCE_MEM, 1);
        irq = platform_get_irq(pdev, 0);
-       if (!addr || !data || irq < 0)
+       if (pdev->num_resources < 3 || irq < 0)
                return -ENODEV;
 
        /* refuse to confuse usbcore */
@@ -1686,39 +1686,41 @@ sl811h_probe(struct device *dev)
                return -EINVAL;
        }
 
-       if (!request_mem_region(addr->start, 1, hcd_name)) {
-               retval = -EBUSY;
-               goto err1;
-       }
-       addr_reg = ioremap(addr->start, resource_len(addr));
-       if (addr_reg == NULL) {
-               retval = -ENOMEM;
-               goto err2;
-       }
+       /* the chip may be wired for either kind of addressing */
+       addr = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       data = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       retval = -EBUSY;
+       if (!addr || !data) {
+               addr = platform_get_resource(pdev, IORESOURCE_IO, 0);
+               data = platform_get_resource(pdev, IORESOURCE_IO, 1);
+               if (!addr || !data)
+                       return -ENODEV;
+               ioaddr = 1;
+
+               addr_reg = (void __iomem *) addr->start;
+               data_reg = (void __iomem *) data->start;
+       } else {
+               addr_reg = ioremap(addr->start, 1);
+               if (addr_reg == NULL) {
+                       retval = -ENOMEM;
+                       goto err2;
+               }
 
-       if (!request_mem_region(data->start, 1, hcd_name)) {
-               retval = -EBUSY;
-               goto err3;
-       }
-       data_reg = ioremap(data->start, resource_len(addr));
-       if (data_reg == NULL) {
-               retval = -ENOMEM;
-               goto err4;
+               data_reg = ioremap(data->start, 1);
+               if (data_reg == NULL) {
+                       retval = -ENOMEM;
+                       goto err4;
+               }
        }
 
        /* allocate and initialize hcd */
-       hcd = usb_create_hcd(&sl811h_hc_driver);
+       hcd = usb_create_hcd(&sl811h_hc_driver, dev, dev->bus_id);
        if (!hcd) {
-               retval = 0;
+               retval = -ENOMEM;
                goto err5;
        }
+       hcd->rsrc_start = addr->start;
        sl811 = hcd_to_sl811(hcd);
-       dev_set_drvdata(dev, sl811);
-
-       hcd->self.controller = dev;
-       hcd->self.bus_name = dev->bus_id;
-       hcd->irq = irq;
-       hcd->regs = addr_reg;
 
        spin_lock_init(&sl811->lock);
        INIT_LIST_HEAD(&sl811->async);
@@ -1749,44 +1751,29 @@ sl811h_probe(struct device *dev)
                goto err6;
        }
 
-       /* sl811s would need a different handler for this irq */
-#ifdef CONFIG_ARM
-       /* Cypress docs say the IRQ is IRQT_HIGH ... */
-       set_irq_type(irq, IRQT_RISING);
-#endif
-       retval = request_irq(irq, sl811h_irq, SA_INTERRUPT,
-                       hcd->driver->description, hcd);
+       /* The chip's IRQ is level triggered, active high.  A requirement
+        * for platform device setup is to cope with things like signal
+        * inverters (e.g. CF is active low) or working only with edge
+        * triggers (e.g. most ARM CPUs).  Initial driver stress testing
+        * was on a system with single edge triggering, so most sorts of
+        * triggering arrangement should work.
+        */
+       retval = usb_add_hcd(hcd, irq, SA_INTERRUPT | SA_SHIRQ);
        if (retval != 0)
                goto err6;
 
-       INFO("%s, irq %d\n", hcd->product_desc, irq);
-
-       retval = usb_register_bus(&hcd->self);
-       if (retval < 0)
-               goto err7;
-
-       retval = sl811h_start(hcd);
-       if (retval < 0)
-               goto err8;
-
        create_debug_file(sl811);
-       return 0;
+       return retval;
 
- err8:
-       usb_deregister_bus(&hcd->self);
- err7:
-       free_irq(hcd->irq, hcd);
  err6:
        usb_put_hcd(hcd);
  err5:
-       iounmap(data_reg);
+       if (!ioaddr)
+               iounmap(data_reg);
  err4:
-       release_mem_region(data->start, 1);
- err3:
-       iounmap(addr_reg);
+       if (!ioaddr)
+               iounmap(addr_reg);
  err2:
-       release_mem_region(addr->start, 1);
- err1:
        DBG("init error, %d\n", retval);
        return retval;
 }
@@ -1794,21 +1781,22 @@ sl811h_probe(struct device *dev)
 #ifdef CONFIG_PM
 
 /* for this device there's no useful distinction between the controller
- * and its root hub, except that the root hub only gets direct PM calls 
+ * and its root hub, except that the root hub only gets direct PM calls
  * when CONFIG_USB_SUSPEND is enabled.
  */
 
 static int
-sl811h_suspend(struct device *dev, u32 state, u32 phase)
+sl811h_suspend(struct device *dev, pm_message_t state, u32 phase)
 {
-       struct sl811    *sl811 = dev_get_drvdata(dev);
+       struct usb_hcd  *hcd = dev_get_drvdata(dev);
+       struct sl811    *sl811 = hcd_to_sl811(hcd);
        int             retval = 0;
 
        if (phase != SUSPEND_POWER_DOWN)
                return retval;
 
        if (state <= PM_SUSPEND_MEM)
-               retval = sl811h_hub_suspend(sl811_to_hcd(sl811));
+               retval = sl811h_hub_suspend(hcd);
        else
                port_power(sl811, 0);
        if (retval == 0)
@@ -1819,7 +1807,8 @@ sl811h_suspend(struct device *dev, u32 state, u32 phase)
 static int
 sl811h_resume(struct device *dev, u32 phase)
 {
-       struct sl811    *sl811 = dev_get_drvdata(dev);
+       struct usb_hcd  *hcd = dev_get_drvdata(dev);
+       struct sl811    *sl811 = hcd_to_sl811(hcd);
 
        if (phase != RESUME_POWER_ON)
                return 0;
@@ -1828,14 +1817,14 @@ sl811h_resume(struct device *dev, u32 phase)
         * let's assume it'd only be powered to enable remote wakeup.
         */
        if (dev->power.power_state > PM_SUSPEND_MEM
-                       || !sl811_to_hcd(sl811)->can_wakeup) {
+                       || !hcd->can_wakeup) {
                sl811->port1 = 0;
                port_power(sl811, 1);
                return 0;
        }
 
-       dev->power.power_state = PM_SUSPEND_ON;
-       return sl811h_hub_resume(sl811_to_hcd(sl811));
+       dev->power.power_state = PMSG_ON;
+       return sl811h_hub_resume(hcd);
 }
 
 #else
@@ -1846,20 +1835,22 @@ sl811h_resume(struct device *dev, u32 phase)
 #endif
 
 
-static struct device_driver sl811h_driver = {
+/* this driver is exported so sl811_cs can depend on it */
+struct device_driver sl811h_driver = {
        .name =         (char *) hcd_name,
        .bus =          &platform_bus_type,
 
        .probe =        sl811h_probe,
-       .remove =       sl811h_remove,
+       .remove =       __devexit_p(sl811h_remove),
 
        .suspend =      sl811h_suspend,
        .resume =       sl811h_resume,
 };
+EXPORT_SYMBOL(sl811h_driver);
 
 /*-------------------------------------------------------------------------*/
-static int __init sl811h_init(void) 
+
+static int __init sl811h_init(void)
 {
        if (usb_disabled())
                return -ENODEV;
@@ -1869,8 +1860,8 @@ static int __init sl811h_init(void)
 }
 module_init(sl811h_init);
 
-static void __exit sl811h_cleanup(void) 
-{      
+static void __exit sl811h_cleanup(void)
+{
        driver_unregister(&sl811h_driver);
 }
 module_exit(sl811h_cleanup);