aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/cfi_flash.c101
-rw-r--r--drivers/mtd/mtdpart.c14
-rw-r--r--drivers/mtd/nand/Makefile2
-rw-r--r--drivers/mtd/nand/kirkwood_nand.c4
-rw-r--r--drivers/mtd/nand/mxc_nand.c262
-rw-r--r--drivers/mtd/nand/mxc_nand.h225
-rw-r--r--drivers/mtd/nand/mxc_nand_spl.c366
-rw-r--r--drivers/mtd/nand/nand_util.c68
-rw-r--r--drivers/mtd/nand/ndfc.c4
-rw-r--r--drivers/mtd/nand/omap_gpmc.c425
-rw-r--r--drivers/mtd/nand/s3c64xx.c295
-rw-r--r--drivers/mtd/onenand/onenand_base.c4
-rw-r--r--drivers/mtd/onenand/samsung.c60
-rw-r--r--drivers/mtd/spi/atmel.c8
-rw-r--r--drivers/mtd/spi/eon.c8
-rw-r--r--drivers/mtd/spi/macronix.c8
-rw-r--r--drivers/mtd/spi/ramtron.c4
-rw-r--r--drivers/mtd/spi/spansion.c8
-rw-r--r--drivers/mtd/spi/spi_flash.c81
-rw-r--r--drivers/mtd/spi/sst.c8
-rw-r--r--drivers/mtd/spi/stmicro.c8
-rw-r--r--drivers/mtd/spi/winbond.c13
-rw-r--r--drivers/mtd/ubi/build.c8
-rw-r--r--drivers/mtd/ubi/ubi.h4
-rw-r--r--drivers/mtd/ubi/wl.c1
25 files changed, 1319 insertions, 670 deletions
diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c
index 60dbb7864f..cf10b0d4ca 100644
--- a/drivers/mtd/cfi_flash.c
+++ b/drivers/mtd/cfi_flash.c
@@ -210,9 +210,11 @@ unsigned long flash_sector_size(flash_info_t *info, flash_sect_t sect)
static inline void *
flash_map (flash_info_t * info, flash_sect_t sect, uint offset)
{
- unsigned int byte_offset = offset * info->portwidth;
+ unsigned int byte_offset = offset * info->portwidth / info->chipwidth;
+ unsigned int addr = (info->start[sect] + byte_offset);
+ unsigned int mask = 0xffffffff << (info->portwidth - 1);
- return (void *)(info->start[sect] + byte_offset);
+ return (void *)(uintptr_t)(addr & mask);
}
static inline void flash_unmap(flash_info_t *info, flash_sect_t sect,
@@ -398,6 +400,8 @@ void flash_write_cmd (flash_info_t * info, flash_sect_t sect,
#endif
flash_write64(cword.ll, addr);
break;
+ default:
+ printf("fwc: Unknown port width %d\n", info->portwidth);
}
/* Ensure all the instructions are fully finished */
@@ -585,7 +589,6 @@ static int flash_status_check (flash_info_t * info, flash_sect_t sector,
prompt, info->start[sector],
flash_read_long (info, sector, 0));
flash_write_cmd (info, sector, 0, info->cmd_reset);
- udelay(1);
return ERR_TIMOUT;
}
udelay (1); /* also triggers watchdog */
@@ -753,12 +756,8 @@ static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c)
static flash_sect_t find_sector (flash_info_t * info, ulong addr)
{
static flash_sect_t saved_sector; /* previously found sector */
- static flash_info_t *saved_info; /* previously used flash bank */
flash_sect_t sector = saved_sector;
- if ((info != saved_info) || (sector >= info->sector_count))
- sector = 0;
-
while ((info->start[sector] < addr)
&& (sector < info->sector_count - 1))
sector++;
@@ -770,7 +769,6 @@ static flash_sect_t find_sector (flash_info_t * info, ulong addr)
sector--;
saved_sector = sector;
- saved_info = info;
return sector;
}
@@ -787,12 +785,15 @@ static int flash_write_cfiword (flash_info_t * info, ulong dest,
/* Check if Flash is (sufficiently) erased */
switch (info->portwidth) {
case FLASH_CFI_8BIT:
+ debug("%s: 8-bit 0x%02x\n", __func__, cword.c);
flag = ((flash_read8(dstaddr) & cword.c) == cword.c);
break;
case FLASH_CFI_16BIT:
+ debug("%s: 16-bit 0x%04x\n", __func__, cword.w);
flag = ((flash_read16(dstaddr) & cword.w) == cword.w);
break;
case FLASH_CFI_32BIT:
+ debug("%s: 32-bit 0x%08lx\n", __func__, cword.l);
flag = ((flash_read32(dstaddr) & cword.l) == cword.l);
break;
case FLASH_CFI_64BIT:
@@ -1053,6 +1054,8 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
flash_sect_t sect;
int st;
+ debug("%s: erasing sectors %d to %d\n", __func__, s_first, s_last);
+
if (info->flash_id != FLASH_MAN_CFI) {
puts ("Can't erase unknown flash type - aborted\n");
return 1;
@@ -1162,6 +1165,9 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
rcode = 1;
else if (flash_verbose)
putc ('.');
+ } else {
+ debug("\nSector %d is protected.\n",
+ info->protect[sect]);
}
}
@@ -1857,7 +1863,7 @@ static void flash_read_cfi (flash_info_t *info, void *buf,
unsigned int i;
for (i = 0; i < len; i++)
- p[i] = flash_read_uchar(info, start + i);
+ p[i] = flash_read_uchar(info, start + (i * 2));
}
static void __flash_cmd_reset(flash_info_t *info)
@@ -1878,21 +1884,40 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
{
int cfi_offset;
- /* Issue FLASH reset command */
- flash_cmd_reset(info);
-
for (cfi_offset=0;
cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint);
cfi_offset++) {
+ /* Issue FLASH reset command */
+ flash_cmd_reset(info);
flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset],
FLASH_CMD_CFI);
- if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q')
- && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R')
- && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) {
+ if (flash_isequal(info, 0, FLASH_OFFSET_CFI_RESP, 'Q') &&
+ flash_isequal(info, 0,
+ FLASH_OFFSET_CFI_RESP + 2, 'R') &&
+ flash_isequal(info, 0,
+ FLASH_OFFSET_CFI_RESP + 4, 'Y')) {
flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP,
sizeof(struct cfi_qry));
+#ifdef CONFIG_SYS_FLASH_INTERFACE_WIDTH
+ info->interface = CONFIG_SYS_FLASH_INTERFACE_WIDTH;
+#else
info->interface = le16_to_cpu(qry->interface_desc);
-
+ /* Some flash chips can support multiple bus widths.
+ * In this case, override the interface width and
+ * limit it to the port width.
+ */
+ if ((info->interface == FLASH_CFI_X8X16) &&
+ (info->portwidth == FLASH_CFI_8BIT)) {
+ debug("Overriding 16-bit interface"
+ " width to 8-bit port width.\n");
+ info->interface = FLASH_CFI_X8;
+ } else if ((info->interface == FLASH_CFI_X16X32) &&
+ (info->portwidth == FLASH_CFI_16BIT)) {
+ debug("Overriding 16-bit interface"
+ " width to 16-bit port width.\n");
+ info->interface = FLASH_CFI_X16;
+ }
+#endif
info->cfi_offset = flash_offset_cfi[cfi_offset];
debug ("device interface is %d\n",
info->interface);
@@ -1903,8 +1928,8 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
/* calculate command offsets as in the Linux driver */
- info->addr_unlock1 = 0x555;
- info->addr_unlock2 = 0x2aa;
+ info->addr_unlock1 = 0xaaa;
+ info->addr_unlock2 = 0x555;
/*
* modify the unlock address if we are
@@ -1938,8 +1963,12 @@ static int flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
for (info->chipwidth = FLASH_CFI_BY8;
info->chipwidth <= info->portwidth;
info->chipwidth <<= 1)
- if (__flash_detect_cfi(info, qry))
+ if (__flash_detect_cfi(info, qry)) {
+ debug("Found CFI flash, portwidth %d,"
+ " chipwidth %d\n",
+ info->portwidth, info->chipwidth);
return 1;
+ }
}
debug ("not found\n");
return 0;
@@ -1958,7 +1987,7 @@ static void flash_fixup_amd(flash_info_t *info, struct cfi_qry *qry)
/* CFI < 1.1, try to guess from device id */
if ((info->device_id & 0x80) != 0)
cfi_reverse_geometry(qry);
- } else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) {
+ } else if (flash_read_uchar(info, info->ext_addr + 0x1e) == 3) {
/* CFI >= 1.1, deduct from top/bottom flag */
/* note: ext_addr is valid since cfi_version > 0 */
cfi_reverse_geometry(qry);
@@ -2024,6 +2053,26 @@ static void flash_fixup_sst(flash_info_t *info, struct cfi_qry *qry)
}
}
+static void flash_fixup_num(flash_info_t *info, struct cfi_qry *qry)
+{
+ /*
+ * The M29EW devices seem to report the CFI information wrong
+ * when it's in 8 bit mode.
+ * There's an app note from Numonyx on this issue.
+ * So adjust the buffer size for M29EW while operating in 8-bit mode
+ */
+ if (((qry->max_buf_write_size) > 0x8) &&
+ (info->device_id == 0x7E) &&
+ (info->device_id2 == 0x2201 ||
+ info->device_id2 == 0x2301 ||
+ info->device_id2 == 0x2801 ||
+ info->device_id2 == 0x4801)) {
+ debug("Adjusted buffer size on Numonyx flash"
+ " M29EW family in 8 bit mode\n");
+ qry->max_buf_write_size = 0x8;
+ }
+}
+
/*
* The following code cannot be run from FLASH!
*
@@ -2054,14 +2103,15 @@ ulong flash_get_size (phys_addr_t base, int banknum)
if (flash_detect_cfi (info, &qry)) {
info->vendor = le16_to_cpu(qry.p_id);
- info->ext_addr = le16_to_cpu(qry.p_adr);
+ info->ext_addr = le16_to_cpu(qry.p_adr) * 2;
+ debug("extended address is 0x%x\n", info->ext_addr);
num_erase_regions = qry.num_erase_regions;
if (info->ext_addr) {
info->cfi_version = (ushort) flash_read_uchar (info,
- info->ext_addr + 3) << 8;
+ info->ext_addr + 6) << 8;
info->cfi_version |= (ushort) flash_read_uchar (info,
- info->ext_addr + 4);
+ info->ext_addr + 8);
}
#ifdef DEBUG
@@ -2105,6 +2155,9 @@ ulong flash_get_size (phys_addr_t base, int banknum)
case 0x00bf: /* SST */
flash_fixup_sst(info, &qry);
break;
+ case 0x0089: /* Numonyx */
+ flash_fixup_num(info, &qry);
+ break;
}
debug ("manufacturer is %d\n", info->vendor);
@@ -2112,6 +2165,8 @@ ulong flash_get_size (phys_addr_t base, int banknum)
debug ("device id is 0x%x\n", info->device_id);
debug ("device id2 is 0x%x\n", info->device_id2);
debug ("cfi version is 0x%04x\n", info->cfi_version);
+ debug("port width: %d, chipwidth: %d, interface: %d\n",
+ info->portwidth, info->chipwidth, info->interface);
size_ratio = info->portwidth / info->chipwidth;
/* if the chip is x8/x16 reduce the ratio by half */
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 96dcda2b2b..cbfc6796c7 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -347,16 +347,18 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
if (mtd_mod_by_eb(cur_offset, master) != 0) {
/* Round up to next erasesize */
slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
- printk(KERN_NOTICE "Moving partition %d: "
- "0x%012llx -> 0x%012llx\n", partno,
- (unsigned long long)cur_offset, (unsigned long long)slave->offset);
+ debug("Moving partition %d: 0x%012llx -> 0x%012llx\n",
+ partno, (unsigned long long)cur_offset,
+ (unsigned long long)slave->offset);
}
}
if (slave->mtd.size == MTDPART_SIZ_FULL)
slave->mtd.size = master->size - slave->offset;
- printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
- (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
+ debug("0x%012llx-0x%012llx : \"%s\"\n",
+ (unsigned long long)slave->offset,
+ (unsigned long long)(slave->offset + slave->mtd.size),
+ slave->mtd.name);
/* let's do some sanity checks */
if (slave->offset >= master->size) {
@@ -463,7 +465,7 @@ int add_mtd_partitions(struct mtd_info *master,
if (mtd_partitions.next == NULL)
INIT_LIST_HEAD(&mtd_partitions);
- printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
+ debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
for (i = 0; i < nbparts; i++) {
slave = add_one_partition(master, parts + i, i, cur_offset);
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index c77c0c4f0f..35769c5ea3 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -73,7 +73,6 @@ COBJS-$(CONFIG_NAND_MXS) += mxs_nand.o
COBJS-$(CONFIG_NAND_NDFC) += ndfc.o
COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o
COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
-COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
COBJS-$(CONFIG_NAND_SPEAR) += spr_nand.o
COBJS-$(CONFIG_TEGRA_NAND) += tegra_nand.o
COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
@@ -82,6 +81,7 @@ COBJS-$(CONFIG_NAND_PLAT) += nand_plat.o
else # minimal SPL drivers
COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o
+COBJS-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
endif # drivers
endif # nand
diff --git a/drivers/mtd/nand/kirkwood_nand.c b/drivers/mtd/nand/kirkwood_nand.c
index bdab5aa795..0a99a10de1 100644
--- a/drivers/mtd/nand/kirkwood_nand.c
+++ b/drivers/mtd/nand/kirkwood_nand.c
@@ -74,7 +74,11 @@ void kw_nand_select_chip(struct mtd_info *mtd, int chip)
int board_nand_init(struct nand_chip *nand)
{
nand->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING;
+#if defined(CONFIG_NAND_ECC_BCH)
+ nand->ecc.mode = NAND_ECC_SOFT_BCH;
+#else
nand->ecc.mode = NAND_ECC_SOFT;
+#endif
nand->cmd_ctrl = kw_nand_hwcontrol;
nand->chip_delay = 40;
nand->select_chip = kw_nand_select_chip;
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index d0ded483e2..eeba521942 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -22,20 +22,22 @@
#include <nand.h>
#include <linux/err.h>
#include <asm/io.h>
-#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX35)
+#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX35) || \
+ defined(CONFIG_MX51) || defined(CONFIG_MX53)
#include <asm/arch/imx-regs.h>
#endif
-#include <fsl_nfc.h>
+#include "mxc_nand.h"
#define DRIVER_NAME "mxc_nand"
-typedef enum {false, true} bool;
-
struct mxc_nand_host {
struct mtd_info mtd;
struct nand_chip *nand;
- struct fsl_nfc_regs __iomem *regs;
+ struct mxc_nand_regs __iomem *regs;
+#ifdef MXC_NFC_V3_2
+ struct mxc_nand_ip_regs __iomem *ip_regs;
+#endif
int spare_only;
int status_request;
int pagesize_2k;
@@ -77,7 +79,7 @@ static struct nand_ecclayout nand_hw_eccoob2k = {
.oobfree = { {2, 4}, {11, 11}, {27, 11}, {43, 11}, {59, 5} },
};
#endif
-#elif defined(MXC_NFC_V2_1)
+#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
#ifndef CONFIG_SYS_NAND_LARGEPAGE
static struct nand_ecclayout nand_hw_eccoob = {
.eccbytes = 9,
@@ -98,45 +100,14 @@ static struct nand_ecclayout nand_hw_eccoob2k = {
#endif
#endif
-#ifdef CONFIG_MX27
-static int is_16bit_nand(void)
-{
- struct system_control_regs *sc_regs =
- (struct system_control_regs *)IMX_SYSTEM_CTL_BASE;
-
- if (readl(&sc_regs->fmcr) & NF_16BIT_SEL)
- return 1;
- else
- return 0;
-}
-#elif defined(CONFIG_MX31)
static int is_16bit_nand(void)
{
- struct clock_control_regs *sc_regs =
- (struct clock_control_regs *)CCM_BASE;
-
- if (readl(&sc_regs->rcsr) & CCM_RCSR_NF16B)
- return 1;
- else
- return 0;
-}
-#elif defined(CONFIG_MX25) || defined(CONFIG_MX35)
-static int is_16bit_nand(void)
-{
- struct ccm_regs *ccm = (struct ccm_regs *)IMX_CCM_BASE;
-
- if (readl(&ccm->rcsr) & CCM_RCSR_NF_16BIT_SEL)
- return 1;
- else
- return 0;
-}
+#if defined(CONFIG_SYS_NAND_BUSWIDTH_16BIT)
+ return 1;
#else
-#warning "8/16 bit NAND autodetection not supported"
-static int is_16bit_nand(void)
-{
return 0;
-}
#endif
+}
static uint32_t *mxc_nand_memcpy32(uint32_t *dest, uint32_t *source, size_t size)
{
@@ -150,7 +121,7 @@ static uint32_t *mxc_nand_memcpy32(uint32_t *dest, uint32_t *source, size_t size
/*
* This function polls the NANDFC to wait for the basic operation to
- * complete by checking the INT bit of config2 register.
+ * complete by checking the INT bit.
*/
static void wait_op_done(struct mxc_nand_host *host, int max_retries,
uint16_t param)
@@ -158,10 +129,17 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries,
uint32_t tmp;
while (max_retries-- > 0) {
- if (readw(&host->regs->config2) & NFC_INT) {
- tmp = readw(&host->regs->config2);
- tmp &= ~NFC_INT;
- writew(tmp, &host->regs->config2);
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ tmp = readnfc(&host->regs->config2);
+ if (tmp & NFC_V1_V2_CONFIG2_INT) {
+ tmp &= ~NFC_V1_V2_CONFIG2_INT;
+ writenfc(tmp, &host->regs->config2);
+#elif defined(MXC_NFC_V3_2)
+ tmp = readnfc(&host->ip_regs->ipc);
+ if (tmp & NFC_V3_IPC_INT) {
+ tmp &= ~NFC_V3_IPC_INT;
+ writenfc(tmp, &host->ip_regs->ipc);
+#endif
break;
}
udelay(1);
@@ -180,8 +158,8 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd)
{
MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x)\n", cmd);
- writew(cmd, &host->regs->flash_cmd);
- writew(NFC_CMD, &host->regs->config2);
+ writenfc(cmd, &host->regs->flash_cmd);
+ writenfc(NFC_CMD, &host->regs->operation);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, cmd);
@@ -196,8 +174,8 @@ static void send_addr(struct mxc_nand_host *host, uint16_t addr)
{
MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x)\n", addr);
- writew(addr, &host->regs->flash_addr);
- writew(NFC_ADDR, &host->regs->config2);
+ writenfc(addr, &host->regs->flash_addr);
+ writenfc(NFC_ADDR, &host->regs->operation);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, addr);
@@ -213,7 +191,7 @@ static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id,
if (spare_only)
MTDDEBUG(MTD_DEBUG_LEVEL1, "send_prog_page (%d)\n", spare_only);
- if (is_mxc_nfc_21()) {
+ if (is_mxc_nfc_21() || is_mxc_nfc_32()) {
int i;
/*
* The controller copies the 64 bytes of spare data from
@@ -229,19 +207,26 @@ static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id,
}
}
- writew(buf_id, &host->regs->buf_addr);
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ writenfc(buf_id, &host->regs->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+ uint32_t tmp = readnfc(&host->regs->config1);
+ tmp &= ~NFC_V3_CONFIG1_RBA_MASK;
+ tmp |= NFC_V3_CONFIG1_RBA(buf_id);
+ writenfc(tmp, &host->regs->config1);
+#endif
/* Configure spare or page+spare access */
if (!host->pagesize_2k) {
- uint16_t config1 = readw(&host->regs->config1);
+ uint32_t config1 = readnfc(&host->regs->config1);
if (spare_only)
- config1 |= NFC_SP_EN;
+ config1 |= NFC_CONFIG1_SP_EN;
else
- config1 &= ~NFC_SP_EN;
- writew(config1, &host->regs->config1);
+ config1 &= ~NFC_CONFIG1_SP_EN;
+ writenfc(config1, &host->regs->config1);
}
- writew(NFC_INPUT, &host->regs->config2);
+ writenfc(NFC_INPUT, &host->regs->operation);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, spare_only);
@@ -256,24 +241,31 @@ static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id,
{
MTDDEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only);
- writew(buf_id, &host->regs->buf_addr);
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ writenfc(buf_id, &host->regs->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+ uint32_t tmp = readnfc(&host->regs->config1);
+ tmp &= ~NFC_V3_CONFIG1_RBA_MASK;
+ tmp |= NFC_V3_CONFIG1_RBA(buf_id);
+ writenfc(tmp, &host->regs->config1);
+#endif
/* Configure spare or page+spare access */
if (!host->pagesize_2k) {
- uint32_t config1 = readw(&host->regs->config1);
+ uint32_t config1 = readnfc(&host->regs->config1);
if (spare_only)
- config1 |= NFC_SP_EN;
+ config1 |= NFC_CONFIG1_SP_EN;
else
- config1 &= ~NFC_SP_EN;
- writew(config1, &host->regs->config1);
+ config1 &= ~NFC_CONFIG1_SP_EN;
+ writenfc(config1, &host->regs->config1);
}
- writew(NFC_OUTPUT, &host->regs->config2);
+ writenfc(NFC_OUTPUT, &host->regs->operation);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, spare_only);
- if (is_mxc_nfc_21()) {
+ if (is_mxc_nfc_21() || is_mxc_nfc_32()) {
int i;
/*
@@ -293,17 +285,23 @@ static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id,
/* Request the NANDFC to perform a read of the NAND device ID. */
static void send_read_id(struct mxc_nand_host *host)
{
- uint16_t tmp;
+ uint32_t tmp;
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
/* NANDFC buffer 0 is used for device ID output */
- writew(0x0, &host->regs->buf_addr);
+ writenfc(0x0, &host->regs->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+ tmp = readnfc(&host->regs->config1);
+ tmp &= ~NFC_V3_CONFIG1_RBA_MASK;
+ writenfc(tmp, &host->regs->config1);
+#endif
/* Read ID into main buffer */
- tmp = readw(&host->regs->config1);
- tmp &= ~NFC_SP_EN;
- writew(tmp, &host->regs->config1);
+ tmp = readnfc(&host->regs->config1);
+ tmp &= ~NFC_CONFIG1_SP_EN;
+ writenfc(tmp, &host->regs->config1);
- writew(NFC_ID, &host->regs->config2);
+ writenfc(NFC_ID, &host->regs->operation);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, 0);
@@ -315,32 +313,40 @@ static void send_read_id(struct mxc_nand_host *host)
*/
static uint16_t get_dev_status(struct mxc_nand_host *host)
{
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
void __iomem *main_buf = host->regs->main_area[1];
uint32_t store;
- uint16_t ret, tmp;
+#endif
+ uint32_t ret, tmp;
/* Issue status request to NAND device */
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
/* store the main area1 first word, later do recovery */
store = readl(main_buf);
/* NANDFC buffer 1 is used for device status */
- writew(1, &host->regs->buf_addr);
+ writenfc(1, &host->regs->buf_addr);
+#endif
/* Read status into main buffer */
- tmp = readw(&host->regs->config1);
- tmp &= ~NFC_SP_EN;
- writew(tmp, &host->regs->config1);
+ tmp = readnfc(&host->regs->config1);
+ tmp &= ~NFC_CONFIG1_SP_EN;
+ writenfc(tmp, &host->regs->config1);
- writew(NFC_STATUS, &host->regs->config2);
+ writenfc(NFC_STATUS, &host->regs->operation);
/* Wait for operation to complete */
wait_op_done(host, TROP_US_DELAY, 0);
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
/*
* Status is placed in first word of main buffer
* get status, then recovery area 1 data
*/
ret = readw(main_buf);
writel(store, main_buf);
+#elif defined(MXC_NFC_V3_2)
+ ret = readnfc(&host->regs->config1) >> 16;
+#endif
return ret;
}
@@ -359,13 +365,23 @@ static void _mxc_nand_enable_hwecc(struct mtd_info *mtd, int on)
{
struct nand_chip *nand_chip = mtd->priv;
struct mxc_nand_host *host = nand_chip->priv;
- uint16_t tmp = readw(&host->regs->config1);
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ uint16_t tmp = readnfc(&host->regs->config1);
if (on)
- tmp |= NFC_ECC_EN;
+ tmp |= NFC_V1_V2_CONFIG1_ECC_EN;
else
- tmp &= ~NFC_ECC_EN;
- writew(tmp, &host->regs->config1);
+ tmp &= ~NFC_V1_V2_CONFIG1_ECC_EN;
+ writenfc(tmp, &host->regs->config1);
+#elif defined(MXC_NFC_V3_2)
+ uint32_t tmp = readnfc(&host->ip_regs->config2);
+
+ if (on)
+ tmp |= NFC_V3_CONFIG2_ECC_EN;
+ else
+ tmp &= ~NFC_V3_CONFIG2_ECC_EN;
+ writenfc(tmp, &host->ip_regs->config2);
+#endif
}
#ifdef CONFIG_MXC_NAND_HWECC
@@ -377,7 +393,7 @@ static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
*/
}
-#ifdef MXC_NFC_V2_1
+#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd,
struct nand_chip *chip,
int page, int sndcmd)
@@ -391,7 +407,7 @@ static int mxc_nand_read_oob_syndrome(struct mtd_info *mtd,
MTDDEBUG(MTD_DEBUG_LEVEL0,
"%s: Reading OOB area of page %u to oob %p\n",
- __FUNCTION__, host->page_addr, buf);
+ __func__, page, buf);
chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page);
for (i = 0; i < chip->ecc.steps; i++) {
@@ -445,7 +461,7 @@ static int mxc_nand_read_page_raw_syndrome(struct mtd_info *mtd,
int n;
_mxc_nand_enable_hwecc(mtd, 0);
- chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, host->page_addr);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
for (n = 0, steps = chip->ecc.steps; steps > 0; n++, steps--) {
host->col_addr = n * eccsize;
@@ -489,7 +505,7 @@ static int mxc_nand_read_page_syndrome(struct mtd_info *mtd,
uint8_t *oob = chip->oob_poi;
MTDDEBUG(MTD_DEBUG_LEVEL1, "Reading page %u to buf %p oob %p\n",
- host->page_addr, buf, oob);
+ page, buf, oob);
/* first read the data area and the available portion of OOB */
for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) {
@@ -527,7 +543,7 @@ static int mxc_nand_read_page_syndrome(struct mtd_info *mtd,
/* Then switch ECC off and read the OOB area to get the ECC code */
_mxc_nand_enable_hwecc(mtd, 0);
- chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, host->page_addr);
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, mtd->writesize, page);
eccsteps = chip->ecc.steps;
oob = chip->oob_poi + chip->ecc.prepad;
for (n = 0; eccsteps; n++, eccsteps--, p += eccsize) {
@@ -698,7 +714,7 @@ static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,
* additional correction. 2-Bit errors cannot be corrected by
* HW ECC, so we need to return failure
*/
- uint16_t ecc_status = readw(&host->regs->ecc_status_result);
+ uint16_t ecc_status = readnfc(&host->regs->ecc_status_result);
if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
MTDDEBUG(MTD_DEBUG_LEVEL0,
@@ -1167,8 +1183,8 @@ static struct nand_bbt_descr bbt_mirror_descr = {
int board_nand_init(struct nand_chip *this)
{
struct mtd_info *mtd;
-#ifdef MXC_NFC_V2_1
- uint16_t tmp;
+#if defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
+ uint32_t tmp;
#endif
#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT
@@ -1195,14 +1211,18 @@ int board_nand_init(struct nand_chip *this)
this->read_buf = mxc_nand_read_buf;
this->verify_buf = mxc_nand_verify_buf;
- host->regs = (struct fsl_nfc_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE;
+ host->regs = (struct mxc_nand_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE;
+#ifdef MXC_NFC_V3_2
+ host->ip_regs =
+ (struct mxc_nand_ip_regs __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE;
+#endif
host->clk_act = 1;
#ifdef CONFIG_MXC_NAND_HWECC
this->ecc.calculate = mxc_nand_calculate_ecc;
this->ecc.hwctl = mxc_nand_enable_hwecc;
this->ecc.correct = mxc_nand_correct_data;
- if (is_mxc_nfc_21()) {
+ if (is_mxc_nfc_21() || is_mxc_nfc_32()) {
this->ecc.mode = NAND_ECC_HW_SYNDROME;
this->ecc.read_page = mxc_nand_read_page_syndrome;
this->ecc.read_page_raw = mxc_nand_read_page_raw_syndrome;
@@ -1240,25 +1260,26 @@ int board_nand_init(struct nand_chip *this)
this->ecc.layout = &nand_hw_eccoob;
#endif
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
#ifdef MXC_NFC_V2_1
- tmp = readw(&host->regs->config1);
- tmp |= NFC_ONE_CYCLE;
- tmp |= NFC_4_8N_ECC;
- writew(tmp, &host->regs->config1);
+ tmp = readnfc(&host->regs->config1);
+ tmp |= NFC_V2_CONFIG1_ONE_CYCLE;
+ tmp |= NFC_V2_CONFIG1_ECC_MODE_4;
+ writenfc(tmp, &host->regs->config1);
if (host->pagesize_2k)
- writew(64/2, &host->regs->spare_area_size);
+ writenfc(64/2, &host->regs->spare_area_size);
else
- writew(16/2, &host->regs->spare_area_size);
+ writenfc(16/2, &host->regs->spare_area_size);
#endif
/*
* preset operation
* Unlock the internal RAM Buffer
*/
- writew(0x2, &host->regs->config);
+ writenfc(0x2, &host->regs->config);
/* Blocks to be unlocked */
- writew(0x0, &host->regs->unlockstart_blkaddr);
+ writenfc(0x0, &host->regs->unlockstart_blkaddr);
/* Originally (Freescale LTIB 2.6.21) 0x4000 was written to the
* unlockend_blkaddr, but the magic 0x4000 does not always work
* when writing more than some 32 megabytes (on 2k page nands)
@@ -1270,10 +1291,53 @@ int board_nand_init(struct nand_chip *this)
* This might be NAND chip specific and the i.MX31 datasheet is
* extremely vague about the semantics of this register.
*/
- writew(0xFFFF, &host->regs->unlockend_blkaddr);
+ writenfc(0xFFFF, &host->regs->unlockend_blkaddr);
/* Unlock Block Command for given address range */
- writew(0x4, &host->regs->wrprot);
+ writenfc(0x4, &host->regs->wrprot);
+#elif defined(MXC_NFC_V3_2)
+ writenfc(NFC_V3_CONFIG1_RBA(0), &host->regs->config1);
+ writenfc(NFC_V3_IPC_CREQ, &host->ip_regs->ipc);
+
+ /* Unlock the internal RAM Buffer */
+ writenfc(NFC_V3_WRPROT_BLS_UNLOCK | NFC_V3_WRPROT_UNLOCK,
+ &host->ip_regs->wrprot);
+
+ /* Blocks to be unlocked */
+ for (tmp = 0; tmp < CONFIG_SYS_NAND_MAX_CHIPS; tmp++)
+ writenfc(0x0 | 0xFFFF << 16,
+ &host->ip_regs->wrprot_unlock_blkaddr[tmp]);
+
+ writenfc(0, &host->ip_regs->ipc);
+
+ tmp = readnfc(&host->ip_regs->config2);
+ tmp &= ~(NFC_V3_CONFIG2_SPAS_MASK | NFC_V3_CONFIG2_EDC_MASK |
+ NFC_V3_CONFIG2_ECC_MODE_8 | NFC_V3_CONFIG2_PS_MASK);
+ tmp |= NFC_V3_CONFIG2_ONE_CYCLE;
+
+ if (host->pagesize_2k) {
+ tmp |= NFC_V3_CONFIG2_SPAS(64/2);
+ tmp |= NFC_V3_CONFIG2_PS_2048;
+ } else {
+ tmp |= NFC_V3_CONFIG2_SPAS(16/2);
+ tmp |= NFC_V3_CONFIG2_PS_512;
+ }
+
+ writenfc(tmp, &host->ip_regs->config2);
+
+ tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) |
+ NFC_V3_CONFIG3_NO_SDMA |
+ NFC_V3_CONFIG3_RBB_MODE |
+ NFC_V3_CONFIG3_SBB(6) | /* Reset default */
+ NFC_V3_CONFIG3_ADD_OP(0);
+
+ if (!(this->options & NAND_BUSWIDTH_16))
+ tmp |= NFC_V3_CONFIG3_FW8;
+
+ writenfc(tmp, &host->ip_regs->config3);
+
+ writenfc(0, &host->ip_regs->delay_line);
+#endif
return 0;
}
diff --git a/drivers/mtd/nand/mxc_nand.h b/drivers/mtd/nand/mxc_nand.h
new file mode 100644
index 0000000000..308ff8d8a8
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nand.h
@@ -0,0 +1,225 @@
+/*
+ * (c) 2009 Magnus Lilja <lilja.magnus@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __MXC_NAND_H
+#define __MXC_NAND_H
+
+/*
+ * Register map and bit definitions for the Freescale NAND Flash Controller
+ * present in various i.MX devices.
+ *
+ * MX31 and MX27 have version 1, which has:
+ * 4 512-byte main buffers and
+ * 4 16-byte spare buffers
+ * to support up to 2K byte pagesize nand.
+ * Reading or writing a 2K page requires 4 FDI/FDO cycles.
+ *
+ * MX25 and MX35 have version 2.1, and MX51 and MX53 have version 3.2, which
+ * have:
+ * 8 512-byte main buffers and
+ * 8 64-byte spare buffers
+ * to support up to 4K byte pagesize nand.
+ * Reading or writing a 2K or 4K page requires only 1 FDI/FDO cycle.
+ * Also some of registers are moved and/or changed meaning as seen below.
+ */
+#if defined(CONFIG_MX27) || defined(CONFIG_MX31)
+#define MXC_NFC_V1
+#define is_mxc_nfc_1() 1
+#define is_mxc_nfc_21() 0
+#define is_mxc_nfc_32() 0
+#elif defined(CONFIG_MX25) || defined(CONFIG_MX35)
+#define MXC_NFC_V2_1
+#define is_mxc_nfc_1() 0
+#define is_mxc_nfc_21() 1
+#define is_mxc_nfc_32() 0
+#elif defined(CONFIG_MX51) || defined(CONFIG_MX53)
+#define MXC_NFC_V3
+#define MXC_NFC_V3_2
+#define is_mxc_nfc_1() 0
+#define is_mxc_nfc_21() 0
+#define is_mxc_nfc_32() 1
+#else
+#error "MXC NFC implementation not supported"
+#endif
+#define is_mxc_nfc_3() is_mxc_nfc_32()
+
+#if defined(MXC_NFC_V1)
+#define NAND_MXC_NR_BUFS 4
+#define NAND_MXC_SPARE_BUF_SIZE 16
+#define NAND_MXC_REG_OFFSET 0xe00
+#define NAND_MXC_2K_MULTI_CYCLE
+#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
+#define NAND_MXC_NR_BUFS 8
+#define NAND_MXC_SPARE_BUF_SIZE 64
+#define NAND_MXC_REG_OFFSET 0x1e00
+#endif
+
+struct mxc_nand_regs {
+ u8 main_area[NAND_MXC_NR_BUFS][0x200];
+ u8 spare_area[NAND_MXC_NR_BUFS][NAND_MXC_SPARE_BUF_SIZE];
+ /*
+ * reserved size is offset of nfc registers
+ * minus total main and spare sizes
+ */
+ u8 reserved1[NAND_MXC_REG_OFFSET
+ - NAND_MXC_NR_BUFS * (512 + NAND_MXC_SPARE_BUF_SIZE)];
+#if defined(MXC_NFC_V1)
+ u16 buf_size;
+ u16 reserved2;
+ u16 buf_addr;
+ u16 flash_addr;
+ u16 flash_cmd;
+ u16 config;
+ u16 ecc_status_result;
+ u16 rsltmain_area;
+ u16 rsltspare_area;
+ u16 wrprot;
+ u16 unlockstart_blkaddr;
+ u16 unlockend_blkaddr;
+ u16 nf_wrprst;
+ u16 config1;
+ u16 config2;
+#elif defined(MXC_NFC_V2_1)
+ u16 reserved2[2];
+ u16 buf_addr;
+ u16 flash_addr;
+ u16 flash_cmd;
+ u16 config;
+ u32 ecc_status_result;
+ u16 spare_area_size;
+ u16 wrprot;
+ u16 reserved3[2];
+ u16 nf_wrprst;
+ u16 config1;
+ u16 config2;
+ u16 reserved4;
+ u16 unlockstart_blkaddr;
+ u16 unlockend_blkaddr;
+ u16 unlockstart_blkaddr1;
+ u16 unlockend_blkaddr1;
+ u16 unlockstart_blkaddr2;
+ u16 unlockend_blkaddr2;
+ u16 unlockstart_blkaddr3;
+ u16 unlockend_blkaddr3;
+#elif defined(MXC_NFC_V3_2)
+ u32 flash_cmd;
+ u32 flash_addr[12];
+ u32 config1;
+ u32 ecc_status_result;
+ u32 status_sum;
+ u32 launch;
+#endif
+};
+
+#ifdef MXC_NFC_V3_2
+struct mxc_nand_ip_regs {
+ u32 wrprot;
+ u32 wrprot_unlock_blkaddr[8];
+ u32 config2;
+ u32 config3;
+ u32 ipc;
+ u32 err_addr;
+ u32 delay_line;
+};
+#endif
+
+/* Set FCMD to 1, rest to 0 for Command operation */
+#define NFC_CMD 0x1
+
+/* Set FADD to 1, rest to 0 for Address operation */
+#define NFC_ADDR 0x2
+
+/* Set FDI to 1, rest to 0 for Input operation */
+#define NFC_INPUT 0x4
+
+/* Set FDO to 001, rest to 0 for Data Output operation */
+#define NFC_OUTPUT 0x8
+
+/* Set FDO to 010, rest to 0 for Read ID operation */
+#define NFC_ID 0x10
+
+/* Set FDO to 100, rest to 0 for Read Status operation */
+#define NFC_STATUS 0x20
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+#define NFC_CONFIG1_SP_EN (1 << 2)
+#define NFC_CONFIG1_RST (1 << 6)
+#define NFC_CONFIG1_CE (1 << 7)
+#elif defined(MXC_NFC_V3_2)
+#define NFC_CONFIG1_SP_EN (1 << 0)
+#define NFC_CONFIG1_CE (1 << 1)
+#define NFC_CONFIG1_RST (1 << 2)
+#endif
+#define NFC_V1_V2_CONFIG1_ECC_EN (1 << 3)
+#define NFC_V1_V2_CONFIG1_INT_MSK (1 << 4)
+#define NFC_V1_V2_CONFIG1_BIG (1 << 5)
+#define NFC_V2_CONFIG1_ECC_MODE_4 (1 << 0)
+#define NFC_V2_CONFIG1_ONE_CYCLE (1 << 8)
+#define NFC_V2_CONFIG1_FP_INT (1 << 11)
+#define NFC_V3_CONFIG1_RBA_MASK (0x7 << 4)
+#define NFC_V3_CONFIG1_RBA(x) (((x) & 0x7) << 4)
+
+#define NFC_V1_V2_CONFIG2_INT (1 << 15)
+#define NFC_V3_CONFIG2_PS_MASK (0x3 << 0)
+#define NFC_V3_CONFIG2_PS_512 (0 << 0)
+#define NFC_V3_CONFIG2_PS_2048 (1 << 0)
+#define NFC_V3_CONFIG2_PS_4096 (2 << 0)
+#define NFC_V3_CONFIG2_ONE_CYCLE (1 << 2)
+#define NFC_V3_CONFIG2_ECC_EN (1 << 3)
+#define NFC_V3_CONFIG2_2CMD_PHASES (1 << 4)
+#define NFC_V3_CONFIG2_NUM_ADDR_PH0 (1 << 5)
+#define NFC_V3_CONFIG2_ECC_MODE_8 (1 << 6)
+#define NFC_V3_CONFIG2_PPB_MASK (0x3 << 7)
+#define NFC_V3_CONFIG2_PPB(x) (((x) & 0x3) << 7)
+#define NFC_V3_CONFIG2_EDC_MASK (0x7 << 9)
+#define NFC_V3_CONFIG2_EDC(x) (((x) & 0x7) << 9)
+#define NFC_V3_CONFIG2_NUM_ADDR_PH1(x) (((x) & 0x3) << 12)
+#define NFC_V3_CONFIG2_INT_MSK (1 << 15)
+#define NFC_V3_CONFIG2_SPAS_MASK (0xff << 16)
+#define NFC_V3_CONFIG2_SPAS(x) (((x) & 0xff) << 16)
+#define NFC_V3_CONFIG2_ST_CMD_MASK (0xff << 24)
+#define NFC_V3_CONFIG2_ST_CMD(x) (((x) & 0xff) << 24)
+
+#define NFC_V3_CONFIG3_ADD_OP(x) (((x) & 0x3) << 0)
+#define NFC_V3_CONFIG3_FW8 (1 << 3)
+#define NFC_V3_CONFIG3_SBB(x) (((x) & 0x7) << 8)
+#define NFC_V3_CONFIG3_NUM_OF_DEVS(x) (((x) & 0x7) << 12)
+#define NFC_V3_CONFIG3_RBB_MODE (1 << 15)
+#define NFC_V3_CONFIG3_NO_SDMA (1 << 20)
+
+#define NFC_V3_WRPROT_UNLOCK (1 << 2)
+#define NFC_V3_WRPROT_BLS_UNLOCK (2 << 6)
+
+#define NFC_V3_IPC_CREQ (1 << 0)
+#define NFC_V3_IPC_INT (1 << 31)
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+#define operation config2
+#define readnfc readw
+#define writenfc writew
+#elif defined(MXC_NFC_V3_2)
+#define operation launch
+#define readnfc readl
+#define writenfc writel
+#endif
+
+#endif /* __MXC_NAND_H */
diff --git a/drivers/mtd/nand/mxc_nand_spl.c b/drivers/mtd/nand/mxc_nand_spl.c
new file mode 100644
index 0000000000..09f23c30c4
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nand_spl.c
@@ -0,0 +1,366 @@
+/*
+ * (C) Copyright 2009
+ * Magnus Lilja <lilja.magnus@gmail.com>
+ *
+ * (C) Copyright 2008
+ * Maxim Artamonov, <scn1874 at yandex.ru>
+ *
+ * (C) Copyright 2006-2008
+ * Stefan Roese, DENX Software Engineering, sr at denx.de.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/io.h>
+#include "mxc_nand.h"
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR;
+#elif defined(MXC_NFC_V3_2)
+static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR_AXI;
+static struct mxc_nand_ip_regs *const nfc_ip = (void *)NFC_BASE_ADDR;
+#endif
+
+static void nfc_wait_ready(void)
+{
+ uint32_t tmp;
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ while (!(readnfc(&nfc->config2) & NFC_V1_V2_CONFIG2_INT))
+ ;
+
+ /* Reset interrupt flag */
+ tmp = readnfc(&nfc->config2);
+ tmp &= ~NFC_V1_V2_CONFIG2_INT;
+ writenfc(tmp, &nfc->config2);
+#elif defined(MXC_NFC_V3_2)
+ while (!(readnfc(&nfc_ip->ipc) & NFC_V3_IPC_INT))
+ ;
+
+ /* Reset interrupt flag */
+ tmp = readnfc(&nfc_ip->ipc);
+ tmp &= ~NFC_V3_IPC_INT;
+ writenfc(tmp, &nfc_ip->ipc);
+#endif
+}
+
+static void nfc_nand_init(void)
+{
+#if defined(MXC_NFC_V3_2)
+ int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+ int tmp;
+
+ tmp = (readnfc(&nfc_ip->config2) & ~(NFC_V3_CONFIG2_SPAS_MASK |
+ NFC_V3_CONFIG2_EDC_MASK | NFC_V3_CONFIG2_PS_MASK)) |
+ NFC_V3_CONFIG2_SPAS(CONFIG_SYS_NAND_OOBSIZE / 2) |
+ NFC_V3_CONFIG2_INT_MSK | NFC_V3_CONFIG2_ECC_EN |
+ NFC_V3_CONFIG2_ONE_CYCLE;
+ if (CONFIG_SYS_NAND_PAGE_SIZE == 4096)
+ tmp |= NFC_V3_CONFIG2_PS_4096;
+ else if (CONFIG_SYS_NAND_PAGE_SIZE == 2048)
+ tmp |= NFC_V3_CONFIG2_PS_2048;
+ else if (CONFIG_SYS_NAND_PAGE_SIZE == 512)
+ tmp |= NFC_V3_CONFIG2_PS_512;
+ /*
+ * if spare size is larger that 16 bytes per 512 byte hunk
+ * then use 8 symbol correction instead of 4
+ */
+ if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16)
+ tmp |= NFC_V3_CONFIG2_ECC_MODE_8;
+ else
+ tmp &= ~NFC_V3_CONFIG2_ECC_MODE_8;
+ writenfc(tmp, &nfc_ip->config2);
+
+ tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) |
+ NFC_V3_CONFIG3_NO_SDMA |
+ NFC_V3_CONFIG3_RBB_MODE |
+ NFC_V3_CONFIG3_SBB(6) | /* Reset default */
+ NFC_V3_CONFIG3_ADD_OP(0);
+#ifndef CONFIG_SYS_NAND_BUSWIDTH_16
+ tmp |= NFC_V3_CONFIG3_FW8;
+#endif
+ writenfc(tmp, &nfc_ip->config3);
+
+ writenfc(0, &nfc_ip->delay_line);
+#elif defined(MXC_NFC_V2_1)
+ int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+ int config1;
+
+ writenfc(CONFIG_SYS_NAND_OOBSIZE / 2, &nfc->spare_area_size);
+
+ /* unlocking RAM Buff */
+ writenfc(0x2, &nfc->config);
+
+ /* hardware ECC checking and correct */
+ config1 = readnfc(&nfc->config1) | NFC_V1_V2_CONFIG1_ECC_EN |
+ NFC_V1_V2_CONFIG1_INT_MSK | NFC_V2_CONFIG1_ONE_CYCLE |
+ NFC_V2_CONFIG1_FP_INT;
+ /*
+ * if spare size is larger that 16 bytes per 512 byte hunk
+ * then use 8 symbol correction instead of 4
+ */
+ if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16)
+ config1 &= ~NFC_V2_CONFIG1_ECC_MODE_4;
+ else
+ config1 |= NFC_V2_CONFIG1_ECC_MODE_4;
+ writenfc(config1, &nfc->config1);
+#elif defined(MXC_NFC_V1)
+ /* unlocking RAM Buff */
+ writenfc(0x2, &nfc->config);
+
+ /* hardware ECC checking and correct */
+ writenfc(NFC_V1_V2_CONFIG1_ECC_EN | NFC_V1_V2_CONFIG1_INT_MSK,
+ &nfc->config1);
+#endif
+}
+
+static void nfc_nand_command(unsigned short command)
+{
+ writenfc(command, &nfc->flash_cmd);
+ writenfc(NFC_CMD, &nfc->operation);
+ nfc_wait_ready();
+}
+
+static void nfc_nand_address(unsigned short address)
+{
+ writenfc(address, &nfc->flash_addr);
+ writenfc(NFC_ADDR, &nfc->operation);
+ nfc_wait_ready();
+}
+
+static void nfc_nand_page_address(unsigned int page_address)
+{
+ unsigned int page_count;
+
+ nfc_nand_address(0x00);
+
+ /* code only for large page flash */
+ if (CONFIG_SYS_NAND_PAGE_SIZE > 512)
+ nfc_nand_address(0x00);
+
+ page_count = CONFIG_SYS_NAND_SIZE / CONFIG_SYS_NAND_PAGE_SIZE;
+
+ if (page_address <= page_count) {
+ page_count--; /* transform 0x01000000 to 0x00ffffff */
+ do {
+ nfc_nand_address(page_address & 0xff);
+ page_address = page_address >> 8;
+ page_count = page_count >> 8;
+ } while (page_count);
+ }
+
+ nfc_nand_address(0x00);
+}
+
+static void nfc_nand_data_output(void)
+{
+#ifdef NAND_MXC_2K_MULTI_CYCLE
+ int i;
+#endif
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ writenfc(0, &nfc->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+ int config1 = readnfc(&nfc->config1);
+ config1 &= ~NFC_V3_CONFIG1_RBA_MASK;
+ writenfc(config1, &nfc->config1);
+#endif
+ writenfc(NFC_OUTPUT, &nfc->operation);
+ nfc_wait_ready();
+#ifdef NAND_MXC_2K_MULTI_CYCLE
+ /*
+ * This NAND controller requires multiple input commands
+ * for pages larger than 512 bytes.
+ */
+ for (i = 1; i < CONFIG_SYS_NAND_PAGE_SIZE / 512; i++) {
+ writenfc(i, &nfc->buf_addr);
+ writenfc(NFC_OUTPUT, &nfc->operation);
+ nfc_wait_ready();
+ }
+#endif
+}
+
+static int nfc_nand_check_ecc(void)
+{
+#if defined(MXC_NFC_V1)
+ u16 ecc_status = readw(&nfc->ecc_status_result);
+ return (ecc_status & 0x3) == 2 || (ecc_status >> 2) == 2;
+#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
+ u32 ecc_status = readl(&nfc->ecc_status_result);
+ int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+ int err_limit = CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16 ? 8 : 4;
+ int subpages = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+
+ do {
+ if ((ecc_status & 0xf) > err_limit)
+ return 1;
+ ecc_status >>= 4;
+ } while (--subpages);
+
+ return 0;
+#endif
+}
+
+static void nfc_nand_read_page(unsigned int page_address)
+{
+ /* read in first 0 buffer */
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+ writenfc(0, &nfc->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+ int config1 = readnfc(&nfc->config1);
+ config1 &= ~NFC_V3_CONFIG1_RBA_MASK;
+ writenfc(config1, &nfc->config1);
+#endif
+ nfc_nand_command(NAND_CMD_READ0);
+ nfc_nand_page_address(page_address);
+
+ if (CONFIG_SYS_NAND_PAGE_SIZE > 512)
+ nfc_nand_command(NAND_CMD_READSTART);
+
+ nfc_nand_data_output(); /* fill the main buffer 0 */
+}
+
+static int nfc_read_page(unsigned int page_address, unsigned char *buf)
+{
+ int i;
+ u32 *src;
+ u32 *dst;
+
+ nfc_nand_read_page(page_address);
+
+ if (nfc_nand_check_ecc())
+ return -1;
+
+ src = (u32 *)&nfc->main_area[0][0];
+ dst = (u32 *)buf;
+
+ /* main copy loop from NAND-buffer to SDRAM memory */
+ for (i = 0; i < CONFIG_SYS_NAND_PAGE_SIZE / 4; i++) {
+ writel(readl(src), dst);
+ src++;
+ dst++;
+ }
+
+ return 0;
+}
+
+static int is_badblock(int pagenumber)
+{
+ int page = pagenumber;
+ u32 badblock;
+ u32 *src;
+
+ /* Check the first two pages for bad block markers */
+ for (page = pagenumber; page < pagenumber + 2; page++) {
+ nfc_nand_read_page(page);
+
+ src = (u32 *)&nfc->spare_area[0][0];
+
+ /*
+ * IMPORTANT NOTE: The nand flash controller uses a non-
+ * standard layout for large page devices. This can
+ * affect the position of the bad block marker.
+ */
+ /* Get the bad block marker */
+ badblock = readl(&src[CONFIG_SYS_NAND_BAD_BLOCK_POS / 4]);
+ badblock >>= 8 * (CONFIG_SYS_NAND_BAD_BLOCK_POS % 4);
+ badblock &= 0xff;
+
+ /* bad block marker verify */
+ if (badblock != 0xff)
+ return 1; /* potential bad block */
+ }
+
+ return 0;
+}
+
+static int nand_load(unsigned int from, unsigned int size, unsigned char *buf)
+{
+ int i;
+ unsigned int page;
+ unsigned int maxpages = CONFIG_SYS_NAND_SIZE /
+ CONFIG_SYS_NAND_PAGE_SIZE;
+
+ nfc_nand_init();
+
+ /* Convert to page number */
+ page = from / CONFIG_SYS_NAND_PAGE_SIZE;
+ i = 0;
+
+ while (i < size / CONFIG_SYS_NAND_PAGE_SIZE) {
+ if (nfc_read_page(page, buf) < 0)
+ return -1;
+
+ page++;
+ i++;
+ buf = buf + CONFIG_SYS_NAND_PAGE_SIZE;
+
+ /*
+ * Check if we have crossed a block boundary, and if so
+ * check for bad block.
+ */
+ if (!(page % CONFIG_SYS_NAND_PAGE_COUNT)) {
+ /*
+ * Yes, new block. See if this block is good. If not,
+ * loop until we find a good block.
+ */
+ while (is_badblock(page)) {
+ page = page + CONFIG_SYS_NAND_PAGE_COUNT;
+ /* Check i we've reached the end of flash. */
+ if (page >= maxpages)
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * The main entry for NAND booting. It's necessary that SDRAM is already
+ * configured and available since this code loads the main U-Boot image
+ * from NAND into SDRAM and starts it from there.
+ */
+void nand_boot(void)
+{
+ __attribute__((noreturn)) void (*uboot)(void);
+
+ /*
+ * CONFIG_SYS_NAND_U_BOOT_OFFS and CONFIG_SYS_NAND_U_BOOT_SIZE must
+ * be aligned to full pages
+ */
+ if (!nand_load(CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE,
+ (uchar *)CONFIG_SYS_NAND_U_BOOT_DST)) {
+ /* Copy from NAND successful, start U-boot */
+ uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
+ uboot();
+ } else {
+ /* Unrecoverable error when copying from NAND */
+ hang();
+ }
+}
+
+/*
+ * Called in case of an exception.
+ */
+void hang(void)
+{
+ /* Loop forever */
+ while (1) ;
+}
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index ff2d348307..4727f9c989 100644
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -416,11 +416,13 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length,
* @param nand NAND device
* @param offset offset in flash
* @param length image length
+ * @param used length of flash needed for the requested length
* @return 0 if the image fits and there are no bad blocks
* 1 if the image fits, but there are bad blocks
* -1 if the image does not fit
*/
-static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
+static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length,
+ size_t *used)
{
size_t len_excl_bad = 0;
int ret = 0;
@@ -442,8 +444,13 @@ static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
ret = 1;
offset += block_len;
+ *used += block_len;
}
+ /* If the length is not a multiple of block_len, adjust. */
+ if (len_excl_bad > length)
+ *used -= (len_excl_bad - length);
+
return ret;
}
@@ -476,23 +483,36 @@ static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
* Write image to NAND flash.
* Blocks that are marked bad are skipped and the is written to the next
* block instead as long as the image is short enough to fit even after
- * skipping the bad blocks.
+ * skipping the bad blocks. Due to bad blocks we may not be able to
+ * perform the requested write. In the case where the write would
+ * extend beyond the end of the NAND device, both length and actual (if
+ * not NULL) are set to 0. In the case where the write would extend
+ * beyond the limit we are passed, length is set to 0 and actual is set
+ * to the required length.
*
* @param nand NAND device
* @param offset offset in flash
* @param length buffer length
+ * @param actual set to size required to write length worth of
+ * buffer or 0 on error, if not NULL
+ * @param lim maximum size that actual may be in order to not
+ * exceed the buffer
* @param buffer buffer to read from
* @param flags flags modifying the behaviour of the write to NAND
* @return 0 in case of success
*/
int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
- u_char *buffer, int flags)
+ size_t *actual, loff_t lim, u_char *buffer, int flags)
{
int rval = 0, blocksize;
size_t left_to_write = *length;
+ size_t used_for_write = 0;
u_char *p_buffer = buffer;
int need_skip;
+ if (actual)
+ *actual = 0;
+
#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
if (flags & ~WITH_YAFFS_OOB)
@@ -529,13 +549,23 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
return -EINVAL;
}
- need_skip = check_skip_len(nand, offset, *length);
+ need_skip = check_skip_len(nand, offset, *length, &used_for_write);
+
+ if (actual)
+ *actual = used_for_write;
+
if (need_skip < 0) {
printf("Attempt to write outside the flash area\n");
*length = 0;
return -EINVAL;
}
+ if (used_for_write > lim) {
+ puts("Size of write exceeds partition or device limit\n");
+ *length = 0;
+ return -EFBIG;
+ }
+
if (!need_skip && !(flags & WITH_DROP_FFS)) {
rval = nand_write(nand, offset, length, buffer);
if (rval == 0)
@@ -626,36 +656,58 @@ int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
*
* Read image from NAND flash.
* Blocks that are marked bad are skipped and the next block is read
- * instead as long as the image is short enough to fit even after skipping the
- * bad blocks.
+ * instead as long as the image is short enough to fit even after
+ * skipping the bad blocks. Due to bad blocks we may not be able to
+ * perform the requested read. In the case where the read would extend
+ * beyond the end of the NAND device, both length and actual (if not
+ * NULL) are set to 0. In the case where the read would extend beyond
+ * the limit we are passed, length is set to 0 and actual is set to the
+ * required length.
*
* @param nand NAND device
* @param offset offset in flash
* @param length buffer length, on return holds number of read bytes
+ * @param actual set to size required to read length worth of buffer or 0
+ * on error, if not NULL
+ * @param lim maximum size that actual may be in order to not exceed the
+ * buffer
* @param buffer buffer to write to
* @return 0 in case of success
*/
int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
- u_char *buffer)
+ size_t *actual, loff_t lim, u_char *buffer)
{
int rval;
size_t left_to_read = *length;
+ size_t used_for_read = 0;
u_char *p_buffer = buffer;
int need_skip;
if ((offset & (nand->writesize - 1)) != 0) {
printf("Attempt to read non page-aligned data\n");
*length = 0;
+ if (actual)
+ *actual = 0;
return -EINVAL;
}
- need_skip = check_skip_len(nand, offset, *length);
+ need_skip = check_skip_len(nand, offset, *length, &used_for_read);
+
+ if (actual)
+ *actual = used_for_read;
+
if (need_skip < 0) {
printf("Attempt to read outside the flash area\n");
*length = 0;
return -EINVAL;
}
+ if (used_for_read > lim) {
+ puts("Size of read exceeds partition or device limit\n");
+ *length = 0;
+ return -EFBIG;
+ }
+
if (!need_skip) {
rval = nand_read(nand, offset, length, buffer);
if (!rval || rval == -EUCLEAN)
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index 6ebbb5ebee..213d2c945a 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -156,7 +156,7 @@ static uint8_t ndfc_read_byte(struct mtd_info *mtd)
struct nand_chip *chip = mtd->priv;
-#ifdef CONFIG_SYS_NDFC_16BIT
+#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT
return (uint8_t) readw(chip->IO_ADDR_R);
#else
return readb(chip->IO_ADDR_R);
@@ -218,7 +218,7 @@ int board_nand_init(struct nand_chip *nand)
nand->ecc.bytes = 3;
nand->select_chip = ndfc_select_chip;
-#ifdef CONFIG_SYS_NDFC_16BIT
+#ifdef CONFIG_SYS_NAND_BUSWIDTH_16BIT
nand->options |= NAND_BUSWIDTH_16;
#endif
diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c
index cee394ece4..bc1bcad3ba 100644
--- a/drivers/mtd/nand/omap_gpmc.c
+++ b/drivers/mtd/nand/omap_gpmc.c
@@ -25,8 +25,10 @@
#include <asm/io.h>
#include <asm/errno.h>
#include <asm/arch/mem.h>
-#include <asm/arch/omap_gpmc.h>
+#include <asm/arch/cpu.h>
+#include <asm/omap_gpmc.h>
#include <linux/mtd/nand_ecc.h>
+#include <linux/bch.h>
#include <linux/compiler.h>
#include <nand.h>
#ifdef CONFIG_AM33XX
@@ -36,6 +38,8 @@
static uint8_t cs;
static __maybe_unused struct nand_ecclayout hw_nand_oob =
GPMC_NAND_HW_ECC_LAYOUT;
+static __maybe_unused struct nand_ecclayout hw_bch8_nand_oob =
+ GPMC_NAND_HW_BCH8_ECC_LAYOUT;
/*
* omap_nand_hwcontrol - Set the address pointers corretly for the
@@ -238,13 +242,13 @@ static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
}
/*
- * BCH8 support (needs ELM and thus AM33xx-only)
+ * Generic BCH interface
*/
-#ifdef CONFIG_AM33XX
struct nand_bch_priv {
uint8_t mode;
uint8_t type;
uint8_t nibbles;
+ struct bch_control *control;
};
/* bch types */
@@ -252,21 +256,146 @@ struct nand_bch_priv {
#define ECC_BCH8 1
#define ECC_BCH16 2
+/* GPMC ecc engine settings */
+#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */
+#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */
+
/* BCH nibbles for diff bch levels */
#define NAND_ECC_HW_BCH ((uint8_t)(NAND_ECC_HW_OOB_FIRST) + 1)
#define ECC_BCH4_NIBBLES 13
#define ECC_BCH8_NIBBLES 26
#define ECC_BCH16_NIBBLES 52
-static struct nand_ecclayout hw_bch8_nand_oob = GPMC_NAND_HW_BCH8_ECC_LAYOUT;
-
-static struct nand_bch_priv bch_priv = {
+/*
+ * This can be a single instance cause all current users have only one NAND
+ * with nearly the same setup (BCH8, some with ELM and others with sw BCH
+ * library).
+ * When some users with other BCH strength will exists this have to change!
+ */
+static __maybe_unused struct nand_bch_priv bch_priv = {
.mode = NAND_ECC_HW_BCH,
.type = ECC_BCH8,
- .nibbles = ECC_BCH8_NIBBLES
+ .nibbles = ECC_BCH8_NIBBLES,
+ .control = NULL
};
/*
+ * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in
+ * GPMC controller
+ * @mtd: MTD device structure
+ * @mode: Read/Write mode
+ */
+__maybe_unused
+static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)
+{
+ uint32_t val;
+ uint32_t dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1;
+#ifdef CONFIG_AM33XX
+ uint32_t unused_length = 0;
+#endif
+ uint32_t wr_mode = BCH_WRAPMODE_6;
+ struct nand_bch_priv *bch = chip->priv;
+
+ /* Clear the ecc result registers, select ecc reg as 1 */
+ writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
+
+#ifdef CONFIG_AM33XX
+ wr_mode = BCH_WRAPMODE_1;
+
+ switch (bch->nibbles) {
+ case ECC_BCH4_NIBBLES:
+ unused_length = 3;
+ break;
+ case ECC_BCH8_NIBBLES:
+ unused_length = 2;
+ break;
+ case ECC_BCH16_NIBBLES:
+ unused_length = 0;
+ break;
+ }
+
+ /*
+ * This is ecc_size_config for ELM mode.
+ * Here we are using different settings for read and write access and
+ * also depending on BCH strength.
+ */
+ switch (mode) {
+ case NAND_ECC_WRITE:
+ /* write access only setup eccsize1 config */
+ val = ((unused_length + bch->nibbles) << 22);
+ break;
+
+ case NAND_ECC_READ:
+ default:
+ /*
+ * by default eccsize0 selected for ecc1resultsize
+ * eccsize0 config.
+ */
+ val = (bch->nibbles << 12);
+ /* eccsize1 config */
+ val |= (unused_length << 22);
+ break;
+ }
+#else
+ /*
+ * This ecc_size_config setting is for BCH sw library.
+ *
+ * Note: we only support BCH8 currently with BCH sw library!
+ * Should be really easy to adobt to BCH4, however some omap3 have
+ * flaws with BCH4.
+ *
+ * Here we are using wrapping mode 6 both for reading and writing, with:
+ * size0 = 0 (no additional protected byte in spare area)
+ * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
+ */
+ val = (32 << 22) | (0 << 12);
+#endif
+ /* ecc size configuration */
+ writel(val, &gpmc_cfg->ecc_size_config);
+
+ /*
+ * Configure the ecc engine in gpmc
+ * We assume 512 Byte sector pages for access to NAND.
+ */
+ val = (1 << 16); /* enable BCH mode */
+ val |= (bch->type << 12); /* setup BCH type */
+ val |= (wr_mode << 8); /* setup wrapping mode */
+ val |= (dev_width << 7); /* setup device width (16 or 8 bit) */
+ val |= (cs << 1); /* setup chip select to work on */
+ debug("set ECC_CONFIG=0x%08x\n", val);
+ writel(val, &gpmc_cfg->ecc_config);
+}
+
+/*
+ * omap_enable_ecc_bch - This function enables the bch h/w ecc functionality
+ * @mtd: MTD device structure
+ * @mode: Read/Write mode
+ */
+__maybe_unused
+static void omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode)
+{
+ struct nand_chip *chip = mtd->priv;
+
+ omap_hwecc_init_bch(chip, mode);
+ /* enable ecc */
+ writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config);
+}
+
+/*
+ * omap_ecc_disable - Disable H/W ECC calculation
+ *
+ * @mtd: MTD device structure
+ */
+static void __maybe_unused omap_ecc_disable(struct mtd_info *mtd)
+{
+ writel((readl(&gpmc_cfg->ecc_config) & ~0x1), &gpmc_cfg->ecc_config);
+}
+
+/*
+ * BCH8 support (needs ELM and thus AM33xx-only)
+ */
+#ifdef CONFIG_AM33XX
+/*
* omap_read_bch8_result - Read BCH result for BCH8 level
*
* @mtd: MTD device structure
@@ -305,18 +434,6 @@ static void omap_read_bch8_result(struct mtd_info *mtd, uint8_t big_endian,
}
/*
- * omap_ecc_disable - Disable H/W ECC calculation
- *
- * @mtd: MTD device structure
- *
- */
-static void omap_ecc_disable(struct mtd_info *mtd)
-{
- writel((readl(&gpmc_cfg->ecc_config) & ~0x1),
- &gpmc_cfg->ecc_config);
-}
-
-/*
* omap_rotate_ecc_bch - Rotate the syndrome bytes
*
* @mtd: MTD device structure
@@ -467,76 +584,6 @@ static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat,
return 0;
}
-/*
- * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in
- * GPMC controller
- * @mtd: MTD device structure
- * @mode: Read/Write mode
- */
-static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)
-{
- uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1;
- uint32_t unused_length = 0;
- struct nand_bch_priv *bch = chip->priv;
-
- switch (bch->nibbles) {
- case ECC_BCH4_NIBBLES:
- unused_length = 3;
- break;
- case ECC_BCH8_NIBBLES:
- unused_length = 2;
- break;
- case ECC_BCH16_NIBBLES:
- unused_length = 0;
- break;
- }
-
- /* Clear the ecc result registers, select ecc reg as 1 */
- writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
-
- switch (mode) {
- case NAND_ECC_WRITE:
- /* eccsize1 config */
- val = ((unused_length + bch->nibbles) << 22);
- break;
-
- case NAND_ECC_READ:
- default:
- /* by default eccsize0 selected for ecc1resultsize */
- /* eccsize0 config */
- val = (bch->nibbles << 12);
- /* eccsize1 config */
- val |= (unused_length << 22);
- break;
- }
- /* ecc size configuration */
- writel(val, &gpmc_cfg->ecc_size_config);
- /* by default 512bytes sector page is selected */
- /* set bch mode */
- val = (1 << 16);
- /* bch4 / bch8 / bch16 */
- val |= (bch->type << 12);
- /* set wrap mode to 1 */
- val |= (1 << 8);
- val |= (dev_width << 7);
- val |= (cs << 1);
- writel(val, &gpmc_cfg->ecc_config);
-}
-
-/*
- * omap_enable_ecc_bch- This function enables the bch h/w ecc functionality
- * @mtd: MTD device structure
- * @mode: Read/Write mode
- *
- */
-static void omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode)
-{
- struct nand_chip *chip = mtd->priv;
-
- omap_hwecc_init_bch(chip, mode);
- /* enable ecc */
- writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config);
-}
/**
* omap_read_page_bch - hardware ecc based page read function
@@ -601,15 +648,137 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
}
#endif /* CONFIG_AM33XX */
-#ifndef CONFIG_SPL_BUILD
/*
- * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc.
- * The default is to come up on s/w ecc
+ * OMAP3 BCH8 support (with BCH library)
+ */
+#ifdef CONFIG_NAND_OMAP_BCH8
+/*
+ * omap_calculate_ecc_bch - Read BCH ECC result
*
- * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc
+ * @mtd: MTD device structure
+ * @dat: The pointer to data on which ecc is computed (unused here)
+ * @ecc: The ECC output buffer
+ */
+static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat,
+ uint8_t *ecc)
+{
+ int ret = 0;
+ size_t i;
+ unsigned long nsectors, val1, val2, val3, val4;
+
+ nsectors = ((readl(&gpmc_cfg->ecc_config) >> 4) & 0x7) + 1;
+
+ for (i = 0; i < nsectors; i++) {
+ /* Read hw-computed remainder */
+ val1 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[0]);
+ val2 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[1]);
+ val3 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[2]);
+ val4 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[3]);
+
+ /*
+ * Add constant polynomial to remainder, in order to get an ecc
+ * sequence of 0xFFs for a buffer filled with 0xFFs.
+ */
+ *ecc++ = 0xef ^ (val4 & 0xFF);
+ *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF);
+ *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF);
+ *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF);
+ *ecc++ = 0xed ^ (val3 & 0xFF);
+ *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF);
+ *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF);
+ *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
+ *ecc++ = 0x97 ^ (val2 & 0xFF);
+ *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF);
+ *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
+ *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF);
+ *ecc++ = 0xb5 ^ (val1 & 0xFF);
+ }
+
+ /*
+ * Stop reading anymore ECC vals and clear old results
+ * enable will be called if more reads are required
+ */
+ omap_ecc_disable(mtd);
+
+ return ret;
+}
+
+/**
+ * omap_correct_data_bch - Decode received data and correct errors
+ * @mtd: MTD device structure
+ * @data: page data
+ * @read_ecc: ecc read from nand flash
+ * @calc_ecc: ecc read from HW ECC registers
+ */
+static int omap_correct_data_bch(struct mtd_info *mtd, u_char *data,
+ u_char *read_ecc, u_char *calc_ecc)
+{
+ int i, count;
+ /* cannot correct more than 8 errors */
+ unsigned int errloc[8];
+ struct nand_chip *chip = mtd->priv;
+ struct nand_bch_priv *chip_priv = chip->priv;
+ struct bch_control *bch = chip_priv->control;
+
+ count = decode_bch(bch, NULL, 512, read_ecc, calc_ecc, NULL, errloc);
+ if (count > 0) {
+ /* correct errors */
+ for (i = 0; i < count; i++) {
+ /* correct data only, not ecc bytes */
+ if (errloc[i] < 8*512)
+ data[errloc[i]/8] ^= 1 << (errloc[i] & 7);
+ printf("corrected bitflip %u\n", errloc[i]);
+#ifdef DEBUG
+ puts("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: ");
+ for (i = 0; i < 13; i++)
+ printf("%02x ", calc_ecc[i]);
+ puts("\n");
+#endif
+ }
+ } else if (count < 0) {
+ puts("ecc unrecoverable error\n");
+ }
+ return count;
+}
+
+/**
+ * omap_free_bch - Release BCH ecc resources
+ * @mtd: MTD device structure
+ */
+static void __maybe_unused omap_free_bch(struct mtd_info *mtd)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct nand_bch_priv *chip_priv = chip->priv;
+ struct bch_control *bch = NULL;
+
+ if (chip_priv)
+ bch = chip_priv->control;
+
+ if (bch) {
+ free_bch(bch);
+ chip_priv->control = NULL;
+ }
+}
+#endif /* CONFIG_NAND_OMAP_BCH8 */
+
+#ifndef CONFIG_SPL_BUILD
+/*
+ * omap_nand_switch_ecc - switch the ECC operation between different engines
+ * (h/w and s/w) and different algorithms (hamming and BCHx)
*
+ * @hardware - true if one of the HW engines should be used
+ * @eccstrength - the number of bits that could be corrected
+ * (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16)
*/
-void omap_nand_switch_ecc(int32_t hardware)
+void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)
{
struct nand_chip *nand;
struct mtd_info *mtd;
@@ -627,6 +796,7 @@ void omap_nand_switch_ecc(int32_t hardware)
nand->options |= NAND_OWN_BUFFERS;
/* Reset ecc interface */
+ nand->ecc.mode = NAND_ECC_NONE;
nand->ecc.read_page = NULL;
nand->ecc.write_page = NULL;
nand->ecc.read_oob = NULL;
@@ -636,28 +806,35 @@ void omap_nand_switch_ecc(int32_t hardware)
nand->ecc.calculate = NULL;
/* Setup the ecc configurations again */
- if (hardware == 1) {
- nand->ecc.mode = NAND_ECC_HW;
- nand->ecc.layout = &hw_nand_oob;
- nand->ecc.size = 512;
- nand->ecc.bytes = 3;
- nand->ecc.hwctl = omap_enable_hwecc;
- nand->ecc.correct = omap_correct_data;
- nand->ecc.calculate = omap_calculate_ecc;
- omap_hwecc_init(nand);
- printf("HW ECC selected\n");
+ if (hardware) {
+ if (eccstrength == 1) {
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.layout = &hw_nand_oob;
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 3;
+ nand->ecc.hwctl = omap_enable_hwecc;
+ nand->ecc.correct = omap_correct_data;
+ nand->ecc.calculate = omap_calculate_ecc;
+ omap_hwecc_init(nand);
+ printf("1-bit hamming HW ECC selected\n");
+ }
+#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8)
+ else if (eccstrength == 8) {
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.layout = &hw_bch8_nand_oob;
+ nand->ecc.size = 512;
#ifdef CONFIG_AM33XX
- } else if (hardware == 2) {
- nand->ecc.mode = NAND_ECC_HW;
- nand->ecc.layout = &hw_bch8_nand_oob;
- nand->ecc.size = 512;
- nand->ecc.bytes = 14;
- nand->ecc.read_page = omap_read_page_bch;
- nand->ecc.hwctl = omap_enable_ecc_bch;
- nand->ecc.correct = omap_correct_data_bch;
- nand->ecc.calculate = omap_calculate_ecc_bch;
- omap_hwecc_init_bch(nand, NAND_ECC_READ);
- printf("HW BCH8 selected\n");
+ nand->ecc.bytes = 14;
+ nand->ecc.read_page = omap_read_page_bch;
+#else
+ nand->ecc.bytes = 13;
+#endif
+ nand->ecc.hwctl = omap_enable_ecc_bch;
+ nand->ecc.correct = omap_correct_data_bch;
+ nand->ecc.calculate = omap_calculate_ecc_bch;
+ omap_hwecc_init_bch(nand, NAND_ECC_READ);
+ printf("8-bit BCH HW ECC selected\n");
+ }
#endif
} else {
nand->ecc.mode = NAND_ECC_SOFT;
@@ -731,16 +908,28 @@ int board_nand_init(struct nand_chip *nand)
nand->chip_delay = 100;
+#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8)
#ifdef CONFIG_AM33XX
+ /* AM33xx uses the ELM */
/* required in case of BCH */
elm_init();
-
+#else
+ /*
+ * Whereas other OMAP based SoC do not have the ELM, they use the BCH
+ * SW library.
+ */
+ bch_priv.control = init_bch(13, 8, 0x201b /* hw polynominal */);
+ if (!bch_priv.control) {
+ puts("Could not init_bch()\n");
+ return -ENODEV;
+ }
+#endif
/* BCH info that will be correct for SPL or overridden otherwise. */
nand->priv = &bch_priv;
#endif
/* Default ECC mode */
-#ifdef CONFIG_AM33XX
+#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8)
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.layout = &hw_bch8_nand_oob;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
@@ -748,7 +937,9 @@ int board_nand_init(struct nand_chip *nand)
nand->ecc.hwctl = omap_enable_ecc_bch;
nand->ecc.correct = omap_correct_data_bch;
nand->ecc.calculate = omap_calculate_ecc_bch;
+#ifdef CONFIG_AM33XX
nand->ecc.read_page = omap_read_page_bch;
+#endif
omap_hwecc_init_bch(nand, NAND_ECC_READ);
#else
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_NAND_SOFTECC)
diff --git a/drivers/mtd/nand/s3c64xx.c b/drivers/mtd/nand/s3c64xx.c
deleted file mode 100644
index 87f0341066..0000000000
--- a/drivers/mtd/nand/s3c64xx.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * (C) Copyright 2006 DENX Software Engineering
- *
- * Implementation for U-Boot 1.1.6 by Samsung
- *
- * (C) Copyright 2008
- * Guennadi Liakhovetki, DENX Software Engineering, <lg@denx.de>
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <common.h>
-
-#include <nand.h>
-#include <linux/mtd/nand.h>
-
-#include <asm/arch/s3c6400.h>
-
-#include <asm/io.h>
-#include <asm/errno.h>
-
-#define MAX_CHIPS 2
-static int nand_cs[MAX_CHIPS] = {0, 1};
-
-#ifdef CONFIG_NAND_SPL
-#define printf(arg...) do {} while (0)
-#endif
-
-/* Nand flash definition values by jsgood */
-#ifdef S3C_NAND_DEBUG
-/*
- * Function to print out oob buffer for debugging
- * Written by jsgood
- */
-static void print_oob(const char *header, struct mtd_info *mtd)
-{
- int i;
- struct nand_chip *chip = mtd->priv;
-
- printf("%s:\t", header);
-
- for (i = 0; i < 64; i++)
- printf("%02x ", chip->oob_poi[i]);
-
- printf("\n");
-}
-#endif /* S3C_NAND_DEBUG */
-
-static void s3c_nand_select_chip(struct mtd_info *mtd, int chip)
-{
- int ctrl = readl(NFCONT);
-
- switch (chip) {
- case -1:
- ctrl |= 6;
- break;
- case 0:
- ctrl &= ~2;
- break;
- case 1:
- ctrl &= ~4;
- break;
- default:
- return;
- }
-
- writel(ctrl, NFCONT);
-}
-
-/*
- * Hardware specific access to control-lines function
- * Written by jsgood
- */
-static void s3c_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
-{
- struct nand_chip *this = mtd->priv;
-
- if (ctrl & NAND_CTRL_CHANGE) {
- if (ctrl & NAND_CLE)
- this->IO_ADDR_W = (void __iomem *)NFCMMD;
- else if (ctrl & NAND_ALE)
- this->IO_ADDR_W = (void __iomem *)NFADDR;
- else
- this->IO_ADDR_W = (void __iomem *)NFDATA;
- if (ctrl & NAND_NCE)
- s3c_nand_select_chip(mtd, *(int *)this->priv);
- else
- s3c_nand_select_chip(mtd, -1);
- }
-
- if (cmd != NAND_CMD_NONE)
- writeb(cmd, this->IO_ADDR_W);
-}
-
-/*
- * Function for checking device ready pin
- * Written by jsgood
- */
-static int s3c_nand_device_ready(struct mtd_info *mtdinfo)
-{
- return !!(readl(NFSTAT) & NFSTAT_RnB);
-}
-
-#ifdef CONFIG_SYS_S3C_NAND_HWECC
-/*
- * This function is called before encoding ecc codes to ready ecc engine.
- * Written by jsgood
- */
-static void s3c_nand_enable_hwecc(struct mtd_info *mtd, int mode)
-{
- u_long nfcont, nfconf;
-
- /*
- * The original driver used 4-bit ECC for "new" MLC chips, i.e., for
- * those with non-zero ID[3][3:2], which anyway only holds for ST
- * (Numonyx) chips
- */
- nfconf = readl(NFCONF) & ~NFCONF_ECC_4BIT;
-
- writel(nfconf, NFCONF);
-
- /* Initialize & unlock */
- nfcont = readl(NFCONT);
- nfcont |= NFCONT_INITECC;
- nfcont &= ~NFCONT_MECCLOCK;
-
- if (mode == NAND_ECC_WRITE)
- nfcont |= NFCONT_ECC_ENC;
- else if (mode == NAND_ECC_READ)
- nfcont &= ~NFCONT_ECC_ENC;
-
- writel(nfcont, NFCONT);
-}
-
-/*
- * This function is called immediately after encoding ecc codes.
- * This function returns encoded ecc codes.
- * Written by jsgood
- */
-static int s3c_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
- u_char *ecc_code)
-{
- u_long nfcont, nfmecc0;
-
- /* Lock */
- nfcont = readl(NFCONT);
- nfcont |= NFCONT_MECCLOCK;
- writel(nfcont, NFCONT);
-
- nfmecc0 = readl(NFMECC0);
-
- ecc_code[0] = nfmecc0 & 0xff;
- ecc_code[1] = (nfmecc0 >> 8) & 0xff;
- ecc_code[2] = (nfmecc0 >> 16) & 0xff;
- ecc_code[3] = (nfmecc0 >> 24) & 0xff;
-
- return 0;
-}
-
-/*
- * This function determines whether read data is good or not.
- * If SLC, must write ecc codes to controller before reading status bit.
- * If MLC, status bit is already set, so only reading is needed.
- * If status bit is good, return 0.
- * If correctable errors occured, do that.
- * If uncorrectable errors occured, return -1.
- * Written by jsgood
- */
-static int s3c_nand_correct_data(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
-{
- int ret = -1;
- u_long nfestat0, nfmeccdata0, nfmeccdata1, err_byte_addr;
- u_char err_type, repaired;
-
- /* SLC: Write ecc to compare */
- nfmeccdata0 = (calc_ecc[1] << 16) | calc_ecc[0];
- nfmeccdata1 = (calc_ecc[3] << 16) | calc_ecc[2];
- writel(nfmeccdata0, NFMECCDATA0);
- writel(nfmeccdata1, NFMECCDATA1);
-
- /* Read ecc status */
- nfestat0 = readl(NFESTAT0);
- err_type = nfestat0 & 0x3;
-
- switch (err_type) {
- case 0: /* No error */
- ret = 0;
- break;
-
- case 1:
- /*
- * 1 bit error (Correctable)
- * (nfestat0 >> 7) & 0x7ff :error byte number
- * (nfestat0 >> 4) & 0x7 :error bit number
- */
- err_byte_addr = (nfestat0 >> 7) & 0x7ff;
- repaired = dat[err_byte_addr] ^ (1 << ((nfestat0 >> 4) & 0x7));
-
- printf("S3C NAND: 1 bit error detected at byte %ld. "
- "Correcting from 0x%02x to 0x%02x...OK\n",
- err_byte_addr, dat[err_byte_addr], repaired);
-
- dat[err_byte_addr] = repaired;
-
- ret = 1;
- break;
-
- case 2: /* Multiple error */
- case 3: /* ECC area error */
- printf("S3C NAND: ECC uncorrectable error detected. "
- "Not correctable.\n");
- ret = -1;
- break;
- }
-
- return ret;
-}
-#endif /* CONFIG_SYS_S3C_NAND_HWECC */
-
-/*
- * Board-specific NAND initialization. The following members of the
- * argument are board-specific (per include/linux/mtd/nand.h):
- * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
- * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
- * - hwcontrol: hardwarespecific function for accesing control-lines
- * - dev_ready: hardwarespecific function for accesing device ready/busy line
- * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must
- * only be provided if a hardware ECC is available
- * - eccmode: mode of ecc, see defines
- * - chip_delay: chip dependent delay for transfering data from array to
- * read regs (tR)
- * - options: various chip options. They can partly be set to inform
- * nand_scan about special functionality. See the defines for further
- * explanation
- * Members with a "?" were not set in the merged testing-NAND branch,
- * so they are not set here either.
- */
-int board_nand_init(struct nand_chip *nand)
-{
- static int chip_n;
-
- if (chip_n >= MAX_CHIPS)
- return -ENODEV;
-
- NFCONT_REG = (NFCONT_REG & ~NFCONT_WP) | NFCONT_ENABLE | 0x6;
-
- nand->IO_ADDR_R = (void __iomem *)NFDATA;
- nand->IO_ADDR_W = (void __iomem *)NFDATA;
- nand->cmd_ctrl = s3c_nand_hwcontrol;
- nand->dev_ready = s3c_nand_device_ready;
- nand->select_chip = s3c_nand_select_chip;
- nand->options = 0;
-#ifdef CONFIG_NAND_SPL
- nand->read_byte = nand_read_byte;
- nand->write_buf = nand_write_buf;
- nand->read_buf = nand_read_buf;
-#endif
-
-#ifdef CONFIG_SYS_S3C_NAND_HWECC
- nand->ecc.hwctl = s3c_nand_enable_hwecc;
- nand->ecc.calculate = s3c_nand_calculate_ecc;
- nand->ecc.correct = s3c_nand_correct_data;
-
- /*
- * If you get more than 1 NAND-chip with different page-sizes on the
- * board one day, it will get more complicated...
- */
- nand->ecc.mode = NAND_ECC_HW;
- nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
- nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
-#else
- nand->ecc.mode = NAND_ECC_SOFT;
-#endif /* ! CONFIG_SYS_S3C_NAND_HWECC */
-
- nand->priv = nand_cs + chip_n++;
-
- return 0;
-}
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 1a7b40eaa3..858e322743 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -632,10 +632,6 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
int blockpage, found = 0;
unsigned int i;
-#ifdef CONFIG_S3C64XX
- return 0;
-#endif
-
if (ONENAND_IS_2PLANE(this))
blockpage = onenand_get_2x_blockpage(mtd, addr);
else
diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c
index 0d94ea5b1f..5eb2b3a424 100644
--- a/drivers/mtd/onenand/samsung.c
+++ b/drivers/mtd/onenand/samsung.c
@@ -1,5 +1,5 @@
/*
- * S3C64XX/S5PC100 OneNAND driver at U-Boot
+ * S5PC100 OneNAND driver at U-Boot
*
* Copyright (C) 2008-2009 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.com>
@@ -62,12 +62,7 @@ do { \
#define ONENAND_MAIN_SPARE_ACCESS 0x16
#define ONENAND_PIPELINE_READ 0x4000
-#if defined(CONFIG_S3C64XX)
-#define MAP_00 (0x0 << 24)
-#define MAP_01 (0x1 << 24)
-#define MAP_10 (0x2 << 24)
-#define MAP_11 (0x3 << 24)
-#elif defined(CONFIG_S5P)
+#if defined(CONFIG_S5P)
#define MAP_00 (0x0 << 26)
#define MAP_01 (0x1 << 26)
#define MAP_10 (0x2 << 26)
@@ -116,12 +111,7 @@ static void s3c_write_cmd(int value, unsigned int cmd)
* return the buffer address on the memory device
* It will be combined with CMD_MAP_XX
*/
-#if defined(CONFIG_S3C64XX)
-static unsigned int s3c_mem_addr(int fba, int fpa, int fsa)
-{
- return (fba << 12) | (fpa << 6) | (fsa << 4);
-}
-#elif defined(CONFIG_S5P)
+#if defined(CONFIG_S5P)
static unsigned int s3c_mem_addr(int fba, int fpa, int fsa)
{
return (fba << 13) | (fpa << 7) | (fsa << 5);
@@ -550,45 +540,6 @@ static void s3c_onenand_unlock_all(struct mtd_info *mtd)
s3c_onenand_check_lock_status(mtd);
}
-#ifdef CONFIG_S3C64XX
-static void s3c_set_width_regs(struct onenand_chip *this)
-{
- int dev_id, density;
- int fba, fpa, fsa;
- int dbs_dfs;
-
- dev_id = DEVICE_ID0_REG;
-
- density = (dev_id >> ONENAND_DEVICE_DENSITY_SHIFT) & 0xf;
- dbs_dfs = !!(dev_id & ONENAND_DEVICE_IS_DDP);
-
- fba = density + 7;
- if (dbs_dfs)
- fba--; /* Decrease the fba */
- fpa = 6;
- if (density >= ONENAND_DEVICE_DENSITY_512Mb)
- fsa = 2;
- else
- fsa = 1;
-
- DPRINTK("FBA %lu, FPA %lu, FSA %lu, DDP %lu",
- FBA_WIDTH0_REG, FPA_WIDTH0_REG, FSA_WIDTH0_REG,
- DDP_DEVICE_REG);
-
- DPRINTK("mem_cfg0 0x%lx, sync mode %lu, "
- "dev_page_size %lu, BURST LEN %lu",
- MEM_CFG0_REG, SYNC_MODE_REG,
- DEV_PAGE_SIZE_REG, BURST_LEN0_REG);
-
- DEV_PAGE_SIZE_REG = 0x1;
-
- FBA_WIDTH0_REG = fba;
- FPA_WIDTH0_REG = fpa;
- FSA_WIDTH0_REG = fsa;
- DBS_DFS_WIDTH0_REG = dbs_dfs;
-}
-#endif
-
int s5pc110_chip_probe(struct mtd_info *mtd)
{
return 0;
@@ -620,10 +571,7 @@ void s3c_onenand_init(struct mtd_info *mtd)
onenand->mtd = mtd;
-#if defined(CONFIG_S3C64XX)
- onenand->base = (void *)0x70100000;
- onenand->ahb_addr = (void *)0x20000000;
-#elif defined(CONFIG_S5P)
+#if defined(CONFIG_S5P)
onenand->base = (void *)0xE7100000;
onenand->ahb_addr = (void *)0xB0000000;
#endif
diff --git a/drivers/mtd/spi/atmel.c b/drivers/mtd/spi/atmel.c
index 006f6d5d04..6a92c4b774 100644
--- a/drivers/mtd/spi/atmel.c
+++ b/drivers/mtd/spi/atmel.c
@@ -480,15 +480,13 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- asf = malloc(sizeof(struct atmel_spi_flash));
+ asf = spi_flash_alloc(struct atmel_spi_flash, spi, params->name);
if (!asf) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
asf->params = params;
- asf->flash.spi = spi;
- asf->flash.name = params->name;
/* Assuming power-of-two page size initially. */
page_size = 1 << params->l2_page_size;
@@ -513,7 +511,6 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
asf->flash.erase = dataflash_erase_at45;
page_size += 1 << (params->l2_page_size - 5);
} else {
- asf->flash.read = spi_flash_cmd_read_fast;
asf->flash.write = dataflash_write_p2;
asf->flash.erase = dataflash_erase_p2;
}
@@ -524,9 +521,6 @@ struct spi_flash *spi_flash_probe_atmel(struct spi_slave *spi, u8 *idcode)
case DF_FAMILY_AT26F:
case DF_FAMILY_AT26DF:
- asf->flash.read = spi_flash_cmd_read_fast;
- asf->flash.write = spi_flash_cmd_write_multi;
- asf->flash.erase = spi_flash_cmd_erase;
asf->flash.page_size = page_size;
asf->flash.sector_size = 4096;
/* clear SPRL# bit for locked flash */
diff --git a/drivers/mtd/spi/eon.c b/drivers/mtd/spi/eon.c
index 691ed4efc4..b16e7ab098 100644
--- a/drivers/mtd/spi/eon.c
+++ b/drivers/mtd/spi/eon.c
@@ -46,18 +46,12 @@ struct spi_flash *spi_flash_probe_eon(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- flash = malloc(sizeof(*flash));
+ flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
- flash->spi = spi;
- flash->name = params->name;
-
- flash->write = spi_flash_cmd_write_multi;
- flash->erase = spi_flash_cmd_erase;
- flash->read = spi_flash_cmd_read_fast;
flash->page_size = 256;
flash->sector_size = 256 * 16 * 16;
flash->size = 256 * 16
diff --git a/drivers/mtd/spi/macronix.c b/drivers/mtd/spi/macronix.c
index c97a39d499..036c30d3be 100644
--- a/drivers/mtd/spi/macronix.c
+++ b/drivers/mtd/spi/macronix.c
@@ -97,18 +97,12 @@ struct spi_flash *spi_flash_probe_macronix(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- flash = malloc(sizeof(*flash));
+ flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
- flash->spi = spi;
- flash->name = params->name;
-
- flash->write = spi_flash_cmd_write_multi;
- flash->erase = spi_flash_cmd_erase;
- flash->read = spi_flash_cmd_read_fast;
flash->page_size = 256;
flash->sector_size = 256 * 16 * 16;
flash->size = flash->sector_size * params->nr_blocks;
diff --git a/drivers/mtd/spi/ramtron.c b/drivers/mtd/spi/ramtron.c
index 0999781496..5299a6dbde 100644
--- a/drivers/mtd/spi/ramtron.c
+++ b/drivers/mtd/spi/ramtron.c
@@ -284,15 +284,13 @@ struct spi_flash *spi_fram_probe_ramtron(struct spi_slave *spi, u8 *idcode)
return NULL;
found:
- sn = malloc(sizeof(*sn));
+ sn = spi_flash_alloc(struct ramtron_spi_fram, spi, params->name);
if (!sn) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
sn->params = params;
- sn->flash.spi = spi;
- sn->flash.name = params->name;
sn->flash.write = ramtron_write;
sn->flash.read = ramtron_read;
diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c
index 9288672c84..bc558c4c96 100644
--- a/drivers/mtd/spi/spansion.c
+++ b/drivers/mtd/spi/spansion.c
@@ -128,18 +128,12 @@ struct spi_flash *spi_flash_probe_spansion(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- flash = malloc(sizeof(*flash));
+ flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
- flash->spi = spi;
- flash->name = params->name;
-
- flash->write = spi_flash_cmd_write_multi;
- flash->erase = spi_flash_cmd_erase;
- flash->read = spi_flash_cmd_read_fast;
flash->page_size = 256;
flash->sector_size = 256 * params->pages_per_sector;
flash->size = flash->sector_size * params->nr_sectors;
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index 00aece9291..111185af17 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -8,6 +8,7 @@
*/
#include <common.h>
+#include <fdtdec.h>
#include <malloc.h>
#include <spi.h>
#include <spi_flash.h>
@@ -15,6 +16,8 @@
#include "spi_flash_internal.h"
+DECLARE_GLOBAL_DATA_PTR;
+
static void spi_flash_addr(u32 addr, u8 *cmd)
{
/* cmd[0] is actual command */
@@ -87,6 +90,9 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
for (actual = 0; actual < len; actual += chunk_len) {
chunk_len = min(len - actual, page_size - byte_addr);
+ if (flash->spi->max_write_size)
+ chunk_len = min(chunk_len, flash->spi->max_write_size);
+
cmd[1] = page_addr >> 8;
cmd[2] = page_addr;
cmd[3] = byte_addr;
@@ -111,8 +117,11 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset,
if (ret)
break;
- page_addr++;
- byte_addr = 0;
+ byte_addr += chunk_len;
+ if (byte_addr == page_size) {
+ page_addr++;
+ byte_addr = 0;
+ }
}
debug("SF: program %s %zu bytes @ %#x\n",
@@ -140,6 +149,10 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset,
{
u8 cmd[5];
+ /* Handle memory-mapped SPI */
+ if (flash->memory_map)
+ memcpy(data, flash->memory_map + offset, len);
+
cmd[0] = CMD_READ_ARRAY_FAST;
spi_flash_addr(offset, cmd);
cmd[4] = 0x00;
@@ -269,6 +282,34 @@ int spi_flash_cmd_write_status(struct spi_flash *flash, u8 sr)
return 0;
}
+#ifdef CONFIG_OF_CONTROL
+int spi_flash_decode_fdt(const void *blob, struct spi_flash *flash)
+{
+ fdt_addr_t addr;
+ fdt_size_t size;
+ int node;
+
+ /* If there is no node, do nothing */
+ node = fdtdec_next_compatible(blob, 0, COMPAT_GENERIC_SPI_FLASH);
+ if (node < 0)
+ return 0;
+
+ addr = fdtdec_get_addr_size(blob, node, "memory-map", &size);
+ if (addr == FDT_ADDR_T_NONE) {
+ debug("%s: Cannot decode address\n", __func__);
+ return 0;
+ }
+
+ if (flash->size != size) {
+ debug("%s: Memory map must cover entire device\n", __func__);
+ return -1;
+ }
+ flash->memory_map = (void *)addr;
+
+ return 0;
+}
+#endif /* CONFIG_OF_CONTROL */
+
/*
* The following table holds all device probe functions
*
@@ -385,9 +426,18 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs,
goto err_manufacturer_probe;
}
+#ifdef CONFIG_OF_CONTROL
+ if (spi_flash_decode_fdt(gd->fdt_blob, flash)) {
+ debug("SF: FDT decode error\n");
+ goto err_manufacturer_probe;
+ }
+#endif
printf("SF: Detected %s with page size ", flash->name);
print_size(flash->sector_size, ", total ");
- print_size(flash->size, "\n");
+ print_size(flash->size, "");
+ if (flash->memory_map)
+ printf(", mapped at %p", flash->memory_map);
+ puts("\n");
spi_release_bus(spi);
@@ -401,6 +451,31 @@ err_claim_bus:
return NULL;
}
+void *spi_flash_do_alloc(int offset, int size, struct spi_slave *spi,
+ const char *name)
+{
+ struct spi_flash *flash;
+ void *ptr;
+
+ ptr = malloc(size);
+ if (!ptr) {
+ debug("SF: Failed to allocate memory\n");
+ return NULL;
+ }
+ memset(ptr, '\0', size);
+ flash = (struct spi_flash *)(ptr + offset);
+
+ /* Set up some basic fields - caller will sort out sizes */
+ flash->spi = spi;
+ flash->name = name;
+
+ flash->read = spi_flash_cmd_read_fast;
+ flash->write = spi_flash_cmd_write_multi;
+ flash->erase = spi_flash_cmd_erase;
+
+ return flash;
+}
+
void spi_flash_free(struct spi_flash *flash)
{
spi_free_slave(flash->spi);
diff --git a/drivers/mtd/spi/sst.c b/drivers/mtd/spi/sst.c
index ced4f2473f..95f5490c35 100644
--- a/drivers/mtd/spi/sst.c
+++ b/drivers/mtd/spi/sst.c
@@ -203,22 +203,16 @@ spi_flash_probe_sst(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- stm = malloc(sizeof(*stm));
+ stm = spi_flash_alloc(struct sst_spi_flash, spi, params->name);
if (!stm) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
stm->params = params;
- stm->flash.spi = spi;
- stm->flash.name = params->name;
if (stm->params->flags & SST_FEAT_WP)
stm->flash.write = sst_write_wp;
- else
- stm->flash.write = spi_flash_cmd_write_multi;
- stm->flash.erase = spi_flash_cmd_erase;
- stm->flash.read = spi_flash_cmd_read_fast;
stm->flash.page_size = 256;
stm->flash.sector_size = 4096;
stm->flash.size = stm->flash.sector_size * params->nr_sectors;
diff --git a/drivers/mtd/spi/stmicro.c b/drivers/mtd/spi/stmicro.c
index 8a193449d0..2a9972bd4e 100644
--- a/drivers/mtd/spi/stmicro.c
+++ b/drivers/mtd/spi/stmicro.c
@@ -176,18 +176,12 @@ struct spi_flash *spi_flash_probe_stmicro(struct spi_slave *spi, u8 * idcode)
return NULL;
}
- flash = malloc(sizeof(*flash));
+ flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
- flash->spi = spi;
- flash->name = params->name;
-
- flash->write = spi_flash_cmd_write_multi;
- flash->erase = spi_flash_cmd_erase;
- flash->read = spi_flash_cmd_read_fast;
flash->page_size = 256;
flash->sector_size = 256 * params->pages_per_sector;
flash->size = flash->sector_size * params->nr_sectors;
diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c
index 4418302169..27162091c5 100644
--- a/drivers/mtd/spi/winbond.c
+++ b/drivers/mtd/spi/winbond.c
@@ -68,6 +68,11 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = {
.name = "W25Q80",
},
{
+ .id = 0x6016,
+ .nr_blocks = 512,
+ .name = "W25Q32DW",
+ },
+ {
.id = 0x6017,
.nr_blocks = 128,
.name = "W25Q64DW",
@@ -92,18 +97,12 @@ struct spi_flash *spi_flash_probe_winbond(struct spi_slave *spi, u8 *idcode)
return NULL;
}
- flash = malloc(sizeof(*flash));
+ flash = spi_flash_alloc_base(spi, params->name);
if (!flash) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
- flash->spi = spi;
- flash->name = params->name;
-
- flash->write = spi_flash_cmd_write_multi;
- flash->erase = spi_flash_cmd_erase;
- flash->read = spi_flash_cmd_read_fast;
flash->page_size = 256;
flash->sector_size = 4096;
flash->size = 4096 * 16 * params->nr_blocks;
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index d144ac29bc..a708162e43 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -478,19 +478,19 @@ static int attach_by_scanning(struct ubi_device *ubi)
err = ubi_eba_init_scan(ubi, si);
if (err)
- goto out_wl;
+ goto out_vtbl;
err = ubi_wl_init_scan(ubi, si);
if (err)
- goto out_vtbl;
+ goto out_eba;
ubi_scan_destroy_si(si);
return 0;
+out_eba:
+ ubi_eba_close(ubi);
out_vtbl:
vfree(ubi->vtbl);
-out_wl:
- ubi_wl_close(ubi);
out_si:
ubi_scan_destroy_si(si);
return err;
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 14c3a5f76c..044e849907 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -59,7 +59,11 @@
#define UBI_NAME_STR "ubi"
/* Normal UBI messages */
+#ifdef CONFIG_UBI_SILENCE_MSG
+#define ubi_msg(fmt, ...)
+#else
#define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__)
+#endif
/* UBI warning messages */
#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \
__func__, ##__VA_ARGS__)
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 88b867a0c1..d1ba722cb5 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -1538,6 +1538,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si)
if (ubi->avail_pebs < WL_RESERVED_PEBS) {
ubi_err("no enough physical eraseblocks (%d, need %d)",
ubi->avail_pebs, WL_RESERVED_PEBS);
+ err = -ENOSPC;
goto out_free;
}
ubi->avail_pebs -= WL_RESERVED_PEBS;