X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fdma%2Ffsldma.c;h=054eabffc185a893ff32ab51c01dfd876ab84b43;hb=be305042b7a01a1ab03a8adfa95f57bc63e012e1;hp=cc9a68158d992311185d96a9428bc3a627dccaf4;hpb=6c47d773e7d8f784996d9027f035379baa19ded7;p=linux-2.6 diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index cc9a68158d..054eabffc1 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -57,12 +57,12 @@ static void dma_init(struct fsl_dma_chan *fsl_chan) } -static void set_sr(struct fsl_dma_chan *fsl_chan, dma_addr_t val) +static void set_sr(struct fsl_dma_chan *fsl_chan, u32 val) { DMA_OUT(fsl_chan, &fsl_chan->reg_base->sr, val, 32); } -static dma_addr_t get_sr(struct fsl_dma_chan *fsl_chan) +static u32 get_sr(struct fsl_dma_chan *fsl_chan) { return DMA_IN(fsl_chan, &fsl_chan->reg_base->sr, 32); } @@ -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); @@ -406,6 +411,35 @@ static void fsl_dma_free_chan_resources(struct dma_chan *chan) dma_pool_destroy(fsl_chan->desc_pool); } +static struct dma_async_tx_descriptor * +fsl_dma_prep_interrupt(struct dma_chan *chan, unsigned long flags) +{ + struct fsl_dma_chan *fsl_chan; + struct fsl_desc_sw *new; + + if (!chan) + return NULL; + + fsl_chan = to_fsl_chan(chan); + + new = fsl_dma_alloc_descriptor(fsl_chan); + if (!new) { + dev_err(fsl_chan->dev, "No free memory for link descriptor\n"); + return NULL; + } + + new->async_tx.cookie = -EBUSY; + 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); + + return &new->async_tx; +} + static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t dma_src, size_t len, unsigned long flags) @@ -436,7 +470,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy( dev_dbg(fsl_chan->dev, "new link desc alloc %p\n", new); #endif - copy = min(len, FSL_DMA_BCR_MAX_CNT); + copy = min(len, (size_t)FSL_DMA_BCR_MAX_CNT); set_desc_cnt(fsl_chan, &new->hw, copy); set_desc_src(fsl_chan, &new->hw, dma_src); @@ -448,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; @@ -459,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*/ @@ -513,7 +547,6 @@ static void fsl_chan_ld_cleanup(struct fsl_dma_chan *fsl_chan) spin_lock_irqsave(&fsl_chan->desc_lock, flags); - fsl_dma_update_completed_cookie(fsl_chan); dev_dbg(fsl_chan->dev, "chan completed_cookie = %d\n", fsl_chan->completed_cookie); list_for_each_entry_safe(desc, _desc, &fsl_chan->ld_queue, node) { @@ -581,8 +614,8 @@ static void fsl_chan_xfer_ld_queue(struct fsl_dma_chan *fsl_chan) if (ld_node != &fsl_chan->ld_queue) { /* Get the ld start address from ld_queue */ next_dest_addr = to_fsl_desc(ld_node)->async_tx.phys; - dev_dbg(fsl_chan->dev, "xfer LDs staring from 0x%016llx\n", - (u64)next_dest_addr); + dev_dbg(fsl_chan->dev, "xfer LDs staring from %p\n", + (void *)next_dest_addr); set_cdar(fsl_chan, next_dest_addr); dma_start(fsl_chan); } else { @@ -625,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 @@ -662,7 +688,9 @@ static enum dma_status fsl_dma_is_complete(struct dma_chan *chan, static irqreturn_t fsl_dma_chan_do_interrupt(int irq, void *data) { struct fsl_dma_chan *fsl_chan = (struct fsl_dma_chan *)data; - dma_addr_t stat; + 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", @@ -676,27 +704,58 @@ 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. */ if (stat & FSL_DMA_SR_EOSI) { dev_dbg(fsl_chan->dev, "event: End-of-segments INT\n"); - dev_dbg(fsl_chan->dev, "event: clndar 0x%016llx, " - "nlndar 0x%016llx\n", (u64)get_cdar(fsl_chan), - (u64)get_ndar(fsl_chan)); + 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; + 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); @@ -726,10 +785,11 @@ static void dma_do_tasklet(unsigned long data) fsl_chan_ld_cleanup(fsl_chan); } -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"); } static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan) @@ -813,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); @@ -837,9 +900,9 @@ static int fsl_dma_self_test(struct fsl_dma_chan *fsl_chan) if (err) { for (i = 0; (*(src + i) == *(dest + i)) && (i < test_size); i++); - dev_err(fsl_chan->dev, "selftest: Test failed, data %d/%d is " + dev_err(fsl_chan->dev, "selftest: Test failed, data %d/%ld is " "error! src 0x%x, dest 0x%x\n", - i, test_size, *(src + i), *(dest + i)); + i, (long)test_size, *(src + i), *(dest + i)); } free_resources: @@ -938,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); @@ -962,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, }, {} @@ -1008,8 +1069,8 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev, } dev_info(&dev->dev, "Probe the Freescale DMA driver for %s " - "controller at 0x%08x...\n", - match->compatible, fdev->reg.start); + "controller at %p...\n", + match->compatible, (void *)fdev->reg.start); fdev->reg_base = ioremap(fdev->reg.start, fdev->reg.end - fdev->reg.start + 1); @@ -1017,10 +1078,10 @@ static int __devinit of_fsl_dma_probe(struct of_device *dev, dma_cap_set(DMA_INTERRUPT, fdev->common.cap_mask); fdev->common.device_alloc_chan_resources = fsl_dma_alloc_chan_resources; fdev->common.device_free_chan_resources = fsl_dma_free_chan_resources; + fdev->common.device_prep_dma_interrupt = fsl_dma_prep_interrupt; 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); @@ -1047,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", }, {} };