#include <common.h>
#include <asm/io.h>
#include <asm/errno.h>
+#include <asm/arch/hardware.h>
#include <asm/arch/mem.h>
#include <linux/mtd/omap_gpmc.h>
#include <linux/mtd/nand_ecc.h>
static uint8_t cs_next;
static __maybe_unused struct nand_ecclayout omap_ecclayout;
+#if defined(CONFIG_NAND_OMAP_GPMC_WSCFG)
+static const int8_t wscfg[CONFIG_SYS_MAX_NAND_DEVICE] =
+ { CONFIG_NAND_OMAP_GPMC_WSCFG };
+#else
+/* wscfg is preset to zero since its a static variable */
+static const int8_t wscfg[CONFIG_SYS_MAX_NAND_DEVICE];
+#endif
+
/*
* Driver configurations
*/
struct omap_nand_info {
struct bch_control *control;
enum omap_ecc ecc_scheme;
- int cs;
+ uint8_t cs;
+ uint8_t ws; /* wait status pin (0,1) */
};
/* We are wasting a bit of memory but al least we are safe */
static struct omap_nand_info omap_nand_info[GPMC_MAX_CS];
+static struct gpmc __iomem *gpmc_cfg = (void __iomem *)GPMC_BASE;
+
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0, /* may be overwritten depending on ECC layout */
+ .len = 4,
+ .veroffs = 4, /* may be overwritten depending on ECC layout */
+ .maxblocks = 4,
+ .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
+ NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+ .offs = 0, /* may be overwritten depending on ECC layout */
+ .len = 4,
+ .veroffs = 4, /* may be overwritten depending on ECC layout */
+ .maxblocks = 4,
+ .pattern = mirror_pattern,
+};
+#endif
+
+#define PREFETCH_FIFOTHRESHOLD_MAX 0x40
+#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8)
+
+#define PREFETCH_ENABLEOPTIMIZEDACCESS (0x1 << 27)
+
+#define GPMC_PREFETCH_STATUS_FIFO_CNT(val) (((val) >> 24) & 0x7F)
+#define GPMC_PREFETCH_STATUS_COUNT(val) ((val) & 0x00003fff)
+
+#define CS_NUM_SHIFT 24
+#define ENABLE_PREFETCH (0x1 << 7)
+#define DMA_MPU_MODE 2
+
+#define OMAP_NAND_TIMEOUT_MS 5000
+
+#define PRINT_REG(x) debug("+++ %.15s (0x%08x)=0x%08x\n", #x, &gpmc_cfg->x, readl(&gpmc_cfg->x))
+
/*
* omap_nand_hwcontrol - Set the address pointers corretly for the
* following address/data/command operation
/* Check wait pin as dev ready indicator */
static int omap_dev_ready(struct mtd_info *mtd)
{
- return gpmc_cfg->status & (1 << 8);
+ struct nand_chip *this = mtd->priv;
+ struct omap_nand_info *info = this->priv;
+ return !!(readl(&gpmc_cfg->status) & (1 << (8 + info->ws)));
}
/*
return 0;
}
+#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH
+
+#define PREFETCH_CONFIG1_CS_SHIFT 24
+#define PREFETCH_FIFOTHRESHOLD_MAX 0x40
+#define PREFETCH_FIFOTHRESHOLD(val) ((val) << 8)
+#define PREFETCH_STATUS_COUNT(val) (val & 0x00003fff)
+#define PREFETCH_STATUS_FIFO_CNT(val) ((val >> 24) & 0x7F)
+#define ENABLE_PREFETCH (1 << 7)
+
+/**
+ * omap_prefetch_enable - configures and starts prefetch transfer
+ * @fifo_th: fifo threshold to be used for read/ write
+ * @count: number of bytes to be transferred
+ * @is_write: prefetch read(0) or write post(1) mode
+ * @cs: chip select to use
+ */
+static int omap_prefetch_enable(int fifo_th, unsigned int count, int is_write, int cs)
+{
+ uint32_t val;
+
+ if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX)
+ return -EINVAL;
+
+ if (readl(&gpmc_cfg->prefetch_control))
+ return -EBUSY;
+
+ /* Set the amount of bytes to be prefetched */
+ writel(count, &gpmc_cfg->prefetch_config2);
+
+ val = (cs << PREFETCH_CONFIG1_CS_SHIFT) | (is_write & 1) |
+ PREFETCH_FIFOTHRESHOLD(fifo_th) | ENABLE_PREFETCH;
+ writel(val, &gpmc_cfg->prefetch_config1);
+
+ /* Start the prefetch engine */
+ writel(1, &gpmc_cfg->prefetch_control);
+
+ return 0;
+}
+
+/**
+ * omap_prefetch_reset - disables and stops the prefetch engine
+ */
+static void omap_prefetch_reset(void)
+{
+ writel(0, &gpmc_cfg->prefetch_control);
+ writel(0, &gpmc_cfg->prefetch_config1);
+}
+
+static int __read_prefetch_aligned(struct nand_chip *chip, uint32_t *buf, int len)
+{
+ int ret;
+ uint32_t cnt;
+ struct omap_nand_info *info = chip->priv;
+
+ ret = omap_prefetch_enable(PREFETCH_FIFOTHRESHOLD_MAX, len, 0, info->cs);
+ if (ret < 0)
+ return ret;
+
+ do {
+ int i;
+
+ cnt = readl(&gpmc_cfg->prefetch_status);
+ cnt = PREFETCH_STATUS_FIFO_CNT(cnt);
+
+ for (i = 0; i < cnt / 4; i++) {
+ *buf++ = readl(CONFIG_SYS_NAND_BASE);
+ len -= 4;
+ }
+ } while (len);
+
+ omap_prefetch_reset();
+
+ return 0;
+}
+
+static inline void omap_nand_read(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+
+ if (chip->options & NAND_BUSWIDTH_16)
+ nand_read_buf16(mtd, buf, len);
+ else
+ nand_read_buf(mtd, buf, len);
+}
+
+static void omap_nand_read_prefetch(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ int ret;
+ uint32_t head, tail;
+ struct nand_chip *chip = mtd->priv;
+
+ /*
+ * If the destination buffer is unaligned, start with reading
+ * the overlap byte-wise.
+ */
+ head = ((uint32_t) buf) % 4;
+ if (head) {
+ omap_nand_read(mtd, buf, head);
+ buf += head;
+ len -= head;
+ }
+
+ /*
+ * Only transfer multiples of 4 bytes in a pre-fetched fashion.
+ * If there's a residue, care for it byte-wise afterwards.
+ */
+ tail = len % 4;
+
+ ret = __read_prefetch_aligned(chip, (uint32_t *)buf, len - tail);
+ if (ret < 0) {
+ /* fallback in case the prefetch engine is busy */
+ omap_nand_read(mtd, buf, len);
+ } else if (tail) {
+ buf += len - tail;
+ omap_nand_read(mtd, buf, tail);
+ }
+}
+#endif /* CONFIG_NAND_OMAP_GPMC_PREFETCH */
+
#ifdef CONFIG_NAND_OMAP_ELM
/*
* omap_reverse_list - re-orders list elements in reverse order [internal]
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
uint32_t *eccpos = chip->ecc.layout->eccpos;
- uint8_t *oob = chip->oob_poi;
+ uint8_t *oob = &chip->oob_poi[eccpos[0]];
uint32_t data_pos;
uint32_t oob_pos;
data_pos = 0;
/* oob area start */
- oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0];
- oob += chip->ecc.layout->eccpos[0];
+ oob_pos = (eccsize * eccsteps) + eccpos[0];
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize,
oob += eccbytes) {
/* read syndrome */
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+ if (oob_required) {
+ /* reread the OOB area to get the metadata */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ }
+
data_pos += eccsize;
oob_pos += eccbytes;
}
data[errloc[i]/8] ^= 1 << (errloc[i] & 7);
printf("corrected bitflip %u\n", errloc[i]);
#ifdef DEBUG
- puts("read_ecc: ");
+ printf("read_ecc: ");
/*
* BCH8 have 13 bytes of ECC; BCH4 needs adoption
* here!
*/
for (i = 0; i < 13; i++)
printf("%02x ", read_ecc[i]);
- puts("\n");
- puts("calc_ecc: ");
+ printf("\n");
+ printf("calc_ecc: ");
for (i = 0; i < 13; i++)
printf("%02x ", calc_ecc[i]);
- puts("\n");
+ printf("\n");
#endif
}
} else if (count < 0) {
- puts("ecc unrecoverable error\n");
+ printf("ecc unrecoverable error\n");
}
return count;
}
return -EINVAL;
}
} else {
- err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW,
+ if (eccstrength == 1) {
+ err = omap_select_ecc_scheme(nand,
+ OMAP_ECC_HAM1_CODE_SW,
mtd->writesize, mtd->oobsize);
+ } else if (eccstrength == 8) {
+ err = omap_select_ecc_scheme(nand,
+ OMAP_ECC_BCH8_CODE_HW_DETECTION_SW,
+ mtd->writesize, mtd->oobsize);
+ } else {
+ printf("nand: error: unsupported ECC scheme\n");
+ return -EINVAL;
+ }
}
/* Update NAND handling after ECC mode switch */
nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd;
omap_nand_info[cs].control = NULL;
omap_nand_info[cs].cs = cs;
+ omap_nand_info[cs].ws = wscfg[cs];
nand->priv = &omap_nand_info[cs];
nand->cmd_ctrl = omap_nand_hwcontrol;
nand->options |= NAND_NO_PADDING | NAND_CACHEPRG;
#endif
if (err)
return err;
+#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
+ if (nand->ecc.layout) {
+ bbt_main_descr.offs = nand->ecc.layout->oobfree[0].offset;
+ bbt_main_descr.veroffs = bbt_main_descr.offs +
+ sizeof(bbt_pattern);
-#ifdef CONFIG_SPL_BUILD
+ bbt_mirror_descr.offs = nand->ecc.layout->oobfree[0].offset;
+ bbt_mirror_descr.veroffs = bbt_mirror_descr.offs +
+ sizeof(mirror_pattern);
+ }
+
+ nand->bbt_options |= NAND_BBT_USE_FLASH;
+ nand->bbt_td = &bbt_main_descr;
+ nand->bbt_md = &bbt_mirror_descr;
+#endif
+
+#ifdef CONFIG_NAND_OMAP_GPMC_PREFETCH
+ nand->read_buf = omap_nand_read_prefetch;
+#else
if (nand->options & NAND_BUSWIDTH_16)
nand->read_buf = nand_read_buf16;
else