[adm5120] USB driver fixes
[openwrt/svn-archive/archive.git] / target / linux / adm5120-2.6 / files / drivers / usb / host / adm5120-hcd.c
index b4a1899d1d2c37a3ce022bc2eac681b99f096ee7..6e005bffad265a459862307f760157a712a655fe 100644 (file)
@@ -3,72 +3,94 @@
  *
  *     Copyright (C) 2005 Jeroen Vreeken (pe1rxq@amsat.org)
  *
- *     Based on the ADMtek 2.4 driver 
+ *     Based on the ADMtek 2.4 driver
  *     (C) Copyright 2003 Junius Chen <juniusc@admtek.com.tw>
  *     Which again was based on the ohci and uhci drivers.
  */
 
-#include <linux/autoconf.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
 #include <linux/errno.h>
-#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/list.h>
 #include <linux/usb.h>
 #include <linux/platform_device.h>
 
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/mach-adm5120/adm5120_info.h>
+
 #include "../core/hcd.h"
 
 MODULE_DESCRIPTION("ADM5120 USB Host Controller Driver");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Jeroen Vreeken (pe1rxq@amsat.org)");
 
+#define PFX    "adm5120-hcd: "
+
 #define ADMHCD_REG_CONTROL             0x00
+#define  ADMHCD_SW_RESET               0x00000008      /* Reset */
+#define  ADMHCD_DMAA                   0x00000004      /* DMA arbitration control */
+#define  ADMHCD_SW_INTREQ              0x00000002      /* request software int */
+#define  ADMHCD_HOST_EN                        0x00000001      /* Host enable */
 #define ADMHCD_REG_INTSTATUS           0x04
+#define  ADMHCD_INT_ACT                        0x80000000      /* Interrupt active */
+#define  ADMHCD_INT_FATAL              0x40000000      /* Fatal interrupt */
+#define  ADMHCD_INT_SW                 0x20000000      /* software interrupt */
+#define  ADMHCD_INT_TD                 0x00100000      /* TD completed */
+#define  ADMHCD_FNO                    0x00000800      /* Frame number overaflow */
+#define  ADMHCD_SO                     0x00000400      /* Scheduling overrun */
+#define  ADMHCD_INSMI                  0x00000200      /* Root hub status change */
+#define  ADMHCD_BABI                   0x00000100      /* Babble detected, host mode */
+#define  ADMHCD_RESI                   0x00000020      /* Resume detected */
+#define  ADMHCD_SOFI                   0x00000010      /* SOF transmitted/received, host mode */
 #define ADMHCD_REG_INTENABLE           0x08
+#define  ADMHCD_INT_EN                 0x80000000      /* Interrupt enable */
+#define  ADMHCD_INTMASK                        0x00000001      /* Interrupt mask */
 #define ADMHCD_REG_HOSTCONTROL         0x10
+#define  ADMHCD_DMA_EN                 0x00000004      /* USB host DMA enable */
+#define  ADMHCD_STATE_RST              0x00000000      /* bus state reset */
+#define  ADMHCD_STATE_RES              0x00000001      /* bus state resume */
+#define  ADMHCD_STATE_OP               0x00000002      /* bus state operational */
+#define  ADMHCD_STATE_SUS              0x00000003      /* bus state suspended */
 #define ADMHCD_REG_FMINTERVAL          0x18
 #define ADMHCD_REG_FMNUMBER            0x1c
 #define ADMHCD_REG_LSTHRESH            0x70
 #define ADMHCD_REG_RHDESCR             0x74
+#define  ADMHCD_CRWE                   0x20000000      /* Clear wakeup enable */
+#define  ADMHCD_DRWE                   0x10000000      /* Device remote wakeup enable */
+#define  ADMHCD_HW_OCIC                        0x08000000      /* Over current indication change */
+#define  ADMHCD_LPSC                   0x04000000      /* Local power switch change */
+#define  ADMHCD_OCI                    0x02000000      /* Over current indication */
+#define  ADMHCD_LPS                    0x01000000      /* Local power switch/global power switch */
+#define  ADMHCD_NOCP                   0x00000800      /* No over current protect mode */
+#define  ADMHCD_OPCM                   0x00000400      /* Over current protect mode */
+#define  ADMHCD_NPS                    0x00000200      /* No Power Switch */
+#define  ADMHCD_PSM                    0x00000100      /* Power switch mode */
 #define ADMHCD_REG_PORTSTATUS0         0x78
+#define  ADMHCD_CCS                    0x00000001      /* current connect status */
+#define  ADMHCD_PES                    0x00000002      /* port enable status */
+#define  ADMHCD_PSS                    0x00000004      /* port suspend status */
+#define  ADMHCD_POCI                   0x00000008      /* port overcurrent indicator */
+#define  ADMHCD_PRS                    0x00000010      /* port reset status */
+#define  ADMHCD_PPS                    0x00000100      /* port power status */
+#define  ADMHCD_LSDA                   0x00000200      /* low speed device attached */
+#define  ADMHCD_CSC                    0x00010000      /* connect status change */
+#define  ADMHCD_PESC                   0x00020000      /* enable status change */
+#define  ADMHCD_PSSC                   0x00040000      /* suspend status change */
+#define  ADMHCD_OCIC                   0x00080000      /* overcurrent change*/
+#define  ADMHCD_PRSC                   0x00100000      /* reset status change */
 #define ADMHCD_REG_PORTSTATUS1         0x7c
 #define ADMHCD_REG_HOSTHEAD            0x80
 
 
 #define ADMHCD_NUMPORTS                2
 
-#define ADMHCD_HOST_EN         0x00000001      /* Host enable */
-#define ADMHCD_SW_INTREQ       0x00000002      /* request software int */
-#define ADMHCD_SW_RESET                0x00000008      /* Reset */
-
-#define ADMHCD_INT_TD          0x00100000      /* TD completed */
-#define ADMHCD_INT_SW          0x20000000      /* software interrupt */
-#define ADMHCD_INT_FATAL       0x40000000      /* Fatal interrupt */
-#define ADMHCD_INT_ACT         0x80000000      /* Interrupt active */
-
-#define ADMHCD_STATE_RST       0x00000000      /* bus state reset */
-#define ADMHCD_STATE_RES       0x00000001      /* bus state resume */
-#define ADMHCD_STATE_OP                0x00000002      /* bus state operational */
-#define ADMHCD_STATE_SUS       0x00000003      /* bus state suspended */
-#define ADMHCD_DMA_EN          0x00000004      /* enable dma engine */
-
-#define ADMHCD_NPS             0x00000020      /* No Power Switch */
-#define ADMHCD_LPSC            0x04000000      /* Local power switch change */
-
-#define ADMHCD_CCS             0x00000001      /* current connect status */
-#define ADMHCD_PES             0x00000002      /* port enable status */
-#define ADMHCD_PSS             0x00000004      /* port suspend status */
-#define ADMHCD_POCI            0x00000008      /* port overcurrent indicator */
-#define ADMHCD_PRS             0x00000010      /* port reset status */
-#define ADMHCD_PPS             0x00000100      /* port power status */
-#define ADMHCD_LSDA            0x00000200      /* low speed device attached */
-#define ADMHCD_CSC             0x00010000      /* connect status change */
-#define ADMHCD_PESC            0x00020000      /* enable status change */
-#define ADMHCD_PSSC            0x00040000      /* suspend status change */
-#define ADMHCD_OCIC            0x00080000      /* overcurrent change*/
-#define ADMHCD_PRSC            0x00100000      /* reset status change */
-
-
 struct admhcd_ed {
        /* Don't change first four, they used for DMA */
        u32                             control;
@@ -137,13 +159,32 @@ static int admhcd_td_err[16] = {
 #define ED(ed) ((struct admhcd_ed *)(((u32)(ed)) & ~0xf))
 
 struct admhcd {
+       spinlock_t      lock;
+
+       void __iomem *data_reg;
+       /* Root hub registers */
+       u32 rhdesca;
+       u32 rhdescb;
+       u32 rhstatus;
+       u32 rhport[2];
+
+       /* async schedule: control, bulk */
+       struct list_head async;
        u32             base;
        u32             dma_en;
-       spinlock_t      lock;
        unsigned long   flags;
+
 };
 
-#define hcd_to_admhcd(hcd) ((struct admhcd *)(hcd)->hcd_priv)
+static inline struct admhcd *hcd_to_admhcd(struct usb_hcd *hcd)
+{
+       return (struct admhcd *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *admhcd_to_hcd(struct admhcd *admhcd)
+{
+       return container_of((void *)admhcd, struct usb_hcd, hcd_priv);
+}
 
 static char hcd_name[] = "adm5120-hcd";
 
@@ -185,7 +226,7 @@ static struct admhcd_td *admhcd_td_alloc(struct admhcd_ed *ed, struct urb *urb)
        if (ed->cur == NULL) {
                ed->cur = tdn;
                ed->head = tdn;
-               ed->tail = tdn; 
+               ed->tail = tdn;
                td = tdn;
        } else {
                /* Supply back the old tail and link in new td as tail */
@@ -300,9 +341,8 @@ static void admhcd_ed_start(struct admhcd *ahcd, struct admhcd_ed *ed)
        ahcd->dma_en |= ADMHCD_DMA_EN;
 }
 
-static irqreturn_t adm5120hcd_irq(int irq, void *ptr, struct pt_regs *regs)
+static irqreturn_t adm5120hcd_irq(struct usb_hcd *hcd)
 {
-       struct usb_hcd *hcd = (struct usb_hcd *)ptr;
        struct admhcd *ahcd = hcd_to_admhcd(hcd);
        u32 intstatus;
 
@@ -317,7 +357,7 @@ static irqreturn_t adm5120hcd_irq(int irq, void *ptr, struct pt_regs *regs)
        }
        if (intstatus & ADMHCD_INT_TD) {
                struct admhcd_ed *ed, *head;
-               
+
                admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS, ADMHCD_INT_TD);
 
                head = (struct admhcd_ed *)admhcd_reg_get(ahcd, ADMHCD_REG_HOSTHEAD);
@@ -327,7 +367,7 @@ static irqreturn_t adm5120hcd_irq(int irq, void *ptr, struct pt_regs *regs)
                        if (ed->urb && !(ed->cur->control & ADMHCD_TD_OWN)) {
                                struct admhcd_td *td;
                                int error;
-                               
+
                                td = ed->cur;
                                error = (td->control & ADMHCD_TD_ERRMASK) >>
                                    ADMHCD_TD_ERRSHIFT;
@@ -348,7 +388,7 @@ static irqreturn_t adm5120hcd_irq(int irq, void *ptr, struct pt_regs *regs)
 }
 
 static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep,
-    struct urb *urb, int mem_flags)
+    struct urb *urb, gfp_t mem_flags)
 {
        struct admhcd *ahcd = hcd_to_admhcd(hcd);
        struct admhcd_ed *ed;
@@ -414,7 +454,7 @@ static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep,
                        td = admhcd_td_fill(ADMHCD_TD_SETUP | ADMHCD_TD_DATA0,
                            td, (dma_addr_t)urb->setup_packet, 8);
                        while (data_len > 0) {
-                               td = admhcd_td_fill(ADMHCD_TD_DATA1 
+                               td = admhcd_td_fill(ADMHCD_TD_DATA1
                                    | ADMHCD_TD_R |
                                    (usb_pipeout(pipe) ?
                                    ADMHCD_TD_OUT : ADMHCD_TD_IN), td,
@@ -431,7 +471,7 @@ static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep,
                        i = 0;
                        while(data_len > 4096) {
                                td = admhcd_td_fill((usb_pipeout(pipe) ?
-                                   ADMHCD_TD_OUT : 
+                                   ADMHCD_TD_OUT :
                                    ADMHCD_TD_IN | ADMHCD_TD_R) |
                                    (i ? ADMHCD_TD_TOGGLE : toggle), td,
                                    data, 4096);
@@ -439,7 +479,7 @@ static int admhcd_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *ep,
                                data_len -= 4096;
                                i++;
                        }
-                       td = admhcd_td_fill((usb_pipeout(pipe) ? 
+                       td = admhcd_td_fill((usb_pipeout(pipe) ?
                            ADMHCD_TD_OUT : ADMHCD_TD_IN) |
                            (i ? ADMHCD_TD_TOGGLE : toggle), td, data, data_len);
                        i++;
@@ -588,7 +628,7 @@ static int admhcd_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                case USB_PORT_FEAT_RESET:
                        if (admhcd_reg_get(ahcd, ADMHCD_REG_PORTSTATUS0 + port*4)
                            & ADMHCD_CCS) {
-                               admhcd_reg_set(ahcd, 
+                               admhcd_reg_set(ahcd,
                                    ADMHCD_REG_PORTSTATUS0 + port*4,
                                    ADMHCD_PRS | ADMHCD_CSC);
                                mdelay(50);
@@ -657,10 +697,136 @@ err:
        return retval;
 }
 
+static int admhcd_start(struct usb_hcd *hcd)
+{
+       struct admhcd *ahcd = hcd_to_admhcd(hcd);
+       unsigned long flags;
+
+       printk(KERN_DEBUG PFX "calling admhcd_start\n");
+
+       spin_lock_irqsave(&ahcd->lock, flags);
+
+       /* Initialise the HCD registers */
+        admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0);
+        mdelay(10);
+
+        admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_SW_RESET);
+
+        while (admhcd_reg_get(ahcd, ADMHCD_REG_CONTROL) & ADMHCD_SW_RESET) {
+               printk(KERN_WARNING PFX "waiting for reset to complete\n");
+                mdelay(1);
+       }
+
+       hcd->uses_new_polling = 1;
+
+       /* Enable USB host mode */
+        admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_HOST_EN);
+
+       /* Set host specific settings */
+        admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, 0x00000000);
+        admhcd_reg_set(ahcd, ADMHCD_REG_FMINTERVAL, 0x20002edf);
+        admhcd_reg_set(ahcd, ADMHCD_REG_LSTHRESH, 0x628);
+
+       /* Set interrupts */
+        admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE,
+            ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD);
+        admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS,
+            ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD);
+
+       /* Power on all ports */
+        admhcd_reg_set(ahcd, ADMHCD_REG_RHDESCR, ADMHCD_NPS | ADMHCD_LPSC);
+
+       /* HCD is now operationnal */
+        admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, ADMHCD_STATE_OP);
+
+               hcd->state = HC_STATE_RUNNING;
+
+       spin_unlock_irqrestore(&ahcd->lock, flags);
+
+       printk(KERN_DEBUG PFX "returning 0 from admhcd_start\n");
+               return 0;
+}
+
+static int admhcd_sw_reset(struct admhcd *ahcd)
+{
+       int retries = 15;
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&ahcd->lock, flags);
+
+       admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0);
+        mdelay(10);
+
+        admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_SW_RESET);
+
+        while (--retries) {
+               mdelay(1);
+               if (!(admhcd_reg_get(ahcd, ADMHCD_REG_CONTROL) & ADMHCD_SW_RESET))
+                       break;
+       }
+       if (!retries) {
+               printk(KERN_WARNING "%s: software reset timeout\n", hcd_name);
+               ret = -ETIME;
+       }
+       spin_unlock_irqrestore(&ahcd->lock, flags);
+       return ret;
+}
+
+static int admhcd_reset(struct usb_hcd *hcd)
+{
+       struct admhcd *ahcd = hcd_to_admhcd(hcd);
+        u32 val = 0;
+       int ret, timeout = 15; /* ms */
+       unsigned long t;
+
+       ret = admhcd_sw_reset(ahcd);
+       if (ret)
+               return ret;
+
+       t = jiffies + msecs_to_jiffies(timeout);
+        while (time_before_eq(jiffies, t)) {
+                msleep(4);
+                spin_lock_irq(&ahcd->lock);
+                val = admhcd_reg_get(ahcd, ADMHCD_REG_HOSTCONTROL) & ADMHCD_STATE_RST;
+                spin_unlock_irq(&ahcd->lock);
+                if (val)
+                        break;
+        }
+        if (!val) {
+                printk(KERN_WARNING "%s: device not ready after %dms\n",
+                       hcd_name, timeout);
+                ret = -ENODEV;
+        }
+        return ret;
+}
+
+static void admhcd_stop(struct usb_hcd *hcd)
+{
+       struct admhcd *ahcd = hcd_to_admhcd(hcd);
+        unsigned long flags;
+       u32 val;
+
+        spin_lock_irqsave(&ahcd->lock, flags);
+        admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0);
+
+       /* Set global control of power for ports */
+       val = admhcd_reg_get(ahcd, ADMHCD_REG_RHDESCR);
+       val &= (~ADMHCD_PSM | ADMHCD_LPS);
+       admhcd_reg_set(ahcd, ADMHCD_REG_RHDESCR, val);
+
+       spin_unlock_irqrestore(&ahcd->lock, flags);
+
+       /* Ask for software reset */
+       admhcd_sw_reset(ahcd);
+}
+
+
 static struct hc_driver adm5120_hc_driver = {
        .description =          hcd_name,
        .product_desc =         "ADM5120 HCD",
        .hcd_priv_size =        sizeof(struct admhcd),
+       .irq =                  adm5120hcd_irq,
        .flags =                HCD_USB11,
        .urb_enqueue =          admhcd_urb_enqueue,
        .urb_dequeue =          admhcd_urb_dequeue,
@@ -668,122 +834,162 @@ static struct hc_driver adm5120_hc_driver = {
        .get_frame_number =     admhcd_get_frame_number,
        .hub_status_data =      admhcd_hub_status_data,
        .hub_control =          admhcd_hub_control,
+       .start  =               admhcd_start,
+       .stop   =               admhcd_stop,
+       .reset =                admhcd_reset,
 };
 
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+
 static int __init adm5120hcd_probe(struct platform_device *pdev)
 {
-       struct usb_hcd *hcd;
-       struct admhcd *ahcd;
-       struct usb_device *udev;
-       int err = 0;
+        struct usb_hcd *hcd;
+        struct admhcd *ahcd;
+       struct resource *data;
+       void __iomem *data_reg;
+
+        int err = 0, irq;
 
-       if (!request_mem_region(pdev->resource[0].start,
-           pdev->resource[0].end - pdev->resource[0].start, hcd_name)) {
-               pr_debug("couldn't request mem\n");
-               err = -EBUSY;
+       if (pdev->num_resources < 2) {
+               printk(KERN_WARNING PFX "not enough resources\n");
+               err = -ENODEV;
                goto out;
+        }
+
+       irq = platform_get_irq(pdev, 0);
+       data = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       if (pdev->dev.dma_mask) {
+                printk(KERN_DEBUG PFX "no we won't dma\n");
+                return -EINVAL;
+        }
+
+       if (!data || irq < 0) {
+               printk(KERN_DEBUG PFX "either IRQ or data resource is invalid\n");
+                err = -ENODEV;
+                goto out;
+        }
+
+        if (!request_mem_region(data->start, resource_len(data), hcd_name)) {
+               printk(KERN_DEBUG PFX "cannot request memory regions for the data resource\n");
+                err = -EBUSY;
+                goto out;
+        }
+
+        data_reg = ioremap(data->start, resource_len(data));
+        if (data_reg == NULL) {
+               printk(KERN_DEBUG PFX "unable to ioremap\n");
+                err = -ENOMEM;
+                goto out_mem;
+        }
+
+       hcd = usb_create_hcd(&adm5120_hc_driver, &pdev->dev, pdev->dev.bus_id);
+        if (!hcd) {
+               printk(KERN_DEBUG PFX "unable to create the hcd\n");
+               err = -ENOMEM;
+                goto out_unmap;
        }
 
-       //hcd = usb_create_hcd(&adm5120_hc_driver, pdev, pdev->bus_id);
-       if (!hcd)
-               goto out_mem;
+       hcd->rsrc_start = data->start;
+       hcd->rsrc_len = resource_len(data);
+       hcd->regs = data_reg;
 
        ahcd = hcd_to_admhcd(hcd);
-       dev_set_drvdata(pdev, ahcd);
-       hcd->self.controller = pdev;
-       //hcd->self.bus_name = pdev->bus_id;
-       hcd->irq = pdev->resource[1].start;
-       hcd->regs = (void *)pdev->resource[0].start;
-       hcd->product_desc = hcd_name;
-       ahcd->base = pdev->resource[0].start;
-
-       if (request_irq(pdev->resource[1].start, adm5120hcd_irq, 0, hcd_name,
-           hcd)) {
-               pr_debug("couldn't request irq\n");
-               err = -EBUSY;
-               goto out_hcd;
-       }
-
-       //err = usb_register_bus(&hcd->self);
-       //if (err < 0)
-       //      goto out_irq;
+       ahcd->data_reg = data_reg;
+       ahcd->base = (u32)data_reg;
 
        spin_lock_init(&ahcd->lock);
+       INIT_LIST_HEAD(&ahcd->async);
 
-       admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE, 0);
-       mdelay(10);
-       admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_SW_RESET);
-       while (admhcd_reg_get(ahcd, ADMHCD_REG_CONTROL) & ADMHCD_SW_RESET)
-               mdelay(1);
-
-       admhcd_reg_set(ahcd, ADMHCD_REG_CONTROL, ADMHCD_HOST_EN);
-       admhcd_reg_set(ahcd, ADMHCD_REG_HOSTHEAD, 0x00000000);
-       admhcd_reg_set(ahcd, ADMHCD_REG_FMINTERVAL, 0x20002edf);
-       admhcd_reg_set(ahcd, ADMHCD_REG_LSTHRESH, 0x628);
-       admhcd_reg_set(ahcd, ADMHCD_REG_INTENABLE,
-           ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD);
-       admhcd_reg_set(ahcd, ADMHCD_REG_INTSTATUS,
-           ADMHCD_INT_ACT | ADMHCD_INT_FATAL | ADMHCD_INT_SW | ADMHCD_INT_TD);
-       admhcd_reg_set(ahcd, ADMHCD_REG_RHDESCR, ADMHCD_NPS | ADMHCD_LPSC);
-       admhcd_reg_set(ahcd, ADMHCD_REG_HOSTCONTROL, ADMHCD_STATE_OP);
+       hcd->product_desc = "ADM5120 HCD";
 
-       udev = usb_alloc_dev(NULL, &hcd->self, 0);
-       if (!udev) {
-               err = -ENOMEM;
-               goto out_bus;
+       err = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+       if (err) {
+               printk(KERN_DEBUG PFX "unable to add hcd\n");
+               goto out_dev;
        }
 
-       udev->speed = USB_SPEED_FULL;
-       hcd->state = HC_STATE_RUNNING;
-
-       //err = hcd_register_root(udev, hcd);
-       //if (err != 0) {
-       //      usb_put_dev(udev);
-       //      goto out_dev;
-       //}
-
        return 0;
 
 out_dev:
-       usb_put_dev(udev);
-out_bus:
-       //usb_deregister_bus(&hcd->self);
-out_irq:
-       free_irq(pdev->resource[1].start, hcd);
-out_hcd:
        usb_put_hcd(hcd);
+out_unmap:
+       iounmap(data_reg);
 out_mem:
-       release_mem_region(pdev->resource[0].start,
-           pdev->resource[0].end - pdev->resource[0].start);
+       release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start +1);
 out:
        return err;
 }
 
+#ifdef CONFIG_PM
+static int adm5120hcd_suspend(struct platform_device *pdev,  pm_message_t state)
+{
+       pdev-dev.power.power_state = state;
+       mdelay(1);
+       return 0;
+}
+
+static int adm5120hcd_resume(struct platform_device *pdev, pm_message_t state)
+{
+       pdev->dev.power.power_state = PMSG_ON;
+       mdelay(1);
+       return 0;
+}
+#else
+#define adm5120hcd_suspend     NULL
+#define adm5120hcd_resume      NULL
+#endif
+
 static int __init_or_module adm5120hcd_remove(struct platform_device *pdev)
 {
-       device_init_wakeup(&pdev->dev, 0);
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+       struct admhcd *ahcd;
+
+       if (!hcd)
+               return 0;
+       ahcd = hcd_to_admhcd(hcd);
+       usb_remove_hcd(hcd);
+
+       usb_put_hcd(hcd);
+       return 0;
 }
 
 static struct platform_driver adm5120hcd_driver = {
        .probe =        adm5120hcd_probe,
        .remove =       adm5120hcd_remove,
+       .suspend =      adm5120hcd_suspend,
+       .remove =       adm5120hcd_resume,
        .driver =       {
-               .name   = "adm5120-hcd",
+               .name   = (char *)hcd_name,
                .owner  = THIS_MODULE,
        },
 };
 
 static int __init adm5120hcd_init(void)
 {
-       if (usb_disabled())
+       int ret;
+
+       if (usb_disabled()) {
+               printk(KERN_DEBUG PFX "USB support is disabled\n");
                return -ENODEV;
+       }
+
+       if (mips_machgroup != MACH_GROUP_ADM5120) {
+               printk(KERN_DEBUG PFX "unsupported machine group\n");
+               return -ENODEV;
+       }
+
+       ret = platform_driver_register(&adm5120hcd_driver);
+       if (ret == 0)
+               printk(KERN_INFO PFX "registered\n");
 
-       return platform_driver_register(&adm5120hcd_driver);
+       return ret;
 }
 
 static void __exit adm5120hcd_exit(void)
 {
        platform_driver_unregister(&adm5120hcd_driver);
+       printk(KERN_INFO PFX "driver unregistered\n");
 }
 
 module_init(adm5120hcd_init);