X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fdma%2Ffsldma.c;h=054eabffc185a893ff32ab51c01dfd876ab84b43;hb=a6d8113a986c66aeb379a26b6e0062488b3e59e1;hp=ad2f938597e21a06f7c751eedabc97bdab37b6d9;hpb=2f633928cbba8a5858bb39b11e7219a41b0fbef5;p=linux-2.6 diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index ad2f938597..054eabffc1 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -123,6 +123,11 @@ static dma_addr_t get_ndar(struct fsl_dma_chan *fsl_chan) 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); @@ -407,7 +412,7 @@ static void fsl_dma_free_chan_resources(struct dma_chan *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; @@ -424,7 +429,10 @@ fsl_dma_prep_interrupt(struct dma_chan *chan) } 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); @@ -474,7 +482,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( 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; @@ -485,7 +493,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( 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*/ @@ -650,13 +658,6 @@ static void fsl_dma_memcpy_issue_pending(struct dma_chan *chan) 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 @@ -688,6 +689,8 @@ static irqreturn_t fsl_dma_chan_do_interrupt(int irq, void *data) { 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", @@ -701,6 +704,23 @@ static irqreturn_t fsl_dma_chan_do_interrupt(int irq, void *data) 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. */ @@ -709,19 +729,33 @@ static irqreturn_t fsl_dma_chan_do_interrupt(int irq, void *data) 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); @@ -751,15 +785,13 @@ static void dma_do_tasklet(unsigned long data) 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; @@ -841,17 +873,20 @@ static int fsl_dma_self_test(struct fsl_dma_chan *fsl_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); @@ -876,7 +911,6 @@ out: kfree(src); return err; } -#endif static int __devinit of_fsl_dma_chan_probe(struct of_device *dev, const struct of_device_id *match) @@ -967,11 +1001,9 @@ static int __devinit of_fsl_dma_chan_probe(struct of_device *dev, } } -#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); @@ -991,11 +1023,11 @@ const u32 mpc8349_dma_ip_feature = FSL_DMA_IP_83XX | FSL_DMA_LITTLE_ENDIAN; 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, }, {} @@ -1050,7 +1082,6 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev, 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); @@ -1077,8 +1108,8 @@ err: } 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", }, {} };