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 <marcel.ziswiler@toradex.com>
Acked-by: Stephen Warren <swarren@nvidia.com>
Signed-off-by: Tom Warren <twarren@nvidia.com>
This commit is contained in:
Marcel Ziswiler 2015-08-06 00:47:06 +02:00 committed by Tom Warren
parent 1bc66a57c0
commit adf4800d85

View 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);
writel((1 << chip->page_shift) - 1, &info->reg->dma_cfg_a);
writel(virt_to_phys(buf), &info->reg->data_block_ptr);
if (is_writing)
bbflags = GEN_BB_READ;
else
bbflags = GEN_BB_WRITE;
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);
}
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(bbstate.bounce_buffer), &info->reg->data_block_ptr);
/* Set ECC selection, configure ECC settings */
if (with_ecc) {
if (is_writing)
memcpy(tag_ptr, chip->oob_poi + free->offset,
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),