nand_ecc_micron: added support for 4 bits on-die ECC
Subsystems (2)
PATCH 1/1 nand_ecc_micron: added support for 4 bits on-die ECC
The patch below enables support for the 4-bits on-die ECC (aka internal ECC).
Some notes:
- it is detected and activate from nand_scan_tail().
- when called from nand_scan_ident() it fails as at least "atmel_nand.c" overwrites some setup
- if an 4-bits on-die capable Micron nand flash is detected it will kick in
- UBI works like a charm and nicely moves the bit-flipped page/block
- when performing "dd if=/dev/mtdblock1 of=/dev/null" and encountering such a bad page, it fails with an I/O error (is this correct ?)
- raw reads likely do not work. When the on-die ECC is enabled it will automatically correct bitflips...
- Tested on MT29F1G08ABBDAHC and MT29F2G08ABBEAH4
--- drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/nand_base.c | 9 ++ drivers/mtd/nand/nand_ecc_micron.c | 238 +++++++++++++++++++++++++++++++++++ include/linux/mtd/nand_ecc_micron.h | 9 ++ 5 files changed, 264 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/nand_ecc_micron.c create mode 100644 include/linux/mtd/nand_ecc_micron.h
drivers/mtd/nand/Kconfig +7 -0
@@ -46,6 +46,13 @@ config MTD_NAND_ECC_BCH ECC codes. They are used with NAND devices requiring more than 1 bit of error correction. +config MTD_NAND_ECC_MICRON + bool "Support Micron internal ECC" + default n + help + This enables support for Micron 4-bits internal ECC + Do not enable this if you will not be using it + config MTD_SM_COMMON tristate default n
drivers/mtd/nand/Makefile +1 -0
@@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ +obj-$(CONFIG_MTD_NAND_ECC_MICRON) += nand_ecc_micron.o nand-objs := nand_base.o nand_bbt.o
drivers/mtd/nand/nand_base.c +9 -0
@@ -42,6 +42,7 @@#include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +#include <linux/mtd/nand_ecc_micron.h> #include <linux/mtd/nand_bch.h> #include <linux/interrupt.h> #include <linux/bitops.h>
@@ -3234,6 +3235,14 @@ int nand_scan_tail(struct mtd_info *mtd)int i;struct nand_chip *chip = mtd->priv; +#ifdef CONFIG_MTD_NAND_ECC_MICRON +/* Detect and enable Micron 4-bits internal ECC */ +if (nand_ecc_micron_present (mtd)) { +pr_info ("NAND device: Using micron 4-bit internal ECC\n" ); +nand_ecc_micron_init (mtd); + } +#endif +if (!(chip->options &NAND_OWN_BUFFERS )) chip->buffers =kmalloc (sizeof (*chip->buffers),GFP_KERNEL );if (!chip->buffers)
drivers/mtd/nand/nand_ecc_micron.c new file +238 -0
238 lines not on this page · raw message
include/linux/mtd/nand_ecc_micron.h new file +9 -0
@@ -0,0 +1,9 @@ +#ifndef __LINUX_MTD_NAND_ECC_MICRON_H +#define __LINUX_MTD_NAND_ECC_MICRON_H + +#define NAND_STATUS_REWRITE 0x08 + +int nand_ecc_micron_present (struct mtd_info *mtd); +void nand_ecc_micron_init (struct mtd_info *mtd); + +#endif /* __LINUX_MTD_NAND_ECC_MICRON_H */
Signature · 1 line
1.7.0.4
Have you considered my patch:
http://lists.infradead.org/pipermail/linux-mtd/2013-March/046358.html
maybe we should try to consolidate? I can make an updated patch if there is interest.
Quoted 362 lines · Jelle Martijn Kok
The patch below enables support for the 4-bits on-die ECC (aka internal ECC).
Some notes:
- it is detected and activate fromnand_scan_tail().
- when called fromnand_scan_ident()it fails as at least "atmel_nand.c" overwrites some setup
- if an 4-bits on-die capable Micron nand flash is detected it will kick in
- UBI works like a charm and nicely moves the bit-flipped page/block
- when performing "dd if=/dev/mtdblock1 of=/dev/null" and encountering such a bad page, it fails with an I/O error (is this correct ?)
- raw reads likely do not work. When the on-die ECC is enabled it will automatically correct bitflips...
- Tested on MT29F1G08ABBDAHC and MT29F2G08ABBEAH4Signed-off-by Jelle Martijn Kok <[email protected]>--- drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/nand_base.c | 9 ++ drivers/mtd/nand/nand_ecc_micron.c | 238 +++++++++++++++++++++++++++++++++++ include/linux/mtd/nand_ecc_micron.h | 9 ++ 5 files changed, 264 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/nand/nand_ecc_micron.c create mode 100644 include/linux/mtd/nand_ecc_micron.hdrivers/mtd/nand/Kconfig +7
-0@@ -46,6 +46,13 @@ config MTD_NAND_ECC_BCH ECC codes. They are used with NAND devices requiring more than 1 bit of error correction. +config MTD_NAND_ECC_MICRON + bool "Support Micron internal ECC" + default n + help + This enables support for Micron 4-bits internal ECC + Do not enable this if you will not be using it + config MTD_SM_COMMON tristate default ndrivers/mtd/nand/Makefile +1
-0@@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ +obj-$(CONFIG_MTD_NAND_ECC_MICRON) += nand_ecc_micron.o nand-objs := nand_base.o nand_bbt.odrivers/mtd/nand/nand_base.c +9
-0@@ -42,6 +42,7 @@#include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +#include <linux/mtd/nand_ecc_micron.h> #include <linux/mtd/nand_bch.h> #include <linux/interrupt.h> #include <linux/bitops.h> @@ -3234,6 +3235,14 @@ int nand_scan_tail(struct mtd_info *mtd)int i;struct nand_chip *chip = mtd->priv; +#ifdef CONFIG_MTD_NAND_ECC_MICRON +/* Detect and enable Micron 4-bits internal ECC */ +if (nand_ecc_micron_present (mtd)) { +pr_info ("NAND device: Using micron 4-bit internal ECC\n" ); +nand_ecc_micron_init (mtd); + } +#endif +if (!(chip->options &NAND_OWN_BUFFERS )) chip->buffers =kmalloc (sizeof (*chip->buffers),GFP_KERNEL );if (!chip->buffers)drivers/mtd/nand/nand_ecc_micron.c new file +34
-0@@ -0,0 +1,238 @@ +#include <linux/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc_micron.h> + +/* Define the oob placement for the micron internal ECC nand */ +static struct nand_ecclayout nand_oob_micron_ecc_64 = { + .eccbytes =32 , + .eccpos = { +8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 , +24 ,25 ,26 ,27 ,28 ,29 ,30 ,31 , +40 ,41 ,42 ,43 ,44 ,45 ,46 ,47 , +56 ,57 ,58 ,59 ,60 ,61 ,62 ,63 + }, + .oobfree = { + { .offset =4 , .length =4 }, + { .offset =20 , .length =4 }, + { .offset =36 , .length =4 }, + { .offset =52 , .length =4 }, + }, +}; + +/** +* nand_command_ecc_micron +* @mtd: MTD device structure +* @command: the command to be sent +* @column: the column address for this command, -1 if none +* @page_addr: the page address for this command, -1 if none +* +* Identical to nand_command_lp, but additionally checks the micron +* status after a NAND_CMD_READ0. +*/ + +static void nand_command_ecc_micron(struct mtd_info *mtd, unsigned intcommand,
drivers/mtd/nand/nand_ecc_micron.c new file +119
-0+ int column, int page_addr) +{ + registerstruct nand_chip *chip = mtd->priv; + +/* Emulate NAND_CMD_READOOB */ +if (command ==NAND_CMD_READOOB ) { + column += mtd->writesize; + command =NAND_CMD_READ0 ; + } + +/* Command latch cycle */ + chip->cmd_ctrl (mtd, command &0xff , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + +if (column !=-1 || page_addr !=-1 ) { +int ctrl =NAND_CTRL_CHANGE |NAND_NCE |NAND_ALE ; + +/* Serially input address */ +if (column !=-1 ) { +/* Adjust columns for 16 bit buswidth */ +if (chip->options &NAND_BUSWIDTH_16 ) + column >>=1 ; + chip->cmd_ctrl (mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE ; + chip->cmd_ctrl (mtd, column >>8 , ctrl); + } +if (page_addr !=-1 ) { + chip->cmd_ctrl (mtd, page_addr, ctrl); + chip->cmd_ctrl (mtd, page_addr >>8 , +NAND_NCE |NAND_ALE ); +/* One more address cycle for devices > 128MiB */ +if (chip->chipsize > (128 <<20 )) + chip->cmd_ctrl (mtd, page_addr >>16 , +NAND_NCE |NAND_ALE ); + } + } + chip->cmd_ctrl (mtd,NAND_CMD_NONE ,NAND_NCE |NAND_CTRL_CHANGE ); + +/* +* Program and erase have their own busy handlers status, sequential +* in, and deplete1 need no delay. +*/ +switch (command) { + +case NAND_CMD_CACHEDPROG : +case NAND_CMD_PAGEPROG : +case NAND_CMD_ERASE1 : +case NAND_CMD_ERASE2 : +case NAND_CMD_SEQIN : +case NAND_CMD_RNDIN : +case NAND_CMD_STATUS : +case NAND_CMD_DEPLETE1 : +return ; + +case NAND_CMD_STATUS_ERROR : +case NAND_CMD_STATUS_ERROR0 : +case NAND_CMD_STATUS_ERROR1 : +case NAND_CMD_STATUS_ERROR2 : +case NAND_CMD_STATUS_ERROR3 : +/* Read error status commands require only a short delay */ +udelay (chip->chip_delay); +return ; + +case NAND_CMD_RESET : +if (chip->dev_ready) +break ; +udelay (chip->chip_delay); + chip->cmd_ctrl (mtd,NAND_CMD_STATUS , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + chip->cmd_ctrl (mtd,NAND_CMD_NONE , +NAND_NCE |NAND_CTRL_CHANGE ); +while (!(chip->read_byte (mtd) &NAND_STATUS_READY )) + ; +return ; + +case NAND_CMD_RNDOUT : +/* No ready / busy check necessary */ + chip->cmd_ctrl (mtd,NAND_CMD_RNDOUTSTART , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + chip->cmd_ctrl (mtd,NAND_CMD_NONE , +NAND_NCE |NAND_CTRL_CHANGE ); +return ; + +case NAND_CMD_READ0 : + chip->cmd_ctrl (mtd,NAND_CMD_READSTART , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + chip->cmd_ctrl (mtd,NAND_CMD_NONE , +NAND_NCE |NAND_CTRL_CHANGE ); + +/* This applies to read commands */ +default : +/* +* If we don't have access to the busy pin, we apply the given +* command delay. +*/ +if (!chip->dev_ready) { +udelay (chip->chip_delay); +return ; + } + } + +/* Some additional things need to be done on read */ +if (command ==NAND_CMD_READ0 ) { +int status,cnt; +/* Wait until nand flash is ready - tR_ECC max */ +for (cnt=0 ; cnt<70 ; cnt++) { +ndelay (1000 );/* 1 usec delay */ + chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); + status = chip->read_byte (mtd); +if (status &NAND_STATUS_READY ) +break ; + } +/* Check the status bits */ +if (status &NAND_STATUS_FAIL ) + mtd->ecc_stats.failed++; +if (status &NAND_STATUS_REWRITE ) + mtd->ecc_stats.corrected++; +/* Re-issue CMD0 after the status check */ + chip->cmd_ctrl(mtd, NAND_CMD_READ0,NAND_NCE | NAND_CLE |
NAND_CTRL_CHANGE);drivers/mtd/nand/nand_ecc_micron.c new file +21
-0+ chip->cmd_ctrl (mtd,NAND_CMD_NONE ,NAND_NCE |NAND_CTRL_CHANGE ); + } + +/* +* Apply this short delay always to ensure that we do wait tWB in +* any case on any machine. +*/ +ndelay (100 ); + +nand_wait_ready (mtd); +} + +/** +* nand_read_page_ecc_micron - IDENTICAL to nand_read_page_raw +* @mtd: mtd info structure +* @chip: nand chip info structure +* @buf: buffer to store read data +* @page: page number to read +*/ + +static int nand_read_page_ecc_micron(struct mtd_info *mtd, struct nand_chip*chip,
uint8_t*buf, int page) {drivers/mtd/nand/nand_ecc_micron.c new file +13
-0+ chip->read_buf (mtd, buf, mtd->writesize); + chip->read_buf (mtd, chip->oob_poi, mtd->oobsize); +return 0 ; +} + +/** +* nand_write_page_ecc_micron - IDENTICAL to nand_write_page_raw +* @mtd: mtd info structure +* @chip: nand chip info structure +* @buf: data buffer +*/ + +static void nand_write_page_ecc_micron(struct mtd_info *mtd, struct
nand_chip*chip, constuint8_t*buf) {drivers/mtd/nand/nand_ecc_micron.c new file +10
-0+ chip->write_buf (mtd, buf, mtd->writesize); + chip->write_buf (mtd, chip->oob_poi, mtd->oobsize); +} + +int nand_ecc_micron_present (struct mtd_info *mtd) { +int i; +u8 id_data[5 ]; +struct nand_chip *chip = mtd->priv; + +/* Currently it seems it cannot be detected by onfi, but only via READID byte 4.
+ * So see if byte 4 indicates internal ECC is present (we do not
require it to
+ * be already enabled. This might be the case if the kernel is loaded
from anotherdrivers/mtd/nand/nand_ecc_micron.c new file +2
-0+* source (NOR flash, SD card or network) +* We ALWAYS enabled internal ECC if present, if you do not wish this, then do not
drivers/mtd/nand/nand_ecc_micron.c new file +19
-0+* enable this feature in the kernel config +*/ +if (chip->onfi_params.jedec_id !=NAND_MFR_MICRON ) +return 0 ; + chip->cmdfunc (mtd,NAND_CMD_READID ,0x00 ,-1 ); +for (i =0 ; i <5 ; i++) + id_data[i] = chip->read_byte (mtd); +if (id_data[0 ] ==NAND_MFR_MICRON && (id_data[4 ] &0x03 ) ==0x02 ) +return 1 ; +return 0 ; +} + +static char micron_enable_ecc[] = {0x08 ,0 ,0 ,0 }; + +void nand_ecc_micron_init (struct mtd_info *mtd) { +struct nand_chip *chip = mtd->priv; + +/* Setup the read/write functions +* NOTE: we are required to do so, or we are not allowed to set the ecc.mode to
NAND_ECC_HW*/drivers/mtd/nand/nand_ecc_micron.c new file +8
-0+ chip->ecc.read_page = nand_read_page_ecc_micron; + chip->ecc.write_page = nand_write_page_ecc_micron; + +/* We need to modify NAND_CMD_READ0, so hook into the cmdfunc */ + chip->cmdfunc = nand_command_ecc_micron; + +/* Make sure the internel ECC engine of the nand is active */ + chip->cmd_ctrl (mtd,0xEF ,NAND_CTRL_CHANGE |NAND_NCE |NAND_CLE );/* SET FEATURES */
+chip->cmd_ctrl(mtd, 0x90, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE); /*
Array operation mode */drivers/mtd/nand/nand_ecc_micron.c new file +9
-0+ chip->write_buf (mtd, micron_enable_ecc,4 ); +ndelay (1000 );/* tFEAT 1 usec delay */ + +/* Setup the ecc structure */ + chip->ecc.layout = &nand_oob_micron_ecc_64; + chip->ecc.mode =NAND_ECC_HW ; + chip->ecc.size =512 ; + chip->ecc.bytes =8 ; +}include/linux/mtd/nand_ecc_micron.h new file +9
-1@@ -0,0 +1,9 @@ +#ifndef __LINUX_MTD_NAND_ECC_MICRON_H +#define __LINUX_MTD_NAND_ECC_MICRON_H + +#define NAND_STATUS_REWRITE 0x08 + +int nand_ecc_micron_present (struct mtd_info *mtd); +void nand_ecc_micron_init (struct mtd_info *mtd); + +#endif /* __LINUX_MTD_NAND_ECC_MICRON_H */ --1.7.0.4
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
Hi David,
I looked at your code and I expect it to contain too many sub-patches to be accepted.
- do not modify any code in "nand_base.c" as this likely will get the code rejected
- adding "NAND_ECC_HW_ON_DIE" is a indeed good choice for the ecc.mode.
- do not add any onfi patches, do this in a separate patch
- do not add set_column(), do this in a separate patch
- set_on_die_ecc() should have 1us delay. So a raw read/write might not be that straight forward.
Note: It will be a pleasure to make any changes to my code if one of the maintainers would require this before it can be accepted.
Jelle Martijn
Quoted 373 lines · David Mosberger-Tang
Have you considered my patch:
http://lists.infradead.org/pipermail/linux-mtd/2013-March/046358.htmlmaybe we should try to consolidate? I can make an updated patch if there is interest.
Quoted 359 lines · Jelle Martijn Kok
The patch below enables support for the 4-bits on-die ECC (aka internal ECC).
Some notes:
- it is detected and activate fromnand_scan_tail().
- when called fromnand_scan_ident()it fails as at least "atmel_nand.c" overwrites some setup
- if an 4-bits on-die capable Micron nand flash is detected it will kick in
- UBI works like a charm and nicely moves the bit-flipped page/block
- when performing "dd if=/dev/mtdblock1 of=/dev/null" and encountering such a bad page, it fails with an I/O error (is this correct ?)
- raw reads likely do not work. When the on-die ECC is enabled it will automatically correct bitflips...
- Tested on MT29F1G08ABBDAHC and MT29F2G08ABBEAH4Signed-off-by Jelle Martijn Kok <[email protected]>--- drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/nand_base.c | 9 ++ drivers/mtd/nand/nand_ecc_micron.c | 238 +++++++++++++++++++++++++++++++++++ include/linux/mtd/nand_ecc_micron.h | 9 ++ 5 files changed, 264 insertions(+), 0 deletions(-)create mode 100644
drivers/mtd/nand/nand_ecc_micron.ccreate mode 100644include/linux/mtd/nand_ecc_micron.hdrivers/mtd/nand/Kconfig +7
-0@@ -46,6 +46,13 @@ config MTD_NAND_ECC_BCH ECC codes. They are used with NAND devices requiring more than 1 bit of error correction. +config MTD_NAND_ECC_MICRON + bool "Support Micron internal ECC" + default n + help + This enables support for Micron 4-bits internal ECC + Do not enable this if you will not be using it + config MTD_SM_COMMON tristate default ndrivers/mtd/nand/Makefile +1
-0@@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ +obj-$(CONFIG_MTD_NAND_ECC_MICRON) += nand_ecc_micron.o nand-objs := nand_base.o nand_bbt.odrivers/mtd/nand/nand_base.c +9
-0@@ -42,6 +42,7 @@#include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +#include <linux/mtd/nand_ecc_micron.h> #include <linux/mtd/nand_bch.h> #include <linux/interrupt.h> #include <linux/bitops.h> @@ -3234,6 +3235,14 @@ int nand_scan_tail(struct mtd_info *mtd)int i;struct nand_chip *chip = mtd->priv; +#ifdef CONFIG_MTD_NAND_ECC_MICRON +/* Detect and enable Micron 4-bits internal ECC */ +if (nand_ecc_micron_present (mtd)) { +pr_info ("NAND device: Using micron 4-bit internal ECC\n" ); +nand_ecc_micron_init (mtd); + } +#endif +if (!(chip->options &NAND_OWN_BUFFERS )) chip->buffers =kmalloc (sizeof (*chip->buffers),GFP_KERNEL );if (!chip->buffers)drivers/mtd/nand/nand_ecc_micron.c new file +34
-0@@ -0,0 +1,238 @@ +#include <linux/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc_micron.h> + +/* Define the oob placement for the micron internal ECC nand */ +static struct nand_ecclayout nand_oob_micron_ecc_64 = { + .eccbytes =32 , + .eccpos = { +8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 , +24 ,25 ,26 ,27 ,28 ,29 ,30 ,31 , +40 ,41 ,42 ,43 ,44 ,45 ,46 ,47 , +56 ,57 ,58 ,59 ,60 ,61 ,62 ,63 + }, + .oobfree = { + { .offset =4 , .length =4 }, + { .offset =20 , .length =4 }, + { .offset =36 , .length =4 }, + { .offset =52 , .length =4 }, + }, +}; + +/** +* nand_command_ecc_micron +* @mtd: MTD device structure +* @command: the command to be sent +* @column: the column address for this command, -1 if none +* @page_addr: the page address for this command, -1 if none +* +* Identical to nand_command_lp, but additionally checks the micron +* status after a NAND_CMD_READ0. +*/ + +static void nand_command_ecc_micron(struct mtd_info *mtd, unsigned intcommand,
drivers/mtd/nand/nand_ecc_micron.c new file +119
-0+ int column, int page_addr) +{ + registerstruct nand_chip *chip = mtd->priv; + +/* Emulate NAND_CMD_READOOB */ +if (command ==NAND_CMD_READOOB ) { + column += mtd->writesize; + command =NAND_CMD_READ0 ; + } + +/* Command latch cycle */ + chip->cmd_ctrl (mtd, command &0xff , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + +if (column !=-1 || page_addr !=-1 ) { +int ctrl =NAND_CTRL_CHANGE |NAND_NCE |NAND_ALE ; + +/* Serially input address */ +if (column !=-1 ) { +/* Adjust columns for 16 bit buswidth */ +if (chip->options &NAND_BUSWIDTH_16 ) + column >>=1 ; + chip->cmd_ctrl (mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE ; + chip->cmd_ctrl (mtd, column >>8 , ctrl); + } +if (page_addr !=-1 ) { + chip->cmd_ctrl (mtd, page_addr, ctrl); + chip->cmd_ctrl (mtd, page_addr >>8 , +NAND_NCE |NAND_ALE ); +/* One more address cycle for devices > 128MiB */ +if (chip->chipsize > (128 <<20 )) + chip->cmd_ctrl (mtd, page_addr >>16 , +NAND_NCE |NAND_ALE ); + } + } + chip->cmd_ctrl (mtd,NAND_CMD_NONE ,NAND_NCE |NAND_CTRL_CHANGE ); + +/* +* Program and erase have their own busy handlers status, sequential +* in, and deplete1 need no delay. +*/ +switch (command) { + +case NAND_CMD_CACHEDPROG : +case NAND_CMD_PAGEPROG : +case NAND_CMD_ERASE1 : +case NAND_CMD_ERASE2 : +case NAND_CMD_SEQIN : +case NAND_CMD_RNDIN : +case NAND_CMD_STATUS : +case NAND_CMD_DEPLETE1 : +return ; + +case NAND_CMD_STATUS_ERROR : +case NAND_CMD_STATUS_ERROR0 : +case NAND_CMD_STATUS_ERROR1 : +case NAND_CMD_STATUS_ERROR2 : +case NAND_CMD_STATUS_ERROR3 : +/* Read error status commands require only a short delay */ +udelay (chip->chip_delay); +return ; + +case NAND_CMD_RESET : +if (chip->dev_ready) +break ; +udelay (chip->chip_delay); + chip->cmd_ctrl (mtd,NAND_CMD_STATUS , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + chip->cmd_ctrl (mtd,NAND_CMD_NONE , +NAND_NCE |NAND_CTRL_CHANGE ); +while (!(chip->read_byte (mtd) &NAND_STATUS_READY )) + ; +return ; + +case NAND_CMD_RNDOUT : +/* No ready / busy check necessary */ + chip->cmd_ctrl (mtd,NAND_CMD_RNDOUTSTART , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + chip->cmd_ctrl (mtd,NAND_CMD_NONE , +NAND_NCE |NAND_CTRL_CHANGE ); +return ; + +case NAND_CMD_READ0 : + chip->cmd_ctrl (mtd,NAND_CMD_READSTART , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + chip->cmd_ctrl (mtd,NAND_CMD_NONE , +NAND_NCE |NAND_CTRL_CHANGE ); + +/* This applies to read commands */ +default : +/* +* If we don't have access to the busy pin, we apply the given +* command delay. +*/ +if (!chip->dev_ready) { +udelay (chip->chip_delay); +return ; + } + } + +/* Some additional things need to be done on read */ +if (command ==NAND_CMD_READ0 ) { +int status,cnt; +/* Wait until nand flash is ready - tR_ECC max */ +for (cnt=0 ; cnt<70 ; cnt++) { +ndelay (1000 );/* 1 usec delay */ + chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); + status = chip->read_byte (mtd); +if (status &NAND_STATUS_READY ) +break ; + } +/* Check the status bits */ +if (status &NAND_STATUS_FAIL ) + mtd->ecc_stats.failed++; +if (status &NAND_STATUS_REWRITE ) + mtd->ecc_stats.corrected++; +/* Re-issue CMD0 after the status check */ + chip->cmd_ctrl(mtd, NAND_CMD_READ0,NAND_NCE | NAND_CLE |
NAND_CTRL_CHANGE);drivers/mtd/nand/nand_ecc_micron.c new file +21
-0+ chip->cmd_ctrl (mtd,NAND_CMD_NONE ,NAND_NCE |NAND_CTRL_CHANGE ); + } + +/* +* Apply this short delay always to ensure that we do wait tWB in +* any case on any machine. +*/ +ndelay (100 ); + +nand_wait_ready (mtd); +} + +/** +* nand_read_page_ecc_micron - IDENTICAL to nand_read_page_raw +* @mtd: mtd info structure +* @chip: nand chip info structure +* @buf: buffer to store read data +* @page: page number to read +*/ + +static int nand_read_page_ecc_micron(struct mtd_info *mtd, struct nand_chip*chip,
uint8_t*buf, int page) {drivers/mtd/nand/nand_ecc_micron.c new file +13
-0+ chip->read_buf (mtd, buf, mtd->writesize); + chip->read_buf (mtd, chip->oob_poi, mtd->oobsize); +return 0 ; +} + +/** +* nand_write_page_ecc_micron - IDENTICAL to nand_write_page_raw +* @mtd: mtd info structure +* @chip: nand chip info structure +* @buf: data buffer +*/ + +static void nand_write_page_ecc_micron(struct mtd_info *mtd, struct
nand_chip*chip, constuint8_t*buf) {drivers/mtd/nand/nand_ecc_micron.c new file +10
-0+ chip->write_buf (mtd, buf, mtd->writesize); + chip->write_buf (mtd, chip->oob_poi, mtd->oobsize); +} + +int nand_ecc_micron_present (struct mtd_info *mtd) { +int i; +u8 id_data[5 ]; +struct nand_chip *chip = mtd->priv; + +/* Currently it seems it cannot be detected by onfi, but only via READID byte 4.
+ * So see if byte 4 indicates internal ECC is present (we do not
require it to
+ * be already enabled. This might be the case if the kernel is loaded
from anotherdrivers/mtd/nand/nand_ecc_micron.c new file +2
-0+* source (NOR flash, SD card or network) +* We ALWAYS enabled internal ECC if present, if you do not wish this, then do not
drivers/mtd/nand/nand_ecc_micron.c new file +19
-0+* enable this feature in the kernel config +*/ +if (chip->onfi_params.jedec_id !=NAND_MFR_MICRON ) +return 0 ; + chip->cmdfunc (mtd,NAND_CMD_READID ,0x00 ,-1 ); +for (i =0 ; i <5 ; i++) + id_data[i] = chip->read_byte (mtd); +if (id_data[0 ] ==NAND_MFR_MICRON && (id_data[4 ] &0x03 ) ==0x02 ) +return 1 ; +return 0 ; +} + +static char micron_enable_ecc[] = {0x08 ,0 ,0 ,0 }; + +void nand_ecc_micron_init (struct mtd_info *mtd) { +struct nand_chip *chip = mtd->priv; + +/* Setup the read/write functions +* NOTE: we are required to do so, or we are not allowed to set the ecc.mode to
NAND_ECC_HW*/drivers/mtd/nand/nand_ecc_micron.c new file +8
-0+ chip->ecc.read_page = nand_read_page_ecc_micron; + chip->ecc.write_page = nand_write_page_ecc_micron; + +/* We need to modify NAND_CMD_READ0, so hook into the cmdfunc */ + chip->cmdfunc = nand_command_ecc_micron; + +/* Make sure the internel ECC engine of the nand is active */ + chip->cmd_ctrl (mtd,0xEF ,NAND_CTRL_CHANGE |NAND_NCE |NAND_CLE );/* SET FEATURES */
+chip->cmd_ctrl(mtd, 0x90, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE); /*
Array operation mode */drivers/mtd/nand/nand_ecc_micron.c new file +9
-0+ chip->write_buf (mtd, micron_enable_ecc,4 ); +ndelay (1000 );/* tFEAT 1 usec delay */ + +/* Setup the ecc structure */ + chip->ecc.layout = &nand_oob_micron_ecc_64; + chip->ecc.mode =NAND_ECC_HW ; + chip->ecc.size =512 ; + chip->ecc.bytes =8 ; +}include/linux/mtd/nand_ecc_micron.h new file +9
-1@@ -0,0 +1,9 @@ +#ifndef __LINUX_MTD_NAND_ECC_MICRON_H +#define __LINUX_MTD_NAND_ECC_MICRON_H + +#define NAND_STATUS_REWRITE 0x08 + +int nand_ecc_micron_present (struct mtd_info *mtd); +void nand_ecc_micron_init (struct mtd_info *mtd); + +#endif /* __LINUX_MTD_NAND_ECC_MICRON_H */ --1.7.0.4
Quoted 3 lines
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
Signature · 10 lines
------------------------------------------------------------------------ You/Com Audiocommunicatie BV Motorenweg 5k 2623CR Delft The Netherlands tel. : (+31) 15 262 59 55 fax. : (+31) 15 257 15 95 mail : [email protected] http : http://www.youcom.nl ------------------------------------------------------------------------
Jelle,
The patch was meant to provide working code, not for direct integration. I am working on submitting the changes in nice, small, incremental steps.
--david
Quoted 416 lines · Jelle Martijn Kok
Hi David,
I looked at your code and I expect it to contain too many sub-patches to be accepted.
- do not modify any code in "nand_base.c" as this likely will get the code rejected
- adding "NAND_ECC_HW_ON_DIE" is a indeed good choice for the ecc.mode.
- do not add any onfi patches, do this in a separate patch
- do not addset_column(), do this in a separate patch
-set_on_die_ecc()should have 1us delay. So a raw read/write might not be that straight forward.Note: It will be a pleasure to make any changes to my code if one of the maintainers would require this before it can be accepted.
Jelle Martijn
Quoted 377 lines · David Mosberger-Tang
Have you considered my patch:
http://lists.infradead.org/pipermail/linux-mtd/2013-March/046358.htmlmaybe we should try to consolidate? I can make an updated patch if there is interest.
Quoted 368 lines · Jelle Martijn Kok
The patch below enables support for the 4-bits on-die ECC (aka internal ECC).
Some notes:
- it is detected and activate fromnand_scan_tail().
- when called fromnand_scan_ident()it fails as at least "atmel_nand.c" overwrites some setup
- if an 4-bits on-die capable Micron nand flash is detected it will kick in
- UBI works like a charm and nicely moves the bit-flipped page/block
- when performing "dd if=/dev/mtdblock1 of=/dev/null" and encountering such
a bad page, it fails with an I/O error (is this correct ?)
- raw reads likely do not work. When the on-die ECC is enabled it will automatically correct bitflips...
- Tested on MT29F1G08ABBDAHC and MT29F2G08ABBEAH4Signed-off-by Jelle Martijn Kok <[email protected]>--- drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/nand_base.c | 9 ++ drivers/mtd/nand/nand_ecc_micron.c | 238 +++++++++++++++++++++++++++++++++++ include/linux/mtd/nand_ecc_micron.h | 9 ++ 5 files changed, 264 insertions(+), 0 deletions(-)create mode 100644
drivers/mtd/nand/nand_ecc_micron.ccreate mode 100644include/linux/mtd/nand_ecc_micron.hdrivers/mtd/nand/Kconfig +0
-0@@ -46,6 +46,13 @@ config MTD_NAND_ECC_BCH ECC codes. They are used with NAND devices requiring more than 1bit
of error correction.drivers/mtd/nand/Kconfig +7
-0+config MTD_NAND_ECC_MICRON + bool "Support Micron internal ECC" + default n + help + This enables support for Micron 4-bits internal ECC + Do not enable this if you will not be using it + config MTD_SM_COMMON tristate default ndrivers/mtd/nand/Makefile +1
-0@@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/ +obj-$(CONFIG_MTD_NAND_ECC_MICRON) += nand_ecc_micron.o nand-objs := nand_base.o nand_bbt.odrivers/mtd/nand/nand_base.c +9
-0@@ -42,6 +42,7 @@#include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +#include <linux/mtd/nand_ecc_micron.h> #include <linux/mtd/nand_bch.h> #include <linux/interrupt.h> #include <linux/bitops.h> @@ -3234,6 +3235,14 @@ int nand_scan_tail(struct mtd_info *mtd)int i;struct nand_chip *chip = mtd->priv; +#ifdef CONFIG_MTD_NAND_ECC_MICRON +/* Detect and enable Micron 4-bits internal ECC */ +if (nand_ecc_micron_present (mtd)) { +pr_info ("NAND device: Using micron 4-bit internal ECC\n" ); +nand_ecc_micron_init (mtd); + } +#endif +if (!(chip->options &NAND_OWN_BUFFERS )) chip->buffers =kmalloc (sizeof (*chip->buffers),GFP_KERNEL );if (!chip->buffers)drivers/mtd/nand/nand_ecc_micron.c new file +34
-0@@ -0,0 +1,238 @@ +#include <linux/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc_micron.h> + +/* Define the oob placement for the micron internal ECC nand */ +static struct nand_ecclayout nand_oob_micron_ecc_64 = { + .eccbytes =32 , + .eccpos = { +8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 , +24 ,25 ,26 ,27 ,28 ,29 ,30 ,31 , +40 ,41 ,42 ,43 ,44 ,45 ,46 ,47 , +56 ,57 ,58 ,59 ,60 ,61 ,62 ,63 + }, + .oobfree = { + { .offset =4 , .length =4 }, + { .offset =20 , .length =4 }, + { .offset =36 , .length =4 }, + { .offset =52 , .length =4 }, + }, +}; + +/** +* nand_command_ecc_micron +* @mtd: MTD device structure +* @command: the command to be sent +* @column: the column address for this command, -1 if none +* @page_addr: the page address for this command, -1 if none +* +* Identical to nand_command_lp, but additionally checks the micron +* status after a NAND_CMD_READ0. +*/ + +static void nand_command_ecc_micron(struct mtd_info *mtd, unsigned intcommand,
drivers/mtd/nand/nand_ecc_micron.c new file +119
-0+ int column, int page_addr) +{ + registerstruct nand_chip *chip = mtd->priv; + +/* Emulate NAND_CMD_READOOB */ +if (command ==NAND_CMD_READOOB ) { + column += mtd->writesize; + command =NAND_CMD_READ0 ; + } + +/* Command latch cycle */ + chip->cmd_ctrl (mtd, command &0xff , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + +if (column !=-1 || page_addr !=-1 ) { +int ctrl =NAND_CTRL_CHANGE |NAND_NCE |NAND_ALE ; + +/* Serially input address */ +if (column !=-1 ) { +/* Adjust columns for 16 bit buswidth */ +if (chip->options &NAND_BUSWIDTH_16 ) + column >>=1 ; + chip->cmd_ctrl (mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE ; + chip->cmd_ctrl (mtd, column >>8 , ctrl); + } +if (page_addr !=-1 ) { + chip->cmd_ctrl (mtd, page_addr, ctrl); + chip->cmd_ctrl (mtd, page_addr >>8 , +NAND_NCE |NAND_ALE ); +/* One more address cycle for devices > 128MiB */ +if (chip->chipsize > (128 <<20 )) + chip->cmd_ctrl (mtd, page_addr >>16 , +NAND_NCE |NAND_ALE ); + } + } + chip->cmd_ctrl (mtd,NAND_CMD_NONE ,NAND_NCE |NAND_CTRL_CHANGE ); + +/* +* Program and erase have their own busy handlers status, sequential +* in, and deplete1 need no delay. +*/ +switch (command) { + +case NAND_CMD_CACHEDPROG : +case NAND_CMD_PAGEPROG : +case NAND_CMD_ERASE1 : +case NAND_CMD_ERASE2 : +case NAND_CMD_SEQIN : +case NAND_CMD_RNDIN : +case NAND_CMD_STATUS : +case NAND_CMD_DEPLETE1 : +return ; + +case NAND_CMD_STATUS_ERROR : +case NAND_CMD_STATUS_ERROR0 : +case NAND_CMD_STATUS_ERROR1 : +case NAND_CMD_STATUS_ERROR2 : +case NAND_CMD_STATUS_ERROR3 : +/* Read error status commands require only a short delay */ +udelay (chip->chip_delay); +return ; + +case NAND_CMD_RESET : +if (chip->dev_ready) +break ; +udelay (chip->chip_delay); + chip->cmd_ctrl (mtd,NAND_CMD_STATUS , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + chip->cmd_ctrl (mtd,NAND_CMD_NONE , +NAND_NCE |NAND_CTRL_CHANGE ); +while (!(chip->read_byte (mtd) &NAND_STATUS_READY )) + ; +return ; + +case NAND_CMD_RNDOUT : +/* No ready / busy check necessary */ + chip->cmd_ctrl (mtd,NAND_CMD_RNDOUTSTART , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + chip->cmd_ctrl (mtd,NAND_CMD_NONE , +NAND_NCE |NAND_CTRL_CHANGE ); +return ; + +case NAND_CMD_READ0 : + chip->cmd_ctrl (mtd,NAND_CMD_READSTART , +NAND_NCE |NAND_CLE |NAND_CTRL_CHANGE ); + chip->cmd_ctrl (mtd,NAND_CMD_NONE , +NAND_NCE |NAND_CTRL_CHANGE ); + +/* This applies to read commands */ +default : +/* +* If we don't have access to the busy pin, we apply the given +* command delay. +*/ +if (!chip->dev_ready) { +udelay (chip->chip_delay); +return ; + } + } + +/* Some additional things need to be done on read */ +if (command ==NAND_CMD_READ0 ) { +int status,cnt; +/* Wait until nand flash is ready - tR_ECC max */ +for (cnt=0 ; cnt<70 ; cnt++) { +ndelay (1000 );/* 1 usec delay */ + chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); + status = chip->read_byte (mtd); +if (status &NAND_STATUS_READY ) +break ; + } +/* Check the status bits */ +if (status &NAND_STATUS_FAIL ) + mtd->ecc_stats.failed++; +if (status &NAND_STATUS_REWRITE ) + mtd->ecc_stats.corrected++; +/* Re-issue CMD0 after the status check */ + chip->cmd_ctrl(mtd, NAND_CMD_READ0,NAND_NCE | NAND_CLE |
NAND_CTRL_CHANGE);drivers/mtd/nand/nand_ecc_micron.c new file +21
-0+ chip->cmd_ctrl (mtd,NAND_CMD_NONE ,NAND_NCE |NAND_CTRL_CHANGE ); + } + +/* +* Apply this short delay always to ensure that we do wait tWB in +* any case on any machine. +*/ +ndelay (100 ); + +nand_wait_ready (mtd); +} + +/** +* nand_read_page_ecc_micron - IDENTICAL to nand_read_page_raw +* @mtd: mtd info structure +* @chip: nand chip info structure +* @buf: buffer to store read data +* @page: page number to read +*/ + +static int nand_read_page_ecc_micron(struct mtd_info *mtd, struct
nand_chip
*chip,uint8_t*buf, int page) {drivers/mtd/nand/nand_ecc_micron.c new file +13
-0+ chip->read_buf (mtd, buf, mtd->writesize); + chip->read_buf (mtd, chip->oob_poi, mtd->oobsize); +return 0 ; +} + +/** +* nand_write_page_ecc_micron - IDENTICAL to nand_write_page_raw +* @mtd: mtd info structure +* @chip: nand chip info structure +* @buf: data buffer +*/ + +static void nand_write_page_ecc_micron(struct mtd_info *mtd, struct
nand_chip*chip, constuint8_t*buf) {drivers/mtd/nand/nand_ecc_micron.c new file +10
-0+ chip->write_buf (mtd, buf, mtd->writesize); + chip->write_buf (mtd, chip->oob_poi, mtd->oobsize); +} + +int nand_ecc_micron_present (struct mtd_info *mtd) { +int i; +u8 id_data[5 ]; +struct nand_chip *chip = mtd->priv; + +/* Currently it seems it cannot be detected by onfi, but only via READID byte 4.
+ * So see if byte 4 indicates internal ECC is present (we do not
require it to
+ * be already enabled. This might be the case if the kernel is
loaded
from anotherdrivers/mtd/nand/nand_ecc_micron.c new file +2
-0+* source (NOR flash, SD card or network) +* We ALWAYS enabled internal ECC if present, if you do not wish this,
then do notdrivers/mtd/nand/nand_ecc_micron.c new file +19
-0+* enable this feature in the kernel config +*/ +if (chip->onfi_params.jedec_id !=NAND_MFR_MICRON ) +return 0 ; + chip->cmdfunc (mtd,NAND_CMD_READID ,0x00 ,-1 ); +for (i =0 ; i <5 ; i++) + id_data[i] = chip->read_byte (mtd); +if (id_data[0 ] ==NAND_MFR_MICRON && (id_data[4 ] &0x03 ) ==0x02 ) +return 1 ; +return 0 ; +} + +static char micron_enable_ecc[] = {0x08 ,0 ,0 ,0 }; + +void nand_ecc_micron_init (struct mtd_info *mtd) { +struct nand_chip *chip = mtd->priv; + +/* Setup the read/write functions +* NOTE: we are required to do so, or we are not allowed to set the ecc.mode to
NAND_ECC_HW*/drivers/mtd/nand/nand_ecc_micron.c new file +8
-0+ chip->ecc.read_page = nand_read_page_ecc_micron; + chip->ecc.write_page = nand_write_page_ecc_micron; + +/* We need to modify NAND_CMD_READ0, so hook into the cmdfunc */ + chip->cmdfunc = nand_command_ecc_micron; + +/* Make sure the internel ECC engine of the nand is active */ + chip->cmd_ctrl (mtd,0xEF ,NAND_CTRL_CHANGE |NAND_NCE |NAND_CLE );/*
SET FEATURES */
+chip->cmd_ctrl(mtd, 0x90, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);
/*
Array operation mode */drivers/mtd/nand/nand_ecc_micron.c new file +9
-0+ chip->write_buf (mtd, micron_enable_ecc,4 ); +ndelay (1000 );/* tFEAT 1 usec delay */ + +/* Setup the ecc structure */ + chip->ecc.layout = &nand_oob_micron_ecc_64; + chip->ecc.mode =NAND_ECC_HW ; + chip->ecc.size =512 ; + chip->ecc.bytes =8 ; +}include/linux/mtd/nand_ecc_micron.h new file +9
-1@@ -0,0 +1,9 @@ +#ifndef __LINUX_MTD_NAND_ECC_MICRON_H +#define __LINUX_MTD_NAND_ECC_MICRON_H + +#define NAND_STATUS_REWRITE 0x08 + +int nand_ecc_micron_present (struct mtd_info *mtd); +void nand_ecc_micron_init (struct mtd_info *mtd); + +#endif /* __LINUX_MTD_NAND_ECC_MICRON_H */ --1.7.0.4
Quoted 7 lines
Quoted 3 lines
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/--
------------------------------------------------------------------------
You/Com Audiocommunicatie BV
Motorenweg 5k
2623CR Delft
The Netherlands
tel. : (+31) 15 262 59 55
fax. : (+31) 15 257 15 95
mail : [email protected]
http : http://www.youcom.nl
------------------------------------------------------------------------