* Russell King - ARM Linux <li...@arm.linux.org.uk> [120424 03:42]:
> Here's another patch - for the OMAP NAND driver.
> 
> One thing this doesn't do is configure up the source/destination bursts,
> which the old code did:
> 
>                         omap_set_dma_dest_burst_mode(info->dma_ch,
>                                         OMAP_DMA_DATA_BURST_16);
>                         omap_set_dma_src_burst_mode(info->dma_ch,
>                                         OMAP_DMA_DATA_BURST_16);

Grazvydas, care to give this patch a go?
 
> In dma-engine speak, I'm using "burst" for the number of elements to
> transfer for each frame, with frame sync in place (in other words, the
> number of transfers to occur for every assertion of the DMA request.)
> That's how burst is defined on other DMA hardware, so I'm not entirely
> sure at the moment how critical (or what) the above bursts are doing,
> whether they're configuring the memory side of the transfer or not.
> I'll take a deeper look into that this evening, but in the mean time,
> what's below should be a direct conversion.

For omaps it enables multiple access.
 
> MTD does have this weirdness that it uses vmalloc regions and passes
> addresses in vmalloc regions into drivers - I've left that hack in but
> it is _highly_ undefined whether the DMA activity would be visible via
> the vmalloc mapping as things currently stand.  (You're probably going
> to be okay with non-aliasing VIPT caches, but VIVT and aliasing VIPT
> caches are potential random data corruption candidates.)  That's a
> short-coming across many MTD drivers, one which needs sorting out across
> the board.
> 
>  drivers/mtd/nand/omap2.c |   93 
> +++++++++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 92 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
> index c2b0bba..bd4ed08 100644
> --- a/drivers/mtd/nand/omap2.c
> +++ b/drivers/mtd/nand/omap2.c
> @@ -9,6 +9,7 @@
>   */
>  
>  #include <linux/platform_device.h>
> +#include <linux/dmaengine.h>
>  #include <linux/dma-mapping.h>
>  #include <linux/delay.h>
>  #include <linux/module.h>
> @@ -119,6 +120,7 @@ struct omap_nand_info {
>       int                             gpmc_cs;
>       unsigned long                   phys_base;
>       struct completion               comp;
> +     struct dma_chan                 *dma;
>       int                             dma_ch;
>       int                             gpmc_irq;
>       enum {
> @@ -336,6 +338,10 @@ static void omap_nand_dma_cb(int lch, u16 ch_status, 
> void *data)
>  {
>       complete((struct completion *) data);
>  }
> +static void omap_nand_dma_callback(void *data)
> +{
> +     complete((struct completion *) data);
> +}
>  
>  /*
>   * omap_nand_dma_transfer: configer and start dma transfer
> @@ -373,6 +379,56 @@ static inline int omap_nand_dma_transfer(struct mtd_info 
> *mtd, void *addr,
>               addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK);
>       }
>  
> +     if (info->dma) {
> +             struct dma_async_tx_descriptor *tx;
> +             struct scatterlist sg;
> +             unsigned n;
> +
> +             sg_init_one(&sg, addr, len);
> +             n = dma_map_sg(info->dma->device->dev, &sg, 1, dir);
> +             if (n == 0) {
> +                     dev_err(&info->pdev->dev,
> +                             "Couldn't DMA map a %d byte buffer\n", len);
> +                     goto out_copy;
> +             }
> +
> +             tx = dmaengine_prep_slave_sg(info->dma, &sg, n,
> +                     is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
> +                     DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> +             if (!tx) {
> +                     dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
> +                     goto out_copy;
> +             }
> +             tx->callback = omap_nand_dma_callback;
> +             tx->callback_param = &info->comp;
> +             dmaengine_submit(tx);
> +
> +             /*  configure and start prefetch transfer */
> +             ret = gpmc_prefetch_enable(info->gpmc_cs,
> +                     PREFETCH_FIFOTHRESHOLD_MAX, 0x1, len, is_write);
> +             if (ret) {
> +                     /* PFPW engine is busy, use cpu copy method */
> +                     dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
> +                     goto out_copy;
> +             }
> +
> +             init_completion(&info->comp);
> +             dma_async_issue_pending(info->dma);
> +
> +             /* setup and start DMA using dma_addr */
> +             wait_for_completion(&info->comp);
> +             tim = 0;
> +             limit = (loops_per_jiffy * 
> msecs_to_jiffies(OMAP_NAND_TIMEOUT_MS));
> +             while (gpmc_read_status(GPMC_PREFETCH_COUNT) && (tim++ < limit))
> +                     cpu_relax();
> +
> +             /* disable and stop the PFPW engine */
> +             gpmc_prefetch_reset(info->gpmc_cs);
> +
> +             dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
> +             return 0;
> +     }
> +
>       dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir);
>       if (dma_mapping_error(&info->pdev->dev, dma_addr)) {
>               dev_err(&info->pdev->dev,
> @@ -405,7 +461,6 @@ static inline int omap_nand_dma_transfer(struct mtd_info 
> *mtd, void *addr,
>               goto out_copy;
>  
>       init_completion(&info->comp);
> -
>       omap_start_dma(info->dma_ch);
>  
>       /* setup and start DMA using dma_addr */
> @@ -925,12 +980,16 @@ static int omap_dev_ready(struct mtd_info *mtd)
>       return 1;
>  }
>  
> +extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param);
> +
>  static int __devinit omap_nand_probe(struct platform_device *pdev)
>  {
>       struct omap_nand_info           *info;
>       struct omap_nand_platform_data  *pdata;
>       int                             err;
>       int                             i, offset;
> +     dma_cap_mask_t mask;
> +     unsigned sig;
>  
>       pdata = pdev->dev.platform_data;
>       if (pdata == NULL) {
> @@ -1011,6 +1070,33 @@ static int __devinit omap_nand_probe(struct 
> platform_device *pdev)
>               break;
>  
>       case NAND_OMAP_PREFETCH_DMA:
> +             dma_cap_zero(mask);
> +             dma_cap_set(DMA_SLAVE, mask);
> +             sig = OMAP24XX_DMA_GPMC;
> +             info->dma = dma_request_channel(mask, omap_dma_filter_fn, &sig);
> +             if (!info->dma) {
> +                     dev_warn(&pdev->dev, "DMA engine request failed\n");
> +             } else {
> +                     struct dma_slave_config cfg;
> +                     int rc;
> +
> +                     memset(&cfg, 0, sizeof(cfg));
> +                     cfg.src_addr = info->phys_base;
> +                     cfg.dst_addr = info->phys_base;
> +                     cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +                     cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +                     cfg.src_maxburst = 16;
> +                     cfg.dst_maxburst = 16;
> +                     rc = dmaengine_slave_config(info->dma, &cfg);
> +                     if (rc) {
> +                             dev_err(&pdev->dev, "DMA engine slave config 
> failed: %d\n",
> +                                     rc);
> +                             goto out_release_mem_region;
> +                     }
> +                     info->nand.read_buf   = omap_read_buf_dma_pref;
> +                     info->nand.write_buf  = omap_write_buf_dma_pref;
> +                     break;
> +             }
>               err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND",
>                               omap_nand_dma_cb, &info->comp, &info->dma_ch);
>               if (err < 0) {
> @@ -1110,6 +1196,8 @@ static int __devinit omap_nand_probe(struct 
> platform_device *pdev)
>       return 0;
>  
>  out_release_mem_region:
> +     if (info->dma)
> +             dma_release_channel(info->dma);
>       release_mem_region(info->phys_base, NAND_IO_SIZE);
>  out_free_info:
>       kfree(info);
> @@ -1127,6 +1215,9 @@ static int omap_nand_remove(struct platform_device 
> *pdev)
>       if (info->dma_ch != -1)
>               omap_free_dma(info->dma_ch);
>  
> +     if (info->dma)
> +             dma_release_channel(info->dma);
> +
>       if (info->gpmc_irq)
>               free_irq(info->gpmc_irq, info);
>  
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to