fsldma: implement a private tx_list
[safe/jmp/linux-2.6] / drivers / dma / fsldma.c
index b1b45eb..73dd748 100644 (file)
  *   also fit for MPC8560, MPC8555, MPC8548, MPC8641, and etc.
  *   The support for MPC8349 DMA contorller is also added.
  *
+ * This driver instructs the DMA controller to issue the PCI Read Multiple
+ * command for PCI read operations, instead of using the default PCI Read Line
+ * command. Please be aware that this setting may result in read pre-fetching
+ * on some platforms.
+ *
  * This 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
@@ -49,9 +54,10 @@ static void dma_init(struct fsl_dma_chan *fsl_chan)
        case FSL_DMA_IP_83XX:
                /* Set the channel to below modes:
                 * EOTIE - End-of-transfer interrupt enable
+                * PRC_RM - PCI read multiple
                 */
-               DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, FSL_DMA_MR_EOTIE,
-                               32);
+               DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr, FSL_DMA_MR_EOTIE
+                               | FSL_DMA_MR_PRC_RM, 32);
                break;
        }
 
@@ -136,15 +142,16 @@ static int dma_is_idle(struct fsl_dma_chan *fsl_chan)
 
 static void dma_start(struct fsl_dma_chan *fsl_chan)
 {
-       u32 mr_set = 0;;
+       u32 mr_set = 0;
 
        if (fsl_chan->feature & FSL_DMA_CHAN_PAUSE_EXT) {
                DMA_OUT(fsl_chan, &fsl_chan->reg_base->bcr, 0, 32);
                mr_set |= FSL_DMA_MR_EMP_EN;
-       } else
+       } else if ((fsl_chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_85XX) {
                DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
                        DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32)
                                & ~FSL_DMA_MR_EMP_EN, 32);
+       }
 
        if (fsl_chan->feature & FSL_DMA_CHAN_START_EXT)
                mr_set |= FSL_DMA_MR_EMS_EN;
@@ -158,7 +165,8 @@ static void dma_start(struct fsl_dma_chan *fsl_chan)
 
 static void dma_halt(struct fsl_dma_chan *fsl_chan)
 {
-       int i = 0;
+       int i;
+
        DMA_OUT(fsl_chan, &fsl_chan->reg_base->mr,
                DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32) | FSL_DMA_MR_CA,
                32);
@@ -166,8 +174,11 @@ static void dma_halt(struct fsl_dma_chan *fsl_chan)
                DMA_IN(fsl_chan, &fsl_chan->reg_base->mr, 32) & ~(FSL_DMA_MR_CS
                | FSL_DMA_MR_EMS_EN | FSL_DMA_MR_CA), 32);
 
-       while (!dma_is_idle(fsl_chan) && (i++ < 100))
+       for (i = 0; i < 100; i++) {
+               if (dma_is_idle(fsl_chan))
+                       break;
                udelay(10);
+       }
        if (i >= 100 && !dma_is_idle(fsl_chan))
                dev_err(fsl_chan->dev, "DMA halt timeout!\n");
 }
@@ -175,9 +186,14 @@ static void dma_halt(struct fsl_dma_chan *fsl_chan)
 static void set_ld_eol(struct fsl_dma_chan *fsl_chan,
                        struct fsl_desc_sw *desc)
 {
+       u64 snoop_bits;
+
+       snoop_bits = ((fsl_chan->feature & FSL_DMA_IP_MASK) == FSL_DMA_IP_83XX)
+               ? FSL_DMA_SNEN : 0;
+
        desc->hw.next_ln_addr = CPU_TO_DMA(fsl_chan,
-               DMA_TO_CPU(fsl_chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL,
-               64);
+               DMA_TO_CPU(fsl_chan, desc->hw.next_ln_addr, 64) | FSL_DMA_EOL
+                       | snoop_bits, 64);
 }
 
 static void append_ld_queue(struct fsl_dma_chan *fsl_chan,
@@ -309,8 +325,9 @@ static void fsl_chan_toggle_ext_start(struct fsl_dma_chan *fsl_chan, int enable)
 
 static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
 {
-       struct fsl_desc_sw *desc = tx_to_fsl_desc(tx);
        struct fsl_dma_chan *fsl_chan = to_fsl_chan(tx->chan);
+       struct fsl_desc_sw *desc = tx_to_fsl_desc(tx);
+       struct fsl_desc_sw *child;
        unsigned long flags;
        dma_cookie_t cookie;
 
@@ -318,14 +335,17 @@ static dma_cookie_t fsl_dma_tx_submit(struct dma_async_tx_descriptor *tx)
        spin_lock_irqsave(&fsl_chan->desc_lock, flags);
 
        cookie = fsl_chan->common.cookie;
-       cookie++;
-       if (cookie < 0)
-               cookie = 1;
-       desc->async_tx.cookie = cookie;
-       fsl_chan->common.cookie = desc->async_tx.cookie;
+       list_for_each_entry(child, &desc->tx_list, node) {
+               cookie++;
+               if (cookie < 0)
+                       cookie = 1;
+
+               desc->async_tx.cookie = cookie;
+       }
 
+       fsl_chan->common.cookie = cookie;
        append_ld_queue(fsl_chan, desc);
-       list_splice_init(&desc->async_tx.tx_list, fsl_chan->ld_queue.prev);
+       list_splice_init(&desc->tx_list, fsl_chan->ld_queue.prev);
 
        spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
 
@@ -347,10 +367,10 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor(
        desc_sw = dma_pool_alloc(fsl_chan->desc_pool, GFP_ATOMIC, &pdesc);
        if (desc_sw) {
                memset(desc_sw, 0, sizeof(struct fsl_desc_sw));
+               INIT_LIST_HEAD(&desc_sw->tx_list);
                dma_async_tx_descriptor_init(&desc_sw->async_tx,
                                                &fsl_chan->common);
                desc_sw->async_tx.tx_submit = fsl_dma_tx_submit;
-               INIT_LIST_HEAD(&desc_sw->async_tx.tx_list);
                desc_sw->async_tx.phys = pdesc;
        }
 
@@ -437,7 +457,7 @@ fsl_dma_prep_interrupt(struct dma_chan *chan, unsigned long flags)
        new->async_tx.flags = flags;
 
        /* Insert the link descriptor to the LD ring */
-       list_add_tail(&new->node, &new->async_tx.tx_list);
+       list_add_tail(&new->node, &new->tx_list);
 
        /* Set End-of-link to the last link descriptor of new list*/
        set_ld_eol(fsl_chan, new);
@@ -451,8 +471,8 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
 {
        struct fsl_dma_chan *fsl_chan;
        struct fsl_desc_sw *first = NULL, *prev = NULL, *new;
+       struct list_head *list;
        size_t copy;
-       LIST_HEAD(link_chain);
 
        if (!chan)
                return NULL;
@@ -469,7 +489,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
                if (!new) {
                        dev_err(fsl_chan->dev,
                                        "No free memory for link descriptor\n");
-                       return NULL;
+                       goto fail;
                }
 #ifdef FSL_DMA_LD_DEBUG
                dev_dbg(fsl_chan->dev, "new link desc alloc %p\n", new);
@@ -495,7 +515,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
                dma_dest += copy;
 
                /* Insert the link descriptor to the LD ring */
-               list_add_tail(&new->node, &first->async_tx.tx_list);
+               list_add_tail(&new->node, &first->tx_list);
        } while (len);
 
        new->async_tx.flags = flags; /* client is in control of this ack */
@@ -504,7 +524,19 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
        /* Set End-of-link to the last link descriptor of new list*/
        set_ld_eol(fsl_chan, new);
 
-       return first ? &first->async_tx : NULL;
+       return &first->async_tx;
+
+fail:
+       if (!first)
+               return NULL;
+
+       list = &first->tx_list;
+       list_for_each_entry_safe_reverse(new, prev, list, node) {
+               list_del(&new->node);
+               dma_pool_free(fsl_chan->desc_pool, new, new->async_tx.phys);
+       }
+
+       return NULL;
 }
 
 /**
@@ -595,15 +627,16 @@ static void fsl_chan_xfer_ld_queue(struct fsl_dma_chan *fsl_chan)
        dma_addr_t next_dest_addr;
        unsigned long flags;
 
+       spin_lock_irqsave(&fsl_chan->desc_lock, flags);
+
        if (!dma_is_idle(fsl_chan))
-               return;
+               goto out_unlock;
 
        dma_halt(fsl_chan);
 
        /* If there are some link descriptors
         * not transfered in queue. We need to start it.
         */
-       spin_lock_irqsave(&fsl_chan->desc_lock, flags);
 
        /* Find the first un-transfer desciptor */
        for (ld_node = fsl_chan->ld_queue.next;
@@ -614,19 +647,20 @@ static void fsl_chan_xfer_ld_queue(struct fsl_dma_chan *fsl_chan)
                                fsl_chan->common.cookie) == DMA_SUCCESS);
                ld_node = ld_node->next);
 
-       spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
-
        if (ld_node != &fsl_chan->ld_queue) {
                /* Get the ld start address from ld_queue */
                next_dest_addr = to_fsl_desc(ld_node)->async_tx.phys;
-               dev_dbg(fsl_chan->dev, "xfer LDs staring from %p\n",
-                               (void *)next_dest_addr);
+               dev_dbg(fsl_chan->dev, "xfer LDs staring from 0x%llx\n",
+                               (unsigned long long)next_dest_addr);
                set_cdar(fsl_chan, next_dest_addr);
                dma_start(fsl_chan);
        } else {
                set_cdar(fsl_chan, 0);
                set_ndar(fsl_chan, 0);
        }
+
+out_unlock:
+       spin_unlock_irqrestore(&fsl_chan->desc_lock, flags);
 }
 
 /**
@@ -731,8 +765,9 @@ static irqreturn_t fsl_dma_chan_do_interrupt(int irq, void *data)
         */
        if (stat & FSL_DMA_SR_EOSI) {
                dev_dbg(fsl_chan->dev, "event: End-of-segments INT\n");
-               dev_dbg(fsl_chan->dev, "event: clndar %p, nlndar %p\n",
-                       (void *)get_cdar(fsl_chan), (void *)get_ndar(fsl_chan));
+               dev_dbg(fsl_chan->dev, "event: clndar 0x%llx, nlndar 0x%llx\n",
+                       (unsigned long long)get_cdar(fsl_chan),
+                       (unsigned long long)get_ndar(fsl_chan));
                stat &= ~FSL_DMA_SR_EOSI;
                update_cookie = 1;
        }
@@ -827,7 +862,7 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev,
                        new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1);
 
        new_fsl_chan->id = ((new_fsl_chan->reg.start - 0x100) & 0xfff) >> 7;
-       if (new_fsl_chan->id > FSL_DMA_MAX_CHANS_PER_DEVICE) {
+       if (new_fsl_chan->id >= FSL_DMA_MAX_CHANS_PER_DEVICE) {
                dev_err(fdev->dev, "There is no %d channel!\n",
                                new_fsl_chan->id);
                err = -EINVAL;
@@ -845,9 +880,9 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev,
 
        switch (new_fsl_chan->feature & FSL_DMA_IP_MASK) {
        case FSL_DMA_IP_85XX:
-               new_fsl_chan->toggle_ext_start = fsl_chan_toggle_ext_start;
                new_fsl_chan->toggle_ext_pause = fsl_chan_toggle_ext_pause;
        case FSL_DMA_IP_83XX:
+               new_fsl_chan->toggle_ext_start = fsl_chan_toggle_ext_start;
                new_fsl_chan->set_src_loop_size = fsl_chan_set_src_loop_size;
                new_fsl_chan->set_dest_loop_size = fsl_chan_set_dest_loop_size;
        }
@@ -875,7 +910,8 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev,
        }
 
        dev_info(fdev->dev, "#%d (%s), irq %d\n", new_fsl_chan->id,
-                               compatible, new_fsl_chan->irq);
+                compatible,
+                new_fsl_chan->irq != NO_IRQ ? new_fsl_chan->irq : fdev->irq);
 
        return 0;
 
@@ -921,8 +957,8 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev,
        }
 
        dev_info(&dev->dev, "Probe the Freescale DMA driver for %s "
-                       "controller at %p...\n",
-                       match->compatible, (void *)fdev->reg.start);
+                       "controller at 0x%llx...\n",
+                       match->compatible, (unsigned long long)fdev->reg.start);
        fdev->reg_base = ioremap(fdev->reg.start, fdev->reg.end
                                                - fdev->reg.start + 1);