Merge git://git.denx.de/u-boot-nand-flash

This commit is contained in:
Tom Rini 2015-05-24 21:01:30 -04:00
commit 980267a144
12 changed files with 218 additions and 172 deletions

View file

@ -1,3 +1,5 @@
CONFIG_ARM=y CONFIG_ARM=y
CONFIG_TARGET_COLIBRI_VF=y CONFIG_TARGET_COLIBRI_VF=y
CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/toradex/colibri_vf/imximage.cfg,ENV_IS_IN_NAND,IMX_NAND" CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/toradex/colibri_vf/imximage.cfg,ENV_IS_IN_NAND,IMX_NAND"
CONFIG_NAND_VF610_NFC=y
CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES=y

View file

@ -1,3 +1,5 @@
CONFIG_ARM=y CONFIG_ARM=y
CONFIG_TARGET_VF610TWR=y CONFIG_TARGET_VF610TWR=y
CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/vf610twr/imximage.cfg,ENV_IS_IN_MMC" CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/vf610twr/imximage.cfg,ENV_IS_IN_MMC"
CONFIG_NAND_VF610_NFC=y
CONFIG_SYS_NAND_BUSWIDTH_16BIT=y

View file

@ -1,3 +1,5 @@
CONFIG_ARM=y CONFIG_ARM=y
CONFIG_TARGET_VF610TWR=y CONFIG_TARGET_VF610TWR=y
CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/vf610twr/imximage.cfg,ENV_IS_IN_NAND" CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/vf610twr/imximage.cfg,ENV_IS_IN_NAND"
CONFIG_NAND_VF610_NFC=y
CONFIG_SYS_NAND_BUSWIDTH_16BIT=y

View file

@ -188,24 +188,6 @@ Configuration Options:
This is used by SoC platforms which do not have built-in ELM This is used by SoC platforms which do not have built-in ELM
hardware engine required for BCH ECC correction. hardware engine required for BCH ECC correction.
CONFIG_SYS_NAND_BUSWIDTH_16BIT
Indicates that NAND device has 16-bit wide data-bus. In absence of this
config, bus-width of NAND device is assumed to be either 8-bit and later
determined by reading ONFI params.
Above config is useful when NAND device's bus-width information cannot
be determined from on-chip ONFI params, like in following scenarios:
- SPL boot does not support reading of ONFI parameters. This is done to
keep SPL code foot-print small.
- In current U-Boot flow using nand_init(), driver initialization
happens in board_nand_init() which is called before any device probe
(nand_scan_ident + nand_scan_tail), thus device's ONFI parameters are
not available while configuring controller. So a static CONFIG_NAND_xx
is needed to know the device's bus-width in advance.
Some drivers using above config are:
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/ndfc.c
drivers/mtd/nand/omap_gpmc.c
Platform specific options Platform specific options
========================= =========================

View file

@ -32,6 +32,51 @@ config NAND_DENALI_SPARE_AREA_SKIP_BYTES
of OOB area before last ECC sector data starts. This is potentially of OOB area before last ECC sector data starts. This is potentially
used to preserve the bad block marker in the OOB area. used to preserve the bad block marker in the OOB area.
config NAND_VF610_NFC
bool "Support for Freescale NFC for VF610/MPC5125"
select SYS_NAND_SELF_INIT
help
Enables support for NAND Flash Controller on some Freescale
processors like the VF610, MPC5125, MCF54418 or Kinetis K70.
The driver supports a maximum 2k page size. The driver
currently does not support hardware ECC.
choice
prompt "Hardware ECC strength"
depends on NAND_VF610_NFC
default SYS_NAND_VF610_NFC_45_ECC_BYTES
help
Select the ECC strength used in the hardware BCH ECC block.
config SYS_NAND_VF610_NFC_45_ECC_BYTES
bool "24-error correction (45 ECC bytes)"
config SYS_NAND_VF610_NFC_60_ECC_BYTES
bool "32-error correction (60 ECC bytes)"
endchoice
comment "Generic NAND options"
# Enhance depends when converting drivers to Kconfig which use this config
# option (mxc_nand, ndfc, omap_gpmc).
config SYS_NAND_BUSWIDTH_16BIT
bool "Use 16-bit NAND interface"
depends on NAND_VF610_NFC
help
Indicates that NAND device has 16-bit wide data-bus. In absence of this
config, bus-width of NAND device is assumed to be either 8-bit and later
determined by reading ONFI params.
Above config is useful when NAND device's bus-width information cannot
be determined from on-chip ONFI params, like in following scenarios:
- SPL boot does not support reading of ONFI parameters. This is done to
keep SPL code foot-print small.
- In current U-Boot flow using nand_init(), driver initialization
happens in board_nand_init() which is called before any device probe
(nand_scan_ident + nand_scan_tail), thus device's ONFI parameters are
not available while configuring controller. So a static CONFIG_NAND_xx
is needed to know the device's bus-width in advance.
if SPL if SPL
config SPL_NAND_DENALI config SPL_NAND_DENALI

View file

@ -679,6 +679,7 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr)
if (priv->bank >= MAX_BANKS) { if (priv->bank >= MAX_BANKS) {
printf("fsl_elbc_nand: address did not match any " printf("fsl_elbc_nand: address did not match any "
"chip selects\n"); "chip selects\n");
kfree(priv);
return -ENODEV; return -ENODEV;
} }

View file

@ -36,7 +36,7 @@
#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 0 #define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 0
#endif #endif
#define MXS_NAND_METADATA_SIZE 10 #define MXS_NAND_METADATA_SIZE 10
#define MXS_NAND_BITS_PER_ECC_LEVEL 13
#define MXS_NAND_COMMAND_BUFFER_SIZE 32 #define MXS_NAND_COMMAND_BUFFER_SIZE 32
#define MXS_NAND_BCH_TIMEOUT 10000 #define MXS_NAND_BCH_TIMEOUT 10000
@ -135,7 +135,7 @@ static uint32_t mxs_nand_ecc_chunk_cnt(uint32_t page_data_size)
static uint32_t mxs_nand_ecc_size_in_bits(uint32_t ecc_strength) static uint32_t mxs_nand_ecc_size_in_bits(uint32_t ecc_strength)
{ {
return ecc_strength * 13; return ecc_strength * MXS_NAND_BITS_PER_ECC_LEVEL;
} }
static uint32_t mxs_nand_aux_status_offset(void) static uint32_t mxs_nand_aux_status_offset(void)
@ -146,26 +146,21 @@ static uint32_t mxs_nand_aux_status_offset(void)
static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size, static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size,
uint32_t page_oob_size) uint32_t page_oob_size)
{ {
if (page_data_size == 2048) { int ecc_strength;
if (page_oob_size == 64)
return 8;
if (page_oob_size == 112) /*
return 14; * Determine the ECC layout with the formula:
} * ECC bits per chunk = (total page spare data bits) /
* (bits per ECC level) / (chunks per page)
* where:
* total page spare data bits =
* (page oob size - meta data size) * (bits per byte)
*/
ecc_strength = ((page_oob_size - MXS_NAND_METADATA_SIZE) * 8)
/ (MXS_NAND_BITS_PER_ECC_LEVEL *
mxs_nand_ecc_chunk_cnt(page_data_size));
if (page_data_size == 4096) { return round_down(ecc_strength, 2);
if (page_oob_size == 128)
return 8;
if (page_oob_size == 218)
return 16;
if (page_oob_size == 224)
return 16;
}
return 0;
} }
static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size,

View file

@ -483,7 +483,7 @@ int nand_verify_page_oob(nand_info_t *nand, struct mtd_oob_ops *ops, loff_t ofs)
memcpy(&vops, ops, sizeof(vops)); memcpy(&vops, ops, sizeof(vops));
vops.datbuf = malloc(verlen); vops.datbuf = memalign(ARCH_DMA_MINALIGN, verlen);
if (!vops.datbuf) if (!vops.datbuf)
return -ENOMEM; return -ENOMEM;
@ -520,7 +520,7 @@ int nand_verify(nand_info_t *nand, loff_t ofs, size_t len, u_char *buf)
int rval = 0; int rval = 0;
size_t verofs; size_t verofs;
size_t verlen = nand->writesize; size_t verlen = nand->writesize;
uint8_t *verbuf = malloc(verlen); uint8_t *verbuf = memalign(ARCH_DMA_MINALIGN, verlen);
if (!verbuf) if (!verbuf)
return -ENOMEM; return -ENOMEM;

View file

@ -62,6 +62,7 @@
* Briefly these are bitmasks of controller cycles. * Briefly these are bitmasks of controller cycles.
*/ */
#define READ_PAGE_CMD_CODE 0x7EE0 #define READ_PAGE_CMD_CODE 0x7EE0
#define READ_ONFI_PARAM_CMD_CODE 0x4860
#define PROGRAM_PAGE_CMD_CODE 0x7FC0 #define PROGRAM_PAGE_CMD_CODE 0x7FC0
#define ERASE_CMD_CODE 0x4EC0 #define ERASE_CMD_CODE 0x4EC0
#define READ_ID_CMD_CODE 0x4804 #define READ_ID_CMD_CODE 0x4804
@ -71,6 +72,7 @@
/* NFC ECC mode define */ /* NFC ECC mode define */
#define ECC_BYPASS 0 #define ECC_BYPASS 0
#define ECC_45_BYTE 6 #define ECC_45_BYTE 6
#define ECC_60_BYTE 7
/*** Register Mask and bit definitions */ /*** Register Mask and bit definitions */
@ -145,43 +147,21 @@ struct vf610_nfc {
struct nand_chip chip; struct nand_chip chip;
void __iomem *regs; void __iomem *regs;
uint column; uint column;
int spareonly;
int page_sz;
int page;
/* Status and ID are in alternate locations. */ /* Status and ID are in alternate locations. */
int alt_buf; int alt_buf;
#define ALT_BUF_ID 1 #define ALT_BUF_ID 1
#define ALT_BUF_STAT 2 #define ALT_BUF_STAT 2
#define ALT_BUF_ONFI 3
struct clk *clk; struct clk *clk;
}; };
#define mtd_to_nfc(_mtd) \ #define mtd_to_nfc(_mtd) \
(struct vf610_nfc *)((struct nand_chip *)_mtd->priv)->priv (struct vf610_nfc *)((struct nand_chip *)_mtd->priv)->priv
static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; #if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES)
static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; #define ECC_HW_MODE ECC_45_BYTE
static struct nand_bbt_descr bbt_main_descr = { static struct nand_ecclayout vf610_nfc_ecc = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE |
NAND_BBT_2BIT | NAND_BBT_VERSION,
.offs = 11,
.len = 4,
.veroffs = 15,
.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,
.offs = 11,
.len = 4,
.veroffs = 15,
.maxblocks = 4,
.pattern = mirror_pattern,
};
static struct nand_ecclayout vf610_nfc_ecc45 = {
.eccbytes = 45, .eccbytes = 45,
.eccpos = {19, 20, 21, 22, 23, .eccpos = {19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31, 24, 25, 26, 27, 28, 29, 30, 31,
@ -193,6 +173,24 @@ static struct nand_ecclayout vf610_nfc_ecc45 = {
{.offset = 8, {.offset = 8,
.length = 11} } .length = 11} }
}; };
#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES)
#define ECC_HW_MODE ECC_60_BYTE
static struct nand_ecclayout vf610_nfc_ecc = {
.eccbytes = 60,
.eccpos = { 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, 62, 63 },
.oobfree = {
{.offset = 2,
.length = 2} }
};
#endif
static inline u32 vf610_nfc_read(struct mtd_info *mtd, uint reg) static inline u32 vf610_nfc_read(struct mtd_info *mtd, uint reg)
{ {
@ -320,7 +318,7 @@ static void vf610_nfc_addr_cycle(struct mtd_info *mtd, int column, int page)
{ {
if (column != -1) { if (column != -1) {
struct vf610_nfc *nfc = mtd_to_nfc(mtd); struct vf610_nfc *nfc = mtd_to_nfc(mtd);
if (nfc->chip.options | NAND_BUSWIDTH_16) if (nfc->chip.options & NAND_BUSWIDTH_16)
column = column / 2; column = column / 2;
vf610_nfc_set_field(mtd, NFC_COL_ADDR, COL_ADDR_MASK, vf610_nfc_set_field(mtd, NFC_COL_ADDR, COL_ADDR_MASK,
COL_ADDR_SHIFT, column); COL_ADDR_SHIFT, column);
@ -330,6 +328,13 @@ static void vf610_nfc_addr_cycle(struct mtd_info *mtd, int column, int page)
ROW_ADDR_SHIFT, page); ROW_ADDR_SHIFT, page);
} }
static inline void vf610_nfc_ecc_mode(struct mtd_info *mtd, int ecc_mode)
{
vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG,
CONFIG_ECC_MODE_MASK,
CONFIG_ECC_MODE_SHIFT, ecc_mode);
}
static inline void vf610_nfc_transfer_size(void __iomem *regbase, int size) static inline void vf610_nfc_transfer_size(void __iomem *regbase, int size)
{ {
__raw_writel(size, regbase + NFC_SECTOR_SIZE); __raw_writel(size, regbase + NFC_SECTOR_SIZE);
@ -340,45 +345,64 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command,
int column, int page) int column, int page)
{ {
struct vf610_nfc *nfc = mtd_to_nfc(mtd); struct vf610_nfc *nfc = mtd_to_nfc(mtd);
int page_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0;
nfc->column = max(column, 0); nfc->column = max(column, 0);
nfc->spareonly = 0;
nfc->alt_buf = 0; nfc->alt_buf = 0;
switch (command) { switch (command) {
case NAND_CMD_SEQIN:
/* Use valid column/page from preread... */
vf610_nfc_addr_cycle(mtd, column, page);
/*
* SEQIN => data => PAGEPROG sequence is done by the controller
* hence we do not need to issue the command here...
*/
return;
case NAND_CMD_PAGEPROG: case NAND_CMD_PAGEPROG:
nfc->page = -1; page_sz += mtd->writesize + mtd->oobsize;
vf610_nfc_transfer_size(nfc->regs, nfc->page_sz); vf610_nfc_transfer_size(nfc->regs, page_sz);
vf610_nfc_send_commands(nfc->regs, NAND_CMD_SEQIN, vf610_nfc_send_commands(nfc->regs, NAND_CMD_SEQIN,
command, PROGRAM_PAGE_CMD_CODE); command, PROGRAM_PAGE_CMD_CODE);
vf610_nfc_addr_cycle(mtd, column, page); vf610_nfc_ecc_mode(mtd, ECC_HW_MODE);
break; break;
case NAND_CMD_RESET: case NAND_CMD_RESET:
vf610_nfc_transfer_size(nfc->regs, 0); vf610_nfc_transfer_size(nfc->regs, 0);
vf610_nfc_send_command(nfc->regs, command, RESET_CMD_CODE); vf610_nfc_send_command(nfc->regs, command, RESET_CMD_CODE);
break; break;
/*
* NFC does not support sub-page reads and writes,
* so emulate them using full page transfers.
*/
case NAND_CMD_READOOB: case NAND_CMD_READOOB:
nfc->spareonly = 1; page_sz += mtd->oobsize;
case NAND_CMD_SEQIN: /* Pre-read for partial writes. */ column = mtd->writesize;
case NAND_CMD_READ0: vf610_nfc_transfer_size(nfc->regs, page_sz);
column = 0;
/* Already read? */
if (nfc->page == page)
return;
nfc->page = page;
vf610_nfc_transfer_size(nfc->regs, nfc->page_sz);
vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0, vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0,
NAND_CMD_READSTART, READ_PAGE_CMD_CODE); NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
vf610_nfc_addr_cycle(mtd, column, page); vf610_nfc_addr_cycle(mtd, column, page);
vf610_nfc_ecc_mode(mtd, ECC_BYPASS);
break;
case NAND_CMD_READ0:
page_sz += mtd->writesize + mtd->oobsize;
column = 0;
vf610_nfc_transfer_size(nfc->regs, page_sz);
vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0,
NAND_CMD_READSTART, READ_PAGE_CMD_CODE);
vf610_nfc_addr_cycle(mtd, column, page);
vf610_nfc_ecc_mode(mtd, ECC_HW_MODE);
break;
case NAND_CMD_PARAM:
nfc->alt_buf = ALT_BUF_ONFI;
vf610_nfc_transfer_size(nfc->regs, 768);
vf610_nfc_send_command(nfc->regs, NAND_CMD_PARAM,
READ_ONFI_PARAM_CMD_CODE);
vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK,
ROW_ADDR_SHIFT, column);
vf610_nfc_ecc_mode(mtd, ECC_BYPASS);
break; break;
case NAND_CMD_ERASE1: case NAND_CMD_ERASE1:
nfc->page = -1;
vf610_nfc_transfer_size(nfc->regs, 0); vf610_nfc_transfer_size(nfc->regs, 0);
vf610_nfc_send_commands(nfc->regs, command, vf610_nfc_send_commands(nfc->regs, command,
NAND_CMD_ERASE2, ERASE_CMD_CODE); NAND_CMD_ERASE2, ERASE_CMD_CODE);
@ -387,8 +411,11 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command,
case NAND_CMD_READID: case NAND_CMD_READID:
nfc->alt_buf = ALT_BUF_ID; nfc->alt_buf = ALT_BUF_ID;
nfc->column = 0;
vf610_nfc_transfer_size(nfc->regs, 0); vf610_nfc_transfer_size(nfc->regs, 0);
vf610_nfc_send_command(nfc->regs, command, READ_ID_CMD_CODE); vf610_nfc_send_command(nfc->regs, command, READ_ID_CMD_CODE);
vf610_nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_MASK,
ROW_ADDR_SHIFT, column);
break; break;
case NAND_CMD_STATUS: case NAND_CMD_STATUS:
@ -404,46 +431,19 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command,
vf610_nfc_done(mtd); vf610_nfc_done(mtd);
} }
static inline void vf610_nfc_read_spare(struct mtd_info *mtd, void *buf,
int len)
{
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
len = min(mtd->oobsize, (uint)len);
if (len > 0)
vf610_nfc_memcpy(buf, nfc->regs + mtd->writesize, len);
}
/* Read data from NFC buffers */ /* Read data from NFC buffers */
static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{ {
struct vf610_nfc *nfc = mtd_to_nfc(mtd); struct vf610_nfc *nfc = mtd_to_nfc(mtd);
uint c = nfc->column; uint c = nfc->column;
uint l;
/* Handle main area */ /* Alternate buffers are only supported through read_byte */
if (!nfc->spareonly) { if (nfc->alt_buf)
l = min((uint)len, mtd->writesize - c); return;
nfc->column += l;
if (!nfc->alt_buf) vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len);
vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c,
l);
else
if (nfc->alt_buf & ALT_BUF_ID)
*buf = vf610_nfc_get_id(mtd, c);
else
*buf = vf610_nfc_get_status(mtd);
buf += l;
len -= l;
}
/* Handle spare area access */
if (len) {
nfc->column += len; nfc->column += len;
vf610_nfc_read_spare(mtd, buf, len);
}
} }
/* Write data to NFC buffers */ /* Write data to NFC buffers */
@ -462,8 +462,29 @@ static void vf610_nfc_write_buf(struct mtd_info *mtd, const u_char *buf,
/* Read byte from NFC buffers */ /* Read byte from NFC buffers */
static u8 vf610_nfc_read_byte(struct mtd_info *mtd) static u8 vf610_nfc_read_byte(struct mtd_info *mtd)
{ {
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
u8 tmp; u8 tmp;
vf610_nfc_read_buf(mtd, &tmp, sizeof(tmp)); uint c = nfc->column;
switch (nfc->alt_buf) {
case ALT_BUF_ID:
tmp = vf610_nfc_get_id(mtd, c);
break;
case ALT_BUF_STAT:
tmp = vf610_nfc_get_status(mtd);
break;
case ALT_BUF_ONFI:
#ifdef __LITTLE_ENDIAN
/* Reverse byte since the controller uses big endianness */
c = nfc->column ^ 0x3;
tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c));
break;
#endif
default:
tmp = *((u8 *)(nfc->regs + NFC_MAIN_AREA(0) + c));
break;
}
nfc->column++;
return tmp; return tmp;
} }
@ -532,10 +553,8 @@ static inline int vf610_nfc_correct_data(struct mtd_info *mtd, u_char *dat)
flip = count_written_bits(dat, nfc->chip.ecc.size, ecc_count); flip = count_written_bits(dat, nfc->chip.ecc.size, ecc_count);
/* ECC failed. */ /* ECC failed. */
if (flip > ecc_count) { if (flip > ecc_count && flip > (nfc->chip.ecc.strength / 2))
nfc->page = -1;
return -1; return -1;
}
/* Erased page. */ /* Erased page. */
memset(dat, 0xff, nfc->chip.ecc.size); memset(dat, 0xff, nfc->chip.ecc.size);
@ -613,13 +632,11 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr)
mtd->priv = chip; mtd->priv = chip;
chip->priv = nfc; chip->priv = nfc;
if (cfg.width == 16) { if (cfg.width == 16)
chip->options |= NAND_BUSWIDTH_16; chip->options |= NAND_BUSWIDTH_16;
vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT);
} else { /* Use 8-bit mode during initialization */
chip->options &= ~NAND_BUSWIDTH_16;
vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT); vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT);
}
/* Disable subpage writes as we do not provide ecc->hwctl */ /* Disable subpage writes as we do not provide ecc->hwctl */
chip->options |= NAND_NO_SUBPAGE_WRITE; chip->options |= NAND_NO_SUBPAGE_WRITE;
@ -634,18 +651,8 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr)
/* Bad block options. */ /* Bad block options. */
if (cfg.flash_bbt) if (cfg.flash_bbt)
chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_CREATE; chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB |
NAND_BBT_CREATE;
/* Default to software ECC until flash ID. */
vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG,
CONFIG_ECC_MODE_MASK,
CONFIG_ECC_MODE_SHIFT, ECC_BYPASS);
chip->bbt_td = &bbt_main_descr;
chip->bbt_md = &bbt_mirror_descr;
nfc->page_sz = PAGE_2K + OOB_64;
nfc->page_sz += cfg.width == 16 ? 1 : 0;
/* Set configuration register. */ /* Set configuration register. */
vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT); vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT);
@ -672,17 +679,17 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr)
goto error; goto error;
} }
if (cfg.width == 16)
vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT);
chip->ecc.mode = NAND_ECC_SOFT; /* default */ chip->ecc.mode = NAND_ECC_SOFT; /* default */
nfc->page_sz = mtd->writesize + mtd->oobsize;
/* Single buffer only, max 256 OOB minus ECC status */ /* Single buffer only, max 256 OOB minus ECC status */
if (nfc->page_sz > PAGE_2K + 256 - 8) { if (mtd->writesize + mtd->oobsize > PAGE_2K + 256 - 8) {
dev_err(nfc->dev, "Unsupported flash size\n"); dev_err(nfc->dev, "Unsupported flash size\n");
err = -ENXIO; err = -ENXIO;
goto error; goto error;
} }
nfc->page_sz += cfg.width == 16 ? 1 : 0;
if (cfg.hardware_ecc) { if (cfg.hardware_ecc) {
if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) {
@ -691,7 +698,9 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr)
goto error; goto error;
} }
chip->ecc.layout = &vf610_nfc_ecc45; /* Current HW ECC layouts only use 64 bytes of OOB */
if (mtd->oobsize > 64)
mtd->oobsize = 64;
/* propagate ecc.layout to mtd_info */ /* propagate ecc.layout to mtd_info */
mtd->ecclayout = chip->ecc.layout; mtd->ecclayout = chip->ecc.layout;
@ -699,14 +708,15 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr)
chip->ecc.write_page = vf610_nfc_write_page; chip->ecc.write_page = vf610_nfc_write_page;
chip->ecc.mode = NAND_ECC_HW; chip->ecc.mode = NAND_ECC_HW;
chip->ecc.bytes = 45;
chip->ecc.size = PAGE_2K; chip->ecc.size = PAGE_2K;
chip->ecc.layout = &vf610_nfc_ecc;
#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES)
chip->ecc.strength = 24; chip->ecc.strength = 24;
chip->ecc.bytes = 45;
/* set ECC mode to 45 bytes OOB with 24 bits correction */ #elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES)
vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, chip->ecc.strength = 32;
CONFIG_ECC_MODE_MASK, chip->ecc.bytes = 60;
CONFIG_ECC_MODE_SHIFT, ECC_45_BYTE); #endif
/* Enable ECC_STATUS */ /* Enable ECC_STATUS */
vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT); vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT);

View file

@ -50,8 +50,7 @@
/* NAND support */ /* NAND support */
#define CONFIG_CMD_NAND #define CONFIG_CMD_NAND
#define CONFIG_NAND_VF610_NFC #define CONFIG_SYS_NAND_ONFI_DETECTION
#define CONFIG_SYS_NAND_SELF_INIT
#define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE NFC_BASE_ADDR #define CONFIG_SYS_NAND_BASE NFC_BASE_ADDR

View file

@ -48,12 +48,10 @@
/* NAND support */ /* NAND support */
#define CONFIG_CMD_NAND #define CONFIG_CMD_NAND
#define CONFIG_CMD_NAND_TRIMFFS #define CONFIG_CMD_NAND_TRIMFFS
#define CONFIG_SYS_NAND_ONFI_DETECTION
#ifdef CONFIG_CMD_NAND #ifdef CONFIG_CMD_NAND
#define CONFIG_NAND_VF610_NFC
#define CONFIG_SYS_NAND_SELF_INIT
#define CONFIG_USE_ARCH_MEMCPY #define CONFIG_USE_ARCH_MEMCPY
#define CONFIG_SYS_NAND_BUSWIDTH_16BIT
#define CONFIG_SYS_MAX_NAND_DEVICE 1 #define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE NFC_BASE_ADDR #define CONFIG_SYS_NAND_BASE NFC_BASE_ADDR

View file

@ -14,6 +14,10 @@
#include "compiler.h" #include "compiler.h"
/* Taken from <linux/kernel.h> */
#define __round_mask(x, y) ((__typeof__(x))((y)-1))
#define round_down(x, y) ((x) & ~__round_mask(x, y))
/* /*
* Default BCB layout. * Default BCB layout.
* *
@ -48,6 +52,7 @@ static uint32_t sd_sector = 2048;
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 #define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512 #define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512
#define MXS_NAND_METADATA_SIZE 10 #define MXS_NAND_METADATA_SIZE 10
#define MXS_NAND_BITS_PER_ECC_LEVEL 13
#define MXS_NAND_COMMAND_BUFFER_SIZE 32 #define MXS_NAND_COMMAND_BUFFER_SIZE 32
struct mx28_nand_fcb { struct mx28_nand_fcb {
@ -125,29 +130,34 @@ struct mx28_sd_config_block {
struct mx28_sd_drive_info drv_info[1]; struct mx28_sd_drive_info drv_info[1];
}; };
static inline uint32_t mx28_nand_ecc_chunk_cnt(uint32_t page_data_size)
{
return page_data_size / MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
}
static inline uint32_t mx28_nand_ecc_size_in_bits(uint32_t ecc_strength) static inline uint32_t mx28_nand_ecc_size_in_bits(uint32_t ecc_strength)
{ {
return ecc_strength * 13; return ecc_strength * MXS_NAND_BITS_PER_ECC_LEVEL;
} }
static inline uint32_t mx28_nand_get_ecc_strength(uint32_t page_data_size, static inline uint32_t mx28_nand_get_ecc_strength(uint32_t page_data_size,
uint32_t page_oob_size) uint32_t page_oob_size)
{ {
if (page_data_size == 2048) int ecc_strength;
return 8;
if (page_data_size == 4096) { /*
if (page_oob_size == 128) * Determine the ECC layout with the formula:
return 8; * ECC bits per chunk = (total page spare data bits) /
* (bits per ECC level) / (chunks per page)
* where:
* total page spare data bits =
* (page oob size - meta data size) * (bits per byte)
*/
ecc_strength = ((page_oob_size - MXS_NAND_METADATA_SIZE) * 8)
/ (MXS_NAND_BITS_PER_ECC_LEVEL *
mx28_nand_ecc_chunk_cnt(page_data_size));
if (page_oob_size == 218) return round_down(ecc_strength, 2);
return 16;
if (page_oob_size == 224)
return 16;
}
return 0;
} }
static inline uint32_t mx28_nand_get_mark_offset(uint32_t page_data_size, static inline uint32_t mx28_nand_get_mark_offset(uint32_t page_data_size,