+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 state = 0;
+ int ret, timeout = 15; /* ms */
+ unsigned long t;
+
+ ret = admhcd_sw_reset(ahcd);
+ if (ret)
+ return ret;
+
+ t = jiffies + msecs_to_jiffies(timeout);
+ do {
+ spin_lock_irq(&ahcd->lock);
+ state = admhcd_reg_get(ahcd, ADMHCD_REG_HOSTCONTROL);
+ spin_unlock_irq(&ahcd->lock);
+ state &= ADMHCD_STATE_MASK;
+ if (state == ADMHCD_STATE_RST)
+ break;
+ msleep(4);
+ } while (time_before_eq(jiffies, t));
+
+ if (state != ADMHCD_STATE_RST) {
+ 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);
+}
+
+