summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2016-04-08 13:37:28 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2016-04-08 13:37:28 +0200
commit660fc0ad87a71dc51980e1ddb511d12382e052d0 (patch)
treed054a53ef8e1e4ce02f21a559c51957d8bd43d49
parent0316be12a51b3301dd78ef661e5d72e04b7de5cc (diff)
parent2ec908609f367bccfc9472647d4d34ed406a7104 (diff)
downloadbarebox-660fc0ad87a71dc51980e1ddb511d12382e052d0.tar.gz
barebox-660fc0ad87a71dc51980e1ddb511d12382e052d0.tar.xz
Merge branch 'for-next/mtd-imx-nand'
-rw-r--r--drivers/mtd/nand/nand_imx.c241
-rw-r--r--include/linux/mtd/mtd.h19
2 files changed, 139 insertions, 121 deletions
diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c
index 6ea82a110d..f54fe21f7d 100644
--- a/drivers/mtd/nand/nand_imx.c
+++ b/drivers/mtd/nand/nand_imx.c
@@ -100,6 +100,7 @@ struct imx_nand_host {
unsigned int buf_start;
int spare_len;
int eccsize;
+ int eccstatus_v1;
int hw_ecc;
int data_width;
@@ -113,6 +114,8 @@ struct imx_nand_host {
void (*send_read_param)(struct imx_nand_host *);
uint16_t (*get_dev_status)(struct imx_nand_host *);
int (*check_int)(struct imx_nand_host *);
+ int (*correct)(struct mtd_info *mtd);
+ void (*enable_hwecc)(struct nand_chip *, bool enable);
};
/*
@@ -260,8 +263,6 @@ static void send_cmd_v3(struct imx_nand_host *host, uint16_t cmd)
static void send_cmd_v1_v2(struct imx_nand_host *host, u16 cmd)
{
- MTD_DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x)\n", cmd);
-
writew(cmd, host->regs + NFC_V1_V2_FLASH_CMD);
writew(NFC_CMD, host->regs + NFC_V1_V2_CONFIG2);
@@ -300,8 +301,6 @@ static void send_addr_v3(struct imx_nand_host *host, uint16_t addr)
static void send_addr_v1_v2(struct imx_nand_host *host, u16 addr)
{
- MTD_DEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x %d)\n", addr, islast);
-
writew(addr, host->regs + NFC_V1_V2_FLASH_ADDR);
writew(NFC_ADDR, host->regs + NFC_V1_V2_CONFIG2);
@@ -335,12 +334,17 @@ static void send_page_v1_v2(struct imx_nand_host *host,
{
int bufs, i;
+ host->eccstatus_v1 = 0;
+
if (nfc_is_v1() && host->pagesize_2k)
bufs = 4;
else
bufs = 1;
for (i = 0; i < bufs; i++) {
+ u16 status;
+ int errors;
+
/* NANDFC buffer 0 is used for page read/write */
writew(i, host->regs + NFC_V1_V2_BUF_ADDR);
@@ -348,6 +352,14 @@ static void send_page_v1_v2(struct imx_nand_host *host,
/* Wait for operation to complete */
wait_op_done(host);
+
+ status = readw(host->regs + NFC_V1_ECC_STATUS_RESULT);
+ errors = max(status & 0x3, status >> 2);
+
+ if (errors == 1 && host->eccstatus_v1 >= 0)
+ host->eccstatus_v1++;
+ if (errors == 2)
+ host->eccstatus_v1 = -EBADMSG;
}
}
@@ -475,45 +487,66 @@ static int imx_nand_dev_ready(struct mtd_info *mtd)
return 1;
}
-static void imx_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+static void imx_nand_enable_hwecc_v1_v2(struct nand_chip *chip, bool enable)
{
- /*
- * If HW ECC is enabled, we turn it on during init. There is
- * no need to enable again here.
- */
+ struct imx_nand_host *host = chip->priv;
+ uint16_t config1;
+
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return;
+
+ config1 = readw(host->regs + NFC_V1_V2_CONFIG1);
+
+ if (enable)
+ config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
+ else
+ config1 &= ~NFC_V1_V2_CONFIG1_ECC_EN;
+
+ writew(config1, host->regs + NFC_V1_V2_CONFIG1);
+
}
-static int imx_nand_correct_data_v1(struct mtd_info *mtd, u_char * dat,
- u_char * read_ecc, u_char * calc_ecc)
+static void imx_nand_enable_hwecc_v3(struct nand_chip *chip, bool enable)
+{
+ struct imx_nand_host *host = chip->priv;
+ uint32_t config2;
+
+ if (chip->ecc.mode != NAND_ECC_HW)
+ return;
+
+ config2 = readl(NFC_V3_CONFIG2);
+
+ if (enable)
+ config2 |= NFC_V3_CONFIG2_ECC_EN;
+ else
+ config2 &= ~NFC_V3_CONFIG2_ECC_EN;
+
+ writel(config2, NFC_V3_CONFIG2);
+}
+
+static int imx_nand_correct_data_v1(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd->priv;
struct imx_nand_host *host = nand_chip->priv;
- /*
- * 1-Bit errors are automatically corrected in HW. No need for
- * additional correction. 2-Bit errors cannot be corrected by
- * HW ECC, so we need to return failure
- */
- u16 ecc_status = readw(host->regs + NFC_V1_ECC_STATUS_RESULT);
+ if (host->eccstatus_v1 < 0)
+ return host->eccstatus_v1;
- if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0,
- "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n");
- return -1;
- }
+ mtd->ecc_stats.corrected += host->eccstatus_v1;
- return 0;
+ if (host->eccstatus_v1 > 0)
+ return 1;
+ else
+ return 0;
}
-static int imx_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat,
- u_char *read_ecc, u_char *calc_ecc)
+static int imx_nand_correct_data_v2_v3(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd->priv;
struct imx_nand_host *host = nand_chip->priv;
u32 ecc_stat, err;
int no_subpages;
- int ret = 0;
- u8 ecc_bit_mask, err_limit;
+ u8 ecc_bit_mask, err_limit, max_bitflips = 0;
ecc_bit_mask = (host->eccsize == 4) ? 0x7 : 0xf;
err_limit = (host->eccsize == 4) ? 0x4 : 0x8;
@@ -527,19 +560,14 @@ static int imx_nand_correct_data_v2_v3(struct mtd_info *mtd, u_char *dat,
do {
err = ecc_stat & ecc_bit_mask;
- if (err > err_limit) {
- printk(KERN_WARNING "UnCorrectable RS-ECC Error\n");
- return -1;
- } else {
- ret += err;
- }
+ if (err > err_limit)
+ return -EBADMSG;
ecc_stat >>= 4;
+ max_bitflips = max_t(unsigned int, max_bitflips, err);
+ mtd->ecc_stats.corrected += err;
} while (--no_subpages);
- mtd->ecc_stats.corrected += ret;
- pr_debug("%d Symbol Correctable RS-ECC Error\n", ret);
-
- return ret;
+ return max_bitflips;
}
static int imx_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat,
@@ -652,13 +680,13 @@ static void imx_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len)
/*
* Function to transfer data to/from spare area.
*/
-static void copy_spare(struct mtd_info *mtd, int bfrom)
+static void copy_spare(struct mtd_info *mtd, int bfrom, void *buf)
{
struct nand_chip *this = mtd->priv;
struct imx_nand_host *host = this->priv;
u16 i, j;
u16 n = mtd->writesize >> 9;
- u8 *d = host->data_buf + mtd->writesize;
+ u8 *d = buf;
u8 *s = host->spare0;
u16 t = host->spare_len;
@@ -688,26 +716,6 @@ static void copy_spare(struct mtd_info *mtd, int bfrom)
*/
static void imx_nand_select_chip(struct mtd_info *mtd, int chip)
{
-#ifdef CONFIG_MTD_NAND_MXC_FORCE_CE
- u16 tmp;
-
- if (chip > 0) {
- MTD_DEBUG(MTD_DEBUG_LEVEL0,
- "ERROR: Illegal chip select (chip = %d)\n", chip);
- return;
- }
-
- if (chip == -1) {
- tmp = readw(host->regs + NFC_CONFIG1);
- tmp &= ~NFC_CE;
- writew(tmp, host->regs + NFC_CONFIG1);
- return;
- }
-
- tmp = readw(host->regs + NFC_CONFIG1);
- tmp |= NFC_CE;
- writew(tmp, host->regs + NFC_CONFIG1);
-#endif
}
static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr)
@@ -782,9 +790,6 @@ static void preset_v1_v2(struct mtd_info *mtd)
struct imx_nand_host *host = nand_chip->priv;
uint16_t config1 = 0;
- if (nand_chip->ecc.mode == NAND_ECC_HW)
- config1 |= NFC_V1_V2_CONFIG1_ECC_EN;
-
if (nfc_is_v21())
config1 |= NFC_V2_CONFIG1_FP_INT;
@@ -849,9 +854,6 @@ static void preset_v3(struct mtd_info *mtd)
NFC_V3_CONFIG2_ST_CMD(0x70) |
NFC_V3_CONFIG2_NUM_ADDR_PHASE0;
- if (chip->ecc.mode == NAND_ECC_HW)
- config2 |= NFC_V3_CONFIG2_ECC_EN;
-
addr_phases = fls(chip->pagemask) >> 3;
if (mtd->writesize == 2048) {
@@ -893,6 +895,68 @@ static void preset_v3(struct mtd_info *mtd)
writel(0, NFC_V3_DELAY_LINE);
}
+static int imx_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t offset, int data_len, const uint8_t *buf,
+ int oob_required, int page, int cached, int raw)
+{
+ struct imx_nand_host *host = chip->priv;
+ int status;
+
+ host->enable_hwecc(chip, !raw);
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+ memcpy32(host->main_area0, buf, mtd->writesize);
+ if (oob_required)
+ copy_spare(mtd, 0, chip->oob_poi);
+
+ host->send_page(host, NFC_INPUT);
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+
+ if (status & NAND_STATUS_FAIL)
+ return -EIO;
+
+ return 0;
+}
+
+static void imx_nand_do_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required)
+{
+ struct imx_nand_host *host = chip->priv;
+
+ host->send_page(host, NFC_OUTPUT);
+
+ memcpy32(buf, host->main_area0, mtd->writesize);
+
+ if (oob_required)
+ copy_spare(mtd, 1, chip->oob_poi);
+}
+
+static int imx_nand_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+{
+ struct imx_nand_host *host = chip->priv;
+
+ host->enable_hwecc(chip, true);
+
+ imx_nand_do_read_page(mtd, chip, buf, oob_required);
+
+ return host->correct(mtd);
+}
+
+static int imx_nand_read_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+{
+ struct imx_nand_host *host = chip->priv;
+
+ host->enable_hwecc(chip, false);
+
+ imx_nand_do_read_page(mtd, chip, buf, oob_required);
+
+ return 0;
+}
+
/*
* This function is used by the upper layer to write command to NAND Flash for
* different operations to be carried out on NAND Flash
@@ -908,7 +972,7 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command,
struct nand_chip *nand_chip = mtd->priv;
struct imx_nand_host *host = nand_chip->priv;
- MTD_DEBUG(MTD_DEBUG_LEVEL3,
+ dev_dbg(host->dev,
"imx_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
command, column, page_addr);
@@ -948,11 +1012,6 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command,
if (host->pagesize_2k)
/* send read confirm command */
host->send_cmd(host, NAND_CMD_READSTART);
-
- host->send_page(host, NFC_OUTPUT);
-
- memcpy32(host->data_buf, host->main_area0, mtd->writesize);
- copy_spare(mtd, 1);
break;
case NAND_CMD_SEQIN:
@@ -987,9 +1046,6 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command,
break;
case NAND_CMD_PAGEPROG:
- memcpy32(host->main_area0, host->data_buf, mtd->writesize);
- copy_spare(mtd, 0);
- host->send_page(host, NFC_INPUT);
host->send_cmd(host, command);
mxc_do_addr_cycle(mtd, column, page_addr);
break;
@@ -1016,31 +1072,6 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command,
}
}
-#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE
-static void imx_low_erase(struct mtd_info *mtd)
-{
-
- struct nand_chip *this = mtd->priv;
- unsigned int page_addr, addr;
- u_char status;
-
- MTD_DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : imx_low_erase:Erasing NAND\n");
- for (addr = 0; addr < this->chipsize; addr += mtd->erasesize) {
- page_addr = addr / mtd->writesize;
- imx_nand_command(mtd, NAND_CMD_ERASE1, -1, page_addr);
- imx_nand_command(mtd, NAND_CMD_ERASE2, -1, -1);
- imx_nand_command(mtd, NAND_CMD_STATUS, -1, -1);
- status = imx_nand_read_byte(mtd);
- if (status & NAND_STATUS_FAIL) {
- printk(KERN_ERR
- "ERASE FAILED(block = %d,status = 0x%x)\n",
- addr / mtd->erasesize, status);
- }
- }
-
-}
-#endif
-
/*
* The generic flash bbt decriptors overlap with our ecc
* hardware, so define some i.MX specific ones.
@@ -1225,16 +1256,22 @@ static int __init imxnd_probe(struct device_d *dev)
this->read_word = imx_nand_read_word;
this->write_buf = imx_nand_write_buf;
this->read_buf = imx_nand_read_buf;
+ this->write_page = imx_nand_write_page;
if (host->hw_ecc) {
this->ecc.calculate = imx_nand_calculate_ecc;
- this->ecc.hwctl = imx_nand_enable_hwecc;
+ if (nfc_is_v3())
+ host->enable_hwecc = imx_nand_enable_hwecc_v3;
+ else
+ host->enable_hwecc = imx_nand_enable_hwecc_v1_v2;
if (nfc_is_v1())
- this->ecc.correct = imx_nand_correct_data_v1;
+ host->correct = imx_nand_correct_data_v1;
else
- this->ecc.correct = imx_nand_correct_data_v2_v3;
+ host->correct = imx_nand_correct_data_v2_v3;
this->ecc.mode = NAND_ECC_HW;
this->ecc.size = 512;
+ this->ecc.read_page_raw = imx_nand_read_page_raw;
+ this->ecc.read_page = imx_nand_read_page;
} else {
this->ecc.size = 512;
this->ecc.mode = NAND_ECC_SOFT;
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index 18d886649a..f93fac00f2 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -315,25 +315,6 @@ int mtd_block_markgood(struct mtd_info *mtd, loff_t ofs);
int mtd_buf_all_ff(const void *buf, unsigned int len);
int mtd_buf_check_pattern(const void *buf, uint8_t patt, int size);
-/*
- * Debugging macro and defines
- */
-#define MTD_DEBUG_LEVEL0 (0) /* Quiet */
-#define MTD_DEBUG_LEVEL1 (1) /* Audible */
-#define MTD_DEBUG_LEVEL2 (2) /* Loud */
-#define MTD_DEBUG_LEVEL3 (3) /* Noisy */
-
-#ifdef CONFIG_MTD_DEBUG
-#define MTD_DEBUG(n, args...) \
- do { \
- if (n <= CONFIG_MTD_DEBUG_VERBOSE) \
- pr_info( args); \
- } while(0)
-#else /* CONFIG_MTD_DEBUG */
-#define MTD_DEBUG(n, args...) do { } while(0)
-
-#endif /* CONFIG_MTD_DEBUG */
-
static inline int mtd_is_bitflip(int err) {
return err == -EUCLEAN;
}