From adf4800d8570a1212be329ab6fcead4c284ede89 Mon Sep 17 00:00:00 2001 From: Marcel Ziswiler Date: Thu, 6 Aug 2015 00:47:06 +0200 Subject: mtd/nand/tegra: alignment workaround 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 Acked-by: Stephen Warren Signed-off-by: Tom Warren --- drivers/mtd/nand/tegra_nand.c | 87 ++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 51 deletions(-) (limited to 'drivers/mtd/nand/tegra_nand.c') diff --git a/drivers/mtd/nand/tegra_nand.c b/drivers/mtd/nand/tegra_nand.c index ca19625705..723d0ecad7 100644 --- a/drivers/mtd/nand/tegra_nand.c +++ b/drivers/mtd/nand/tegra_nand.c @@ -16,6 +16,7 @@ #include #include #include +#include #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), -- cgit v1.2.1