virtio-net: remove big packet XDP codes
[linux-2.6-microblaze.git] / drivers / dma / omap-dma.c
index 7ca27d4..ac68666 100644 (file)
@@ -166,6 +166,9 @@ enum {
        CSDP_DST_BURST_16       = 1 << 14,
        CSDP_DST_BURST_32       = 2 << 14,
        CSDP_DST_BURST_64       = 3 << 14,
+       CSDP_WRITE_NON_POSTED   = 0 << 16,
+       CSDP_WRITE_POSTED       = 1 << 16,
+       CSDP_WRITE_LAST_NON_POSTED = 2 << 16,
 
        CICR_TOUT_IE            = BIT(0),       /* OMAP1 only */
        CICR_DROP_IE            = BIT(1),
@@ -422,7 +425,30 @@ static void omap_dma_start(struct omap_chan *c, struct omap_desc *d)
        c->running = true;
 }
 
-static void omap_dma_stop(struct omap_chan *c)
+static void omap_dma_drain_chan(struct omap_chan *c)
+{
+       int i;
+       u32 val;
+
+       /* Wait for sDMA FIFO to drain */
+       for (i = 0; ; i++) {
+               val = omap_dma_chan_read(c, CCR);
+               if (!(val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE)))
+                       break;
+
+               if (i > 100)
+                       break;
+
+               udelay(5);
+       }
+
+       if (val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE))
+               dev_err(c->vc.chan.device->dev,
+                       "DMA drain did not complete on lch %d\n",
+                       c->dma_ch);
+}
+
+static int omap_dma_stop(struct omap_chan *c)
 {
        struct omap_dmadev *od = to_omap_dma_dev(c->vc.chan.device);
        uint32_t val;
@@ -435,7 +461,6 @@ static void omap_dma_stop(struct omap_chan *c)
        val = omap_dma_chan_read(c, CCR);
        if (od->plat->errata & DMA_ERRATA_i541 && val & CCR_TRIGGER_SRC) {
                uint32_t sysconfig;
-               unsigned i;
 
                sysconfig = omap_dma_glbl_read(od, OCP_SYSCONFIG);
                val = sysconfig & ~DMA_SYSCONFIG_MIDLEMODE_MASK;
@@ -446,27 +471,19 @@ static void omap_dma_stop(struct omap_chan *c)
                val &= ~CCR_ENABLE;
                omap_dma_chan_write(c, CCR, val);
 
-               /* Wait for sDMA FIFO to drain */
-               for (i = 0; ; i++) {
-                       val = omap_dma_chan_read(c, CCR);
-                       if (!(val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE)))
-                               break;
-
-                       if (i > 100)
-                               break;
-
-                       udelay(5);
-               }
-
-               if (val & (CCR_RD_ACTIVE | CCR_WR_ACTIVE))
-                       dev_err(c->vc.chan.device->dev,
-                               "DMA drain did not complete on lch %d\n",
-                               c->dma_ch);
+               if (!(c->ccr & CCR_BUFFERING_DISABLE))
+                       omap_dma_drain_chan(c);
 
                omap_dma_glbl_write(od, OCP_SYSCONFIG, sysconfig);
        } else {
+               if (!(val & CCR_ENABLE))
+                       return -EINVAL;
+
                val &= ~CCR_ENABLE;
                omap_dma_chan_write(c, CCR, val);
+
+               if (!(c->ccr & CCR_BUFFERING_DISABLE))
+                       omap_dma_drain_chan(c);
        }
 
        mb();
@@ -481,8 +498,8 @@ static void omap_dma_stop(struct omap_chan *c)
 
                omap_dma_chan_write(c, CLNK_CTRL, val);
        }
-
        c->running = false;
+       return 0;
 }
 
 static void omap_dma_start_sg(struct omap_chan *c, struct omap_desc *d)
@@ -836,6 +853,8 @@ static enum dma_status omap_dma_tx_status(struct dma_chan *chan,
        } else {
                txstate->residue = 0;
        }
+       if (ret == DMA_IN_PROGRESS && c->paused)
+               ret = DMA_PAUSED;
        spin_unlock_irqrestore(&c->vc.lock, flags);
 
        return ret;
@@ -865,15 +884,18 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
        unsigned i, es, en, frame_bytes;
        bool ll_failed = false;
        u32 burst;
+       u32 port_window, port_window_bytes;
 
        if (dir == DMA_DEV_TO_MEM) {
                dev_addr = c->cfg.src_addr;
                dev_width = c->cfg.src_addr_width;
                burst = c->cfg.src_maxburst;
+               port_window = c->cfg.src_port_window_size;
        } else if (dir == DMA_MEM_TO_DEV) {
                dev_addr = c->cfg.dst_addr;
                dev_width = c->cfg.dst_addr_width;
                burst = c->cfg.dst_maxburst;
+               port_window = c->cfg.dst_port_window_size;
        } else {
                dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
                return NULL;
@@ -894,6 +916,12 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
                return NULL;
        }
 
+       /* When the port_window is used, one frame must cover the window */
+       if (port_window) {
+               burst = port_window;
+               port_window_bytes = port_window * es_bytes[es];
+       }
+
        /* Now allocate and setup the descriptor. */
        d = kzalloc(sizeof(*d) + sglen * sizeof(d->sg[0]), GFP_ATOMIC);
        if (!d)
@@ -905,11 +933,45 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
 
        d->ccr = c->ccr | CCR_SYNC_FRAME;
        if (dir == DMA_DEV_TO_MEM) {
-               d->ccr |= CCR_DST_AMODE_POSTINC | CCR_SRC_AMODE_CONSTANT;
                d->csdp = CSDP_DST_BURST_64 | CSDP_DST_PACKED;
+
+               d->ccr |= CCR_DST_AMODE_POSTINC;
+               if (port_window) {
+                       d->ccr |= CCR_SRC_AMODE_DBLIDX;
+                       d->ei = 1;
+                       /*
+                        * One frame covers the port_window and by  configure
+                        * the source frame index to be -1 * (port_window - 1)
+                        * we instruct the sDMA that after a frame is processed
+                        * it should move back to the start of the window.
+                        */
+                       d->fi = -(port_window_bytes - 1);
+
+                       if (port_window_bytes >= 64)
+                               d->csdp = CSDP_SRC_BURST_64 | CSDP_SRC_PACKED;
+                       else if (port_window_bytes >= 32)
+                               d->csdp = CSDP_SRC_BURST_32 | CSDP_SRC_PACKED;
+                       else if (port_window_bytes >= 16)
+                               d->csdp = CSDP_SRC_BURST_16 | CSDP_SRC_PACKED;
+               } else {
+                       d->ccr |= CCR_SRC_AMODE_CONSTANT;
+               }
        } else {
-               d->ccr |= CCR_DST_AMODE_CONSTANT | CCR_SRC_AMODE_POSTINC;
                d->csdp = CSDP_SRC_BURST_64 | CSDP_SRC_PACKED;
+
+               d->ccr |= CCR_SRC_AMODE_POSTINC;
+               if (port_window) {
+                       d->ccr |= CCR_DST_AMODE_DBLIDX;
+
+                       if (port_window_bytes >= 64)
+                               d->csdp = CSDP_DST_BURST_64 | CSDP_DST_PACKED;
+                       else if (port_window_bytes >= 32)
+                               d->csdp = CSDP_DST_BURST_32 | CSDP_DST_PACKED;
+                       else if (port_window_bytes >= 16)
+                               d->csdp = CSDP_DST_BURST_16 | CSDP_DST_PACKED;
+               } else {
+                       d->ccr |= CCR_DST_AMODE_CONSTANT;
+               }
        }
 
        d->cicr = CICR_DROP_IE | CICR_BLOCK_IE;
@@ -927,6 +989,9 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
                        d->ccr |= CCR_TRIGGER_SRC;
 
                d->cicr |= CICR_MISALIGNED_ERR_IE | CICR_TRANS_ERR_IE;
+
+               if (port_window)
+                       d->csdp |= CSDP_WRITE_LAST_NON_POSTED;
        }
        if (od->plat->errata & DMA_ERRATA_PARALLEL_CHANNELS)
                d->clnk_ctrl = c->dma_ch;
@@ -952,6 +1017,16 @@ static struct dma_async_tx_descriptor *omap_dma_prep_slave_sg(
                osg->addr = sg_dma_address(sgent);
                osg->en = en;
                osg->fn = sg_dma_len(sgent) / frame_bytes;
+               if (port_window && dir == DMA_MEM_TO_DEV) {
+                       osg->ei = 1;
+                       /*
+                        * One frame covers the port_window and by  configure
+                        * the source frame index to be -1 * (port_window - 1)
+                        * we instruct the sDMA that after a frame is processed
+                        * it should move back to the start of the window.
+                        */
+                       osg->fi = -(port_window_bytes - 1);
+               }
 
                if (d->using_ll) {
                        osg->t2_desc = dma_pool_alloc(od->desc_pool, GFP_ATOMIC,
@@ -1247,10 +1322,8 @@ static int omap_dma_terminate_all(struct dma_chan *chan)
                        omap_dma_stop(c);
        }
 
-       if (c->cyclic) {
-               c->cyclic = false;
-               c->paused = false;
-       }
+       c->cyclic = false;
+       c->paused = false;
 
        vchan_get_all_descriptors(&c->vc, &head);
        spin_unlock_irqrestore(&c->vc.lock, flags);
@@ -1269,28 +1342,66 @@ static void omap_dma_synchronize(struct dma_chan *chan)
 static int omap_dma_pause(struct dma_chan *chan)
 {
        struct omap_chan *c = to_omap_dma_chan(chan);
+       struct omap_dmadev *od = to_omap_dma_dev(chan->device);
+       unsigned long flags;
+       int ret = -EINVAL;
+       bool can_pause = false;
 
-       /* Pause/Resume only allowed with cyclic mode */
-       if (!c->cyclic)
-               return -EINVAL;
+       spin_lock_irqsave(&od->irq_lock, flags);
 
-       if (!c->paused) {
-               omap_dma_stop(c);
-               c->paused = true;
+       if (!c->desc)
+               goto out;
+
+       if (c->cyclic)
+               can_pause = true;
+
+       /*
+        * We do not allow DMA_MEM_TO_DEV transfers to be paused.
+        * From the AM572x TRM, 16.1.4.18 Disabling a Channel During Transfer:
+        * "When a channel is disabled during a transfer, the channel undergoes
+        * an abort, unless it is hardware-source-synchronized …".
+        * A source-synchronised channel is one where the fetching of data is
+        * under control of the device. In other words, a device-to-memory
+        * transfer. So, a destination-synchronised channel (which would be a
+        * memory-to-device transfer) undergoes an abort if the the CCR_ENABLE
+        * bit is cleared.
+        * From 16.1.4.20.4.6.2 Abort: "If an abort trigger occurs, the channel
+        * aborts immediately after completion of current read/write
+        * transactions and then the FIFO is cleaned up." The term "cleaned up"
+        * is not defined. TI recommends to check that RD_ACTIVE and WR_ACTIVE
+        * are both clear _before_ disabling the channel, otherwise data loss
+        * will occur.
+        * The problem is that if the channel is active, then device activity
+        * can result in DMA activity starting between reading those as both
+        * clear and the write to DMA_CCR to clear the enable bit hitting the
+        * hardware. If the DMA hardware can't drain the data in its FIFO to the
+        * destination, then data loss "might" occur (say if we write to an UART
+        * and the UART is not accepting any further data).
+        */
+       else if (c->desc->dir == DMA_DEV_TO_MEM)
+               can_pause = true;
+
+       if (can_pause && !c->paused) {
+               ret = omap_dma_stop(c);
+               if (!ret)
+                       c->paused = true;
        }
+out:
+       spin_unlock_irqrestore(&od->irq_lock, flags);
 
-       return 0;
+       return ret;
 }
 
 static int omap_dma_resume(struct dma_chan *chan)
 {
        struct omap_chan *c = to_omap_dma_chan(chan);
+       struct omap_dmadev *od = to_omap_dma_dev(chan->device);
+       unsigned long flags;
+       int ret = -EINVAL;
 
-       /* Pause/Resume only allowed with cyclic mode */
-       if (!c->cyclic)
-               return -EINVAL;
+       spin_lock_irqsave(&od->irq_lock, flags);
 
-       if (c->paused) {
+       if (c->paused && c->desc) {
                mb();
 
                /* Restore channel link register */
@@ -1298,9 +1409,11 @@ static int omap_dma_resume(struct dma_chan *chan)
 
                omap_dma_start(c, c->desc);
                c->paused = false;
+               ret = 0;
        }
+       spin_unlock_irqrestore(&od->irq_lock, flags);
 
-       return 0;
+       return ret;
 }
 
 static int omap_dma_chan_init(struct omap_dmadev *od)