Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / usb / host / sl811-hcd.c
index c932c53..a923430 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,
 #undef PACKET_TRACE
 
 #include <linux/config.h>
-
-#ifdef CONFIG_USB_DEBUG
-#      define DEBUG
-#else
-#      undef DEBUG
-#endif
-
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/kernel.h>
@@ -54,6 +47,7 @@
 #include <linux/interrupt.h>
 #include <linux/usb.h>
 #include <linux/usb_sl811.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -67,7 +61,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 +84,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 +95,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 +115,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 +132,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 +441,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 +471,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 +642,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 +660,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 +721,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 +731,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 +758,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);
 
@@ -779,6 +776,9 @@ retry:
 /* usb 1.1 says max 90% of a frame is available for periodic transfers.
  * this driver doesn't promise that much since it's got to handle an
  * IRQ per packet; irq handling latencies also use up that time.
+ *
+ * NOTE:  the periodic schedule is a sparse tree, with the load for
+ * each branch minimized.  see fig 3.5 in the OHCI spec for example.
  */
 #define        MAX_PERIODIC_LOAD       500     /* out of 1000 usec */
 
@@ -800,7 +800,7 @@ static int balance(struct sl811 *sl811, u16 period, u16 load)
                        }
                        if (j < PERIODIC_SIZE)
                                continue;
-                       branch = i; 
+                       branch = i;
                }
        }
        return branch;
@@ -812,7 +812,7 @@ static int sl811h_urb_enqueue(
        struct usb_hcd          *hcd,
        struct usb_host_endpoint *hep,
        struct urb              *urb,
-       int                     mem_flags
+       gfp_t                   mem_flags
 ) {
        struct sl811            *sl811 = hcd_to_sl811(hcd);
        struct usb_device       *udev = urb->dev;
@@ -832,14 +832,15 @@ static int sl811h_urb_enqueue(
 
        /* avoid all allocations within spinlocks */
        if (!hep->hcpriv)
-               ep = kcalloc(1, sizeof *ep, mem_flags);
+               ep = kzalloc(sizeof *ep, mem_flags);
 
        spin_lock_irqsave(&sl811->lock, flags);
 
        /* 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;
+               kfree(ep);
                goto fail;
        }
 
@@ -852,7 +853,7 @@ static int sl811h_urb_enqueue(
 
        } else {
                INIT_LIST_HEAD(&ep->schedule);
-               ep->udev = usb_get_dev(udev);
+               ep->udev = udev;
                ep->epnum = epnum;
                ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
                ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE;
@@ -894,6 +895,7 @@ static int sl811h_urb_enqueue(
                        break;
                }
 
+               ep->hep = hep;
                hep->hcpriv = ep;
        }
 
@@ -907,8 +909,16 @@ static int sl811h_urb_enqueue(
        case PIPE_ISOCHRONOUS:
        case PIPE_INTERRUPT:
                urb->interval = ep->period;
-               if (ep->branch < PERIODIC_SIZE)
+               if (ep->branch < PERIODIC_SIZE) {
+                       /* NOTE:  the phase is correct here, but the value
+                        * needs offsetting by the transfer queue depth.
+                        * All current drivers ignore start_frame, so this
+                        * is unlikely to ever matter...
+                        */
+                       urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1))
+                                               + ep->branch;
                        break;
+               }
 
                retval = balance(sl811, ep->period, ep->load);
                if (retval < 0)
@@ -965,15 +975,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 +1032,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;
@@ -1040,7 +1052,6 @@ sl811h_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep)
        if (!list_empty(&hep->urb_list))
                WARN("ep %p not empty?\n", ep);
 
-       usb_put_dev(ep->udev);
        kfree(ep);
        hep->hcpriv = NULL;
 }
@@ -1073,7 +1084,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);
@@ -1116,7 +1127,7 @@ sl811h_hub_descriptor (
        desc->wHubCharacteristics = (__force __u16)cpu_to_le16(temp);
 
        /* two bitmaps:  ports removable, and legacy PortPwrCtrlMask */
-       desc->bitmap[0] = 1 << 1;
+       desc->bitmap[0] = 0 << 1;
        desc->bitmap[1] = ~0;
 }
 
@@ -1345,7 +1356,7 @@ error:
 #ifdef CONFIG_PM
 
 static int
-sl811h_hub_suspend(struct usb_hcd *hcd)
+sl811h_bus_suspend(struct usb_hcd *hcd)
 {
        // SOFs off
        DBG("%s\n", __FUNCTION__);
@@ -1353,7 +1364,7 @@ sl811h_hub_suspend(struct usb_hcd *hcd)
 }
 
 static int
-sl811h_hub_resume(struct usb_hcd *hcd)
+sl811h_bus_resume(struct usb_hcd *hcd)
 {
        // SOFs on
        DBG("%s\n", __FUNCTION__);
@@ -1362,8 +1373,8 @@ sl811h_hub_resume(struct usb_hcd *hcd)
 
 #else
 
-#define        sl811h_hub_suspend      NULL
-#define        sl811h_hub_resume       NULL
+#define        sl811h_bus_suspend      NULL
+#define        sl811h_bus_resume       NULL
 
 #endif
 
@@ -1557,28 +1568,19 @@ static int
 sl811h_start(struct usb_hcd *hcd)
 {
        struct sl811            *sl811 = hcd_to_sl811(hcd);
-       struct usb_device       *udev;
 
        /* chip has been reset, VBUS power is off */
+       hcd->state = HC_STATE_RUNNING;
 
-       udev = usb_alloc_dev(NULL, &hcd->self, 0);
-       if (!udev)
-               return -ENOMEM;
-
-       udev->speed = USB_SPEED_FULL;
-       hcd->state = USB_STATE_RUNNING;
-
-       if (sl811->board)
-               hcd->can_wakeup = sl811->board->can_wakeup;
-
-       if (hcd_register_root(udev, hcd) != 0) {
-               usb_put_dev(udev);
-               sl811h_stop(hcd);
-               return -ENODEV;
+       if (sl811->board) {
+               if (!device_can_wakeup(hcd->self.controller))
+                       device_init_wakeup(hcd->self.controller,
+                               sl811->board->can_wakeup);
+               hcd->power_budget = sl811->board->power * 2;
        }
 
-       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 +1594,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
@@ -1611,118 +1618,101 @@ static struct hc_driver sl811h_hc_driver = {
         */
        .hub_status_data =      sl811h_hub_status_data,
        .hub_control =          sl811h_hub_control,
-       .hub_suspend =          sl811h_hub_suspend,
-       .hub_resume =           sl811h_hub_resume,
+       .bus_suspend =          sl811h_bus_suspend,
+       .bus_resume =           sl811h_bus_resume,
 };
 
 /*-------------------------------------------------------------------------*/
 
-static int __init_or_module
-sl811h_remove(struct device *dev)
+static int __devexit
+sl811h_remove(struct platform_device *dev)
 {
-       struct sl811            *sl811 = dev_get_drvdata(dev);
-       struct usb_hcd          *hcd = sl811_to_hcd(sl811);
-       struct platform_device  *pdev;
+       struct usb_hcd          *hcd = platform_get_drvdata(dev);
+       struct sl811            *sl811 = hcd_to_sl811(hcd);
        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_deregister_bus(&hcd->self);
-
-       free_irq(hcd->irq, hcd);
+       usb_remove_hcd(hcd);
 
-       iounmap(sl811->data_reg);
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       release_mem_region(res->start, 1);
+       /* some platforms may use IORESOURCE_IO */
+       res = platform_get_resource(dev, IORESOURCE_MEM, 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);
+       res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       if (res)
+               iounmap(sl811->addr_reg);
 
        usb_put_hcd(hcd);
        return 0;
 }
 
-#define resource_len(r) (((r)->end - (r)->start) + 1)
-
-static int __init
-sl811h_probe(struct device *dev)
+static int __devinit
+sl811h_probe(struct platform_device *dev)
 {
        struct usb_hcd          *hcd;
        struct sl811            *sl811;
-       struct platform_device  *pdev;
        struct resource         *addr, *data;
        int                     irq;
        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
         * specific platform_data.  we don't probe for IRQs, and do only
         * 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)
+       irq = platform_get_irq(dev, 0);
+       if (dev->num_resources < 3 || irq < 0)
                return -ENODEV;
 
        /* refuse to confuse usbcore */
-       if (dev->dma_mask) {
+       if (dev->dev.dma_mask) {
                DBG("no we won't dma\n");
                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(dev, IORESOURCE_MEM, 0);
+       data = platform_get_resource(dev, IORESOURCE_MEM, 1);
+       retval = -EBUSY;
+       if (!addr || !data) {
+               addr = platform_get_resource(dev, IORESOURCE_IO, 0);
+               data = platform_get_resource(dev, 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, 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);
-       sl811->board = dev->platform_data;
+       sl811->board = dev->dev.platform_data;
        init_timer(&sl811->timer);
        sl811->timer.function = sl811h_timer;
        sl811->timer.data = (unsigned long) sl811;
@@ -1749,44 +1739,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,48 +1769,45 @@ 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 platform_device *dev, pm_message_t state)
 {
-       struct sl811    *sl811 = dev_get_drvdata(dev);
+       struct usb_hcd  *hcd = platform_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));
-       else
+       if (state.event == PM_EVENT_FREEZE)
+               retval = sl811h_bus_suspend(hcd);
+       else if (state.event == PM_EVENT_SUSPEND)
                port_power(sl811, 0);
        if (retval == 0)
-               dev->power.power_state = state;
+               dev->dev.power.power_state = state;
        return retval;
 }
 
 static int
-sl811h_resume(struct device *dev, u32 phase)
+sl811h_resume(struct platform_device *dev)
 {
-       struct sl811    *sl811 = dev_get_drvdata(dev);
-
-       if (phase != RESUME_POWER_ON)
-               return 0;
+       struct usb_hcd  *hcd = platform_get_drvdata(dev);
+       struct sl811    *sl811 = hcd_to_sl811(hcd);
 
        /* with no "check to see if VBUS is still powered" board hook,
         * 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) {
+       if (dev->dev.power.power_state.event == PM_EVENT_SUSPEND
+                       || !device_can_wakeup(&hcd->self.root_hub->dev)) {
                sl811->port1 = 0;
                port_power(sl811, 1);
+               usb_root_hub_lost_power(hcd->self.root_hub);
                return 0;
        }
 
-       dev->power.power_state = PM_SUSPEND_ON;
-       return sl811h_hub_resume(sl811_to_hcd(sl811));
+       dev->dev.power.power_state = PMSG_ON;
+       return sl811h_bus_resume(hcd);
 }
 
 #else
@@ -1846,31 +1818,34 @@ sl811h_resume(struct device *dev, u32 phase)
 #endif
 
 
-static struct device_driver sl811h_driver = {
-       .name =         (char *) hcd_name,
-       .bus =          &platform_bus_type,
-
+/* this driver is exported so sl811_cs can depend on it */
+struct platform_driver sl811h_driver = {
        .probe =        sl811h_probe,
-       .remove =       sl811h_remove,
+       .remove =       __devexit_p(sl811h_remove),
 
        .suspend =      sl811h_suspend,
        .resume =       sl811h_resume,
+       .driver = {
+               .name = (char *) hcd_name,
+               .owner = THIS_MODULE,
+       },
 };
+EXPORT_SYMBOL(sl811h_driver);
 
 /*-------------------------------------------------------------------------*/
-static int __init sl811h_init(void) 
+
+static int __init sl811h_init(void)
 {
        if (usb_disabled())
                return -ENODEV;
 
        INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION);
-       return driver_register(&sl811h_driver);
+       return platform_driver_register(&sl811h_driver);
 }
 module_init(sl811h_init);
 
-static void __exit sl811h_cleanup(void) 
-{      
-       driver_unregister(&sl811h_driver);
+static void __exit sl811h_cleanup(void)
+{
+       platform_driver_unregister(&sl811h_driver);
 }
 module_exit(sl811h_cleanup);