]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/usb/gadget/s3c-hsotg.c
USB: s3c-hsotg: Fix the OUT EP0 limit
[net-next-2.6.git] / drivers / usb / gadget / s3c-hsotg.c
index 31d19e1f261d3f9b25ab6f968842682b4cb174d0..552ec899500acde89421392b8aa30c06e4ef4fe4 100644 (file)
@@ -12,6 +12,8 @@
  * published by the Free Software Foundation.
 */
 
+#define DEBUG
+
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
@@ -91,7 +93,9 @@ struct s3c_hsotg_req;
  * For periodic IN endpoints, we have fifo_size and fifo_load to try
  * and keep track of the amount of data in the periodic FIFO for each
  * of these as we don't have a status register that tells us how much
- * is in each of them.
+ * is in each of them. (note, this may actually be useless information
+ * as in shared-fifo mode periodic in acts like a single-frame packet
+ * buffer than a fifo)
  */
 struct s3c_hsotg_ep {
        struct usb_ep           ep;
@@ -128,6 +132,7 @@ struct s3c_hsotg_ep {
  * @regs: The memory area mapped for accessing registers.
  * @regs_res: The resource that was allocated when claiming register space.
  * @irq: The IRQ number we are using
+ * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
  * @debug_root: root directrory for debugfs.
  * @debug_file: main status file for debugfs.
  * @debug_fifo: FIFO status file for debugfs.
@@ -146,6 +151,8 @@ struct s3c_hsotg {
        struct resource         *regs_res;
        int                     irq;
 
+       unsigned int            dedicated_fifos:1;
+
        struct dentry           *debug_root;
        struct dentry           *debug_file;
        struct dentry           *debug_fifo;
@@ -300,6 +307,7 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
        unsigned int ep;
        unsigned int addr;
        unsigned int size;
+       int timeout;
        u32 val;
 
        /* the ryu 2.6.24 release ahs
@@ -309,11 +317,11 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
                hsotg->regs + S3C_GNPTXFSIZ);
        */
 
-       /* set FIFO sizes to 2048/0x1C0 */
+       /* set FIFO sizes to 2048/1024 */
 
        writel(2048, hsotg->regs + S3C_GRXFSIZ);
        writel(S3C_GNPTXFSIZ_NPTxFStAddr(2048) |
-              S3C_GNPTXFSIZ_NPTxFDep(0x1C0),
+              S3C_GNPTXFSIZ_NPTxFDep(1024),
               hsotg->regs + S3C_GNPTXFSIZ);
 
        /* arange all the rest of the TX FIFOs, as some versions of this
@@ -335,6 +343,31 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
 
                writel(val, hsotg->regs + S3C_DPTXFSIZn(ep));
        }
+
+       /* according to p428 of the design guide, we need to ensure that
+        * all fifos are flushed before continuing */
+
+       writel(S3C_GRSTCTL_TxFNum(0x10) | S3C_GRSTCTL_TxFFlsh |
+              S3C_GRSTCTL_RxFFlsh, hsotg->regs + S3C_GRSTCTL);
+
+       /* wait until the fifos are both flushed */
+       timeout = 100;
+       while (1) {
+               val = readl(hsotg->regs + S3C_GRSTCTL);
+
+               if ((val & (S3C_GRSTCTL_TxFFlsh | S3C_GRSTCTL_RxFFlsh)) == 0)
+                       break;
+
+               if (--timeout == 0) {
+                       dev_err(hsotg->dev,
+                               "%s: timeout flushing fifos (GRSTCTL=%08x)\n",
+                               __func__, val);
+               }
+
+               udelay(1);
+       }
+
+       dev_dbg(hsotg->dev, "FIFOs reset, timeout at %d\n", timeout);
 }
 
 /**
@@ -438,7 +471,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
        if (to_write == 0)
                return 0;
 
-       if (periodic) {
+       if (periodic && !hsotg->dedicated_fifos) {
                u32 epsize = readl(hsotg->regs + S3C_DIEPTSIZ(hs_ep->index));
                int size_left;
                int size_done;
@@ -448,6 +481,14 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
 
                size_left = S3C_DxEPTSIZ_XferSize_GET(epsize);
 
+               /* if shared fifo, we cannot write anything until the
+                * previous data has been completely sent.
+                */
+               if (hs_ep->fifo_load != 0) {
+                       s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
+                       return -ENOSPC;
+               }
+
                dev_dbg(hsotg->dev, "%s: left=%d, load=%d, fifo=%d, size %d\n",
                        __func__, size_left,
                        hs_ep->size_loaded, hs_ep->fifo_load, hs_ep->fifo_size);
@@ -468,6 +509,11 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
                        s3c_hsotg_en_gsint(hsotg, S3C_GINTSTS_PTxFEmp);
                        return -ENOSPC;
                }
+       } else if (hsotg->dedicated_fifos && hs_ep->index != 0) {
+               can_write = readl(hsotg->regs + S3C_DTXFSTS(hs_ep->index));
+
+               can_write &= 0xffff;
+               can_write *= 4;
        } else {
                if (S3C_GNPTXSTS_NPTxQSpcAvail_GET(gnptxsts) == 0) {
                        dev_dbg(hsotg->dev,
@@ -479,6 +525,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
                }
 
                can_write = S3C_GNPTXSTS_NPTxFSpcAvail_GET(gnptxsts);
+               can_write *= 4; /* fifo size is in 32bit quantities. */
        }
 
        dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n",
@@ -491,6 +538,17 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
        if (can_write > 512)
                can_write = 512;
 
+       /* limit the write to one max-packet size worth of data, but allow
+        * the transfer to return that it did not run out of fifo space
+        * doing it. */
+       if (to_write > hs_ep->ep.maxpacket) {
+               to_write = hs_ep->ep.maxpacket;
+
+               s3c_hsotg_en_gsint(hsotg,
+                                  periodic ? S3C_GINTSTS_PTxFEmp :
+                                  S3C_GINTSTS_NPTxFEmp);
+       }
+
        /* see if we can write data */
 
        if (to_write > can_write) {
@@ -553,12 +611,10 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep)
                maxsize = S3C_DxEPTSIZ_XferSize_LIMIT + 1;
                maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1;
        } else {
+               maxsize = 64+64;
                if (hs_ep->dir_in) {
-                       /* maxsize = S3C_DIEPTSIZ0_XferSize_LIMIT + 1; */
-                       maxsize = 64+64+1;
                        maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1;
                } else {
-                       maxsize = 0x3f;
                        maxpkt = 2;
                }
        }
@@ -1732,7 +1788,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
                if (dir_in) {
                        s3c_hsotg_complete_in(hsotg, hs_ep);
 
-                       if (idx == 0)
+                       if (idx == 0 && !hs_ep->req)
                                s3c_hsotg_enqueue_setup(hsotg);
                } else if (using_dma(hsotg)) {
                        /* We're using DMA, we need to fire an OutDone here
@@ -1792,6 +1848,15 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
                                 __func__, idx);
                        clear |= S3C_DIEPMSK_INTknEPMisMsk;
                }
+
+               /* FIFO has space or is empty (see GAHBCFG) */
+               if (hsotg->dedicated_fifos &&
+                   ints & S3C_DIEPMSK_TxFIFOEmpty) {
+                       dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n",
+                               __func__, idx);
+                       s3c_hsotg_trytx(hsotg, hs_ep);
+                       clear |= S3C_DIEPMSK_TxFIFOEmpty;
+               }
        }
 
        writel(clear, hsotg->regs + epint_reg);
@@ -2045,17 +2110,12 @@ irq_retry:
                kill_all_requests(hsotg, &hsotg->eps[0], -ECONNRESET, true);
 
                /* it seems after a reset we can end up with a situation
-                * where the TXFIFO still has data in it... try flushing
-                * it to remove anything that may still be in it.
+                * where the TXFIFO still has data in it... the docs
+                * suggest resetting all the fifos, so use the init_fifo
+                * code to relayout and flush the fifos.
                 */
 
-               if (1) {
-                       writel(S3C_GRSTCTL_TxFNum(0) | S3C_GRSTCTL_TxFFlsh,
-                              hsotg->regs + S3C_GRSTCTL);
-
-                       dev_info(hsotg->dev, "GNPTXSTS=%08x\n",
-                                readl(hsotg->regs + S3C_GNPTXSTS));
-               }
+               s3c_hsotg_init_fifo(hsotg);
 
                s3c_hsotg_enqueue_setup(hsotg);
 
@@ -2248,6 +2308,12 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
                break;
        }
 
+       /* if the hardware has dedicated fifos, we must give each IN EP
+        * a unique tx-fifo even if it is non-periodic.
+        */
+       if (dir_in && hsotg->dedicated_fifos)
+               epctrl |= S3C_DxEPCTL_TxFNum(index);
+
        /* for non control endpoints, set PID to D0 */
        if (index)
                epctrl |= S3C_DxEPCTL_SetD0PID;
@@ -2537,7 +2603,8 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
 
        writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
               S3C_DIEPMSK_INTknEPMisMsk |
-              S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk,
+              S3C_DIEPMSK_EPDisbldMsk | S3C_DIEPMSK_XferComplMsk |
+              ((hsotg->dedicated_fifos) ? S3C_DIEPMSK_TxFIFOEmpty : 0),
               hsotg->regs + S3C_DIEPMSK);
 
        /* don't need XferCompl, we get that from RXFIFO in slave mode. In
@@ -2706,7 +2773,7 @@ static void __devinit s3c_hsotg_initep(struct s3c_hsotg *hsotg,
         */
 
        ptxfifo = readl(hsotg->regs + S3C_DPTXFSIZn(epnum));
-       hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo);
+       hs_ep->fifo_size = S3C_DPTXFSIZn_DPTxFSize_GET(ptxfifo) * 4;
 
        /* if we're using dma, we need to set the next-endpoint pointer
         * to be something valid.
@@ -2746,6 +2813,8 @@ static void s3c_hsotg_otgreset(struct s3c_hsotg *hsotg)
 
 static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
 {
+       u32 cfg4;
+
        /* unmask subset of endpoint interrupts */
 
        writel(S3C_DIEPMSK_TimeOUTMsk | S3C_DIEPMSK_AHBErrMsk |
@@ -2781,6 +2850,14 @@ static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
 
        writel(using_dma(hsotg) ? S3C_GAHBCFG_DMAEn : 0x0,
               hsotg->regs + S3C_GAHBCFG);
+
+       /* check hardware configuration */
+
+       cfg4 = readl(hsotg->regs + 0x50);
+       hsotg->dedicated_fifos = (cfg4 >> 25) & 1;
+
+       dev_info(hsotg->dev, "%s fifos\n",
+                hsotg->dedicated_fifos ? "dedicated" : "shared");
 }
 
 static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)