return DMA_IN(fsl_chan, &fsl_chan->reg_base->ndar, 64);
}
+static u32 get_bcr(struct fsl_dma_chan *fsl_chan)
+{
+ return DMA_IN(fsl_chan, &fsl_chan->reg_base->bcr, 32);
+}
+
static int dma_is_idle(struct fsl_dma_chan *fsl_chan)
{
u32 sr = get_sr(fsl_chan);
}
static struct dma_async_tx_descriptor *
-fsl_dma_prep_interrupt(struct dma_chan *chan)
+fsl_dma_prep_interrupt(struct dma_chan *chan, unsigned long flags)
{
struct fsl_dma_chan *fsl_chan;
struct fsl_desc_sw *new;
}
new->async_tx.cookie = -EBUSY;
- new->async_tx.ack = 0;
+ new->async_tx.flags = flags;
+
+ /* Insert the link descriptor to the LD ring */
+ list_add_tail(&new->node, &new->async_tx.tx_list);
/* Set End-of-link to the last link descriptor of new list*/
set_ld_eol(fsl_chan, new);
set_desc_next(fsl_chan, &prev->hw, new->async_tx.phys);
new->async_tx.cookie = 0;
- new->async_tx.ack = 1;
+ async_tx_ack(&new->async_tx);
prev = new;
len -= copy;
list_add_tail(&new->node, &first->async_tx.tx_list);
} while (len);
- new->async_tx.ack = 0; /* client is in control of this ack */
+ new->async_tx.flags = flags; /* client is in control of this ack */
new->async_tx.cookie = -EBUSY;
/* Set End-of-link to the last link descriptor of new list*/
fsl_chan_xfer_ld_queue(fsl_chan);
}
-static void fsl_dma_dependency_added(struct dma_chan *chan)
-{
- struct fsl_dma_chan *fsl_chan = to_fsl_chan(chan);
-
- fsl_chan_ld_cleanup(fsl_chan);
-}
-
/**
* fsl_dma_is_complete - Determine the DMA status
* @fsl_chan : Freescale DMA channel
{
struct fsl_dma_chan *fsl_chan = (struct fsl_dma_chan *)data;
u32 stat;
+ int update_cookie = 0;
+ int xfer_ld_q = 0;
stat = get_sr(fsl_chan);
dev_dbg(fsl_chan->dev, "event: channel %d, stat = 0x%x\n",
if (stat & FSL_DMA_SR_TE)
dev_err(fsl_chan->dev, "Transfer Error!\n");
+ /* Programming Error
+ * The DMA_INTERRUPT async_tx is a NULL transfer, which will
+ * triger a PE interrupt.
+ */
+ if (stat & FSL_DMA_SR_PE) {
+ dev_dbg(fsl_chan->dev, "event: Programming Error INT\n");
+ if (get_bcr(fsl_chan) == 0) {
+ /* BCR register is 0, this is a DMA_INTERRUPT async_tx.
+ * Now, update the completed cookie, and continue the
+ * next uncompleted transfer.
+ */
+ update_cookie = 1;
+ xfer_ld_q = 1;
+ }
+ stat &= ~FSL_DMA_SR_PE;
+ }
+
/* If the link descriptor segment transfer finishes,
* we will recycle the used descriptor.
*/
dev_dbg(fsl_chan->dev, "event: clndar %p, nlndar %p\n",
(void *)get_cdar(fsl_chan), (void *)get_ndar(fsl_chan));
stat &= ~FSL_DMA_SR_EOSI;
- fsl_dma_update_completed_cookie(fsl_chan);
+ update_cookie = 1;
+ }
+
+ /* For MPC8349, EOCDI event need to update cookie
+ * and start the next transfer if it exist.
+ */
+ if (stat & FSL_DMA_SR_EOCDI) {
+ dev_dbg(fsl_chan->dev, "event: End-of-Chain link INT\n");
+ stat &= ~FSL_DMA_SR_EOCDI;
+ update_cookie = 1;
+ xfer_ld_q = 1;
}
/* If it current transfer is the end-of-transfer,
* we should clear the Channel Start bit for
* prepare next transfer.
*/
- if (stat & (FSL_DMA_SR_EOLNI | FSL_DMA_SR_EOCDI)) {
+ if (stat & FSL_DMA_SR_EOLNI) {
dev_dbg(fsl_chan->dev, "event: End-of-link INT\n");
stat &= ~FSL_DMA_SR_EOLNI;
- fsl_chan_xfer_ld_queue(fsl_chan);
+ xfer_ld_q = 1;
}
+ if (update_cookie)
+ fsl_dma_update_completed_cookie(fsl_chan);
+ if (xfer_ld_q)
+ fsl_chan_xfer_ld_queue(fsl_chan);
if (stat)
dev_dbg(fsl_chan->dev, "event: unhandled sr 0x%02x\n",
stat);
fsl_chan_ld_cleanup(fsl_chan);
}
-#ifdef FSL_DMA_CALLBACKTEST
-static void fsl_dma_callback_test(struct fsl_dma_chan *fsl_chan)
+static void fsl_dma_callback_test(void *param)
{
+ struct fsl_dma_chan *fsl_chan = param;
if (fsl_chan)
- dev_info(fsl_chan->dev, "selftest: callback is ok!\n");
+ dev_dbg(fsl_chan->dev, "selftest: callback is ok!\n");
}
-#endif
-#ifdef CONFIG_FSL_DMA_SELFTEST
static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan)
{
struct dma_chan *chan;
tx3 = fsl_dma_prep_memcpy(chan, dma_dest, dma_src, test_size / 4, 0);
async_tx_ack(tx3);
+ /* Interrupt tx test */
+ tx1 = fsl_dma_prep_interrupt(chan, 0);
+ async_tx_ack(tx1);
+ cookie = fsl_dma_tx_submit(tx1);
+
/* Test exchanging the prepared tx sort */
cookie = fsl_dma_tx_submit(tx3);
cookie = fsl_dma_tx_submit(tx2);
-#ifdef FSL_DMA_CALLBACKTEST
if (dma_has_cap(DMA_INTERRUPT, ((struct fsl_dma_device *)
dev_get_drvdata(fsl_chan->dev->parent))->common.cap_mask)) {
tx3->callback = fsl_dma_callback_test;
tx3->callback_param = fsl_chan;
}
-#endif
fsl_dma_memcpy_issue_pending(chan);
msleep(2);
kfree(src);
return err;
}
-#endif
static int __devinit of_fsl_dma_chan_probe(struct of_device *dev,
const struct of_device_id *match)
}
}
-#ifdef CONFIG_FSL_DMA_SELFTEST
err = fsl_dma_self_test(new_fsl_chan);
if (err)
goto err;
-#endif
dev_info(&dev->dev, "#%d (%s), irq %d\n", new_fsl_chan->id,
match->compatible, new_fsl_chan->irq);
static struct of_device_id of_fsl_dma_chan_ids[] = {
{
- .compatible = "fsl,mpc8540-dma-channel",
+ .compatible = "fsl,eloplus-dma-channel",
.data = (void *)&mpc8540_dma_ip_feature,
},
{
- .compatible = "fsl,mpc8349-dma-channel",
+ .compatible = "fsl,elo-dma-channel",
.data = (void *)&mpc8349_dma_ip_feature,
},
{}
fdev->common.device_prep_dma_memcpy = fsl_dma_prep_memcpy;
fdev->common.device_is_tx_complete = fsl_dma_is_complete;
fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending;
- fdev->common.device_dependency_added = fsl_dma_dependency_added;
fdev->common.dev = &dev->dev;
irq = irq_of_parse_and_map(dev->node, 0);
}
static struct of_device_id of_fsl_dma_ids[] = {
- { .compatible = "fsl,mpc8540-dma", },
- { .compatible = "fsl,mpc8349-dma", },
+ { .compatible = "fsl,eloplus-dma", },
+ { .compatible = "fsl,elo-dma", },
{}
};