dmaengine: edma: Dynamic paRAM slot handling if HW supports it
[firefly-linux-kernel-4.4.55.git] / drivers / dma / edma.c
index 8d9169b7f20813374ba19058663c7367935b8e6d..7eefbf1e1c94a3d68fffa3dc5df33048dc8c66d8 100644 (file)
@@ -413,12 +413,13 @@ static void edma_assign_priority_to_queue(struct edma_cc *ecc, int queue_no,
        edma_modify(ecc, EDMA_QUEPRI, ~(0x7 << bit), ((priority & 0x7) << bit));
 }
 
-static void edma_direct_dmach_to_param_mapping(struct edma_cc *ecc)
+static void edma_set_chmap(struct edma_cc *ecc, int channel, int slot)
 {
-       int i;
-
-       for (i = 0; i < ecc->num_channels; i++)
-               edma_write_array(ecc, EDMA_DCHMAP, i, (i << 5));
+       if (ecc->chmap_exist) {
+               channel = EDMA_CHAN_SLOT(channel);
+               slot = EDMA_CHAN_SLOT(slot);
+               edma_write_array(ecc, EDMA_DCHMAP, channel, (slot << 5));
+       }
 }
 
 static int prepare_unused_channel_list(struct device *dev, void *data)
@@ -528,10 +529,18 @@ static void edma_read_slot(struct edma_cc *ecc, unsigned slot,
  */
 static int edma_alloc_slot(struct edma_cc *ecc, int slot)
 {
-       if (slot > 0)
+       if (slot > 0) {
                slot = EDMA_CHAN_SLOT(slot);
+               /* Requesting entry paRAM slot for a HW triggered channel. */
+               if (ecc->chmap_exist && slot < ecc->num_channels)
+                       slot = EDMA_SLOT_ANY;
+       }
+
        if (slot < 0) {
-               slot = ecc->num_channels;
+               if (ecc->chmap_exist)
+                       slot = 0;
+               else
+                       slot = ecc->num_channels;
                for (;;) {
                        slot = find_next_zero_bit(ecc->slot_inuse,
                                                  ecc->num_slots,
@@ -541,7 +550,7 @@ static int edma_alloc_slot(struct edma_cc *ecc, int slot)
                        if (!test_and_set_bit(slot, ecc->slot_inuse))
                                break;
                }
-       } else if (slot < ecc->num_channels || slot >= ecc->num_slots) {
+       } else if (slot >= ecc->num_slots) {
                return -EINVAL;
        } else if (test_and_set_bit(slot, ecc->slot_inuse)) {
                return -EBUSY;
@@ -555,7 +564,7 @@ static int edma_alloc_slot(struct edma_cc *ecc, int slot)
 static void edma_free_slot(struct edma_cc *ecc, unsigned slot)
 {
        slot = EDMA_CHAN_SLOT(slot);
-       if (slot < ecc->num_channels || slot >= ecc->num_slots)
+       if (slot >= ecc->num_slots)
                return;
 
        edma_write_slot(ecc, slot, &dummy_paramset);
@@ -806,7 +815,6 @@ static void edma_clean_channel(struct edma_cc *ecc, unsigned channel)
 static int edma_alloc_channel(struct edma_cc *ecc, int channel,
                              enum dma_event_q eventq_no)
 {
-       unsigned done = 0;
        int ret = 0;
 
        if (!ecc->unused_chan_list_done) {
@@ -833,24 +841,12 @@ static int edma_alloc_channel(struct edma_cc *ecc, int channel,
        }
 
        if (channel < 0) {
-               channel = 0;
-               for (;;) {
-                       channel = find_next_bit(ecc->channel_unused,
-                                               ecc->num_channels, channel);
-                       if (channel == ecc->num_channels)
-                               break;
-                       if (!test_and_set_bit(channel, ecc->slot_inuse)) {
-                               done = 1;
-                               break;
-                       }
-                       channel++;
-               }
-               if (!done)
-                       return -ENOMEM;
+               channel = find_next_bit(ecc->channel_unused, ecc->num_channels,
+                                       0);
+               if (channel == ecc->num_channels)
+                       return -EBUSY;
        } else if (channel >= ecc->num_channels) {
                return -EINVAL;
-       } else if (test_and_set_bit(channel, ecc->slot_inuse)) {
-               return -EBUSY;
        }
 
        /* ensure access through shadow region 0 */
@@ -858,7 +854,6 @@ static int edma_alloc_channel(struct edma_cc *ecc, int channel,
 
        /* ensure no events are pending */
        edma_stop(ecc, EDMA_CTLR_CHAN(ecc->id, channel));
-       edma_write_slot(ecc, channel, &dummy_paramset);
 
        edma_setup_interrupt(ecc, EDMA_CTLR_CHAN(ecc->id, channel), true);
 
@@ -891,11 +886,8 @@ static void edma_free_channel(struct edma_cc *ecc, unsigned channel)
        if (channel >= ecc->num_channels)
                return;
 
-       edma_setup_interrupt(ecc, channel, false);
        /* REVISIT should probably take out of shadow region 0 */
-
-       edma_write_slot(ecc, channel, &dummy_paramset);
-       clear_bit(channel, ecc->slot_inuse);
+       edma_setup_interrupt(ecc, channel, false);
 }
 
 /* Move channel to a specific event queue */
@@ -1729,7 +1721,15 @@ static int edma_alloc_chan_resources(struct dma_chan *chan)
        }
 
        echan->alloced = true;
-       echan->slot[0] = echan->ch_num;
+       echan->slot[0] = edma_alloc_slot(echan->ecc, echan->ch_num);
+       if (echan->slot[0] < 0) {
+               dev_err(dev, "Entry slot allocation failed for channel %u\n",
+                       EDMA_CHAN_SLOT(echan->ch_num));
+               goto err_wrong_chan;
+       }
+
+       /* Set up channel -> slot mapping for the entry slot */
+       edma_set_chmap(echan->ecc, echan->ch_num, echan->slot[0]);
 
        dev_dbg(dev, "allocated channel %d for %u:%u\n", echan->ch_num,
                EDMA_CTLR(echan->ch_num), EDMA_CHAN_SLOT(echan->ch_num));
@@ -1754,13 +1754,16 @@ static void edma_free_chan_resources(struct dma_chan *chan)
        vchan_free_chan_resources(&echan->vchan);
 
        /* Free EDMA PaRAM slots */
-       for (i = 1; i < EDMA_MAX_SLOTS; i++) {
+       for (i = 0; i < EDMA_MAX_SLOTS; i++) {
                if (echan->slot[i] >= 0) {
                        edma_free_slot(echan->ecc, echan->slot[i]);
                        echan->slot[i] = -1;
                }
        }
 
+       /* Set entry slot to the dummy slot */
+       edma_set_chmap(echan->ecc, echan->ch_num, echan->ecc->dummy_slot);
+
        /* Free EDMA channel */
        if (echan->alloced) {
                edma_free_channel(echan->ecc, echan->ch_num);
@@ -2217,8 +2220,18 @@ static int edma_probe(struct platform_device *pdev)
                }
        }
 
-       for (i = 0; i < ecc->num_channels; i++)
+       ecc->dummy_slot = edma_alloc_slot(ecc, EDMA_SLOT_ANY);
+       if (ecc->dummy_slot < 0) {
+               dev_err(dev, "Can't allocate PaRAM dummy slot\n");
+               return ecc->dummy_slot;
+       }
+
+       for (i = 0; i < ecc->num_channels; i++) {
+               /* Assign all channels to the default queue */
                edma_map_dmach_to_queue(ecc, i, info->default_queue);
+               /* Set entry slot to the dummy slot */
+               edma_set_chmap(ecc, i, ecc->dummy_slot);
+       }
 
        queue_priority_mapping = info->queue_priority_mapping;
 
@@ -2227,10 +2240,6 @@ static int edma_probe(struct platform_device *pdev)
                edma_assign_priority_to_queue(ecc, queue_priority_mapping[i][0],
                                              queue_priority_mapping[i][1]);
 
-       /* Map the channel to param entry if channel mapping logic exist */
-       if (ecc->chmap_exist)
-               edma_direct_dmach_to_param_mapping(ecc);
-
        for (i = 0; i < ecc->num_region; i++) {
                edma_write_array2(ecc, EDMA_DRAE, i, 0, 0x0);
                edma_write_array2(ecc, EDMA_DRAE, i, 1, 0x0);
@@ -2238,12 +2247,6 @@ static int edma_probe(struct platform_device *pdev)
        }
        ecc->info = info;
 
-       ecc->dummy_slot = edma_alloc_slot(ecc, EDMA_SLOT_ANY);
-       if (ecc->dummy_slot < 0) {
-               dev_err(dev, "Can't allocate PaRAM dummy slot\n");
-               return ecc->dummy_slot;
-       }
-
        dma_cap_zero(ecc->dma_slave.cap_mask);
        dma_cap_set(DMA_SLAVE, ecc->dma_slave.cap_mask);
        dma_cap_set(DMA_CYCLIC, ecc->dma_slave.cap_mask);
@@ -2287,6 +2290,7 @@ static int edma_remove(struct platform_device *pdev)
 static int edma_pm_resume(struct device *dev)
 {
        struct edma_cc *ecc = dev_get_drvdata(dev);
+       struct edma_chan *echan = ecc->slave_chans;
        int i;
        s8 (*queue_priority_mapping)[2];
 
@@ -2297,18 +2301,17 @@ static int edma_pm_resume(struct device *dev)
                edma_assign_priority_to_queue(ecc, queue_priority_mapping[i][0],
                                              queue_priority_mapping[i][1]);
 
-       /* Map the channel to param entry if channel mapping logic */
-       if (ecc->chmap_exist)
-               edma_direct_dmach_to_param_mapping(ecc);
-
        for (i = 0; i < ecc->num_channels; i++) {
-               if (test_bit(i, ecc->slot_inuse)) {
+               if (echan[i].alloced) {
                        /* ensure access through shadow region 0 */
                        edma_or_array2(ecc, EDMA_DRAE, 0, i >> 5,
                                       BIT(i & 0x1f));
 
                        edma_setup_interrupt(ecc, EDMA_CTLR_CHAN(ecc->id, i),
                                             true);
+
+                       /* Set up channel -> slot mapping for the entry slot */
+                       edma_set_chmap(ecc, echan[i].ch_num, echan[i].slot[0]);
                }
        }