* hint to completely eliminate some IRQs, if a later IRQ is guaranteed
  * and DMA chaining is enabled.
  *
+ * MSI is enabled by default.  The legacy IRQ is used if MSI couldn't
+ * be enabled.
+ *
  * Note that almost all the errata workarounds here are only needed for
  * rev1 chips.  Rev1a silicon (0110) fixes almost all of them.
  */
 /*
  * Copyright (C) 2003 David Brownell
  * Copyright (C) 2003-2005 PLX Technology, Inc.
+ * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS
  *
  * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
  *     with 2282 chip
  *
+ * Modified Ricardo Ribalda Qtechnology AS  to provide compatibility
+ *     with usb 338x chip. Based on PLX driver
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
 #include <asm/irq.h>
 #include <asm/unaligned.h>
 
-
-#define        DRIVER_DESC             "PLX NET228x USB Peripheral Controller"
-#define        DRIVER_VERSION          "2005 Sept 27"
+#define        DRIVER_DESC             "PLX NET228x/USB338x USB Peripheral Controller"
+#define        DRIVER_VERSION          "2005 Sept 27/v3.0"
 
 #define        EP_DONTUSE              13      /* nonzero */
 
 static const char driver_name [] = "net2280";
 static const char driver_desc [] = DRIVER_DESC;
 
+static const u32 ep_bit[9] = { 0, 17, 2, 19, 4, 1, 18, 3, 20 };
 static const char ep0name [] = "ep0";
 static const char *const ep_name [] = {
        ep0name,
        "ep-a", "ep-b", "ep-c", "ep-d",
-       "ep-e", "ep-f",
+       "ep-e", "ep-f", "ep-g", "ep-h",
 };
 
 /* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO)
  */
 static bool use_dma = 1;
 static bool use_dma_chaining = 0;
+static bool use_msi = 1;
 
 /* "modprobe net2280 use_dma=n" etc */
 module_param (use_dma, bool, S_IRUGO);
 module_param (use_dma_chaining, bool, S_IRUGO);
-
+module_param(use_msi, bool, S_IRUGO);
 
 /* mode 0 == ep-{a,b,c,d} 1K fifo each
  * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
 #define dma_done_ie    cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE)
 
 /*-------------------------------------------------------------------------*/
+static inline void enable_pciirqenb(struct net2280_ep *ep)
+{
+       u32 tmp = readl(&ep->dev->regs->pciirqenb0);
+
+       if (ep->dev->pdev->vendor == 0x17cc)
+               tmp |= 1 << ep->num;
+       else
+               tmp |= 1 << ep_bit[ep->num];
+       writel(tmp, &ep->dev->regs->pciirqenb0);
+
+       return;
+}
 
 static int
 net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
        struct net2280_ep       *ep;
        u32                     max, tmp;
        unsigned long           flags;
+       static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
 
        ep = container_of (_ep, struct net2280_ep, ep);
        if (!_ep || !desc || ep->desc || _ep->name == ep0name
        if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
                return -EDOM;
 
+       if (dev->pdev->vendor == 0x10b5) {
+               if ((desc->bEndpointAddress & 0x0f) >= 0x0c)
+                       return -EDOM;
+               ep->is_in = !!usb_endpoint_dir_in(desc);
+               if (dev->enhanced_mode && ep->is_in && ep_key[ep->num])
+                       return -EINVAL;
+       }
+
        /* sanity check ep-e/ep-f since their fifos are small */
        max = usb_endpoint_maxp (desc) & 0x1fff;
-       if (ep->num > 4 && max > 64)
+       if (ep->num > 4 && max > 64 && (dev->pdev->vendor == 0x17cc))
                return -ERANGE;
 
        spin_lock_irqsave (&dev->lock, flags);
        ep->out_overflow = 0;
 
        /* set speed-dependent max packet; may kick in high bandwidth */
-       set_idx_reg (dev->regs, REG_EP_MAXPKT (dev, ep->num), max);
+       set_max_speed(ep, max);
 
        /* FIFO lines can't go to different packets.  PIO is ok, so
         * use it instead of troublesome (non-bulk) multi-packet DMA.
                                &ep->regs->ep_rsp);
        } else if (tmp == USB_ENDPOINT_XFER_BULK) {
                /* catch some particularly blatant driver bugs */
-               if ((dev->gadget.speed == USB_SPEED_HIGH
-                                       && max != 512)
-                               || (dev->gadget.speed == USB_SPEED_FULL
-                                       && max > 64)) {
-                       spin_unlock_irqrestore (&dev->lock, flags);
+               if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) ||
+                   (dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
+                   (dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
+                       spin_unlock_irqrestore(&dev->lock, flags);
                        return -ERANGE;
                }
        }
        ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0;
-       tmp <<= ENDPOINT_TYPE;
-       tmp |= desc->bEndpointAddress;
-       tmp |= (4 << ENDPOINT_BYTE_COUNT);      /* default full fifo lines */
-       tmp |= 1 << ENDPOINT_ENABLE;
-       wmb ();
+       /* Enable this endpoint */
+       if (dev->pdev->vendor == 0x17cc) {
+               tmp <<= ENDPOINT_TYPE;
+               tmp |= desc->bEndpointAddress;
+               /* default full fifo lines */
+               tmp |= (4 << ENDPOINT_BYTE_COUNT);
+               tmp |= 1 << ENDPOINT_ENABLE;
+               ep->is_in = (tmp & USB_DIR_IN) != 0;
+       } else {
+               /* In Legacy mode, only OUT endpoints are used */
+               if (dev->enhanced_mode && ep->is_in) {
+                       tmp <<= IN_ENDPOINT_TYPE;
+                       tmp |= (1 << IN_ENDPOINT_ENABLE);
+                       /* Not applicable to Legacy */
+                       tmp |= (1 << ENDPOINT_DIRECTION);
+               } else {
+                       tmp <<= OUT_ENDPOINT_TYPE;
+                       tmp |= (1 << OUT_ENDPOINT_ENABLE);
+                       tmp |= (ep->is_in << ENDPOINT_DIRECTION);
+               }
+
+               tmp |= usb_endpoint_num(desc);
+               tmp |= (ep->ep.maxburst << MAX_BURST_SIZE);
+       }
+
+       /* Make sure all the registers are written before ep_rsp*/
+       wmb();
 
        /* for OUT transfers, block the rx fifo until a read is posted */
-       ep->is_in = (tmp & USB_DIR_IN) != 0;
        if (!ep->is_in)
                writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
        else if (dev->pdev->device != 0x2280) {
                        | (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
        }
 
-       writel (tmp, &ep->regs->ep_cfg);
+       writel(tmp, &ep->cfg->ep_cfg);
 
        /* enable irqs */
        if (!ep->dma) {                         /* pio, per-packet */
-               tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0);
-               writel (tmp, &dev->regs->pciirqenb0);
+               enable_pciirqenb(ep);
 
                tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
                        | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE);
                        tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE);
                        writel (tmp, &ep->regs->ep_irqenb);
 
-                       tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0);
-                       writel (tmp, &dev->regs->pciirqenb0);
+                       enable_pciirqenb(ep);
                }
        }
 
 
 static const struct usb_ep_ops net2280_ep_ops;
 
-static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep)
+static void ep_reset_228x(struct net2280_regs __iomem *regs,
+                         struct net2280_ep *ep)
 {
        u32             tmp;
 
        /* fifo size is handled separately */
 }
 
+static void ep_reset_338x(struct net2280_regs __iomem *regs,
+                                       struct net2280_ep *ep)
+{
+       u32 tmp, dmastat;
+
+       ep->desc = NULL;
+       INIT_LIST_HEAD(&ep->queue);
+
+       usb_ep_set_maxpacket_limit(&ep->ep, ~0);
+       ep->ep.ops = &net2280_ep_ops;
+
+       /* disable the dma, irqs, endpoint... */
+       if (ep->dma) {
+               writel(0, &ep->dma->dmactl);
+               writel((1 << DMA_ABORT_DONE_INTERRUPT) |
+                      (1 << DMA_PAUSE_DONE_INTERRUPT) |
+                      (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) |
+                      (1 << DMA_TRANSACTION_DONE_INTERRUPT)
+                      /* | (1 << DMA_ABORT) */
+                      , &ep->dma->dmastat);
+
+               dmastat = readl(&ep->dma->dmastat);
+               if (dmastat == 0x5002) {
+                       WARNING(ep->dev, "The dmastat return = %x!!\n",
+                              dmastat);
+                       writel(0x5a, &ep->dma->dmastat);
+               }
+
+               tmp = readl(®s->pciirqenb0);
+               tmp &= ~(1 << ep_bit[ep->num]);
+               writel(tmp, ®s->pciirqenb0);
+       } else {
+               if (ep->num < 5) {
+                       tmp = readl(®s->pciirqenb1);
+                       tmp &= ~(1 << (8 + ep->num));   /* completion */
+                       writel(tmp, ®s->pciirqenb1);
+               }
+       }
+       writel(0, &ep->regs->ep_irqenb);
+
+       writel((1 << SHORT_PACKET_OUT_DONE_INTERRUPT) |
+              (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+              (1 << FIFO_OVERFLOW) |
+              (1 << DATA_PACKET_RECEIVED_INTERRUPT) |
+              (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) |
+              (1 << DATA_OUT_PING_TOKEN_INTERRUPT) |
+              (1 << DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat);
+}
+
 static void nuke (struct net2280_ep *);
 
 static int net2280_disable (struct usb_ep *_ep)
 
        spin_lock_irqsave (&ep->dev->lock, flags);
        nuke (ep);
-       ep_reset (ep->dev->regs, ep);
+
+       if (ep->dev->pdev->vendor == 0x10b5)
+               ep_reset_338x(ep->dev->regs, ep);
+       else
+               ep_reset_228x(ep->dev->regs, ep);
 
        VDEBUG (ep->dev, "disabled %s %s\n",
                        ep->dma ? "dma" : "pio", _ep->name);
 
        /* synch memory views with the device */
-       (void) readl (&ep->regs->ep_cfg);
+       (void)readl(&ep->cfg->ep_cfg);
 
        if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4)
                ep->dma = &ep->dev->dma [ep->num - 1];
        writel (readl (&dma->dmastat), &dma->dmastat);
 
        writel (td_dma, &dma->dmadesc);
+       if (ep->dev->pdev->vendor == 0x10b5)
+               dmactl |= (0x01 << DMA_REQUEST_OUTSTANDING);
        writel (dmactl, &dma->dmactl);
 
        /* erratum 0116 workaround part 3:  pci arbiter away from net2280 */
        start_queue (ep, tmp, req->td_dma);
 }
 
+static inline void resume_dma(struct net2280_ep *ep)
+{
+       writel(readl(&ep->dma->dmactl) | (1 << DMA_ENABLE), &ep->dma->dmactl);
+
+       ep->dma_started = true;
+}
+
+static inline void ep_stop_dma(struct net2280_ep *ep)
+{
+       writel(readl(&ep->dma->dmactl) & ~(1 << DMA_ENABLE), &ep->dma->dmactl);
+       spin_stop_dma(ep->dma);
+
+       ep->dma_started = false;
+}
+
 static inline void
 queue_dma (struct net2280_ep *ep, struct net2280_request *req, int valid)
 {
 
        /* kickstart this i/o queue? */
        if (list_empty (&ep->queue) && !ep->stopped) {
+               /* DMA request while EP halted */
+               if (ep->dma &&
+                   (readl(&ep->regs->ep_rsp) & (1 << CLEAR_ENDPOINT_HALT)) &&
+                       (dev->pdev->vendor == 0x10b5)) {
+                       int valid = 1;
+                       if (ep->is_in) {
+                               int expect;
+                               expect = likely(req->req.zero ||
+                                               ((req->req.length %
+                                                 ep->ep.maxpacket) != 0));
+                               if (expect != ep->in_fifo_validate)
+                                       valid = 0;
+                       }
+                       queue_dma(ep, req, valid);
+               }
                /* use DMA if the endpoint supports it, else pio */
-               if (ep->dma)
+               else if (ep->dma)
                        start_dma (ep, req);
                else {
                        /* maybe there's no control data, just status ack */
                } else if (!ep->is_in
                                && (req->req.length % ep->ep.maxpacket) != 0) {
                        tmp = readl (&ep->regs->ep_stat);
+                       if (ep->dev->pdev->vendor == 0x10b5)
+                               return dma_done(ep, req, tmp, 0);
 
                        /* AVOID TROUBLE HERE by not issuing short reads from
                         * your gadget driver.  That helps avoids errata 0121,
        start_queue (ep, dmactl, req->td_dma);
 }
 
-static void abort_dma (struct net2280_ep *ep)
+static void abort_dma_228x(struct net2280_ep *ep)
 {
        /* abort the current transfer */
        if (likely (!list_empty (&ep->queue))) {
        scan_dma_completions (ep);
 }
 
+static void abort_dma_338x(struct net2280_ep *ep)
+{
+       writel((1 << DMA_ABORT), &ep->dma->dmastat);
+       spin_stop_dma(ep->dma);
+}
+
+static void abort_dma(struct net2280_ep *ep)
+{
+       if (ep->dev->pdev->vendor == 0x17cc)
+               return abort_dma_228x(ep);
+       return abort_dma_338x(ep);
+}
+
 /* dequeue ALL requests */
 static void nuke (struct net2280_ep *ep)
 {
                                ep->wedged = 1;
                } else {
                        clear_halt (ep);
+                       if (ep->dev->pdev->vendor == 0x10b5 &&
+                               !list_empty(&ep->queue) && ep->td_dma)
+                                       restart_dma(ep);
                        ep->wedged = 0;
                }
                (void) readl (&ep->regs->ep_rsp);
 
        spin_lock_irqsave (&dev->lock, flags);
        tmp = readl (&dev->usb->usbctl);
-       if (value)
+       if (value) {
                tmp |= (1 << SELF_POWERED_STATUS);
-       else
+               dev->selfpowered = 1;
+       } else {
                tmp &= ~(1 << SELF_POWERED_STATUS);
+               dev->selfpowered = 0;
+       }
        writel (tmp, &dev->usb->usbctl);
        spin_unlock_irqrestore (&dev->lock, flags);
 
        /* DMA Control Registers */
 
        /* Configurable EP Control Registers */
-       for (i = 0; i < 7; i++) {
+       for (i = 0; i < dev->n_ep; i++) {
                struct net2280_ep       *ep;
 
                ep = &dev->ep [i];
                if (i && !ep->desc)
                        continue;
 
-               t1 = readl (&ep->regs->ep_cfg);
+               t1 = readl(&ep->cfg->ep_cfg);
                t2 = readl (&ep->regs->ep_rsp) & 0xff;
                t = scnprintf (next, size,
                                "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s"
        t = scnprintf (next, size, "\nirqs:  ");
        size -= t;
        next += t;
-       for (i = 0; i < 7; i++) {
+       for (i = 0; i < dev->n_ep; i++) {
                struct net2280_ep       *ep;
 
                ep = &dev->ep [i];
        size = PAGE_SIZE;
        spin_lock_irqsave (&dev->lock, flags);
 
-       for (i = 0; i < 7; i++) {
+       for (i = 0; i < dev->n_ep; i++) {
                struct net2280_ep               *ep = &dev->ep [i];
                struct net2280_request          *req;
                int                             t;
        list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list);
 }
 
+static void defect7374_disable_data_eps(struct net2280 *dev)
+{
+       /*
+        * For Defect 7374, disable data EPs (and more):
+        *  - This phase undoes the earlier phase of the Defect 7374 workaround,
+        *    returing ep regs back to normal.
+        */
+       struct net2280_ep *ep;
+       int i;
+       unsigned char ep_sel;
+       u32 tmp_reg;
+
+       for (i = 1; i < 5; i++) {
+               ep = &dev->ep[i];
+               writel(0, &ep->cfg->ep_cfg);
+       }
+
+       /* CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN */
+       for (i = 0; i < 6; i++)
+               writel(0, &dev->dep[i].dep_cfg);
+
+       for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
+               /* Select an endpoint for subsequent operations: */
+               tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+               writel(((tmp_reg & ~0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl);
+
+               if (ep_sel < 2 || (ep_sel > 9 && ep_sel < 14) ||
+                                       ep_sel == 18 || ep_sel == 20)
+                       continue;
+
+               /* Change settings on some selected endpoints */
+               tmp_reg = readl(&dev->plregs->pl_ep_cfg_4);
+               tmp_reg &= ~(1 << NON_CTRL_IN_TOLERATE_BAD_DIR);
+               writel(tmp_reg, &dev->plregs->pl_ep_cfg_4);
+               tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+               tmp_reg |= (1 << EP_INITIALIZED);
+               writel(tmp_reg, &dev->plregs->pl_ep_ctrl);
+       }
+}
+
+static void defect7374_enable_data_eps_zero(struct net2280 *dev)
+{
+       u32 tmp = 0, tmp_reg;
+       u32 fsmvalue, scratch;
+       int i;
+       unsigned char ep_sel;
+
+       scratch = get_idx_reg(dev->regs, SCRATCH);
+       fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD);
+       scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
+
+       /*See if firmware needs to set up for workaround*/
+       if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
+               WARNING(dev, "Operate Defect 7374 workaround soft this time");
+               WARNING(dev, "It will operate on cold-reboot and SS connect");
+
+               /*GPEPs:*/
+               tmp = ((0 << ENDPOINT_NUMBER) | (1 << ENDPOINT_DIRECTION) |
+                      (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
+                      ((dev->enhanced_mode) ?
+                       1 << OUT_ENDPOINT_ENABLE : 1 << ENDPOINT_ENABLE) |
+                      (1 << IN_ENDPOINT_ENABLE));
+
+               for (i = 1; i < 5; i++)
+                       writel(tmp, &dev->ep[i].cfg->ep_cfg);
+
+               /* CSRIN, PCIIN, STATIN, RCIN*/
+               tmp = ((0 << ENDPOINT_NUMBER) | (1 << ENDPOINT_ENABLE));
+               writel(tmp, &dev->dep[1].dep_cfg);
+               writel(tmp, &dev->dep[3].dep_cfg);
+               writel(tmp, &dev->dep[4].dep_cfg);
+               writel(tmp, &dev->dep[5].dep_cfg);
+
+               /*Implemented for development and debug.
+                * Can be refined/tuned later.*/
+               for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
+                       /* Select an endpoint for subsequent operations: */
+                       tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+                       writel(((tmp_reg & ~0x1f) | ep_sel),
+                              &dev->plregs->pl_ep_ctrl);
+
+                       if (ep_sel == 1) {
+                               tmp =
+                                   (readl(&dev->plregs->pl_ep_ctrl) |
+                                    (1 << CLEAR_ACK_ERROR_CODE) | 0);
+                               writel(tmp, &dev->plregs->pl_ep_ctrl);
+                               continue;
+                       }
+
+                       if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) ||
+                                       ep_sel == 18  || ep_sel == 20)
+                               continue;
+
+                       tmp = (readl(&dev->plregs->pl_ep_cfg_4) |
+                                (1 << NON_CTRL_IN_TOLERATE_BAD_DIR) | 0);
+                       writel(tmp, &dev->plregs->pl_ep_cfg_4);
+
+                       tmp = readl(&dev->plregs->pl_ep_ctrl) &
+                               ~(1 << EP_INITIALIZED);
+                       writel(tmp, &dev->plregs->pl_ep_ctrl);
+
+               }
+
+               /* Set FSM to focus on the first Control Read:
+                * - Tip: Connection speed is known upon the first
+                * setup request.*/
+               scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ;
+               set_idx_reg(dev->regs, SCRATCH, scratch);
+
+       } else{
+               WARNING(dev, "Defect 7374 workaround soft will NOT operate");
+               WARNING(dev, "It will operate on cold-reboot and SS connect");
+       }
+}
+
 /* keeping it simple:
  * - one bus driver, initted first;
  * - one function driver, initted second
  * perhaps to bind specific drivers to specific devices.
  */
 
-static void usb_reset (struct net2280 *dev)
+static void usb_reset_228x(struct net2280 *dev)
 {
        u32     tmp;
 
 
        /* clear old dma and irq state */
        for (tmp = 0; tmp < 4; tmp++) {
-               struct net2280_ep       *ep = &dev->ep [tmp + 1];
-
+               struct net2280_ep       *ep = &dev->ep[tmp + 1];
                if (ep->dma)
-                       abort_dma (ep);
+                       abort_dma(ep);
        }
+
        writel (~0, &dev->regs->irqstat0),
        writel (~(1 << SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1),
 
        set_fifo_mode (dev, (fifo_mode <= 2) ? fifo_mode : 0);
 }
 
-static void usb_reinit (struct net2280 *dev)
+static void usb_reset_338x(struct net2280 *dev)
+{
+       u32 tmp;
+       u32 fsmvalue;
+
+       dev->gadget.speed = USB_SPEED_UNKNOWN;
+       (void)readl(&dev->usb->usbctl);
+
+       net2280_led_init(dev);
+
+       fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+                       (0xf << DEFECT7374_FSM_FIELD);
+
+       /* See if firmware needs to set up for workaround: */
+       if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
+               INFO(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__,
+                    fsmvalue);
+       } else {
+               /* disable automatic responses, and irqs */
+               writel(0, &dev->usb->stdrsp);
+               writel(0, &dev->regs->pciirqenb0);
+               writel(0, &dev->regs->pciirqenb1);
+       }
+
+       /* clear old dma and irq state */
+       for (tmp = 0; tmp < 4; tmp++) {
+               struct net2280_ep *ep = &dev->ep[tmp + 1];
+
+               if (ep->dma)
+                       abort_dma(ep);
+       }
+
+       writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1);
+
+       if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) {
+               /* reset, and enable pci */
+               tmp = readl(&dev->regs->devinit) |
+                   (1 << PCI_ENABLE) |
+                   (1 << FIFO_SOFT_RESET) |
+                   (1 << USB_SOFT_RESET) |
+                   (1 << M8051_RESET);
+
+               writel(tmp, &dev->regs->devinit);
+       }
+
+       /* always ep-{1,2,3,4} ... maybe not ep-3 or ep-4 */
+       INIT_LIST_HEAD(&dev->gadget.ep_list);
+
+       for (tmp = 1; tmp < dev->n_ep; tmp++)
+               list_add_tail(&dev->ep[tmp].ep.ep_list, &dev->gadget.ep_list);
+
+}
+
+static void usb_reset(struct net2280 *dev)
+{
+       if (dev->pdev->vendor == 0x17cc)
+               return usb_reset_228x(dev);
+       return usb_reset_338x(dev);
+}
+
+static void usb_reinit_228x(struct net2280 *dev)
 {
        u32     tmp;
        int     init_dma;
                } else
                        ep->fifo_size = 64;
                ep->regs = &dev->epregs [tmp];
-               ep_reset (dev->regs, ep);
+               ep->cfg = &dev->epregs[tmp];
+               ep_reset_228x(dev->regs, ep);
        }
        usb_ep_set_maxpacket_limit(&dev->ep [0].ep, 64);
        usb_ep_set_maxpacket_limit(&dev->ep [5].ep, 64);
                writel (EP_DONTUSE, &dev->dep [tmp].dep_cfg);
 }
 
-static void ep0_start (struct net2280 *dev)
+static void usb_reinit_338x(struct net2280 *dev)
+{
+       int init_dma;
+       int i;
+       u32 tmp, val;
+       u32 fsmvalue;
+       static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 };
+       static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00,
+                                               0x00, 0xC0, 0x00, 0xC0 };
+
+       /* use_dma changes are ignored till next device re-init */
+       init_dma = use_dma;
+
+       /* basic endpoint init */
+       for (i = 0; i < dev->n_ep; i++) {
+               struct net2280_ep *ep = &dev->ep[i];
+
+               ep->ep.name = ep_name[i];
+               ep->dev = dev;
+               ep->num = i;
+
+               if (i > 0 && i <= 4 && init_dma)
+                       ep->dma = &dev->dma[i - 1];
+
+               if (dev->enhanced_mode) {
+                       ep->cfg = &dev->epregs[ne[i]];
+                       ep->regs = (struct net2280_ep_regs __iomem *)
+                               (((void *)&dev->epregs[ne[i]]) +
+                               ep_reg_addr[i]);
+                       ep->fiforegs = &dev->fiforegs[i];
+               } else {
+                       ep->cfg = &dev->epregs[i];
+                       ep->regs = &dev->epregs[i];
+                       ep->fiforegs = &dev->fiforegs[i];
+               }
+
+               ep->fifo_size = (i != 0) ? 2048 : 512;
+
+               ep_reset_338x(dev->regs, ep);
+       }
+       usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 512);
+
+       dev->gadget.ep0 = &dev->ep[0].ep;
+       dev->ep[0].stopped = 0;
+
+       /* Link layer set up */
+       fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+                               (0xf << DEFECT7374_FSM_FIELD);
+
+       /* See if driver needs to set up for workaround: */
+       if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
+               INFO(dev, "%s: Defect 7374 FsmValue %08x\n",
+                                               __func__, fsmvalue);
+       else {
+               tmp = readl(&dev->usb_ext->usbctl2) &
+                   ~((1 << U1_ENABLE) | (1 << U2_ENABLE) | (1 << LTM_ENABLE));
+               writel(tmp, &dev->usb_ext->usbctl2);
+       }
+
+       /* Hardware Defect and Workaround */
+       val = readl(&dev->ll_lfps_regs->ll_lfps_5);
+       val &= ~(0xf << TIMER_LFPS_6US);
+       val |= 0x5 << TIMER_LFPS_6US;
+       writel(val, &dev->ll_lfps_regs->ll_lfps_5);
+
+       val = readl(&dev->ll_lfps_regs->ll_lfps_6);
+       val &= ~(0xffff << TIMER_LFPS_80US);
+       val |= 0x0100 << TIMER_LFPS_80US;
+       writel(val, &dev->ll_lfps_regs->ll_lfps_6);
+
+       /*
+        * AA_AB Errata. Issue 4. Workaround for SuperSpeed USB
+        * Hot Reset Exit Handshake may Fail in Specific Case using
+        * Default Register Settings. Workaround for Enumeration test.
+        */
+       val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2);
+       val &= ~(0x1f << HOT_TX_NORESET_TS2);
+       val |= 0x10 << HOT_TX_NORESET_TS2;
+       writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2);
+
+       val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3);
+       val &= ~(0x1f << HOT_RX_RESET_TS2);
+       val |= 0x3 << HOT_RX_RESET_TS2;
+       writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3);
+
+       /*
+        * Set Recovery Idle to Recover bit:
+        * - On SS connections, setting Recovery Idle to Recover Fmw improves
+        *   link robustness with various hosts and hubs.
+        * - It is safe to set for all connection speeds; all chip revisions.
+        * - R-M-W to leave other bits undisturbed.
+        * - Reference PLX TT-7372
+       */
+       val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit);
+       val |= (1 << RECOVERY_IDLE_TO_RECOVER_FMW);
+       writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit);
+
+       INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+       /* disable dedicated endpoints */
+       writel(0x0D, &dev->dep[0].dep_cfg);
+       writel(0x0D, &dev->dep[1].dep_cfg);
+       writel(0x0E, &dev->dep[2].dep_cfg);
+       writel(0x0E, &dev->dep[3].dep_cfg);
+       writel(0x0F, &dev->dep[4].dep_cfg);
+       writel(0x0C, &dev->dep[5].dep_cfg);
+}
+
+static void usb_reinit(struct net2280 *dev)
+{
+       if (dev->pdev->vendor == 0x17cc)
+               return usb_reinit_228x(dev);
+       return usb_reinit_338x(dev);
+}
+
+static void ep0_start_228x(struct net2280 *dev)
 {
        writel (  (1 << CLEAR_EP_HIDE_STATUS_PHASE)
                | (1 << CLEAR_NAK_OUT_PACKETS)
        (void) readl (&dev->usb->usbctl);
 }
 
+static void ep0_start_338x(struct net2280 *dev)
+{
+       u32 fsmvalue;
+
+       fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+                       (0xf << DEFECT7374_FSM_FIELD);
+
+       if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
+               INFO(dev, "%s: Defect 7374 FsmValue %08x\n", __func__,
+                    fsmvalue);
+       else
+               writel((1 << CLEAR_NAK_OUT_PACKETS_MODE) |
+                      (1 << SET_EP_HIDE_STATUS_PHASE),
+                      &dev->epregs[0].ep_rsp);
+
+       /*
+        * hardware optionally handles a bunch of standard requests
+        * that the API hides from drivers anyway.  have it do so.
+        * endpoint status/features are handled in software, to
+        * help pass tests for some dubious behavior.
+        */
+       writel((1 << SET_ISOCHRONOUS_DELAY) |
+              (1 << SET_SEL) |
+              (1 << SET_TEST_MODE) |
+              (1 << SET_ADDRESS) |
+              (1 << GET_INTERFACE_STATUS) |
+              (1 << GET_DEVICE_STATUS),
+               &dev->usb->stdrsp);
+       dev->wakeup_enable = 1;
+       writel((1 << USB_ROOT_PORT_WAKEUP_ENABLE) |
+              (dev->softconnect << USB_DETECT_ENABLE) |
+              (1 << DEVICE_REMOTE_WAKEUP_ENABLE),
+              &dev->usb->usbctl);
+
+       /* enable irqs so we can see ep0 and general operation  */
+       writel((1 << SETUP_PACKET_INTERRUPT_ENABLE) |
+              (1 << ENDPOINT_0_INTERRUPT_ENABLE)
+              , &dev->regs->pciirqenb0);
+       writel((1 << PCI_INTERRUPT_ENABLE) |
+              (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE) |
+              (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) |
+              (1 << VBUS_INTERRUPT_ENABLE),
+              &dev->regs->pciirqenb1);
+
+       /* don't leave any writes posted */
+       (void)readl(&dev->usb->usbctl);
+}
+
+static void ep0_start(struct net2280 *dev)
+{
+       if (dev->pdev->vendor == 0x17cc)
+               return ep0_start_228x(dev);
+       return ep0_start_338x(dev);
+}
+
 /* when a driver is successfully registered, it will receive
  * control requests including set_configuration(), which enables
  * non-control requests.  then usb traffic follows until a
 
        dev = container_of (_gadget, struct net2280, gadget);
 
-       for (i = 0; i < 7; i++)
+       for (i = 0; i < dev->n_ep; i++)
                dev->ep [i].irqs = 0;
 
        /* hook up the driver ... */
        if (retval) goto err_func;
 
        /* Enable force-full-speed testing mode, if desired */
-       if (full_speed)
+       if (full_speed && dev->pdev->vendor == 0x17cc)
                writel(1 << FORCE_FULL_SPEED_MODE, &dev->usb->xcvrdiag);
 
        /* ... then enable host detection and ep0; and we're ready
         * for set_configuration as well as eventual disconnect.
         */
        net2280_led_active (dev, 1);
+
+       if (dev->pdev->vendor == 0x10b5)
+               defect7374_enable_data_eps_zero(dev);
+
        ep0_start (dev);
 
        DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n",
         * and kill any outstanding requests.
         */
        usb_reset (dev);
-       for (i = 0; i < 7; i++)
+       for (i = 0; i < dev->n_ep; i++)
                nuke (&dev->ep [i]);
 
        /* report disconnect; the driver is already quiesced */
        net2280_led_active (dev, 0);
 
        /* Disable full-speed test mode */
-       writel(0, &dev->usb->xcvrdiag);
+       if (dev->pdev->vendor == 0x17cc)
+               writel(0, &dev->usb->xcvrdiag);
 
        device_remove_file (&dev->pdev->dev, &dev_attr_function);
        device_remove_file (&dev->pdev->dev, &dev_attr_queues);
        return NULL;
 }
 
+static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
+{
+       u32 scratch, fsmvalue;
+       u32 ack_wait_timeout, state;
+
+       /* Workaround for Defect 7374 (U1/U2 erroneously rejected): */
+       scratch = get_idx_reg(dev->regs, SCRATCH);
+       fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD);
+       scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
+
+       if (!((fsmvalue == DEFECT7374_FSM_WAITING_FOR_CONTROL_READ) &&
+                               (r.bRequestType & USB_DIR_IN)))
+               return;
+
+       /* This is the first Control Read for this connection: */
+       if (!(readl(&dev->usb->usbstat) & (1 << SUPER_SPEED_MODE))) {
+               /*
+                * Connection is NOT SS:
+                * - Connection must be FS or HS.
+                * - This FSM state should allow workaround software to
+                * run after the next USB connection.
+                */
+               scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ;
+               goto restore_data_eps;
+       }
+
+       /* Connection is SS: */
+       for (ack_wait_timeout = 0;
+                       ack_wait_timeout < DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS;
+                       ack_wait_timeout++) {
+
+               state = readl(&dev->plregs->pl_ep_status_1)
+                       & (0xff << STATE);
+               if ((state >= (ACK_GOOD_NORMAL << STATE)) &&
+                       (state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) {
+                       scratch |= DEFECT7374_FSM_SS_CONTROL_READ;
+                       break;
+               }
+
+               /*
+                * We have not yet received host's Data Phase ACK
+                * - Wait and try again.
+                */
+               udelay(DEFECT_7374_PROCESSOR_WAIT_TIME);
+
+               continue;
+       }
+
+
+       if (ack_wait_timeout >= DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS) {
+               ERROR(dev, "FAIL: Defect 7374 workaround waited but failed "
+               "to detect SS host's data phase ACK.");
+               ERROR(dev, "PL_EP_STATUS_1(23:16):.Expected from 0x11 to 0x16"
+               "got 0x%2.2x.\n", state >> STATE);
+       } else {
+               WARNING(dev, "INFO: Defect 7374 workaround waited about\n"
+               "%duSec for Control Read Data Phase ACK\n",
+                       DEFECT_7374_PROCESSOR_WAIT_TIME * ack_wait_timeout);
+       }
+
+restore_data_eps:
+       /*
+        * Restore data EPs to their pre-workaround settings (disabled,
+        * initialized, and other details).
+        */
+       defect7374_disable_data_eps(dev);
+
+       set_idx_reg(dev->regs, SCRATCH, scratch);
+
+       return;
+}
+
+static void ep_stall(struct net2280_ep *ep, int stall)
+{
+       struct net2280 *dev = ep->dev;
+       u32 val;
+       static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 };
+
+       if (stall) {
+               writel((1 << SET_ENDPOINT_HALT) |
+                      /* (1 << SET_NAK_PACKETS) | */
+                      (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE),
+                      &ep->regs->ep_rsp);
+               ep->is_halt = 1;
+       } else {
+               if (dev->gadget.speed == USB_SPEED_SUPER) {
+                       /*
+                        * Workaround for SS SeqNum not cleared via
+                        * Endpoint Halt (Clear) bit. select endpoint
+                        */
+                       val = readl(&dev->plregs->pl_ep_ctrl);
+                       val = (val & ~0x1f) | ep_pl[ep->num];
+                       writel(val, &dev->plregs->pl_ep_ctrl);
+
+                       val |= (1 << SEQUENCE_NUMBER_RESET);
+                       writel(val, &dev->plregs->pl_ep_ctrl);
+               }
+               val = readl(&ep->regs->ep_rsp);
+               val |= (1 << CLEAR_ENDPOINT_HALT) |
+                       (1 << CLEAR_ENDPOINT_TOGGLE);
+               writel(val
+                      /* | (1 << CLEAR_NAK_PACKETS)*/
+                      , &ep->regs->ep_rsp);
+               ep->is_halt = 0;
+               val = readl(&ep->regs->ep_rsp);
+       }
+}
+
+static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged)
+{
+       /* set/clear, then synch memory views with the device */
+       if (value) {
+               ep->stopped = 1;
+               if (ep->num == 0)
+                       ep->dev->protocol_stall = 1;
+               else {
+                       if (ep->dma)
+                               ep_stop_dma(ep);
+                       ep_stall(ep, true);
+               }
+
+               if (wedged)
+                       ep->wedged = 1;
+       } else {
+               ep->stopped = 0;
+               ep->wedged = 0;
+
+               ep_stall(ep, false);
+
+               /* Flush the queue */
+               if (!list_empty(&ep->queue)) {
+                       struct net2280_request *req =
+                           list_entry(ep->queue.next, struct net2280_request,
+                                      queue);
+                       if (ep->dma)
+                               resume_dma(ep);
+                       else {
+                               if (ep->is_in)
+                                       write_fifo(ep, &req->req);
+                               else {
+                                       if (read_fifo(ep, req))
+                                               done(ep, req, 0);
+                               }
+                       }
+               }
+       }
+}
+
+static void handle_stat0_irqs_superspeed(struct net2280 *dev,
+               struct net2280_ep *ep, struct usb_ctrlrequest r)
+{
+       int tmp = 0;
+
+#define        w_value         le16_to_cpu(r.wValue)
+#define        w_index         le16_to_cpu(r.wIndex)
+#define        w_length        le16_to_cpu(r.wLength)
+
+       switch (r.bRequest) {
+               struct net2280_ep *e;
+               u16 status;
+
+       case USB_REQ_SET_CONFIGURATION:
+               dev->addressed_state = !w_value;
+               goto usb3_delegate;
+
+       case USB_REQ_GET_STATUS:
+               switch (r.bRequestType) {
+               case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+                       status = dev->wakeup_enable ? 0x02 : 0x00;
+                       if (dev->selfpowered)
+                               status |= 1 << 0;
+                       status |= (dev->u1_enable << 2 | dev->u2_enable << 3 |
+                                                       dev->ltm_enable << 4);
+                       writel(0, &dev->epregs[0].ep_irqenb);
+                       set_fifo_bytecount(ep, sizeof(status));
+                       writel((__force u32) status, &dev->epregs[0].ep_data);
+                       allow_status_338x(ep);
+                       break;
+
+               case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+                       e = get_ep_by_addr(dev, w_index);
+                       if (!e)
+                               goto do_stall3;
+                       status = readl(&e->regs->ep_rsp) &
+                                               (1 << CLEAR_ENDPOINT_HALT);
+                       writel(0, &dev->epregs[0].ep_irqenb);
+                       set_fifo_bytecount(ep, sizeof(status));
+                       writel((__force u32) status, &dev->epregs[0].ep_data);
+                       allow_status_338x(ep);
+                       break;
+
+               default:
+                       goto usb3_delegate;
+               }
+               break;
+
+       case USB_REQ_CLEAR_FEATURE:
+               switch (r.bRequestType) {
+               case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+                       if (!dev->addressed_state) {
+                               switch (w_value) {
+                               case USB_DEVICE_U1_ENABLE:
+                                       dev->u1_enable = 0;
+                                       writel(readl(&dev->usb_ext->usbctl2) &
+                                               ~(1 << U1_ENABLE),
+                                               &dev->usb_ext->usbctl2);
+                                       allow_status_338x(ep);
+                                       goto next_endpoints3;
+
+                               case USB_DEVICE_U2_ENABLE:
+                                       dev->u2_enable = 0;
+                                       writel(readl(&dev->usb_ext->usbctl2) &
+                                               ~(1 << U2_ENABLE),
+                                               &dev->usb_ext->usbctl2);
+                                       allow_status_338x(ep);
+                                       goto next_endpoints3;
+
+                               case USB_DEVICE_LTM_ENABLE:
+                                       dev->ltm_enable = 0;
+                                       writel(readl(&dev->usb_ext->usbctl2) &
+                                               ~(1 << LTM_ENABLE),
+                                               &dev->usb_ext->usbctl2);
+                                       allow_status_338x(ep);
+                                       goto next_endpoints3;
+
+                               default:
+                                       break;
+                               }
+                       }
+                       if (w_value == USB_DEVICE_REMOTE_WAKEUP) {
+                               dev->wakeup_enable = 0;
+                               writel(readl(&dev->usb->usbctl) &
+                                       ~(1 << DEVICE_REMOTE_WAKEUP_ENABLE),
+                                       &dev->usb->usbctl);
+                               allow_status_338x(ep);
+                               break;
+                       }
+                       goto usb3_delegate;
+
+               case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+                       e = get_ep_by_addr(dev, w_index);
+                       if (!e)
+                               goto do_stall3;
+                       if (w_value != USB_ENDPOINT_HALT)
+                               goto do_stall3;
+                       VDEBUG(dev, "%s clear halt\n", e->ep.name);
+                       ep_stall(e, false);
+                       if (!list_empty(&e->queue) && e->td_dma)
+                               restart_dma(e);
+                       allow_status(ep);
+                       ep->stopped = 1;
+                       break;
+
+               default:
+                       goto usb3_delegate;
+               }
+               break;
+       case USB_REQ_SET_FEATURE:
+               switch (r.bRequestType) {
+               case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+                       if (!dev->addressed_state) {
+                               switch (w_value) {
+                               case USB_DEVICE_U1_ENABLE:
+                                       dev->u1_enable = 1;
+                                       writel(readl(&dev->usb_ext->usbctl2) |
+                                               (1 << U1_ENABLE),
+                                               &dev->usb_ext->usbctl2);
+                                       allow_status_338x(ep);
+                                       goto next_endpoints3;
+
+                               case USB_DEVICE_U2_ENABLE:
+                                       dev->u2_enable = 1;
+                                       writel(readl(&dev->usb_ext->usbctl2) |
+                                               (1 << U2_ENABLE),
+                                               &dev->usb_ext->usbctl2);
+                                       allow_status_338x(ep);
+                                       goto next_endpoints3;
+
+                               case USB_DEVICE_LTM_ENABLE:
+                                       dev->ltm_enable = 1;
+                                       writel(readl(&dev->usb_ext->usbctl2) |
+                                               (1 << LTM_ENABLE),
+                                               &dev->usb_ext->usbctl2);
+                                       allow_status_338x(ep);
+                                       goto next_endpoints3;
+                               default:
+                                       break;
+                               }
+                       }
+
+                       if (w_value == USB_DEVICE_REMOTE_WAKEUP) {
+                               dev->wakeup_enable = 1;
+                               writel(readl(&dev->usb->usbctl) |
+                                       (1 << DEVICE_REMOTE_WAKEUP_ENABLE),
+                                       &dev->usb->usbctl);
+                               allow_status_338x(ep);
+                               break;
+                       }
+                       goto usb3_delegate;
+
+               case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+                       e = get_ep_by_addr(dev, w_index);
+                       if (!e || (w_value != USB_ENDPOINT_HALT))
+                               goto do_stall3;
+                       ep_stdrsp(e, true, false);
+                       allow_status_338x(ep);
+                       break;
+
+               default:
+                       goto usb3_delegate;
+               }
+
+               break;
+       default:
+
+usb3_delegate:
+               VDEBUG(dev, "setup %02x.%02x v%04x i%04x l%04x ep_cfg %08x\n",
+                               r.bRequestType, r.bRequest,
+                               w_value, w_index, w_length,
+                               readl(&ep->cfg->ep_cfg));
+
+               ep->responded = 0;
+               spin_unlock(&dev->lock);
+               tmp = dev->driver->setup(&dev->gadget, &r);
+               spin_lock(&dev->lock);
+       }
+do_stall3:
+       if (tmp < 0) {
+               VDEBUG(dev, "req %02x.%02x protocol STALL; stat %d\n",
+                               r.bRequestType, r.bRequest, tmp);
+               dev->protocol_stall = 1;
+               /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */
+               ep_stall(ep, true);
+       }
+
+next_endpoints3:
+
+#undef w_value
+#undef w_index
+#undef w_length
+
+       return;
+}
+
 static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
 {
        struct net2280_ep       *ep;
                struct net2280_request          *req;
 
                if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
-                       if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED))
+                       u32 val = readl(&dev->usb->usbstat);
+                       if (val & (1 << SUPER_SPEED)) {
+                               dev->gadget.speed = USB_SPEED_SUPER;
+                               usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+                                               EP0_SS_MAX_PACKET_SIZE);
+                       } else if (val & (1 << HIGH_SPEED)) {
                                dev->gadget.speed = USB_SPEED_HIGH;
-                       else
+                               usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+                                               EP0_HS_MAX_PACKET_SIZE);
+                       } else {
                                dev->gadget.speed = USB_SPEED_FULL;
+                               usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+                                               EP0_HS_MAX_PACKET_SIZE);
+                       }
                        net2280_led_speed (dev, dev->gadget.speed);
                        DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed));
                }
                }
                ep->stopped = 0;
                dev->protocol_stall = 0;
-
-               if (ep->dev->pdev->device == 0x2280)
-                       tmp = (1 << FIFO_OVERFLOW)
-                               | (1 << FIFO_UNDERFLOW);
-               else
-                       tmp = 0;
-
-               writel (tmp | (1 << TIMEOUT)
-                       | (1 << USB_STALL_SENT)
-                       | (1 << USB_IN_NAK_SENT)
-                       | (1 << USB_IN_ACK_RCVD)
-                       | (1 << USB_OUT_PING_NAK_SENT)
-                       | (1 << USB_OUT_ACK_SENT)
-                       | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT)
-                       | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)
-                       | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
-                       | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
-                       | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
-                       | (1 << DATA_IN_TOKEN_INTERRUPT)
-                       , &ep->regs->ep_stat);
-               u.raw [0] = readl (&dev->usb->setup0123);
-               u.raw [1] = readl (&dev->usb->setup4567);
+               if (dev->pdev->vendor == 0x10b5)
+                       ep->is_halt = 0;
+               else{
+                       if (ep->dev->pdev->device == 0x2280)
+                               tmp = (1 << FIFO_OVERFLOW) |
+                                   (1 << FIFO_UNDERFLOW);
+                       else
+                               tmp = 0;
+
+                       writel(tmp | (1 << TIMEOUT) |
+                                  (1 << USB_STALL_SENT) |
+                                  (1 << USB_IN_NAK_SENT) |
+                                  (1 << USB_IN_ACK_RCVD) |
+                                  (1 << USB_OUT_PING_NAK_SENT) |
+                                  (1 << USB_OUT_ACK_SENT) |
+                                  (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) |
+                                  (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+                                  (1 << DATA_PACKET_RECEIVED_INTERRUPT) |
+                                  (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) |
+                                  (1 << DATA_OUT_PING_TOKEN_INTERRUPT) |
+                                  (1 << DATA_IN_TOKEN_INTERRUPT)
+                                  , &ep->regs->ep_stat);
+               }
+               u.raw[0] = readl(&dev->usb->setup0123);
+               u.raw[1] = readl(&dev->usb->setup4567);
 
                cpu_to_le32s (&u.raw [0]);
                cpu_to_le32s (&u.raw [1]);
 
+               if (dev->pdev->vendor == 0x10b5)
+                       defect7374_workaround(dev, u.r);
+
                tmp = 0;
 
 #define        w_value         le16_to_cpu(u.r.wValue)
                 * everything else goes uplevel to the gadget code.
                 */
                ep->responded = 1;
+
+               if (dev->gadget.speed == USB_SPEED_SUPER) {
+                       handle_stat0_irqs_superspeed(dev, ep, u.r);
+                       goto next_endpoints;
+               }
+
                switch (u.r.bRequest) {
                case USB_REQ_GET_STATUS: {
                        struct net2280_ep       *e;
                                VDEBUG(dev, "%s wedged, halt not cleared\n",
                                                ep->ep.name);
                        } else {
-                               VDEBUG(dev, "%s clear halt\n", ep->ep.name);
+                               VDEBUG(dev, "%s clear halt\n", e->ep.name);
                                clear_halt(e);
+                               if (ep->dev->pdev->vendor == 0x10b5 &&
+                                       !list_empty(&e->queue) && e->td_dma)
+                                               restart_dma(e);
                        }
                        allow_status (ep);
                        goto next_endpoints;
                        if (e->ep.name == ep0name)
                                goto do_stall;
                        set_halt (e);
+                       if (dev->pdev->vendor == 0x10b5 && e->dma)
+                               abort_dma(e);
                        allow_status (ep);
                        VDEBUG (dev, "%s set halt\n", ep->ep.name);
                        goto next_endpoints;
                                "ep_cfg %08x\n",
                                u.r.bRequestType, u.r.bRequest,
                                w_value, w_index, w_length,
-                               readl (&ep->regs->ep_cfg));
+                               readl(&ep->cfg->ep_cfg));
                        ep->responded = 0;
                        spin_unlock (&dev->lock);
                        tmp = dev->driver->setup (&dev->gadget, &u.r);
 
        /* after disconnect there's nothing else to do! */
        tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT);
-       mask = (1 << HIGH_SPEED) | (1 << FULL_SPEED);
+       mask = (1 << SUPER_SPEED) | (1 << HIGH_SPEED) | (1 << FULL_SPEED);
 
        /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
         * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and
                tmp = readl (&dma->dmastat);
                writel (tmp, &dma->dmastat);
 
+               /* dma sync*/
+               if (dev->pdev->vendor == 0x10b5) {
+                       u32 r_dmacount = readl(&dma->dmacount);
+                       if (!ep->is_in &&  (r_dmacount & 0x00FFFFFF) &&
+                           (tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT)))
+                               continue;
+               }
+
                /* chaining should stop on abort, short OUT from fifo,
                 * or (stat0 codepath) short OUT transfer.
                 */
                if (!use_dma_chaining) {
-                       if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT))
-                                       == 0) {
+                       if (!(tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT))) {
                                DEBUG (ep->dev, "%s no xact done? %08x\n",
                                        ep->ep.name, tmp);
                                continue;
        struct net2280          *dev = _dev;
 
        /* shared interrupt, not ours */
-       if (!(readl(&dev->regs->irqstat0) & (1 << INTA_ASSERTED)))
+       if (dev->pdev->vendor == 0x17cc &&
+               (!(readl(&dev->regs->irqstat0) & (1 << INTA_ASSERTED))))
                return IRQ_NONE;
 
        spin_lock (&dev->lock);
        /* control requests and PIO */
        handle_stat0_irqs (dev, readl (&dev->regs->irqstat0));
 
+       if (dev->pdev->vendor == 0x10b5) {
+               /* re-enable interrupt to trigger any possible new interrupt */
+               u32 pciirqenb1 = readl(&dev->regs->pciirqenb1);
+               writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1);
+               writel(pciirqenb1, &dev->regs->pciirqenb1);
+       }
+
        spin_unlock (&dev->lock);
 
        return IRQ_HANDLED;
        }
        if (dev->got_irq)
                free_irq (pdev->irq, dev);
+       if (use_msi && dev->pdev->vendor == 0x10b5)
+               pci_disable_msi(pdev);
        if (dev->regs)
                iounmap (dev->regs);
        if (dev->region)
        spin_lock_init (&dev->lock);
        dev->pdev = pdev;
        dev->gadget.ops = &net2280_ops;
-       dev->gadget.max_speed = USB_SPEED_HIGH;
+       dev->gadget.max_speed = (dev->pdev->vendor == 0x10b5) ?
+                               USB_SPEED_SUPER : USB_SPEED_HIGH;
 
        /* the "gadget" abstracts/virtualizes the controller */
        dev->gadget.name = driver_name;
        dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200);
        dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300);
 
-       /* put into initial config, link up all endpoints */
-       writel (0, &dev->usb->usbctl);
+       if (dev->pdev->vendor == 0x10b5) {
+               u32 fsmvalue;
+               u32 usbstat;
+               dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
+                                                       (base + 0x00b4);
+               dev->fiforegs = (struct usb338x_fifo_regs __iomem *)
+                                                       (base + 0x0500);
+               dev->llregs = (struct usb338x_ll_regs __iomem *)
+                                                       (base + 0x0700);
+               dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
+                                                       (base + 0x0748);
+               dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *)
+                                                       (base + 0x077c);
+               dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *)
+                                                       (base + 0x079c);
+               dev->plregs = (struct usb338x_pl_regs __iomem *)
+                                                       (base + 0x0800);
+               usbstat = readl(&dev->usb->usbstat);
+               dev->enhanced_mode = (usbstat & (1 << 11)) ? 1 : 0;
+               dev->n_ep = (dev->enhanced_mode) ? 9 : 5;
+               /* put into initial config, link up all endpoints */
+               fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+                                       (0xf << DEFECT7374_FSM_FIELD);
+               /* See if firmware needs to set up for workaround: */
+               if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ)
+                       writel(0, &dev->usb->usbctl);
+       } else{
+               dev->enhanced_mode = 0;
+               dev->n_ep = 7;
+               /* put into initial config, link up all endpoints */
+               writel(0, &dev->usb->usbctl);
+       }
+
        usb_reset (dev);
        usb_reinit (dev);
 
                goto done;
        }
 
+       if (use_msi && dev->pdev->vendor == 0x10b5)
+               if (pci_enable_msi(pdev))
+                       ERROR(dev, "Failed to enable MSI mode\n");
+
        if (request_irq (pdev->irq, net2280_irq, IRQF_SHARED, driver_name, dev)
                        != 0) {
                ERROR (dev, "request interrupt %d failed\n", pdev->irq);
        }
 
        /* enable lower-overhead pci memory bursts during DMA */
-       writel ( (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE)
+       if (dev->pdev->vendor == 0x17cc)
+               writel((1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE)
                        // 256 write retries may not be enough...
                        // | (1 << PCI_RETRY_ABORT_ENABLE)
                        | (1 << DMA_READ_MULTIPLE_ENABLE)
        INFO (dev, "%s\n", driver_desc);
        INFO (dev, "irq %d, pci mem %p, chip rev %04x\n",
                        pdev->irq, base, dev->chiprev);
-       INFO (dev, "version: " DRIVER_VERSION "; dma %s\n",
-                       use_dma
-                               ? (use_dma_chaining ? "chaining" : "enabled")
-                               : "disabled");
+       INFO(dev, "version: " DRIVER_VERSION "; dma %s %s\n",
+               use_dma ? (use_dma_chaining ? "chaining" : "enabled")
+                       : "disabled",
+               dev->enhanced_mode ? "enhanced mode" : "legacy mode");
        retval = device_create_file (&pdev->dev, &dev_attr_registers);
        if (retval) goto done;
 
        writel (0, &dev->usb->usbctl);
 
        /* Disable full-speed test mode */
-       writel(0, &dev->usb->xcvrdiag);
+       if (dev->pdev->vendor == 0x17cc)
+               writel(0, &dev->usb->xcvrdiag);
 }
 
 
        .device =       0x2282,
        .subvendor =    PCI_ANY_ID,
        .subdevice =    PCI_ANY_ID,
-
-}, { /* end: all zeroes */ }
+},
+       {
+        .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+        .class_mask = ~0,
+        .vendor = 0x10b5,
+        .device = 0x3380,
+        .subvendor = PCI_ANY_ID,
+        .subdevice = PCI_ANY_ID,
+        },
+       {
+        .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+        .class_mask = ~0,
+        .vendor = 0x10b5,
+        .device = 0x3382,
+        .subvendor = PCI_ANY_ID,
+        .subdevice = PCI_ANY_ID,
+        },
+{ /* end: all zeroes */ }
 };
 MODULE_DEVICE_TABLE (pci, pci_ids);