]> git.kernelconcepts.de Git - karo-tx-uboot.git/commitdiff
mtd/nand/tegra: alignment workaround
authorMarcel Ziswiler <marcel.ziswiler@toradex.com>
Wed, 5 Aug 2015 22:47:06 +0000 (00:47 +0200)
committerLothar Waßmann <LW@KARO-electronics.de>
Thu, 10 Sep 2015 08:23:17 +0000 (10:23 +0200)
Integrate cache alignment bounce buffer to workaround issues as follows:

Loading file '/boot/zImage' to addr 0x01000000 with size 4499152 (0x0044a6d0)...
ERROR: v7_dcache_inval_range - start address is not aligned - 0x1f7f0108
ERROR: v7_dcache_inval_range - stop address is not aligned - 0x1f7f1108
Done
Kernel image @ 0x1000000 [ 0x000000 - 0x44a6d0 ]

Starting kernel ...

undefined instruction
pc : [<005ff03c>]          lr : [<0000800c>]
sp : 0144b6e8  ip : 01000188     fp : 0144a6c8
r10: 00000000  r9 : 411fc090     r8 : 00000100
r7 : 00000cfb  r6 : 0144a6d0     r5 : 00000000  r4 : 00008000
r3 : 0000000c  r2 : 00000100     r1 : 00000cfb  r0 : 00000000
Flags: nZCv  IRQs off  FIQs off  Mode SVC_32
Resetting CPU ...

Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
Acked-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Tom Warren <twarren@nvidia.com>
drivers/mtd/nand/tegra_nand.c

index ca196257058cecb91472d8d8bae28d36356f870c..723d0ecad718929fae56e3896552e96115e4e493 100644 (file)
@@ -16,6 +16,7 @@
 #include <asm/errno.h>
 #include <asm/gpio.h>
 #include <fdtdec.h>
+#include <bouncebuf.h>
 #include "tegra_nand.h"
 
 DECLARE_GLOBAL_DATA_PTR;
@@ -93,35 +94,6 @@ static struct nand_drv nand_ctrl;
 static struct mtd_info *our_mtd;
 static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];
 
-#ifdef CONFIG_SYS_DCACHE_OFF
-static inline void dma_prepare(void *start, unsigned long length,
-                              int is_writing)
-{
-}
-#else
-/**
- * Prepare for a DMA transaction
- *
- * For a write we flush out our data. For a read we invalidate, since we
- * need to do this before we read from the buffer after the DMA has
- * completed, so may as well do it now.
- *
- * @param start                Start address for DMA buffer (should be cache-aligned)
- * @param length       Length of DMA buffer in bytes
- * @param is_writing   0 if reading, non-zero if writing
- */
-static void dma_prepare(void *start, unsigned long length, int is_writing)
-{
-       unsigned long addr = (unsigned long)start;
-
-       length = ALIGN(length, ARCH_DMA_MINALIGN);
-       if (is_writing)
-               flush_dcache_range(addr, addr + length);
-       else
-               invalidate_dcache_range(addr, addr + length);
-}
-#endif
-
 /**
  * Wait for command completion
  *
@@ -531,6 +503,8 @@ static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
        char *tag_ptr;
        struct nand_drv *info;
        struct fdt_nand *config;
+       unsigned int bbflags;
+       struct bounce_buffer bbstate, bbstate_oob;
 
        if ((uintptr_t)buf & 0x03) {
                printf("buf %p has to be 4-byte aligned\n", buf);
@@ -547,21 +521,21 @@ static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
 
        stop_command(info->reg);
 
+       if (is_writing)
+               bbflags = GEN_BB_READ;
+       else
+               bbflags = GEN_BB_WRITE;
+
+       bounce_buffer_start(&bbstate, (void *)buf, 1 << chip->page_shift,
+                           bbflags);
        writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a);
-       writel(virt_to_phys(buf), &info->reg->data_block_ptr);
+       writel(virt_to_phys(bbstate.bounce_buffer), &info->reg->data_block_ptr);
 
+       /* Set ECC selection, configure ECC settings */
        if (with_ecc) {
-               writel(virt_to_phys(tag_ptr), &info->reg->tag_ptr);
                if (is_writing)
                        memcpy(tag_ptr, chip->oob_poi + free->offset,
-                               chip->ecc.layout->oobavail +
-                               TAG_ECC_BYTES);
-       } else {
-               writel(virt_to_phys(chip->oob_poi), &info->reg->tag_ptr);
-       }
-
-       /* Set ECC selection, configure ECC settings */
-       if (with_ecc) {
+                              chip->ecc.layout->oobavail + TAG_ECC_BYTES);
                tag_size = chip->ecc.layout->oobavail + TAG_ECC_BYTES;
                reg_val |= (CFG_SKIP_SPARE_SEL_4
                        | CFG_SKIP_SPARE_ENABLE
@@ -574,7 +548,8 @@ static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
 
                if (!is_writing)
                        tag_size += SKIPPED_SPARE_BYTES;
-               dma_prepare(tag_ptr, tag_size, is_writing);
+               bounce_buffer_start(&bbstate_oob, (void *)tag_ptr, tag_size,
+                                   bbflags);
        } else {
                tag_size = mtd->oobsize;
                reg_val |= (CFG_SKIP_SPARE_DISABLE
@@ -582,14 +557,12 @@ static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
                        | CFG_ECC_EN_TAG_DISABLE
                        | CFG_HW_ECC_DISABLE
                        | (tag_size - 1));
-               dma_prepare(chip->oob_poi, tag_size, is_writing);
+               bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi,
+                                   tag_size, bbflags);
        }
        writel(reg_val, &info->reg->config);
-
-       dma_prepare(buf, 1 << chip->page_shift, is_writing);
-
+       writel(virt_to_phys(bbstate_oob.bounce_buffer), &info->reg->tag_ptr);
        writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
-
        writel(tag_size - 1, &info->reg->dma_cfg_b);
 
        nand_clear_interrupt_status(info->reg);
@@ -635,6 +608,9 @@ static int nand_rw_page(struct mtd_info *mtd, struct nand_chip *chip,
                return -EIO;
        }
 
+       bounce_buffer_stop(&bbstate_oob);
+       bounce_buffer_stop(&bbstate);
+
        if (with_ecc && !is_writing) {
                memcpy(chip->oob_poi, tag_ptr,
                        SKIPPED_SPARE_BYTES);
@@ -752,6 +728,8 @@ static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
        int tag_size;
        struct nand_oobfree *free = chip->ecc.layout->oobfree;
        struct nand_drv *info;
+       unsigned int bbflags;
+       struct bounce_buffer bbstate_oob;
 
        if (((int)chip->oob_poi) & 0x03)
                return -EINVAL;
@@ -761,8 +739,6 @@ static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
 
        stop_command(info->reg);
 
-       writel(virt_to_phys(chip->oob_poi), &info->reg->tag_ptr);
-
        /* Set ECC selection */
        tag_size = mtd->oobsize;
        if (with_ecc)
@@ -776,13 +752,20 @@ static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
                CFG_HW_ECC_DISABLE);
        writel(reg_val, &info->reg->config);
 
-       dma_prepare(chip->oob_poi, tag_size, is_writing);
-
-       writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
-
        if (is_writing && with_ecc)
                tag_size -= TAG_ECC_BYTES;
 
+       if (is_writing)
+               bbflags = GEN_BB_READ;
+       else
+               bbflags = GEN_BB_WRITE;
+
+       bounce_buffer_start(&bbstate_oob, (void *)chip->oob_poi, tag_size,
+                           bbflags);
+       writel(virt_to_phys(bbstate_oob.bounce_buffer), &info->reg->tag_ptr);
+
+       writel(BCH_CONFIG_BCH_ECC_DISABLE, &info->reg->bch_config);
+
        writel(tag_size - 1, &info->reg->dma_cfg_b);
 
        nand_clear_interrupt_status(info->reg);
@@ -819,6 +802,8 @@ static int nand_rw_oob(struct mtd_info *mtd, struct nand_chip *chip,
                return -EIO;
        }
 
+       bounce_buffer_stop(&bbstate_oob);
+
        if (with_ecc && !is_writing) {
                reg_val = (u32)check_ecc_error(info->reg, 0, 0,
                        (u8 *)(chip->oob_poi + free->offset),