]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/dma/amba-pl08x.c
ARM: PL08x: make pl08x_fill_lli_for_desc() return void
[mv-sheeva.git] / drivers / dma / amba-pl08x.c
index f0a29885cb838d714cfa8d9f0bd9dfe886fabf6c..f61940434669969b3750fa1445f6860c420b2ede 100644 (file)
@@ -126,6 +126,8 @@ struct pl08x_lli {
  * @phy_chans: array of data for the physical channels
  * @pool: a pool for the LLI descriptors
  * @pool_ctr: counter of LLIs in the pool
+ * @lli_buses: bitmask to or in to LLI pointer selecting AHB port for LLI fetches
+ * @mem_buses: set to indicate memory transfers on AHB2.
  * @lock: a spinlock for this struct
  */
 struct pl08x_driver_data {
@@ -138,6 +140,8 @@ struct pl08x_driver_data {
        struct pl08x_phy_chan *phy_chans;
        struct dma_pool *pool;
        int pool_ctr;
+       u8 lli_buses;
+       u8 mem_buses;
        spinlock_t lock;
 };
 
@@ -517,29 +521,20 @@ static void pl08x_choose_master_bus(struct pl08x_bus_data *src_bus,
  * Fills in one LLI for a certain transfer descriptor
  * and advance the counter
  */
-static int pl08x_fill_lli_for_desc(struct pl08x_driver_data *pl08x,
-                           struct pl08x_txd *txd, int num_llis, int len,
-                           u32 cctl, u32 *remainder)
+static void pl08x_fill_lli_for_desc(struct pl08x_driver_data *pl08x,
+       struct pl08x_txd *txd, int num_llis, int len, u32 cctl, u32 *remainder)
 {
        struct pl08x_lli *llis_va = txd->llis_va;
        dma_addr_t llis_bus = txd->llis_bus;
 
        BUG_ON(num_llis >= MAX_NUM_TSFR_LLIS);
 
-       llis_va[num_llis].cctl          = cctl;
-       llis_va[num_llis].src           = txd->srcbus.addr;
-       llis_va[num_llis].dst           = txd->dstbus.addr;
-
-       /*
-        * On versions with dual masters, you can optionally AND on
-        * PL080_LLI_LM_AHB2 to the LLI to tell the hardware to read
-        * in new LLIs with that controller, but we always try to
-        * choose AHB1 to point into memory. The idea is to have AHB2
-        * fixed on the peripheral and AHB1 messing around in the
-        * memory. So we don't manipulate this bit currently.
-        */
-
+       llis_va[num_llis].cctl = cctl;
+       llis_va[num_llis].src = txd->srcbus.addr;
+       llis_va[num_llis].dst = txd->dstbus.addr;
        llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli);
+       if (pl08x->lli_buses & PL08X_AHB2)
+               llis_va[num_llis].lli |= PL080_LLI_LM_AHB2;
 
        if (cctl & PL080_CONTROL_SRC_INCR)
                txd->srcbus.addr += len;
@@ -549,8 +544,6 @@ static int pl08x_fill_lli_for_desc(struct pl08x_driver_data *pl08x,
        BUG_ON(*remainder < len);
 
        *remainder -= len;
-
-       return num_llis + 1;
 }
 
 /*
@@ -577,7 +570,6 @@ static inline size_t pl08x_pre_boundary(u32 addr, size_t len)
 static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
                              struct pl08x_txd *txd)
 {
-       struct pl08x_channel_data *cd = txd->cd;
        struct pl08x_bus_data *mbus, *sbus;
        size_t remainder;
        int num_llis = 0;
@@ -595,37 +587,8 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
 
        pl08x->pool_ctr++;
 
-       /*
-        * Initialize bus values for this transfer
-        * from the passed optimal values
-        */
-       if (!cd) {
-               dev_err(&pl08x->adev->dev, "%s no channel data\n", __func__);
-               return 0;
-       }
-
-       /* Get the default CCTL from the platform data */
-       cctl = cd->cctl;
-
-       /*
-        * On the PL080 we have two bus masters and we
-        * should select one for source and one for
-        * destination. We try to use AHB2 for the
-        * bus which does not increment (typically the
-        * peripheral) else we just choose something.
-        */
-       cctl &= ~(PL080_CONTROL_DST_AHB2 | PL080_CONTROL_SRC_AHB2);
-       if (pl08x->vd->dualmaster) {
-               if (cctl & PL080_CONTROL_SRC_INCR)
-                       /* Source increments, use AHB2 for destination */
-                       cctl |= PL080_CONTROL_DST_AHB2;
-               else if (cctl & PL080_CONTROL_DST_INCR)
-                       /* Destination increments, use AHB2 for source */
-                       cctl |= PL080_CONTROL_SRC_AHB2;
-               else
-                       /* Just pick something, source AHB1 dest AHB2 */
-                       cctl |= PL080_CONTROL_DST_AHB2;
-       }
+       /* Get the default CCTL */
+       cctl = txd->cctl;
 
        /* Find maximum width of the source bus */
        txd->srcbus.maxwidth =
@@ -669,13 +632,6 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
        pl08x_choose_master_bus(&txd->srcbus, &txd->dstbus,
                                &mbus, &sbus, cctl);
 
-
-       /*
-        * The lowest bit of the LLI register
-        * is also used to indicate which master to
-        * use for reading the LLIs.
-        */
-
        if (txd->len < mbus->buswidth) {
                /*
                 * Less than a bus width available
@@ -687,8 +643,7 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
                                 "less than a bus width (remain 0x%08x)\n",
                                 __func__, remainder);
                        cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
-                       num_llis =
-                               pl08x_fill_lli_for_desc(pl08x, txd, num_llis, 1,
+                       pl08x_fill_lli_for_desc(pl08x, txd, num_llis++, 1,
                                        cctl, &remainder);
                        total_bytes++;
                }
@@ -703,8 +658,8 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
                                 "(remain 0x%08x)\n",
                                 __func__, remainder);
                        cctl = pl08x_cctl_bits(cctl, 1, 1, 1);
-                       num_llis = pl08x_fill_lli_for_desc
-                               (pl08x, txd, num_llis, 1, cctl, &remainder);
+                       pl08x_fill_lli_for_desc(pl08x, txd, num_llis++, 1,
+                                       cctl, &remainder);
                        total_bytes++;
                }
 
@@ -828,9 +783,8 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
                                dev_vdbg(&pl08x->adev->dev,
                                        "%s fill lli with single lli chunk of size 0x%08zx (remainder 0x%08zx)\n",
                                        __func__, lli_len, remainder);
-                               num_llis = pl08x_fill_lli_for_desc(pl08x, txd,
-                                               num_llis, lli_len, cctl,
-                                               &remainder);
+                               pl08x_fill_lli_for_desc(pl08x, txd, num_llis++,
+                                               lli_len, cctl, &remainder);
                                total_bytes += lli_len;
                        }
 
@@ -847,10 +801,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
                                        dev_vdbg(&pl08x->adev->dev,
                                                "%s align with boundary, single byte (remain 0x%08zx)\n",
                                                __func__, remainder);
-                                       num_llis =
-                                               pl08x_fill_lli_for_desc(pl08x,
-                                                       txd, num_llis, 1,
-                                                       cctl, &remainder);
+                                       pl08x_fill_lli_for_desc(pl08x, txd,
+                                                       num_llis++, 1, cctl,
+                                                       &remainder);
                                        total_bytes++;
                                }
                        }
@@ -864,8 +817,8 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
                        dev_vdbg(&pl08x->adev->dev,
                                "%s align with boundary, single odd byte (remain %zu)\n",
                                __func__, remainder);
-                       num_llis = pl08x_fill_lli_for_desc(pl08x, txd, num_llis,
-                                       1, cctl, &remainder);
+                       pl08x_fill_lli_for_desc(pl08x, txd, num_llis++, 1,
+                                       cctl, &remainder);
                        total_bytes++;
                }
        }
@@ -1158,12 +1111,10 @@ static void dma_set_runtime_config(struct dma_chan *chan,
        plchan->runtime_direction = config->direction;
        if (config->direction == DMA_TO_DEVICE) {
                plchan->runtime_addr = config->dst_addr;
-               cctl |= PL080_CONTROL_SRC_INCR;
                addr_width = config->dst_addr_width;
                maxburst = config->dst_maxburst;
        } else if (config->direction == DMA_FROM_DEVICE) {
                plchan->runtime_addr = config->src_addr;
-               cctl |= PL080_CONTROL_DST_INCR;
                addr_width = config->src_addr_width;
                maxburst = config->src_maxburst;
        } else {
@@ -1207,10 +1158,6 @@ static void dma_set_runtime_config(struct dma_chan *chan,
                cctl |= burst_sizes[i].reg;
        }
 
-       /* Access the cell in privileged mode, non-bufferable, non-cacheable */
-       cctl &= ~PL080_CONTROL_PROT_MASK;
-       cctl |= PL080_CONTROL_PROT_SYS;
-
        /* Modify the default channel data to fit PrimeCell request */
        cd->cctl = cctl;
 
@@ -1318,6 +1265,23 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
        return 0;
 }
 
+/*
+ * Given the source and destination available bus masks, select which
+ * will be routed to each port.  We try to have source and destination
+ * on separate ports, but always respect the allowable settings.
+ */
+static u32 pl08x_select_bus(struct pl08x_driver_data *pl08x, u8 src, u8 dst)
+{
+       u32 cctl = 0;
+
+       if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
+               cctl |= PL080_CONTROL_DST_AHB2;
+       if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
+               cctl |= PL080_CONTROL_SRC_AHB2;
+
+       return cctl;
+}
+
 static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan)
 {
        struct pl08x_txd *txd = kzalloc(sizeof(struct pl08x_txd), GFP_NOWAIT);
@@ -1356,14 +1320,19 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
        txd->direction = DMA_NONE;
        txd->srcbus.addr = src;
        txd->dstbus.addr = dest;
+       txd->len = len;
 
        /* Set platform data for m2m */
-       txd->cd = &pl08x->pd->memcpy_channel;
        txd->ccfg |= PL080_FLOW_MEM2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+       txd->cctl = pl08x->pd->memcpy_channel.cctl &
+                       ~(PL080_CONTROL_DST_AHB2 | PL080_CONTROL_SRC_AHB2);
 
        /* Both to be incremented or the code will break */
-       txd->cd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR;
-       txd->len = len;
+       txd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR;
+
+       if (pl08x->vd->dualmaster)
+               txd->cctl |= pl08x_select_bus(pl08x,
+                                       pl08x->mem_buses, pl08x->mem_buses);
 
        ret = pl08x_prep_channel_resources(plchan, txd);
        if (ret)
@@ -1384,6 +1353,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;
+       u8 src_buses, dst_buses;
        int ret;
 
        /*
@@ -1415,27 +1385,43 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
         * channel target address dynamically at runtime.
         */
        txd->direction = direction;
+       txd->len = sgl->length;
+
+       txd->cctl = plchan->cd->cctl &
+                       ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
+                         PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
+                         PL080_CONTROL_PROT_MASK);
+
+       /* Access the cell in privileged mode, non-bufferable, non-cacheable */
+       txd->cctl |= PL080_CONTROL_PROT_SYS;
+
        if (direction == DMA_TO_DEVICE) {
                txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+               txd->cctl |= PL080_CONTROL_SRC_INCR;
                txd->srcbus.addr = sgl->dma_address;
                if (plchan->runtime_addr)
                        txd->dstbus.addr = plchan->runtime_addr;
                else
                        txd->dstbus.addr = plchan->cd->addr;
+               src_buses = pl08x->mem_buses;
+               dst_buses = plchan->cd->periph_buses;
        } else if (direction == DMA_FROM_DEVICE) {
                txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
+               txd->cctl |= PL080_CONTROL_DST_INCR;
                if (plchan->runtime_addr)
                        txd->srcbus.addr = plchan->runtime_addr;
                else
                        txd->srcbus.addr = plchan->cd->addr;
                txd->dstbus.addr = sgl->dma_address;
+               src_buses = plchan->cd->periph_buses;
+               dst_buses = pl08x->mem_buses;
        } else {
                dev_err(&pl08x->adev->dev,
                        "%s direction unsupported\n", __func__);
                return NULL;
        }
-       txd->cd = plchan->cd;
-       txd->len = sgl->length;
+
+       txd->cctl |= pl08x_select_bus(pl08x, src_buses, dst_buses);
 
        ret = pl08x_prep_channel_resources(plchan, txd);
        if (ret)
@@ -1548,32 +1534,29 @@ static void pl08x_tasklet(unsigned long data)
 {
        struct pl08x_dma_chan *plchan = (struct pl08x_dma_chan *) data;
        struct pl08x_driver_data *pl08x = plchan->host;
+       struct pl08x_txd *txd;
+       dma_async_tx_callback callback = NULL;
+       void *callback_param = NULL;
        unsigned long flags;
 
        spin_lock_irqsave(&plchan->lock, flags);
 
-       if (plchan->at) {
-               dma_async_tx_callback callback =
-                       plchan->at->tx.callback;
-               void *callback_param =
-                       plchan->at->tx.callback_param;
+       txd = plchan->at;
+       plchan->at = NULL;
 
-               /*
-                * Update last completed
-                */
-               plchan->lc = plchan->at->tx.cookie;
+       if (txd) {
+               callback = txd->tx.callback;
+               callback_param = txd->tx.callback_param;
 
                /*
-                * Callback to signal completion
+                * Update last completed
                 */
-               if (callback)
-                       callback(callback_param);
+               plchan->lc = txd->tx.cookie;
 
                /*
                 * Free the descriptor
                 */
-               pl08x_free_txd(pl08x, plchan->at);
-               plchan->at = NULL;
+               pl08x_free_txd(pl08x, txd);
        }
        /*
         * If a new descriptor is queued, set it up
@@ -1624,6 +1607,10 @@ static void pl08x_tasklet(unsigned long data)
        }
 
        spin_unlock_irqrestore(&plchan->lock, flags);
+
+       /* Callback to signal completion */
+       if (callback)
+               callback(callback_param);
 }
 
 static irqreturn_t pl08x_irq(int irq, void *dev)
@@ -1888,6 +1875,14 @@ static int pl08x_probe(struct amba_device *adev, struct amba_id *id)
        pl08x->adev = adev;
        pl08x->vd = vd;
 
+       /* By default, AHB1 only.  If dualmaster, from platform */
+       pl08x->lli_buses = PL08X_AHB1;
+       pl08x->mem_buses = PL08X_AHB1;
+       if (pl08x->vd->dualmaster) {
+               pl08x->lli_buses = pl08x->pd->lli_buses;
+               pl08x->mem_buses = pl08x->pd->mem_buses;
+       }
+
        /* A DMA memory pool for LLIs, align on 1-byte boundary */
        pl08x->pool = dma_pool_create(DRIVER_NAME, &pl08x->adev->dev,
                        PL08X_LLI_TSFR_SIZE, PL08X_ALIGN, 0);