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_TARGET_COLIBRI_VF=y
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_TARGET_VF610TWR=y
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_TARGET_VF610TWR=y
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
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
=========================

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
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
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) {
printf("fsl_elbc_nand: address did not match any "
"chip selects\n");
kfree(priv);
return -ENODEV;
}

View File

@ -36,7 +36,7 @@
#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 0
#endif
#define MXS_NAND_METADATA_SIZE 10
#define MXS_NAND_BITS_PER_ECC_LEVEL 13
#define MXS_NAND_COMMAND_BUFFER_SIZE 32
#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)
{
return ecc_strength * 13;
return ecc_strength * MXS_NAND_BITS_PER_ECC_LEVEL;
}
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,
uint32_t page_oob_size)
{
if (page_data_size == 2048) {
if (page_oob_size == 64)
return 8;
int ecc_strength;
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) {
if (page_oob_size == 128)
return 8;
if (page_oob_size == 218)
return 16;
if (page_oob_size == 224)
return 16;
}
return 0;
return round_down(ecc_strength, 2);
}
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));
vops.datbuf = malloc(verlen);
vops.datbuf = memalign(ARCH_DMA_MINALIGN, verlen);
if (!vops.datbuf)
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;
size_t verofs;
size_t verlen = nand->writesize;
uint8_t *verbuf = malloc(verlen);
uint8_t *verbuf = memalign(ARCH_DMA_MINALIGN, verlen);
if (!verbuf)
return -ENOMEM;

View File

@ -62,6 +62,7 @@
* Briefly these are bitmasks of controller cycles.
*/
#define READ_PAGE_CMD_CODE 0x7EE0
#define READ_ONFI_PARAM_CMD_CODE 0x4860
#define PROGRAM_PAGE_CMD_CODE 0x7FC0
#define ERASE_CMD_CODE 0x4EC0
#define READ_ID_CMD_CODE 0x4804
@ -71,6 +72,7 @@
/* NFC ECC mode define */
#define ECC_BYPASS 0
#define ECC_45_BYTE 6
#define ECC_60_BYTE 7
/*** Register Mask and bit definitions */
@ -145,43 +147,21 @@ struct vf610_nfc {
struct nand_chip chip;
void __iomem *regs;
uint column;
int spareonly;
int page_sz;
int page;
/* Status and ID are in alternate locations. */
int alt_buf;
#define ALT_BUF_ID 1
#define ALT_BUF_STAT 2
#define ALT_BUF_ONFI 3
struct clk *clk;
};
#define mtd_to_nfc(_mtd) \
(struct vf610_nfc *)((struct nand_chip *)_mtd->priv)->priv
static u8 bbt_pattern[] = {'B', 'b', 't', '0' };
static u8 mirror_pattern[] = {'1', 't', 'b', 'B' };
#if defined(CONFIG_SYS_NAND_VF610_NFC_45_ECC_BYTES)
#define ECC_HW_MODE ECC_45_BYTE
static struct nand_bbt_descr bbt_main_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 = 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 = {
static struct nand_ecclayout vf610_nfc_ecc = {
.eccbytes = 45,
.eccpos = {19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
@ -193,6 +173,24 @@ static struct nand_ecclayout vf610_nfc_ecc45 = {
{.offset = 8,
.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)
{
@ -320,8 +318,8 @@ static void vf610_nfc_addr_cycle(struct mtd_info *mtd, int column, int page)
{
if (column != -1) {
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
if (nfc->chip.options | NAND_BUSWIDTH_16)
column = column/2;
if (nfc->chip.options & NAND_BUSWIDTH_16)
column = column / 2;
vf610_nfc_set_field(mtd, NFC_COL_ADDR, COL_ADDR_MASK,
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);
}
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)
{
__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)
{
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->spareonly = 0;
nfc->alt_buf = 0;
nfc->column = max(column, 0);
nfc->alt_buf = 0;
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:
nfc->page = -1;
vf610_nfc_transfer_size(nfc->regs, nfc->page_sz);
page_sz += mtd->writesize + mtd->oobsize;
vf610_nfc_transfer_size(nfc->regs, page_sz);
vf610_nfc_send_commands(nfc->regs, NAND_CMD_SEQIN,
command, PROGRAM_PAGE_CMD_CODE);
vf610_nfc_addr_cycle(mtd, column, page);
vf610_nfc_ecc_mode(mtd, ECC_HW_MODE);
break;
case NAND_CMD_RESET:
vf610_nfc_transfer_size(nfc->regs, 0);
vf610_nfc_send_command(nfc->regs, command, RESET_CMD_CODE);
break;
/*
* NFC does not support sub-page reads and writes,
* so emulate them using full page transfers.
*/
case NAND_CMD_READOOB:
nfc->spareonly = 1;
case NAND_CMD_SEQIN: /* Pre-read for partial writes. */
case NAND_CMD_READ0:
column = 0;
/* Already read? */
if (nfc->page == page)
return;
nfc->page = page;
vf610_nfc_transfer_size(nfc->regs, nfc->page_sz);
page_sz += mtd->oobsize;
column = mtd->writesize;
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_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;
case NAND_CMD_ERASE1:
nfc->page = -1;
vf610_nfc_transfer_size(nfc->regs, 0);
vf610_nfc_send_commands(nfc->regs, command,
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:
nfc->alt_buf = ALT_BUF_ID;
nfc->column = 0;
vf610_nfc_transfer_size(nfc->regs, 0);
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;
case NAND_CMD_STATUS:
@ -404,46 +431,19 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command,
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 */
static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
uint c = nfc->column;
uint l;
/* Handle main area */
if (!nfc->spareonly) {
l = min((uint)len, mtd->writesize - c);
nfc->column += l;
/* Alternate buffers are only supported through read_byte */
if (nfc->alt_buf)
return;
if (!nfc->alt_buf)
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);
vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len);
buf += l;
len -= l;
}
/* Handle spare area access */
if (len) {
nfc->column += len;
vf610_nfc_read_spare(mtd, buf, len);
}
nfc->column += len;
}
/* 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 */
static u8 vf610_nfc_read_byte(struct mtd_info *mtd)
{
struct vf610_nfc *nfc = mtd_to_nfc(mtd);
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;
}
@ -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);
/* ECC failed. */
if (flip > ecc_count) {
nfc->page = -1;
if (flip > ecc_count && flip > (nfc->chip.ecc.strength / 2))
return -1;
}
/* Erased page. */
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;
chip->priv = nfc;
if (cfg.width == 16) {
if (cfg.width == 16)
chip->options |= NAND_BUSWIDTH_16;
vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT);
} else {
chip->options &= ~NAND_BUSWIDTH_16;
vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT);
}
/* Use 8-bit mode during initialization */
vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT);
/* Disable subpage writes as we do not provide ecc->hwctl */
chip->options |= NAND_NO_SUBPAGE_WRITE;
@ -634,18 +651,8 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr)
/* Bad block options. */
if (cfg.flash_bbt)
chip->bbt_options = NAND_BBT_USE_FLASH | 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;
chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB |
NAND_BBT_CREATE;
/* Set configuration register. */
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;
}
if (cfg.width == 16)
vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_16BIT);
chip->ecc.mode = NAND_ECC_SOFT; /* default */
nfc->page_sz = mtd->writesize + mtd->oobsize;
/* 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");
err = -ENXIO;
goto error;
}
nfc->page_sz += cfg.width == 16 ? 1 : 0;
if (cfg.hardware_ecc) {
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;
}
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 */
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.mode = NAND_ECC_HW;
chip->ecc.bytes = 45;
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;
/* set ECC mode to 45 bytes OOB with 24 bits correction */
vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG,
CONFIG_ECC_MODE_MASK,
CONFIG_ECC_MODE_SHIFT, ECC_45_BYTE);
chip->ecc.bytes = 45;
#elif defined(CONFIG_SYS_NAND_VF610_NFC_60_ECC_BYTES)
chip->ecc.strength = 32;
chip->ecc.bytes = 60;
#endif
/* Enable ECC_STATUS */
vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT);

View File

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

View File

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

View File

@ -14,6 +14,10 @@
#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.
*
@ -48,6 +52,7 @@ static uint32_t sd_sector = 2048;
#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512
#define MXS_NAND_METADATA_SIZE 10
#define MXS_NAND_BITS_PER_ECC_LEVEL 13
#define MXS_NAND_COMMAND_BUFFER_SIZE 32
struct mx28_nand_fcb {
@ -125,29 +130,34 @@ struct mx28_sd_config_block {
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)
{
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,
uint32_t page_oob_size)
{
if (page_data_size == 2048)
return 8;
int ecc_strength;
if (page_data_size == 4096) {
if (page_oob_size == 128)
return 8;
/*
* 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 *
mx28_nand_ecc_chunk_cnt(page_data_size));
if (page_oob_size == 218)
return 16;
if (page_oob_size == 224)
return 16;
}
return 0;
return round_down(ecc_strength, 2);
}
static inline uint32_t mx28_nand_get_mark_offset(uint32_t page_data_size,