Merge branch 'next/topic-gpio-samsung' into next-samsung-devel
[pandora-kernel.git] / drivers / dma / amba-pl08x.c
index be9a1c7..cd8df7f 100644 (file)
  *    after the final transfer signalled by LBREQ or LSREQ.  The DMAC
  *    will then move to the next LLI entry.
  *
- * Only the former works sanely with scatter lists, so we only implement
- * the DMAC flow control method.  However, peripherals which use the LBREQ
- * and LSREQ signals (eg, MMCI) are unable to use this mode, which through
- * these hardware restrictions prevents them from using scatter DMA.
- *
  * Global TODO:
  * - Break out common code from arch/arm/mach-s3c64xx and share
  */
@@ -81,6 +76,7 @@
 #include <linux/device.h>
 #include <linux/dmaengine.h>
 #include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
@@ -91,6 +87,8 @@
 
 #define DRIVER_NAME    "pl08xdmac"
 
+static struct amba_driver pl08x_amba_driver;
+
 /**
  * struct vendor_data - vendor-specific config parameters for PL08x derivatives
  * @channels: the number of channels available in this variant
@@ -499,34 +497,24 @@ struct pl08x_lli_build_data {
  * byte data), slave is still not aligned, then its width will be reduced to
  * BYTE.
  * - prefers the destination bus if both available
- * - if fixed address on one bus the other will be chosen
+ * - prefers bus with fixed address (i.e. peripheral)
  */
 static void pl08x_choose_master_bus(struct pl08x_lli_build_data *bd,
        struct pl08x_bus_data **mbus, struct pl08x_bus_data **sbus, u32 cctl)
 {
        if (!(cctl & PL080_CONTROL_DST_INCR)) {
-               *mbus = &bd->srcbus;
-               *sbus = &bd->dstbus;
-       } else if (!(cctl & PL080_CONTROL_SRC_INCR)) {
                *mbus = &bd->dstbus;
                *sbus = &bd->srcbus;
+       } else if (!(cctl & PL080_CONTROL_SRC_INCR)) {
+               *mbus = &bd->srcbus;
+               *sbus = &bd->dstbus;
        } else {
-               if (bd->dstbus.buswidth == 4) {
-                       *mbus = &bd->dstbus;
-                       *sbus = &bd->srcbus;
-               } else if (bd->srcbus.buswidth == 4) {
-                       *mbus = &bd->srcbus;
-                       *sbus = &bd->dstbus;
-               } else if (bd->dstbus.buswidth == 2) {
+               if (bd->dstbus.buswidth >= bd->srcbus.buswidth) {
                        *mbus = &bd->dstbus;
                        *sbus = &bd->srcbus;
-               } else if (bd->srcbus.buswidth == 2) {
+               } else {
                        *mbus = &bd->srcbus;
                        *sbus = &bd->dstbus;
-               } else {
-                       /* bd->srcbus.buswidth == 1 */
-                       *mbus = &bd->dstbus;
-                       *sbus = &bd->srcbus;
                }
        }
 }
@@ -559,6 +547,14 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,
        bd->remainder -= len;
 }
 
+static inline void prep_byte_width_lli(struct pl08x_lli_build_data *bd,
+               u32 *cctl, u32 len, int num_llis, size_t *total_bytes)
+{
+       *cctl = pl08x_cctl_bits(*cctl, 1, 1, len);
+       pl08x_fill_lli_for_desc(bd, num_llis, len, *cctl);
+       (*total_bytes) += len;
+}
+
 /*
  * This fills in the table of LLIs for the transfer descriptor
  * Note that we assume we never have to change the burst sizes
@@ -570,7 +566,7 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
        struct pl08x_bus_data *mbus, *sbus;
        struct pl08x_lli_build_data bd;
        int num_llis = 0;
-       u32 cctl;
+       u32 cctl, early_bytes = 0;
        size_t max_bytes_per_lli, total_bytes = 0;
        struct pl08x_lli *llis_va;
 
@@ -604,50 +600,85 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
        bd.srcbus.buswidth = bd.srcbus.maxwidth;
        bd.dstbus.buswidth = bd.dstbus.maxwidth;
 
-       /*
-        * Bytes transferred == tsize * MIN(buswidths), not max(buswidths)
-        */
-       max_bytes_per_lli = min(bd.srcbus.buswidth, bd.dstbus.buswidth) *
-               PL080_CONTROL_TRANSFER_SIZE_MASK;
-
        /* We need to count this down to zero */
        bd.remainder = txd->len;
 
        pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl);
 
-       dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu llimax=%zu\n",
+       dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu\n",
                 bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "",
                 bd.srcbus.buswidth,
                 bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "",
                 bd.dstbus.buswidth,
-                bd.remainder, max_bytes_per_lli);
+                bd.remainder);
        dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n",
                 mbus == &bd.srcbus ? "src" : "dst",
                 sbus == &bd.srcbus ? "src" : "dst");
 
-       if (txd->len < mbus->buswidth) {
-               /* Less than a bus width available - send as single bytes */
-               while (bd.remainder) {
-                       dev_vdbg(&pl08x->adev->dev,
-                                "%s single byte LLIs for a transfer of "
-                                "less than a bus width (remain 0x%08x)\n",
-                                __func__, bd.remainder);
-                       cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
-                       pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl);
-                       total_bytes++;
+       /*
+        * Zero length is only allowed if all these requirements are met:
+        * - flow controller is peripheral.
+        * - src.addr is aligned to src.width
+        * - dst.addr is aligned to dst.width
+        *
+        * sg_len == 1 should be true, as there can be two cases here:
+        * - Memory addresses are contiguous and are not scattered. Here, Only
+        * one sg will be passed by user driver, with memory address and zero
+        * length. We pass this to controller and after the transfer it will
+        * receive the last burst request from peripheral and so transfer
+        * finishes.
+        *
+        * - Memory addresses are scattered and are not contiguous. Here,
+        * Obviously as DMA controller doesn't know when a lli's transfer gets
+        * over, it can't load next lli. So in this case, there has to be an
+        * assumption that only one lli is supported. Thus, we can't have
+        * scattered addresses.
+        */
+       if (!bd.remainder) {
+               u32 fc = (txd->ccfg & PL080_CONFIG_FLOW_CONTROL_MASK) >>
+                       PL080_CONFIG_FLOW_CONTROL_SHIFT;
+               if (!((fc >= PL080_FLOW_SRC2DST_DST) &&
+                                       (fc <= PL080_FLOW_SRC2DST_SRC))) {
+                       dev_err(&pl08x->adev->dev, "%s sg len can't be zero",
+                               __func__);
+                       return 0;
                }
-       } else {
-               /* Make one byte LLIs until master bus is aligned */
-               while ((mbus->addr) % (mbus->buswidth)) {
-                       dev_vdbg(&pl08x->adev->dev,
-                               "%s adjustment lli for less than bus width "
-                                "(remain 0x%08x)\n",
-                                __func__, bd.remainder);
-                       cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
-                       pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl);
-                       total_bytes++;
+
+               if ((bd.srcbus.addr % bd.srcbus.buswidth) ||
+                               (bd.srcbus.addr % bd.srcbus.buswidth)) {
+                       dev_err(&pl08x->adev->dev,
+                               "%s src & dst address must be aligned to src"
+                               " & dst width if peripheral is flow controller",
+                               __func__);
+                       return 0;
                }
 
+               cctl = pl08x_cctl_bits(cctl, bd.srcbus.buswidth,
+                               bd.dstbus.buswidth, 0);
+               pl08x_fill_lli_for_desc(&bd, num_llis++, 0, cctl);
+       }
+
+       /*
+        * Send byte by byte for following cases
+        * - Less than a bus width available
+        * - until master bus is aligned
+        */
+       if (bd.remainder < mbus->buswidth)
+               early_bytes = bd.remainder;
+       else if ((mbus->addr) % (mbus->buswidth)) {
+               early_bytes = mbus->buswidth - (mbus->addr) % (mbus->buswidth);
+               if ((bd.remainder - early_bytes) < mbus->buswidth)
+                       early_bytes = bd.remainder;
+       }
+
+       if (early_bytes) {
+               dev_vdbg(&pl08x->adev->dev, "%s byte width LLIs "
+                               "(remain 0x%08x)\n", __func__, bd.remainder);
+               prep_byte_width_lli(&bd, &cctl, early_bytes, num_llis++,
+                               &total_bytes);
+       }
+
+       if (bd.remainder) {
                /*
                 * Master now aligned
                 * - if slave is not then we must set its width down
@@ -660,25 +691,31 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
                        sbus->buswidth = 1;
                }
 
+               /* Bytes transferred = tsize * src width, not MIN(buswidths) */
+               max_bytes_per_lli = bd.srcbus.buswidth *
+                       PL080_CONTROL_TRANSFER_SIZE_MASK;
+
                /*
                 * Make largest possible LLIs until less than one bus
                 * width left
                 */
                while (bd.remainder > (mbus->buswidth - 1)) {
-                       size_t lli_len, tsize;
+                       size_t lli_len, tsize, width;
 
                        /*
                         * If enough left try to send max possible,
                         * otherwise try to send the remainder
                         */
                        lli_len = min(bd.remainder, max_bytes_per_lli);
+
                        /*
-                        * Check against minimum bus alignment: Calculate actual
+                        * Check against maximum bus alignment: Calculate actual
                         * transfer size in relation to bus width and get a
-                        * maximum remainder of the smallest bus width - 1
+                        * maximum remainder of the highest bus width - 1
                         */
-                       tsize = lli_len / min(mbus->buswidth, sbus->buswidth);
-                       lli_len = tsize * min(mbus->buswidth, sbus->buswidth);
+                       width = max(mbus->buswidth, sbus->buswidth);
+                       lli_len = (lli_len / width) * width;
+                       tsize = lli_len / bd.srcbus.buswidth;
 
                        dev_vdbg(&pl08x->adev->dev,
                                "%s fill lli with single lli chunk of "
@@ -694,13 +731,12 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
                /*
                 * Send any odd bytes
                 */
-               while (bd.remainder) {
-                       cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
+               if (bd.remainder) {
                        dev_vdbg(&pl08x->adev->dev,
-                               "%s align with boundary, single odd byte (remain %zu)\n",
+                               "%s align with boundary, send odd bytes (remain %zu)\n",
                                __func__, bd.remainder);
-                       pl08x_fill_lli_for_desc(&bd, num_llis++, 1, cctl);
-                       total_bytes++;
+                       prep_byte_width_lli(&bd, &cctl, bd.remainder,
+                                       num_llis++, &total_bytes);
                }
        }
 
@@ -1141,7 +1177,9 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
 
        num_llis = pl08x_fill_llis_for_desc(pl08x, txd);
        if (!num_llis) {
-               kfree(txd);
+               spin_lock_irqsave(&plchan->lock, flags);
+               pl08x_free_txd(pl08x, txd);
+               spin_unlock_irqrestore(&plchan->lock, flags);
                return -EINVAL;
        }
 
@@ -1255,7 +1293,7 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
        struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
        struct pl08x_driver_data *pl08x = plchan->host;
        struct pl08x_txd *txd;
-       int ret;
+       int ret, tmp;
 
        /*
         * Current implementation ASSUMES only one sg
@@ -1289,12 +1327,10 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
        txd->len = sgl->length;
 
        if (direction == DMA_TO_DEVICE) {
-               txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT;
                txd->cctl = plchan->dst_cctl;
                txd->src_addr = sgl->dma_address;
                txd->dst_addr = plchan->dst_addr;
        } else if (direction == DMA_FROM_DEVICE) {
-               txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
                txd->cctl = plchan->src_cctl;
                txd->src_addr = plchan->src_addr;
                txd->dst_addr = sgl->dma_address;
@@ -1304,6 +1340,15 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
                return NULL;
        }
 
+       if (plchan->cd->device_fc)
+               tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER_PER :
+                       PL080_FLOW_PER2MEM_PER;
+       else
+               tmp = (direction == DMA_TO_DEVICE) ? PL080_FLOW_MEM2PER :
+                       PL080_FLOW_PER2MEM;
+
+       txd->ccfg |= tmp << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+
        ret = pl08x_prep_channel_resources(plchan, txd);
        if (ret)
                return NULL;
@@ -1377,9 +1422,15 @@ static int pl08x_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
 
 bool pl08x_filter_id(struct dma_chan *chan, void *chan_id)
 {
-       struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
+       struct pl08x_dma_chan *plchan;
        char *name = chan_id;
 
+       /* Reject channels for devices not bound to this driver */
+       if (chan->device->dev->driver != &pl08x_amba_driver.drv)
+               return false;
+
+       plchan = to_pl08x_chan(chan);
+
        /* Check that the channel is not taken! */
        if (!strcmp(plchan->name, name))
                return true;