diff options
author | Manoj Chourasia <mchourasia@nvidia.com> | 2012-02-15 14:22:28 +0530 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-02-17 07:44:02 -0800 |
commit | fa554fc5842695970f5568a129a61bafaf7c998a (patch) | |
tree | 0d8c5c9fb293b13d06328d62a7ed25fed1b63c3b /drivers/mtd | |
parent | 7108e0a05e1d07996d7c3ba9835ce57f7a1f7e77 (diff) |
mtd: tegra-nor: Use staged read from NOR
Most of the large read requests passed by upper MTD block
layer are for highmem region which dma_map_single cannot
handle. Those requests were getting serviced by memcpy_fromio,
this was degrading performance. Moreover the memory region
passed to dma_map_single should be on cache line boundaries.
If the requirement is not met then system may crash due
to cache incoherency.
The approach added in this patch is to have a DMA coherent
memory buffer. Read device via DMA to this coherent buffer
and them memcpy it to user space pointer. This not only
fixes some bugs but also enhances the NOR read performance.
bug 928788
bug 898250
Signed-off-by: Manoj Chourasia <mchourasia@nvidia.com>
Reviewed-on: http://git-master/r/76866
(cherry picked from commit 1491c0461a627c3bb63b01e126585eff9922ba1a)
Change-Id: Ic8e24d2cc965f84bb97d2b6b29f27458aba17720
Reviewed-on: http://git-master/r/84026
Tested-by: Manoj Chourasia <mchourasia@nvidia.com>
Reviewed-by: Varun Wadekar <vwadekar@nvidia.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/maps/tegra_nor.c | 43 |
1 files changed, 26 insertions, 17 deletions
diff --git a/drivers/mtd/maps/tegra_nor.c b/drivers/mtd/maps/tegra_nor.c index 2059ce066cbb..b455fd5e1c00 100644 --- a/drivers/mtd/maps/tegra_nor.c +++ b/drivers/mtd/maps/tegra_nor.c @@ -118,6 +118,8 @@ struct tegra_nor_info { struct map_info map; struct completion dma_complete; void __iomem *base; + void *dma_virt_buffer; + dma_addr_t dma_phys_buffer; u32 init_config; u32 timing0_default, timing1_default; u32 timing0_read, timing1_read; @@ -154,7 +156,8 @@ static void tegra_flash_dma(struct map_info *map, { u32 snor_config, dma_config = 0; int dma_transfer_count = 0, word32_count = 0; - u32 nor_address, ahb_address, current_transfer; + u32 nor_address, current_transfer = 0; + u32 copy_to = (u32)to; struct tegra_nor_info *c = container_of(map, struct tegra_nor_info, map); unsigned int bytes_remaining = len; @@ -164,16 +167,6 @@ static void tegra_flash_dma(struct map_info *map, snor_tegra_writel(c, c->timing1_read, TEGRA_SNOR_TIMING1_REG); if (len > 32) { - - if (to >= high_memory) - goto out_copy; - - ahb_address = dma_map_single(c->dev, to, len, DMA_FROM_DEVICE); - if (dma_mapping_error(c->dev, ahb_address)) { - dev_err(c->dev, - "Couldn't DMA map a %d byte buffer\n", len); - goto out_copy; - } word32_count = len >> 2; bytes_remaining = len & 0x00000003; /* @@ -196,7 +189,7 @@ static void tegra_flash_dma(struct map_info *map, word32_count -= current_transfer, dma_transfer_count += current_transfer, nor_address += (current_transfer * 4), - ahb_address += (current_transfer * 4)) { + copy_to += (current_transfer * 4)) { current_transfer = (word32_count > TEGRA_SNOR_DMA_LIMIT_WORDS) @@ -210,7 +203,7 @@ static void tegra_flash_dma(struct map_info *map, /* Num of AHB (32-bit) words to transferred minus 1 */ dma_config |= TEGRA_SNOR_DMA_CFG_WRD_CNT(current_transfer - 1); - snor_tegra_writel(c, ahb_address, + snor_tegra_writel(c, c->dma_phys_buffer, TEGRA_SNOR_AHB_ADDR_PTR_REG); snor_tegra_writel(c, nor_address, TEGRA_SNOR_NOR_ADDR_PTR_REG); @@ -224,15 +217,17 @@ static void tegra_flash_dma(struct map_info *map, bytes_remaining += (word32_count << 2); break; } + memcpy((char *)(copy_to), (char *)(c->dma_virt_buffer), + (current_transfer << 2)); + } - dma_unmap_single(c->dev, ahb_address, len, DMA_FROM_DEVICE); } /* Put the controller back into slave mode. */ snor_config = snor_tegra_readl(c, TEGRA_SNOR_CONFIG_REG); snor_config &= ~TEGRA_SNOR_CONFIG_MST_ENB; snor_config |= TEGRA_SNOR_CONFIG_DEVICE_MODE(0); snor_tegra_writel(c, snor_config, TEGRA_SNOR_CONFIG_REG); -out_copy: + memcpy_fromio(((char *)to + (dma_transfer_count << 2)), ((char *)(map->virt + from) + (dma_transfer_count << 2)), bytes_remaining); @@ -313,7 +308,7 @@ static int tegra_nor_probe(struct platform_device *pdev) goto fail; } - /* Get NOR flash aperture & map the same */ + /* Get NOR controller & map the same */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "no mem resource?\n"); @@ -401,11 +396,20 @@ static int tegra_nor_probe(struct platform_device *pdev) dev_err(dev, "Failed to request irq %i\n", irq); goto out_clk_disable; } + info->dma_virt_buffer = dma_alloc_coherent(dev, + TEGRA_SNOR_DMA_LIMIT, + &info->dma_phys_buffer, + GFP_KERNEL); + if (info->dma_virt_buffer == NULL) { + dev_err(&pdev->dev, "Could not allocate buffer for DMA"); + err = -ENOMEM; + goto out_clk_disable; + } info->mtd = do_map_probe(plat->flash.map_name, &info->map); if (!info->mtd) { err = -EIO; - goto out_clk_disable; + goto out_dma_free_coherent; } info->mtd->owner = THIS_MODULE; info->parts = NULL; @@ -423,6 +427,9 @@ static int tegra_nor_probe(struct platform_device *pdev) return 0; +out_dma_free_coherent: + dma_free_coherent(dev, TEGRA_SNOR_DMA_LIMIT, + info->dma_virt_buffer, info->dma_phys_buffer); out_clk_disable: clk_disable(info->clk); out_clk_put: @@ -439,6 +446,8 @@ static int tegra_nor_remove(struct platform_device *pdev) mtd_device_unregister(info->mtd); if (info->parts) kfree(info->parts); + dma_free_coherent(&pdev->dev, TEGRA_SNOR_DMA_LIMIT, + info->dma_virt_buffer, info->dma_phys_buffer); map_destroy(info->mtd); clk_disable(info->clk); clk_put(info->clk); |