[PATCH] USB: fix initdata issue in isp116x-hcd
[safe/jmp/linux-2.6] / drivers / usb / host / isp116x-hcd.c
index 75128c3..e99210b 100644 (file)
 /* enqueuing/finishing log of urbs */
 //#define URB_TRACE
 
-#include <linux/config.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
 #include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/list.h>
-#include <linux/interrupt.h>
 #include <linux/usb.h>
 #include <linux/usb_isp116x.h>
+#include <linux/platform_device.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/system.h>
 #include <asm/byteorder.h>
 
-#ifndef DEBUG
-#      define  STUB_DEBUG_FILE
-#endif
-
 #include "../core/hcd.h"
 #include "isp116x.h"
 
-#define DRIVER_VERSION "08 Apr 2005"
+#define DRIVER_VERSION "03 Nov 2005"
 #define DRIVER_DESC    "ISP116x USB Host Controller Driver"
 
 MODULE_DESCRIPTION(DRIVER_DESC);
@@ -163,13 +154,11 @@ static void pack_fifo(struct isp116x *isp116x)
        struct ptd *ptd;
        int buflen = isp116x->atl_last_dir == PTD_DIR_IN
            ? isp116x->atl_bufshrt : isp116x->atl_buflen;
-       int ptd_count = 0;
 
        isp116x_write_reg16(isp116x, HCuPINT, HCuPINT_AIIEOT);
        isp116x_write_reg16(isp116x, HCXFERCTR, buflen);
        isp116x_write_addr(isp116x, HCATLPORT | ISP116x_WRITE_OFFSET);
        for (ep = isp116x->atl_active; ep; ep = ep->active) {
-               ++ptd_count;
                ptd = &ep->ptd;
                dump_ptd(ptd);
                dump_ptd_out_data(ptd, ep->data);
@@ -304,9 +293,8 @@ static void postproc_atl_queue(struct isp116x *isp116x)
                udev = urb->dev;
                ptd = &ep->ptd;
                cc = PTD_GET_CC(ptd);
-
-               spin_lock(&urb->lock);
                short_not_ok = 1;
+               spin_lock(&urb->lock);
 
                /* Data underrun is special. For allowed underrun
                   we clear the error and continue as normal. For
@@ -326,7 +314,8 @@ static void postproc_atl_queue(struct isp116x *isp116x)
                                        usb_settoggle(udev, ep->epnum,
                                                      ep->nextpid ==
                                                      USB_PID_OUT,
-                                                     PTD_GET_TOGGLE(ptd) ^ 1);
+                                                     PTD_GET_TOGGLE(ptd));
+                               urb->actual_length += PTD_GET_COUNT(ptd);
                                urb->status = cc_to_error[TD_DATAUNDERRUN];
                                spin_unlock(&urb->lock);
                                continue;
@@ -418,7 +407,7 @@ static void postproc_atl_queue(struct isp116x *isp116x)
                        ep->nextpid = 0;
                        break;
                default:
-                       BUG_ON(1);
+                       BUG();
                }
                spin_unlock(&urb->lock);
        }
@@ -626,21 +615,22 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
                u32 intstat = isp116x_read_reg32(isp116x, HCINTSTAT);
                isp116x_write_reg32(isp116x, HCINTSTAT, intstat);
                if (intstat & HCINT_UE) {
-                       ERR("Unrecoverable error\n");
-                       /* What should we do here? Reset?  */
-               }
-               if (intstat & HCINT_RHSC) {
-                       isp116x->rhstatus =
-                           isp116x_read_reg32(isp116x, HCRHSTATUS);
-                       isp116x->rhport[0] =
-                           isp116x_read_reg32(isp116x, HCRHPORT1);
-                       isp116x->rhport[1] =
-                           isp116x_read_reg32(isp116x, HCRHPORT2);
+                       ERR("Unrecoverable error, HC is dead!\n");
+                       /* IRQ's are off, we do no DMA,
+                          perfectly ready to die ... */
+                       hcd->state = HC_STATE_HALT;
+                       ret = IRQ_HANDLED;
+                       goto done;
                }
+               if (intstat & HCINT_RHSC)
+                       /* When root hub or any of its ports is going
+                          to come out of suspend, it may take more
+                          than 10ms for status bits to stabilize. */
+                       mod_timer(&hcd->rh_timer, jiffies
+                                 + msecs_to_jiffies(20) + 1);
                if (intstat & HCINT_RD) {
                        DBG("---- remote wakeup\n");
-                       schedule_work(&isp116x->rh_resume);
-                       ret = IRQ_HANDLED;
+                       usb_hcd_resume_root_hub(hcd);
                }
                irqstat &= ~HCuPINT_OPR;
                ret = IRQ_HANDLED;
@@ -651,6 +641,7 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd, struct pt_regs *regs)
        }
 
        isp116x_write_reg16(isp116x, HCuPINTENB, isp116x->irqenb);
+      done:
        spin_unlock(&isp116x->lock);
        return ret;
 }
@@ -695,7 +686,7 @@ static int balance(struct isp116x *isp116x, u16 period, u16 load)
 
 static int isp116x_urb_enqueue(struct usb_hcd *hcd,
                               struct usb_host_endpoint *hep, struct urb *urb,
-                              unsigned mem_flags)
+                              gfp_t mem_flags)
 {
        struct isp116x *isp116x = hcd_to_isp116x(hcd);
        struct usb_device *udev = urb->dev;
@@ -724,6 +715,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
 
        spin_lock_irqsave(&isp116x->lock, flags);
        if (!HC_IS_RUNNING(hcd->state)) {
+               kfree(ep);
                ret = -ENODEV;
                goto fail;
        }
@@ -732,7 +724,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
                ep = hep->hcpriv;
        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);
                usb_settoggle(udev, epnum, is_out, 0);
@@ -888,7 +880,7 @@ static void isp116x_endpoint_disable(struct usb_hcd *hcd,
                                     struct usb_host_endpoint *hep)
 {
        int i;
-       struct isp116x_ep *ep = hep->hcpriv;;
+       struct isp116x_ep *ep = hep->hcpriv;
 
        if (!ep)
                return;
@@ -899,7 +891,6 @@ static void isp116x_endpoint_disable(struct usb_hcd *hcd,
        if (!list_empty(&hep->urb_list))
                WARN("ep %p not empty?\n", ep);
 
-       usb_put_dev(ep->udev);
        kfree(ep);
        hep->hcpriv = NULL;
 }
@@ -916,8 +907,6 @@ static int isp116x_get_frame(struct usb_hcd *hcd)
        return (int)fmnum;
 }
 
-/*----------------------------------------------------------------*/
-
 /*
   Adapted from ohci-hub.c. Currently we don't support autosuspend.
 */
@@ -925,20 +914,27 @@ static int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf)
 {
        struct isp116x *isp116x = hcd_to_isp116x(hcd);
        int ports, i, changed = 0;
+       unsigned long flags;
 
        if (!HC_IS_RUNNING(hcd->state))
                return -ESHUTDOWN;
 
-       ports = isp116x->rhdesca & RH_A_NDP;
+       /* Report no status change now, if we are scheduled to be
+          called later */
+       if (timer_pending(&hcd->rh_timer))
+               return 0;
 
-       /* init status */
+       ports = isp116x->rhdesca & RH_A_NDP;
+       spin_lock_irqsave(&isp116x->lock, flags);
+       isp116x->rhstatus = isp116x_read_reg32(isp116x, HCRHSTATUS);
        if (isp116x->rhstatus & (RH_HS_LPSC | RH_HS_OCIC))
                buf[0] = changed = 1;
        else
                buf[0] = 0;
 
        for (i = 0; i < ports; i++) {
-               u32 status = isp116x->rhport[i];
+               u32 status = isp116x->rhport[i] =
+                   isp116x_read_reg32(isp116x, i ? HCRHPORT2 : HCRHPORT1);
 
                if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
                              | RH_PS_OCIC | RH_PS_PRSC)) {
@@ -947,6 +943,7 @@ static int isp116x_hub_status_data(struct usb_hcd *hcd, char *buf)
                        continue;
                }
        }
+       spin_unlock_irqrestore(&isp116x->lock, flags);
        return changed;
 }
 
@@ -960,11 +957,10 @@ static void isp116x_hub_descriptor(struct isp116x *isp116x,
        desc->bHubContrCurrent = 0;
        desc->bNbrPorts = (u8) (reg & 0x3);
        /* Power switching, device type, overcurrent. */
-       desc->wHubCharacteristics =
-           (__force __u16) cpu_to_le16((u16) ((reg >> 8) & 0x1f));
+       desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) & 0x1f));
        desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff);
        /* two bitmaps:  ports removable, and legacy PortPwrCtrlMask */
-       desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
+       desc->bitmap[0] = 0;
        desc->bitmap[1] = ~0;
 }
 
@@ -1151,145 +1147,9 @@ static int isp116x_hub_control(struct usb_hcd *hcd,
        return ret;
 }
 
-#ifdef CONFIG_PM
-
-static int isp116x_hub_suspend(struct usb_hcd *hcd)
-{
-       struct isp116x *isp116x = hcd_to_isp116x(hcd);
-       unsigned long flags;
-       u32 val;
-       int ret = 0;
-
-       spin_lock_irqsave(&isp116x->lock, flags);
-
-       val = isp116x_read_reg32(isp116x, HCCONTROL);
-       switch (val & HCCONTROL_HCFS) {
-       case HCCONTROL_USB_OPER:
-               hcd->state = HC_STATE_QUIESCING;
-               val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE);
-               val |= HCCONTROL_USB_SUSPEND;
-               if (hcd->remote_wakeup)
-                       val |= HCCONTROL_RWE;
-               /* Wait for usb transfers to finish */
-               mdelay(2);
-               isp116x_write_reg32(isp116x, HCCONTROL, val);
-               hcd->state = HC_STATE_SUSPENDED;
-               /* Wait for devices to suspend */
-               mdelay(5);
-       case HCCONTROL_USB_SUSPEND:
-               break;
-       case HCCONTROL_USB_RESUME:
-               isp116x_write_reg32(isp116x, HCCONTROL,
-                                   (val & ~HCCONTROL_HCFS) |
-                                   HCCONTROL_USB_RESET);
-       case HCCONTROL_USB_RESET:
-               ret = -EBUSY;
-               break;
-       default:
-               ret = -EINVAL;
-       }
-
-       spin_unlock_irqrestore(&isp116x->lock, flags);
-       return ret;
-}
-
-static int isp116x_hub_resume(struct usb_hcd *hcd)
-{
-       struct isp116x *isp116x = hcd_to_isp116x(hcd);
-       u32 val;
-       int ret = -EINPROGRESS;
-
-       msleep(5);
-       spin_lock_irq(&isp116x->lock);
-
-       val = isp116x_read_reg32(isp116x, HCCONTROL);
-       switch (val & HCCONTROL_HCFS) {
-       case HCCONTROL_USB_SUSPEND:
-               val &= ~HCCONTROL_HCFS;
-               val |= HCCONTROL_USB_RESUME;
-               isp116x_write_reg32(isp116x, HCCONTROL, val);
-       case HCCONTROL_USB_RESUME:
-               break;
-       case HCCONTROL_USB_OPER:
-               /* Without setting power_state here the
-                  SUSPENDED state won't be removed from
-                  sysfs/usbN/power.state as a response to remote
-                  wakeup. Maybe in the future. */
-               hcd->self.root_hub->dev.power.power_state = PMSG_ON;
-               ret = 0;
-               break;
-       default:
-               ret = -EBUSY;
-       }
-
-       if (ret != -EINPROGRESS) {
-               spin_unlock_irq(&isp116x->lock);
-               return ret;
-       }
-
-       val = isp116x->rhdesca & RH_A_NDP;
-       while (val--) {
-               u32 stat =
-                   isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1);
-               /* force global, not selective, resume */
-               if (!(stat & RH_PS_PSS))
-                       continue;
-               DBG("%s: Resuming port %d\n", __func__, val);
-               isp116x_write_reg32(isp116x, RH_PS_POCI, val
-                                   ? HCRHPORT2 : HCRHPORT1);
-       }
-       spin_unlock_irq(&isp116x->lock);
-
-       hcd->state = HC_STATE_RESUMING;
-       mdelay(20);
-
-       /* Go operational */
-       spin_lock_irq(&isp116x->lock);
-       val = isp116x_read_reg32(isp116x, HCCONTROL);
-       isp116x_write_reg32(isp116x, HCCONTROL,
-                           (val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER);
-       spin_unlock_irq(&isp116x->lock);
-       /* see analogous comment above */
-       hcd->self.root_hub->dev.power.power_state = PMSG_ON;
-       hcd->state = HC_STATE_RUNNING;
-
-       return 0;
-}
-
-static void isp116x_rh_resume(void *_hcd)
-{
-       struct usb_hcd *hcd = _hcd;
-
-       usb_resume_device(hcd->self.root_hub);
-}
-
-#else
-
-#define        isp116x_hub_suspend     NULL
-#define        isp116x_hub_resume      NULL
-
-static void isp116x_rh_resume(void *_hcd)
-{
-}
-
-#endif
-
 /*-----------------------------------------------------------------*/
 
-#ifdef STUB_DEBUG_FILE
-
-static inline void create_debug_file(struct isp116x *isp116x)
-{
-}
-
-static inline void remove_debug_file(struct isp116x *isp116x)
-{
-}
-
-#else
-
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
+#ifdef CONFIG_DEBUG_FS
 
 static void dump_irq(struct seq_file *s, char *label, u16 mask)
 {
@@ -1313,13 +1173,9 @@ static void dump_int(struct seq_file *s, char *label, u32 mask)
                   mask & HCINT_SF ? " sof" : "", mask & HCINT_SO ? " so" : "");
 }
 
-static int proc_isp116x_show(struct seq_file *s, void *unused)
+static int isp116x_show_dbg(struct seq_file *s, void *unused)
 {
        struct isp116x *isp116x = s->private;
-       struct isp116x_ep *ep;
-       struct urb *urb;
-       unsigned i;
-       char *str;
 
        seq_printf(s, "%s\n%s version %s\n",
                   isp116x_to_hcd(isp116x)->product_desc, hcd_name,
@@ -1335,105 +1191,50 @@ static int proc_isp116x_show(struct seq_file *s, void *unused)
        }
 
        spin_lock_irq(&isp116x->lock);
-
        dump_irq(s, "hc_irq_enable", isp116x_read_reg16(isp116x, HCuPINTENB));
        dump_irq(s, "hc_irq_status", isp116x_read_reg16(isp116x, HCuPINT));
        dump_int(s, "hc_int_enable", isp116x_read_reg32(isp116x, HCINTENB));
        dump_int(s, "hc_int_status", isp116x_read_reg32(isp116x, HCINTSTAT));
-
-       list_for_each_entry(ep, &isp116x->async, schedule) {
-
-               switch (ep->nextpid) {
-               case USB_PID_IN:
-                       str = "in";
-                       break;
-               case USB_PID_OUT:
-                       str = "out";
-                       break;
-               case USB_PID_SETUP:
-                       str = "setup";
-                       break;
-               case USB_PID_ACK:
-                       str = "status";
-                       break;
-               default:
-                       str = "?";
-                       break;
-               };
-               seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep,
-                          ep->epnum, str, ep->maxpacket);
-               list_for_each_entry(urb, &ep->hep->urb_list, urb_list) {
-                       seq_printf(s, "  urb%p, %d/%d\n", urb,
-                                  urb->actual_length,
-                                  urb->transfer_buffer_length);
-               }
-       }
-       if (!list_empty(&isp116x->async))
-               seq_printf(s, "\n");
-
-       seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE);
-
-       for (i = 0; i < PERIODIC_SIZE; i++) {
-               ep = isp116x->periodic[i];
-               if (!ep)
-                       continue;
-               seq_printf(s, "%2d [%3d]:\n", i, isp116x->load[i]);
-
-               /* DUMB: prints shared entries multiple times */
-               do {
-                       seq_printf(s, "   %d/%p (%sdev%d ep%d%s max %d)\n",
-                                  ep->period, ep,
-                                  (ep->udev->speed ==
-                                   USB_SPEED_FULL) ? "" : "ls ",
-                                  ep->udev->devnum, ep->epnum,
-                                  (ep->epnum ==
-                                   0) ? "" : ((ep->nextpid ==
-                                               USB_PID_IN) ? "in" : "out"),
-                                  ep->maxpacket);
-                       ep = ep->next;
-               } while (ep);
-       }
+       isp116x_show_regs_seq(isp116x, s);
        spin_unlock_irq(&isp116x->lock);
        seq_printf(s, "\n");
 
        return 0;
 }
 
-static int proc_isp116x_open(struct inode *inode, struct file *file)
+static int isp116x_open_seq(struct inode *inode, struct file *file)
 {
-       return single_open(file, proc_isp116x_show, PDE(inode)->data);
+       return single_open(file, isp116x_show_dbg, inode->u.generic_ip);
 }
 
-static struct file_operations proc_ops = {
-       .open = proc_isp116x_open,
+static struct file_operations isp116x_debug_fops = {
+       .open = isp116x_open_seq,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
 };
 
-/* expect just one isp116x per system */
-static const char proc_filename[] = "driver/isp116x";
-
-static void create_debug_file(struct isp116x *isp116x)
+static int create_debug_file(struct isp116x *isp116x)
 {
-       struct proc_dir_entry *pde;
-
-       pde = create_proc_entry(proc_filename, 0, NULL);
-       if (pde == NULL)
-               return;
-
-       pde->proc_fops = &proc_ops;
-       pde->data = isp116x;
-       isp116x->pde = pde;
+       isp116x->dentry = debugfs_create_file(hcd_name,
+                                             S_IRUGO, NULL, isp116x,
+                                             &isp116x_debug_fops);
+       if (!isp116x->dentry)
+               return -ENOMEM;
+       return 0;
 }
 
 static void remove_debug_file(struct isp116x *isp116x)
 {
-       if (isp116x->pde)
-               remove_proc_entry(proc_filename, NULL);
+       debugfs_remove(isp116x->dentry);
 }
 
-#endif
+#else
+
+#define        create_debug_file(d)    0
+#define        remove_debug_file(d)    do{}while(0)
+
+#endif                         /* CONFIG_DEBUG_FS */
 
 /*-----------------------------------------------------------------*/
 
@@ -1463,28 +1264,14 @@ static int isp116x_sw_reset(struct isp116x *isp116x)
        return ret;
 }
 
-/*
-  Reset. Tries to perform platform-specific hardware
-  reset first; falls back to software reset.
-*/
 static int isp116x_reset(struct usb_hcd *hcd)
 {
        struct isp116x *isp116x = hcd_to_isp116x(hcd);
        unsigned long t;
        u16 clkrdy = 0;
-       int ret = 0, timeout = 15 /* ms */ ;
-
-       if (isp116x->board && isp116x->board->reset) {
-               /* Hardware reset */
-               isp116x->board->reset(hcd->self.controller, 1);
-               msleep(10);
-               if (isp116x->board->clock)
-                       isp116x->board->clock(hcd->self.controller, 1);
-               msleep(1);
-               isp116x->board->reset(hcd->self.controller, 0);
-       } else
-               ret = isp116x_sw_reset(isp116x);
+       int ret, timeout = 15 /* ms */ ;
 
+       ret = isp116x_sw_reset(isp116x);
        if (ret)
                return ret;
 
@@ -1498,13 +1285,10 @@ static int isp116x_reset(struct usb_hcd *hcd)
                        break;
        }
        if (!clkrdy) {
-               ERR("Clock not ready after 20ms\n");
+               ERR("Clock not ready after %dms\n", timeout);
                /* After sw_reset the clock won't report to be ready, if
                   H_WAKEUP pin is high. */
-               if (!isp116x->board || !isp116x->board->reset)
-                       ERR("The driver does not support hardware wakeup.\n");
-                       ERR("Please make sure that the H_WAKEUP pin "
-                               "is pulled low!\n");
+               ERR("Please make sure that the H_WAKEUP pin is pulled low!\n");
                ret = -ENODEV;
        }
        return ret;
@@ -1527,15 +1311,7 @@ static void isp116x_stop(struct usb_hcd *hcd)
        isp116x_write_reg32(isp116x, HCRHSTATUS, RH_HS_LPS);
        spin_unlock_irqrestore(&isp116x->lock, flags);
 
-       /* Put the chip into reset state */
-       if (isp116x->board && isp116x->board->reset)
-               isp116x->board->reset(hcd->self.controller, 0);
-       else
-               isp116x_sw_reset(isp116x);
-
-       /* Stop the clock */
-       if (isp116x->board && isp116x->board->clock)
-               isp116x->board->clock(hcd->self.controller, 0);
+       isp116x_sw_reset(isp116x);
 }
 
 /*
@@ -1561,6 +1337,9 @@ static int isp116x_start(struct usb_hcd *hcd)
                return -ENODEV;
        }
 
+       /* To be removed in future */
+       hcd->uses_new_polling = 1;
+
        isp116x_write_reg16(isp116x, HCITLBUFLEN, ISP116x_ITL_BUFSIZE);
        isp116x_write_reg16(isp116x, HCATLBUFLEN, ISP116x_ATL_BUFSIZE);
 
@@ -1569,7 +1348,7 @@ static int isp116x_start(struct usb_hcd *hcd)
        if (board->sel15Kres)
                val |= HCHWCFG_15KRSEL;
        /* Remote wakeup won't work without working clock */
-       if (board->clknotstop || board->remote_wakeup_enable)
+       if (board->remote_wakeup_enable)
                val |= HCHWCFG_CLKNOTSTOP;
        if (board->oc_enable)
                val |= HCHWCFG_ANALOG_OC;
@@ -1580,16 +1359,13 @@ static int isp116x_start(struct usb_hcd *hcd)
        isp116x_write_reg16(isp116x, HCHWCFG, val);
 
        /* ----- Root hub conf */
-       val = 0;
-       /* AN10003_1.pdf recommends NPS to be always 1 */
-       if (board->no_power_switching)
-               val |= RH_A_NPS;
-       if (board->power_switching_mode)
-               val |= RH_A_PSM;
-       if (board->potpg)
-               val |= (board->potpg << 24) & RH_A_POTPGT;
-       else
-               val |= (25 << 24) & RH_A_POTPGT;
+       val = (25 << 24) & RH_A_POTPGT;
+       /* AN10003_1.pdf recommends RH_A_NPS (no power switching) to
+          be always set. Yet, instead, we request individual port
+          power switching. */
+       val |= RH_A_PSM;
+       /* Report overcurrent per port */
+       val |= RH_A_OCPM;
        isp116x_write_reg32(isp116x, HCRHDESCA, val);
        isp116x->rhdesca = isp116x_read_reg32(isp116x, HCRHDESCA);
 
@@ -1599,7 +1375,8 @@ static int isp116x_start(struct usb_hcd *hcd)
 
        val = 0;
        if (board->remote_wakeup_enable) {
-               hcd->can_wakeup = 1;
+               if (!device_can_wakeup(hcd->self.controller))
+                       device_init_wakeup(hcd->self.controller, 1);
                val |= RH_HS_DRWE;
        }
        isp116x_write_reg32(isp116x, HCRHSTATUS, val);
@@ -1619,9 +1396,6 @@ static int isp116x_start(struct usb_hcd *hcd)
 
        /* Go operational */
        val = HCCONTROL_USB_OPER;
-       /* Remote wakeup connected - NOT SUPPORTED */
-       /*  if (board->remote_wakeup_connected)
-          val |= HCCONTROL_RWC;  */
        if (board->remote_wakeup_enable)
                val |= HCCONTROL_RWE;
        isp116x_write_reg32(isp116x, HCCONTROL, val);
@@ -1630,12 +1404,127 @@ static int isp116x_start(struct usb_hcd *hcd)
        isp116x_write_reg32(isp116x, HCRHPORT1, RH_PS_CCS);
        isp116x_write_reg32(isp116x, HCRHPORT2, RH_PS_CCS);
 
-       isp116x_show_regs(isp116x);
+       isp116x_show_regs_log(isp116x);
        spin_unlock_irqrestore(&isp116x->lock, flags);
        return 0;
 }
 
-/*-----------------------------------------------------------------*/
+#ifdef CONFIG_PM
+
+static int isp116x_bus_suspend(struct usb_hcd *hcd)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       unsigned long flags;
+       u32 val;
+       int ret = 0;
+
+       spin_lock_irqsave(&isp116x->lock, flags);
+       val = isp116x_read_reg32(isp116x, HCCONTROL);
+
+       switch (val & HCCONTROL_HCFS) {
+       case HCCONTROL_USB_OPER:
+               spin_unlock_irqrestore(&isp116x->lock, flags);
+               val &= (~HCCONTROL_HCFS & ~HCCONTROL_RWE);
+               val |= HCCONTROL_USB_SUSPEND;
+               if (device_may_wakeup(&hcd->self.root_hub->dev))
+                       val |= HCCONTROL_RWE;
+               /* Wait for usb transfers to finish */
+               msleep(2);
+               spin_lock_irqsave(&isp116x->lock, flags);
+               isp116x_write_reg32(isp116x, HCCONTROL, val);
+               spin_unlock_irqrestore(&isp116x->lock, flags);
+               /* Wait for devices to suspend */
+               msleep(5);
+               break;
+       case HCCONTROL_USB_RESUME:
+               isp116x_write_reg32(isp116x, HCCONTROL,
+                                   (val & ~HCCONTROL_HCFS) |
+                                   HCCONTROL_USB_RESET);
+       case HCCONTROL_USB_RESET:
+               ret = -EBUSY;
+       default:                /* HCCONTROL_USB_SUSPEND */
+               spin_unlock_irqrestore(&isp116x->lock, flags);
+               break;
+       }
+
+       return ret;
+}
+
+static int isp116x_bus_resume(struct usb_hcd *hcd)
+{
+       struct isp116x *isp116x = hcd_to_isp116x(hcd);
+       u32 val;
+
+       msleep(5);
+       spin_lock_irq(&isp116x->lock);
+
+       val = isp116x_read_reg32(isp116x, HCCONTROL);
+       switch (val & HCCONTROL_HCFS) {
+       case HCCONTROL_USB_SUSPEND:
+               val &= ~HCCONTROL_HCFS;
+               val |= HCCONTROL_USB_RESUME;
+               isp116x_write_reg32(isp116x, HCCONTROL, val);
+       case HCCONTROL_USB_RESUME:
+               break;
+       case HCCONTROL_USB_OPER:
+               spin_unlock_irq(&isp116x->lock);
+               /* Without setting power_state here the
+                  SUSPENDED state won't be removed from
+                  sysfs/usbN/power.state as a response to remote
+                  wakeup. Maybe in the future. */
+               hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+               return 0;
+       default:
+               /* HCCONTROL_USB_RESET: this may happen, when during
+                  suspension the HC lost power. Reinitialize completely */
+               spin_unlock_irq(&isp116x->lock);
+               DBG("Chip has been reset while suspended. Reinit from scratch.\n");
+               isp116x_reset(hcd);
+               isp116x_start(hcd);
+               isp116x_hub_control(hcd, SetPortFeature,
+                                   USB_PORT_FEAT_POWER, 1, NULL, 0);
+               if ((isp116x->rhdesca & RH_A_NDP) == 2)
+                       isp116x_hub_control(hcd, SetPortFeature,
+                                           USB_PORT_FEAT_POWER, 2, NULL, 0);
+               hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+               return 0;
+       }
+
+       val = isp116x->rhdesca & RH_A_NDP;
+       while (val--) {
+               u32 stat =
+                   isp116x_read_reg32(isp116x, val ? HCRHPORT2 : HCRHPORT1);
+               /* force global, not selective, resume */
+               if (!(stat & RH_PS_PSS))
+                       continue;
+               DBG("%s: Resuming port %d\n", __func__, val);
+               isp116x_write_reg32(isp116x, RH_PS_POCI, val
+                                   ? HCRHPORT2 : HCRHPORT1);
+       }
+       spin_unlock_irq(&isp116x->lock);
+
+       hcd->state = HC_STATE_RESUMING;
+       msleep(20);
+
+       /* Go operational */
+       spin_lock_irq(&isp116x->lock);
+       val = isp116x_read_reg32(isp116x, HCCONTROL);
+       isp116x_write_reg32(isp116x, HCCONTROL,
+                           (val & ~HCCONTROL_HCFS) | HCCONTROL_USB_OPER);
+       spin_unlock_irq(&isp116x->lock);
+       /* see analogous comment above */
+       hcd->self.root_hub->dev.power.power_state = PMSG_ON;
+       hcd->state = HC_STATE_RUNNING;
+
+       return 0;
+}
+
+#else
+
+#define        isp116x_bus_suspend     NULL
+#define        isp116x_bus_resume      NULL
+
+#endif
 
 static struct hc_driver isp116x_hc_driver = {
        .description = hcd_name,
@@ -1657,23 +1546,21 @@ static struct hc_driver isp116x_hc_driver = {
 
        .hub_status_data = isp116x_hub_status_data,
        .hub_control = isp116x_hub_control,
-       .hub_suspend = isp116x_hub_suspend,
-       .hub_resume = isp116x_hub_resume,
+       .bus_suspend = isp116x_bus_suspend,
+       .bus_resume = isp116x_bus_resume,
 };
 
 /*----------------------------------------------------------------*/
 
-static int __init_or_module isp116x_remove(struct device *dev)
+static int isp116x_remove(struct platform_device *pdev)
 {
-       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
        struct isp116x *isp116x;
-       struct platform_device *pdev;
        struct resource *res;
 
-       if(!hcd)
+       if (!hcd)
                return 0;
        isp116x = hcd_to_isp116x(hcd);
-       pdev = container_of(dev, struct platform_device, dev);
        remove_debug_file(isp116x);
        usb_remove_hcd(hcd);
 
@@ -1690,18 +1577,16 @@ static int __init_or_module isp116x_remove(struct device *dev)
 
 #define resource_len(r) (((r)->end - (r)->start) + 1)
 
-static int __init isp116x_probe(struct device *dev)
+static int __init isp116x_probe(struct platform_device *pdev)
 {
        struct usb_hcd *hcd;
        struct isp116x *isp116x;
-       struct platform_device *pdev;
        struct resource *addr, *data;
        void __iomem *addr_reg;
        void __iomem *data_reg;
        int irq;
        int ret = 0;
 
-       pdev = container_of(dev, struct platform_device, dev);
        if (pdev->num_resources < 3) {
                ret = -ENODEV;
                goto err1;
@@ -1715,7 +1600,7 @@ static int __init isp116x_probe(struct device *dev)
                goto err1;
        }
 
-       if (dev->dma_mask) {
+       if (pdev->dev.dma_mask) {
                DBG("DMA not supported\n");
                ret = -EINVAL;
                goto err1;
@@ -1741,7 +1626,7 @@ static int __init isp116x_probe(struct device *dev)
        }
 
        /* allocate and initialize hcd */
-       hcd = usb_create_hcd(&isp116x_hc_driver, dev, dev->bus_id);
+       hcd = usb_create_hcd(&isp116x_hc_driver, &pdev->dev, pdev->dev.bus_id);
        if (!hcd) {
                ret = -ENOMEM;
                goto err5;
@@ -1753,8 +1638,7 @@ static int __init isp116x_probe(struct device *dev)
        isp116x->addr_reg = addr_reg;
        spin_lock_init(&isp116x->lock);
        INIT_LIST_HEAD(&isp116x->async);
-       INIT_WORK(&isp116x->rh_resume, isp116x_rh_resume, hcd);
-       isp116x->board = dev->platform_data;
+       isp116x->board = pdev->dev.platform_data;
 
        if (!isp116x->board) {
                ERR("Platform data structure not initialized\n");
@@ -1770,12 +1654,19 @@ static int __init isp116x_probe(struct device *dev)
        }
 
        ret = usb_add_hcd(hcd, irq, SA_INTERRUPT);
-       if (ret != 0)
+       if (ret)
                goto err6;
 
-       create_debug_file(isp116x);
+       ret = create_debug_file(isp116x);
+       if (ret) {
+               ERR("Couldn't create debugfs entry\n");
+               goto err7;
+       }
+
        return 0;
 
+      err7:
+       usb_remove_hcd(hcd);
       err6:
        usb_put_hcd(hcd);
       err5:
@@ -1795,45 +1686,21 @@ static int __init isp116x_probe(struct device *dev)
 /*
   Suspend of platform device
 */
-static int isp116x_suspend(struct device *dev, pm_message_t state, u32 phase)
+static int isp116x_suspend(struct platform_device *dev, pm_message_t state)
 {
-       int ret = 0;
-       struct usb_hcd *hcd = dev_get_drvdata(dev);
-
-       VDBG("%s: state %x, phase %x\n", __func__, state, phase);
-
-       if (phase != SUSPEND_DISABLE && phase != SUSPEND_POWER_DOWN)
-               return 0;
-
-       ret = usb_suspend_device(hcd->self.root_hub, state);
-       if (!ret) {
-               dev->power.power_state = state;
-               INFO("%s suspended\n", hcd_name);
-       } else
-               ERR("%s suspend failed\n", hcd_name);
-
-       return ret;
+       VDBG("%s: state %x\n", __func__, state.event);
+       dev->dev.power.power_state = state;
+       return 0;
 }
 
 /*
   Resume platform device
 */
-static int isp116x_resume(struct device *dev, u32 phase)
+static int isp116x_resume(struct platform_device *dev)
 {
-       int ret = 0;
-       struct usb_hcd *hcd = dev_get_drvdata(dev);
-
-       VDBG("%s:  state %x, phase %x\n", __func__, dev->power.power_state,
-            phase);
-       if (phase != RESUME_POWER_ON)
-               return 0;
-
-       ret = usb_resume_device(hcd->self.root_hub);
-       if (!ret) {
-               dev->power.power_state = PMSG_ON;
-               VDBG("%s resumed\n", (char *)hcd_name);
-       }
-       return ret;
+       VDBG("%s:  state %x\n", __func__, dev->power.power_state.event);
+       dev->dev.power.power_state = PMSG_ON;
+       return 0;
 }
 
 #else
@@ -1843,13 +1710,14 @@ static int isp116x_resume(struct device *dev, u32 phase)
 
 #endif
 
-static struct device_driver isp116x_driver = {
-       .name = (char *)hcd_name,
-       .bus = &platform_bus_type,
+static struct platform_driver isp116x_driver = {
        .probe = isp116x_probe,
        .remove = isp116x_remove,
        .suspend = isp116x_suspend,
        .resume = isp116x_resume,
+       .driver = {
+                  .name = (char *)hcd_name,
+                  },
 };
 
 /*-----------------------------------------------------------------*/
@@ -1860,14 +1728,14 @@ static int __init isp116x_init(void)
                return -ENODEV;
 
        INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION);
-       return driver_register(&isp116x_driver);
+       return platform_driver_register(&isp116x_driver);
 }
 
 module_init(isp116x_init);
 
 static void __exit isp116x_cleanup(void)
 {
-       driver_unregister(&isp116x_driver);
+       platform_driver_unregister(&isp116x_driver);
 }
 
 module_exit(isp116x_cleanup);