Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm...
[cascardo/linux.git] / drivers / staging / octeon-usb / octeon-hcd.c
index 1f501ff..9e5476e 100644 (file)
@@ -393,16 +393,18 @@ struct octeon_hcd {
        struct cvmx_usb_state usb;
 };
 
-/* This macro spins on a field waiting for it to reach a value */
-#define CVMX_WAIT_FOR_FIELD32(address, type, field, op, value, timeout_usec)\
+/* This macro spins on a register waiting for it to reach a condition. */
+#define CVMX_WAIT_FOR_FIELD32(address, _union, cond, timeout_usec)         \
        ({int result;                                                       \
        do {                                                                \
                uint64_t done = cvmx_get_cycle() + (uint64_t)timeout_usec * \
                        octeon_get_clock_rate() / 1000000;                  \
-               type c;                                                     \
+               union _union c;                                             \
+                                                                           \
                while (1) {                                                 \
                        c.u32 = cvmx_usb_read_csr32(usb, address);          \
-                       if (c.s.field op (value)) {                         \
+                                                                           \
+                       if (cond) {                                         \
                                result = 0;                                 \
                                break;                                      \
                        } else if (cvmx_get_cycle() > done) {               \
@@ -418,9 +420,10 @@ struct octeon_hcd {
  * This macro logically sets a single field in a CSR. It does the sequence
  * read, modify, and write
  */
-#define USB_SET_FIELD32(address, type, field, value)           \
+#define USB_SET_FIELD32(address, _union, field, value)         \
        do {                                                    \
-               type c;                                         \
+               union _union c;                                 \
+                                                               \
                c.u32 = cvmx_usb_read_csr32(usb, address);      \
                c.s.field = value;                              \
                cvmx_usb_write_csr32(usb, address, c.u32);      \
@@ -608,20 +611,112 @@ static inline int cvmx_usb_get_data_pid(struct cvmx_usb_pipe *pipe)
        return 0; /* Data0 */
 }
 
+static void cvmx_fifo_setup(struct cvmx_usb_state *usb)
+{
+       union cvmx_usbcx_ghwcfg3 usbcx_ghwcfg3;
+       union cvmx_usbcx_gnptxfsiz npsiz;
+       union cvmx_usbcx_hptxfsiz psiz;
+
+       usbcx_ghwcfg3.u32 = cvmx_usb_read_csr32(usb,
+                                               CVMX_USBCX_GHWCFG3(usb->index));
+
+       /*
+        * Program the USBC_GRXFSIZ register to select the size of the receive
+        * FIFO (25%).
+        */
+       USB_SET_FIELD32(CVMX_USBCX_GRXFSIZ(usb->index), cvmx_usbcx_grxfsiz,
+                       rxfdep, usbcx_ghwcfg3.s.dfifodepth / 4);
+
+       /*
+        * Program the USBC_GNPTXFSIZ register to select the size and the start
+        * address of the non-periodic transmit FIFO for nonperiodic
+        * transactions (50%).
+        */
+       npsiz.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index));
+       npsiz.s.nptxfdep = usbcx_ghwcfg3.s.dfifodepth / 2;
+       npsiz.s.nptxfstaddr = usbcx_ghwcfg3.s.dfifodepth / 4;
+       cvmx_usb_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index), npsiz.u32);
+
+       /*
+        * Program the USBC_HPTXFSIZ register to select the size and start
+        * address of the periodic transmit FIFO for periodic transactions
+        * (25%).
+        */
+       psiz.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index));
+       psiz.s.ptxfsize = usbcx_ghwcfg3.s.dfifodepth / 4;
+       psiz.s.ptxfstaddr = 3 * usbcx_ghwcfg3.s.dfifodepth / 4;
+       cvmx_usb_write_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index), psiz.u32);
+
+       /* Flush all FIFOs */
+       USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
+                       cvmx_usbcx_grstctl, txfnum, 0x10);
+       USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
+                       cvmx_usbcx_grstctl, txfflsh, 1);
+       CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
+                             cvmx_usbcx_grstctl, c.s.txfflsh == 0, 100);
+       USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
+                       cvmx_usbcx_grstctl, rxfflsh, 1);
+       CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
+                             cvmx_usbcx_grstctl, c.s.rxfflsh == 0, 100);
+}
+
+/**
+ * Shutdown a USB port after a call to cvmx_usb_initialize().
+ * The port should be disabled with all pipes closed when this
+ * function is called.
+ *
+ * @usb: USB device state populated by cvmx_usb_initialize().
+ *
+ * Returns: 0 or a negative error code.
+ */
+static int cvmx_usb_shutdown(struct cvmx_usb_state *usb)
+{
+       union cvmx_usbnx_clk_ctl usbn_clk_ctl;
+
+       /* Make sure all pipes are closed */
+       if (!list_empty(&usb->idle_pipes) ||
+           !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_ISOCHRONOUS]) ||
+           !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_INTERRUPT]) ||
+           !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_CONTROL]) ||
+           !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_BULK]))
+               return -EBUSY;
+
+       /* Disable the clocks and put them in power on reset */
+       usbn_clk_ctl.u64 = cvmx_read64_uint64(CVMX_USBNX_CLK_CTL(usb->index));
+       usbn_clk_ctl.s.enable = 1;
+       usbn_clk_ctl.s.por = 1;
+       usbn_clk_ctl.s.hclk_rst = 1;
+       usbn_clk_ctl.s.prst = 0;
+       usbn_clk_ctl.s.hrst = 0;
+       cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
+       return 0;
+}
+
 /**
  * Initialize a USB port for use. This must be called before any
  * other access to the Octeon USB port is made. The port starts
  * off in the disabled state.
  *
+ * @dev:        Pointer to struct device for logging purposes.
  * @usb:        Pointer to struct cvmx_usb_state.
  *
  * Returns: 0 or a negative error code.
  */
-static int cvmx_usb_initialize(struct cvmx_usb_state *usb)
+static int cvmx_usb_initialize(struct device *dev,
+                              struct cvmx_usb_state *usb)
 {
+       int channel;
+       int divisor;
+       int retries = 0;
+       union cvmx_usbcx_hcfg usbcx_hcfg;
        union cvmx_usbnx_clk_ctl usbn_clk_ctl;
+       union cvmx_usbcx_gintsts usbc_gintsts;
+       union cvmx_usbcx_gahbcfg usbcx_gahbcfg;
+       union cvmx_usbcx_gintmsk usbcx_gintmsk;
+       union cvmx_usbcx_gusbcfg usbcx_gusbcfg;
        union cvmx_usbnx_usbp_ctl_status usbn_usbp_ctl_status;
 
+retry:
        /*
         * Power On Reset and PHY Initialization
         *
@@ -686,15 +781,14 @@ static int cvmx_usb_initialize(struct cvmx_usb_state *usb)
         *     setting USBN0/1_CLK_CTL[ENABLE] = 1. Divide the core clock down
         *     such that USB is as close as possible to 125Mhz
         */
-       {
-               int divisor = DIV_ROUND_UP(octeon_get_clock_rate(), 125000000);
-               /* Lower than 4 doesn't seem to work properly */
-               if (divisor < 4)
-                       divisor = 4;
-               usbn_clk_ctl.s.divide = divisor;
-               usbn_clk_ctl.s.divide2 = 0;
-       }
+       divisor = DIV_ROUND_UP(octeon_get_clock_rate(), 125000000);
+       /* Lower than 4 doesn't seem to work properly */
+       if (divisor < 4)
+               divisor = 4;
+       usbn_clk_ctl.s.divide = divisor;
+       usbn_clk_ctl.s.divide2 = 0;
        cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
+
        /* 2d. Write USBN0/1_CLK_CTL[HCLK_RST] = 1 */
        usbn_clk_ctl.s.hclk_rst = 1;
        cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
@@ -773,18 +867,16 @@ static int cvmx_usb_initialize(struct cvmx_usb_state *usb)
         *    USBC_GAHBCFG[PTXFEMPLVL]
         *    Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1
         */
-       {
-               union cvmx_usbcx_gahbcfg usbcx_gahbcfg;
-               usbcx_gahbcfg.u32 = 0;
-               usbcx_gahbcfg.s.dmaen = !(usb->init_flags &
-                                         CVMX_USB_INITIALIZE_FLAGS_NO_DMA);
-               usbcx_gahbcfg.s.hbstlen = 0;
-               usbcx_gahbcfg.s.nptxfemplvl = 1;
-               usbcx_gahbcfg.s.ptxfemplvl = 1;
-               usbcx_gahbcfg.s.glblintrmsk = 1;
-               cvmx_usb_write_csr32(usb, CVMX_USBCX_GAHBCFG(usb->index),
-                                    usbcx_gahbcfg.u32);
-       }
+       usbcx_gahbcfg.u32 = 0;
+       usbcx_gahbcfg.s.dmaen = !(usb->init_flags &
+                                 CVMX_USB_INITIALIZE_FLAGS_NO_DMA);
+       usbcx_gahbcfg.s.hbstlen = 0;
+       usbcx_gahbcfg.s.nptxfemplvl = 1;
+       usbcx_gahbcfg.s.ptxfemplvl = 1;
+       usbcx_gahbcfg.s.glblintrmsk = 1;
+       cvmx_usb_write_csr32(usb, CVMX_USBCX_GAHBCFG(usb->index),
+                            usbcx_gahbcfg.u32);
+
        /*
         * 3. Program the following fields in USBC_GUSBCFG register.
         *    HS/FS timeout calibration, USBC_GUSBCFG[TOUTCAL] = 0
@@ -792,151 +884,98 @@ static int cvmx_usb_initialize(struct cvmx_usb_state *usb)
         *    USB turnaround time, USBC_GUSBCFG[USBTRDTIM] = 0x5
         *    PHY low-power clock select, USBC_GUSBCFG[PHYLPWRCLKSEL] = 0
         */
-       {
-               union cvmx_usbcx_gusbcfg usbcx_gusbcfg;
-
-               usbcx_gusbcfg.u32 = cvmx_usb_read_csr32(usb,
-                               CVMX_USBCX_GUSBCFG(usb->index));
-               usbcx_gusbcfg.s.toutcal = 0;
-               usbcx_gusbcfg.s.ddrsel = 0;
-               usbcx_gusbcfg.s.usbtrdtim = 0x5;
-               usbcx_gusbcfg.s.phylpwrclksel = 0;
-               cvmx_usb_write_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index),
-                                    usbcx_gusbcfg.u32);
-       }
+       usbcx_gusbcfg.u32 = cvmx_usb_read_csr32(usb,
+                                               CVMX_USBCX_GUSBCFG(usb->index));
+       usbcx_gusbcfg.s.toutcal = 0;
+       usbcx_gusbcfg.s.ddrsel = 0;
+       usbcx_gusbcfg.s.usbtrdtim = 0x5;
+       usbcx_gusbcfg.s.phylpwrclksel = 0;
+       cvmx_usb_write_csr32(usb, CVMX_USBCX_GUSBCFG(usb->index),
+                            usbcx_gusbcfg.u32);
+
        /*
         * 4. The software must unmask the following bits in the USBC_GINTMSK
         *    register.
         *    OTG interrupt mask, USBC_GINTMSK[OTGINTMSK] = 1
         *    Mode mismatch interrupt mask, USBC_GINTMSK[MODEMISMSK] = 1
         */
-       {
-               union cvmx_usbcx_gintmsk usbcx_gintmsk;
-               int channel;
-
-               usbcx_gintmsk.u32 = cvmx_usb_read_csr32(usb,
-                               CVMX_USBCX_GINTMSK(usb->index));
-               usbcx_gintmsk.s.otgintmsk = 1;
-               usbcx_gintmsk.s.modemismsk = 1;
-               usbcx_gintmsk.s.hchintmsk = 1;
-               usbcx_gintmsk.s.sofmsk = 0;
-               /* We need RX FIFO interrupts if we don't have DMA */
-               if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
-                       usbcx_gintmsk.s.rxflvlmsk = 1;
-               cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTMSK(usb->index),
-                                    usbcx_gintmsk.u32);
-
-               /*
-                * Disable all channel interrupts. We'll enable them per channel
-                * later.
-                */
-               for (channel = 0; channel < 8; channel++)
-                       cvmx_usb_write_csr32(usb,
-                               CVMX_USBCX_HCINTMSKX(channel, usb->index), 0);
-       }
-
-       {
-               /*
-                * Host Port Initialization
-                *
-                * 1. Program the host-port interrupt-mask field to unmask,
-                *    USBC_GINTMSK[PRTINT] = 1
-                */
-               USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
-                               union cvmx_usbcx_gintmsk, prtintmsk, 1);
-               USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
-                               union cvmx_usbcx_gintmsk, disconnintmsk, 1);
-               /*
-                * 2. Program the USBC_HCFG register to select full-speed host
-                *    or high-speed host.
-                */
-               {
-                       union cvmx_usbcx_hcfg usbcx_hcfg;
-
-                       usbcx_hcfg.u32 = cvmx_usb_read_csr32(usb,
-                                       CVMX_USBCX_HCFG(usb->index));
-                       usbcx_hcfg.s.fslssupp = 0;
-                       usbcx_hcfg.s.fslspclksel = 0;
-                       cvmx_usb_write_csr32(usb, CVMX_USBCX_HCFG(usb->index),
-                                            usbcx_hcfg.u32);
-               }
-               /*
-                * 3. Program the port power bit to drive VBUS on the USB,
-                *    USBC_HPRT[PRTPWR] = 1
-                */
-               USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index),
-                               union cvmx_usbcx_hprt, prtpwr, 1);
-
-               /*
-                * Steps 4-15 from the manual are done later in the port enable
-                */
-       }
+       usbcx_gintmsk.u32 = cvmx_usb_read_csr32(usb,
+                                               CVMX_USBCX_GINTMSK(usb->index));
+       usbcx_gintmsk.s.otgintmsk = 1;
+       usbcx_gintmsk.s.modemismsk = 1;
+       usbcx_gintmsk.s.hchintmsk = 1;
+       usbcx_gintmsk.s.sofmsk = 0;
+       /* We need RX FIFO interrupts if we don't have DMA */
+       if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
+               usbcx_gintmsk.s.rxflvlmsk = 1;
+       cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTMSK(usb->index),
+                            usbcx_gintmsk.u32);
 
-       return 0;
-}
+       /*
+        * Disable all channel interrupts. We'll enable them per channel later.
+        */
+       for (channel = 0; channel < 8; channel++)
+               cvmx_usb_write_csr32(usb,
+                                    CVMX_USBCX_HCINTMSKX(channel, usb->index),
+                                    0);
 
+       /*
+        * Host Port Initialization
+        *
+        * 1. Program the host-port interrupt-mask field to unmask,
+        *    USBC_GINTMSK[PRTINT] = 1
+        */
+       USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
+                       cvmx_usbcx_gintmsk, prtintmsk, 1);
+       USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
+                       cvmx_usbcx_gintmsk, disconnintmsk, 1);
 
-/**
- * Shutdown a USB port after a call to cvmx_usb_initialize().
- * The port should be disabled with all pipes closed when this
- * function is called.
- *
- * @usb: USB device state populated by cvmx_usb_initialize().
- *
- * Returns: 0 or a negative error code.
- */
-static int cvmx_usb_shutdown(struct cvmx_usb_state *usb)
-{
-       union cvmx_usbnx_clk_ctl usbn_clk_ctl;
+       /*
+        * 2. Program the USBC_HCFG register to select full-speed host
+        *    or high-speed host.
+        */
+       usbcx_hcfg.u32 = cvmx_usb_read_csr32(usb, CVMX_USBCX_HCFG(usb->index));
+       usbcx_hcfg.s.fslssupp = 0;
+       usbcx_hcfg.s.fslspclksel = 0;
+       cvmx_usb_write_csr32(usb, CVMX_USBCX_HCFG(usb->index), usbcx_hcfg.u32);
 
-       /* Make sure all pipes are closed */
-       if (!list_empty(&usb->idle_pipes) ||
-           !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_ISOCHRONOUS]) ||
-           !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_INTERRUPT]) ||
-           !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_CONTROL]) ||
-           !list_empty(&usb->active_pipes[CVMX_USB_TRANSFER_BULK]))
-               return -EBUSY;
+       cvmx_fifo_setup(usb);
 
-       /* Disable the clocks and put them in power on reset */
-       usbn_clk_ctl.u64 = cvmx_read64_uint64(CVMX_USBNX_CLK_CTL(usb->index));
-       usbn_clk_ctl.s.enable = 1;
-       usbn_clk_ctl.s.por = 1;
-       usbn_clk_ctl.s.hclk_rst = 1;
-       usbn_clk_ctl.s.prst = 0;
-       usbn_clk_ctl.s.hrst = 0;
-       cvmx_write64_uint64(CVMX_USBNX_CLK_CTL(usb->index), usbn_clk_ctl.u64);
-       return 0;
+       /*
+        * If the controller is getting port events right after the reset, it
+        * means the initialization failed. Try resetting the controller again
+        * in such case. This is seen to happen after cold boot on DSR-1000N.
+        */
+       usbc_gintsts.u32 = cvmx_usb_read_csr32(usb,
+                                              CVMX_USBCX_GINTSTS(usb->index));
+       cvmx_usb_write_csr32(usb, CVMX_USBCX_GINTSTS(usb->index),
+                            usbc_gintsts.u32);
+       dev_dbg(dev, "gintsts after reset: 0x%x\n", (int)usbc_gintsts.u32);
+       if (!usbc_gintsts.s.disconnint && !usbc_gintsts.s.prtint)
+               return 0;
+       if (retries++ >= 5)
+               return -EAGAIN;
+       dev_info(dev, "controller reset failed (gintsts=0x%x) - retrying\n",
+                (int)usbc_gintsts.u32);
+       msleep(50);
+       cvmx_usb_shutdown(usb);
+       msleep(50);
+       goto retry;
 }
 
-
 /**
- * Enable a USB port. After this call succeeds, the USB port is
+ * Reset a USB port. After this call succeeds, the USB port is
  * online and servicing requests.
  *
  * @usb: USB device state populated by cvmx_usb_initialize().
- *
- * Returns: 0 or a negative error code.
  */
-static int cvmx_usb_enable(struct cvmx_usb_state *usb)
+static void cvmx_usb_reset_port(struct cvmx_usb_state *usb)
 {
-       union cvmx_usbcx_ghwcfg3 usbcx_ghwcfg3;
-
        usb->usbcx_hprt.u32 = cvmx_usb_read_csr32(usb,
                                                  CVMX_USBCX_HPRT(usb->index));
 
-       /*
-        * If the port is already enabled the just return. We don't need to do
-        * anything
-        */
-       if (usb->usbcx_hprt.s.prtena)
-               return 0;
-
-       /* If there is nothing plugged into the port then fail immediately */
-       if (!usb->usbcx_hprt.s.prtconnsts)
-               return -ETIMEDOUT;
-
        /* Program the port reset bit to start the reset process */
-       USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), union cvmx_usbcx_hprt,
+       USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt,
                        prtrst, 1);
 
        /*
@@ -946,75 +985,15 @@ static int cvmx_usb_enable(struct cvmx_usb_state *usb)
        mdelay(50);
 
        /* Program the port reset bit to 0, USBC_HPRT[PRTRST] = 0 */
-       USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), union cvmx_usbcx_hprt,
+       USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt,
                        prtrst, 0);
 
-       /* Wait for the USBC_HPRT[PRTENA]. */
-       if (CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_HPRT(usb->index),
-                               union cvmx_usbcx_hprt, prtena, ==, 1, 100000))
-               return -ETIMEDOUT;
-
        /*
         * Read the port speed field to get the enumerated speed,
         * USBC_HPRT[PRTSPD].
         */
        usb->usbcx_hprt.u32 = cvmx_usb_read_csr32(usb,
                                                  CVMX_USBCX_HPRT(usb->index));
-       usbcx_ghwcfg3.u32 = cvmx_usb_read_csr32(usb,
-                                               CVMX_USBCX_GHWCFG3(usb->index));
-
-       /*
-        * 13. Program the USBC_GRXFSIZ register to select the size of the
-        *     receive FIFO (25%).
-        */
-       USB_SET_FIELD32(CVMX_USBCX_GRXFSIZ(usb->index),
-                       union cvmx_usbcx_grxfsiz, rxfdep,
-                       usbcx_ghwcfg3.s.dfifodepth / 4);
-       /*
-        * 14. Program the USBC_GNPTXFSIZ register to select the size and the
-        *     start address of the non- periodic transmit FIFO for nonperiodic
-        *     transactions (50%).
-        */
-       {
-               union cvmx_usbcx_gnptxfsiz siz;
-
-               siz.u32 = cvmx_usb_read_csr32(usb,
-                                             CVMX_USBCX_GNPTXFSIZ(usb->index));
-               siz.s.nptxfdep = usbcx_ghwcfg3.s.dfifodepth / 2;
-               siz.s.nptxfstaddr = usbcx_ghwcfg3.s.dfifodepth / 4;
-               cvmx_usb_write_csr32(usb, CVMX_USBCX_GNPTXFSIZ(usb->index),
-                                    siz.u32);
-       }
-       /*
-        * 15. Program the USBC_HPTXFSIZ register to select the size and start
-        *     address of the periodic transmit FIFO for periodic transactions
-        *     (25%).
-        */
-       {
-               union cvmx_usbcx_hptxfsiz siz;
-
-               siz.u32 = cvmx_usb_read_csr32(usb,
-                                             CVMX_USBCX_HPTXFSIZ(usb->index));
-               siz.s.ptxfsize = usbcx_ghwcfg3.s.dfifodepth / 4;
-               siz.s.ptxfstaddr = 3 * usbcx_ghwcfg3.s.dfifodepth / 4;
-               cvmx_usb_write_csr32(usb, CVMX_USBCX_HPTXFSIZ(usb->index),
-                                    siz.u32);
-       }
-       /* Flush all FIFOs */
-       USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
-                       union cvmx_usbcx_grstctl, txfnum, 0x10);
-       USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
-                       union cvmx_usbcx_grstctl, txfflsh, 1);
-       CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
-                             union cvmx_usbcx_grstctl,
-                             txfflsh, ==, 0, 100);
-       USB_SET_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
-                       union cvmx_usbcx_grstctl, rxfflsh, 1);
-       CVMX_WAIT_FOR_FIELD32(CVMX_USBCX_GRSTCTL(usb->index),
-                             union cvmx_usbcx_grstctl,
-                             rxfflsh, ==, 0, 100);
-
-       return 0;
 }
 
 
@@ -1031,7 +1010,7 @@ static int cvmx_usb_enable(struct cvmx_usb_state *usb)
 static int cvmx_usb_disable(struct cvmx_usb_state *usb)
 {
        /* Disable the port */
-       USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), union cvmx_usbcx_hprt,
+       USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index), cvmx_usbcx_hprt,
                        prtena, 1);
        return 0;
 }
@@ -1306,12 +1285,10 @@ static void cvmx_usb_poll_tx_fifo(struct cvmx_usb_state *usb)
                if (cvmx_usb_fill_tx_hw(usb, &usb->periodic,
                                        tx_status.s.ptxfspcavail))
                        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
-                                       union cvmx_usbcx_gintmsk,
-                                       ptxfempmsk, 1);
+                                       cvmx_usbcx_gintmsk, ptxfempmsk, 1);
                else
                        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
-                                       union cvmx_usbcx_gintmsk,
-                                       ptxfempmsk, 0);
+                                       cvmx_usbcx_gintmsk, ptxfempmsk, 0);
        }
 
        if (usb->nonperiodic.head != usb->nonperiodic.tail) {
@@ -1322,12 +1299,10 @@ static void cvmx_usb_poll_tx_fifo(struct cvmx_usb_state *usb)
                if (cvmx_usb_fill_tx_hw(usb, &usb->nonperiodic,
                                        tx_status.s.nptxfspcavail))
                        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
-                                       union cvmx_usbcx_gintmsk,
-                                       nptxfempmsk, 1);
+                                       cvmx_usbcx_gintmsk, nptxfempmsk, 1);
                else
                        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
-                                       union cvmx_usbcx_gintmsk,
-                                       nptxfempmsk, 0);
+                                       cvmx_usbcx_gintmsk, nptxfempmsk, 0);
        }
 }
 
@@ -1422,7 +1397,7 @@ static void cvmx_usb_start_channel_control(struct cvmx_usb_state *usb,
                bytes_to_transfer = sizeof(*header);
                /* All Control operations start with a setup going OUT */
                USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
-                               union cvmx_usbcx_hccharx, epdir,
+                               cvmx_usbcx_hccharx, epdir,
                                CVMX_USB_DIRECTION_OUT);
                /*
                 * Setup send the control header instead of the buffer data. The
@@ -1437,11 +1412,11 @@ static void cvmx_usb_start_channel_control(struct cvmx_usb_state *usb,
                bytes_to_transfer = 0;
                /* All Control operations start with a setup going OUT */
                USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
-                               union cvmx_usbcx_hccharx, epdir,
+                               cvmx_usbcx_hccharx, epdir,
                                CVMX_USB_DIRECTION_OUT);
 
                USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index),
-                               union cvmx_usbcx_hcspltx, compsplt, 1);
+                               cvmx_usbcx_hcspltx, compsplt, 1);
                break;
        case CVMX_USB_STAGE_DATA:
                usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe);
@@ -1452,7 +1427,7 @@ static void cvmx_usb_start_channel_control(struct cvmx_usb_state *usb,
                                bytes_to_transfer = pipe->max_packet;
                }
                USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
-                               union cvmx_usbcx_hccharx, epdir,
+                               cvmx_usbcx_hccharx, epdir,
                                ((header->bRequestType & USB_DIR_IN) ?
                                        CVMX_USB_DIRECTION_IN :
                                        CVMX_USB_DIRECTION_OUT));
@@ -1462,18 +1437,18 @@ static void cvmx_usb_start_channel_control(struct cvmx_usb_state *usb,
                if (!(header->bRequestType & USB_DIR_IN))
                        bytes_to_transfer = 0;
                USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
-                               union cvmx_usbcx_hccharx, epdir,
+                               cvmx_usbcx_hccharx, epdir,
                                ((header->bRequestType & USB_DIR_IN) ?
                                        CVMX_USB_DIRECTION_IN :
                                        CVMX_USB_DIRECTION_OUT));
                USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index),
-                               union cvmx_usbcx_hcspltx, compsplt, 1);
+                               cvmx_usbcx_hcspltx, compsplt, 1);
                break;
        case CVMX_USB_STAGE_STATUS:
                usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe);
                bytes_to_transfer = 0;
                USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
-                               union cvmx_usbcx_hccharx, epdir,
+                               cvmx_usbcx_hccharx, epdir,
                                ((header->bRequestType & USB_DIR_IN) ?
                                        CVMX_USB_DIRECTION_OUT :
                                        CVMX_USB_DIRECTION_IN));
@@ -1482,12 +1457,12 @@ static void cvmx_usb_start_channel_control(struct cvmx_usb_state *usb,
                usbc_hctsiz.s.pid = cvmx_usb_get_data_pid(pipe);
                bytes_to_transfer = 0;
                USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
-                               union cvmx_usbcx_hccharx, epdir,
+                               cvmx_usbcx_hccharx, epdir,
                                ((header->bRequestType & USB_DIR_IN) ?
                                        CVMX_USB_DIRECTION_OUT :
                                        CVMX_USB_DIRECTION_IN));
                USB_SET_FIELD32(CVMX_USBCX_HCSPLTX(channel, usb->index),
-                               union cvmx_usbcx_hcspltx, compsplt, 1);
+                               cvmx_usbcx_hcspltx, compsplt, 1);
                break;
        }
 
@@ -1851,14 +1826,12 @@ static void cvmx_usb_start_channel(struct cvmx_usb_state *usb, int channel,
                                        USB_SET_FIELD32(
                                                CVMX_USBCX_HCTSIZX(channel,
                                                                   usb->index),
-                                               union cvmx_usbcx_hctsizx,
-                                               pid, 0);
+                                               cvmx_usbcx_hctsizx, pid, 0);
                                else /* Need MDATA */
                                        USB_SET_FIELD32(
                                                CVMX_USBCX_HCTSIZX(channel,
                                                                   usb->index),
-                                               union cvmx_usbcx_hctsizx,
-                                               pid, 3);
+                                               cvmx_usbcx_hctsizx, pid, 3);
                        }
                }
                break;
@@ -1874,7 +1847,7 @@ static void cvmx_usb_start_channel(struct cvmx_usb_state *usb, int channel,
        if (cvmx_usb_pipe_needs_split(usb, pipe))
                usb->active_split = transaction;
        USB_SET_FIELD32(CVMX_USBCX_HCCHARX(channel, usb->index),
-                       union cvmx_usbcx_hccharx, chena, 1);
+                       cvmx_usbcx_hccharx, chena, 1);
        if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA)
                cvmx_usb_fill_tx_fifo(usb, channel);
 }
@@ -2004,7 +1977,7 @@ done:
                }
        }
        USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
-                       union cvmx_usbcx_gintmsk, sofmsk, need_sof);
+                       cvmx_usbcx_gintmsk, sofmsk, need_sof);
 }
 
 static void octeon_usb_urb_complete_callback(struct cvmx_usb_state *usb,
@@ -2646,6 +2619,17 @@ static int cvmx_usb_poll_channel(struct cvmx_usb_state *usb, int channel)
                (pipe->transfer_dir == CVMX_USB_DIRECTION_OUT))
                pipe->flags |= CVMX_USB_PIPE_FLAGS_NEED_PING;
 
+       if (unlikely(WARN_ON_ONCE(bytes_this_transfer < 0))) {
+               /*
+                * In some rare cases the DMA engine seems to get stuck and
+                * keeps substracting same byte count over and over again. In
+                * such case we just need to fail every transaction.
+                */
+               cvmx_usb_perform_complete(usb, pipe, transaction,
+                                         CVMX_USB_COMPLETE_ERROR);
+               return 0;
+       }
+
        if (usbc_hcint.s.stall) {
                /*
                 * STALL as a response means this transaction cannot be
@@ -3354,6 +3338,7 @@ static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
        struct octeon_hcd *priv = hcd_to_octeon(hcd);
        struct device *dev = hcd->self.controller;
        struct cvmx_usb_port_status usb_port_status;
+       struct cvmx_usb_state *usb = &priv->usb;
        int port_status;
        struct usb_hub_descriptor *desc;
        unsigned long flags;
@@ -3531,13 +3516,18 @@ static int octeon_usb_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        return -EINVAL;
                case USB_PORT_FEAT_POWER:
                        dev_dbg(dev, " POWER\n");
-                       return -EINVAL;
+                       /*
+                        * Program the port power bit to drive VBUS on the USB.
+                        */
+                       spin_lock_irqsave(&priv->lock, flags);
+                       USB_SET_FIELD32(CVMX_USBCX_HPRT(usb->index),
+                                       cvmx_usbcx_hprt, prtpwr, 1);
+                       spin_unlock_irqrestore(&priv->lock, flags);
+                       return 0;
                case USB_PORT_FEAT_RESET:
                        dev_dbg(dev, " RESET\n");
                        spin_lock_irqsave(&priv->lock, flags);
-                       cvmx_usb_disable(&priv->usb);
-                       if (cvmx_usb_enable(&priv->usb))
-                               dev_dbg(dev, "Failed to enable the port\n");
+                       cvmx_usb_reset_port(&priv->usb);
                        spin_unlock_irqrestore(&priv->lock, flags);
                        return 0;
                case USB_PORT_FEAT_INDICATOR:
@@ -3585,7 +3575,6 @@ static int octeon_usb_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct octeon_hcd *priv;
        struct usb_hcd *hcd;
-       unsigned long flags;
        u32 clock_rate = 48000000;
        bool is_crystal_clock = false;
        const char *clock_type;
@@ -3702,16 +3691,13 @@ static int octeon_usb_probe(struct platform_device *pdev)
                priv->usb.idle_hardware_channels = 0xff;
        }
 
-       status = cvmx_usb_initialize(&priv->usb);
+       status = cvmx_usb_initialize(dev, &priv->usb);
        if (status) {
                dev_dbg(dev, "USB initialization failed with %d\n", status);
                kfree(hcd);
                return -1;
        }
 
-       /* This delay is needed for CN3010, but I don't know why... */
-       mdelay(10);
-
        status = usb_add_hcd(hcd, irq, 0);
        if (status) {
                dev_dbg(dev, "USB add HCD failed with %d\n", status);