mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-03-28 18:11:33 +00:00
Merge git://git.denx.de/u-boot-nand-flash
This commit is contained in:
commit
980267a144
12 changed files with 218 additions and 172 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
=========================
|
=========================
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,8 +318,8 @@ 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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Add table
Reference in a new issue