summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
authorMasahiro Yamada <yamada.masahiro@socionext.com>2017-03-30 02:45:52 -0400
committerBoris Brezillon <boris.brezillon@free-electrons.com>2017-04-25 08:18:34 -0400
commit24715c749b2ff5545f0316e7ad8b65026f9e9612 (patch)
tree911df863e0e9eed6782d75c56032758379390fa9 /drivers/mtd/nand
parentd29109be2e8d4a102d8304d7b8bb0d6dfe5e1d27 (diff)
mtd: nand: denali: support HW_ECC_FIXUP capability
Some old versions of the Denali IP (perhaps used only for Intel?) detects ECC errors and provides correct data via a register, but does not touch the transferred data. So, the software must fixup the data in the buffer according to the provided ECC correction information. Newer versions perform ECC correction before transferring the data. No more software intervention is needed. The ECC_ERROR_ADDRESS and ECC_CORRECTION_INFO registers were deprecated. Instead, the number of corrected bit-flips are reported via the ECC_COR_INFO register. When an uncorrectable ECC error happens, a status flag is set to the INTR_STATUS and ECC_COR_INFO registers. As is often the case with this IP, the register view of INTR_STATUS had broken compatibility. For older versions (SW ECC fixup): bit 0: ECC_TRANSACTION_DONE bit 1: ECC_ERR For newer versions (HW ECC fixup): bit 0: ECC_UNCOR_ERR bit 1: Reserved Due to this difference, the irq_mask must be fixed too. The existing handle_ecc() has been renamed to denali_sw_ecc_fixup() for clarification. What is unfortunate with this feature is we can not know the total number of corrected/uncorrected errors in a page. The register ECC_COR_INFO reports the maximum of per-sector bitflips. This is useful for ->read_page return value, but ecc_stats.{corrected,failed} increments may not be precise. Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/denali.c52
-rw-r--r--drivers/mtd/nand/denali.h14
2 files changed, 57 insertions, 9 deletions
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 64a3bdcc6b04..b95e33a84201 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -856,6 +856,41 @@ static int denali_check_erased_page(struct mtd_info *mtd,
856 return max_bitflips; 856 return max_bitflips;
857} 857}
858 858
859static int denali_hw_ecc_fixup(struct mtd_info *mtd,
860 struct denali_nand_info *denali,
861 unsigned long *uncor_ecc_flags)
862{
863 struct nand_chip *chip = mtd_to_nand(mtd);
864 int bank = denali->flash_bank;
865 uint32_t ecc_cor;
866 unsigned int max_bitflips;
867
868 ecc_cor = ioread32(denali->flash_reg + ECC_COR_INFO(bank));
869 ecc_cor >>= ECC_COR_INFO__SHIFT(bank);
870
871 if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) {
872 /*
873 * This flag is set when uncorrectable error occurs at least in
874 * one ECC sector. We can not know "how many sectors", or
875 * "which sector(s)". We need erase-page check for all sectors.
876 */
877 *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0);
878 return 0;
879 }
880
881 max_bitflips = ecc_cor & ECC_COR_INFO__MAX_ERRORS;
882
883 /*
884 * The register holds the maximum of per-sector corrected bitflips.
885 * This is suitable for the return value of the ->read_page() callback.
886 * Unfortunately, we can not know the total number of corrected bits in
887 * the page. Increase the stats by max_bitflips. (compromised solution)
888 */
889 mtd->ecc_stats.corrected += max_bitflips;
890
891 return max_bitflips;
892}
893
859#define ECC_SECTOR_SIZE 512 894#define ECC_SECTOR_SIZE 512
860 895
861#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12) 896#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
@@ -865,8 +900,9 @@ static int denali_check_erased_page(struct mtd_info *mtd,
865#define ECC_ERR_DEVICE(x) (((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8) 900#define ECC_ERR_DEVICE(x) (((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8)
866#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) 901#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO)
867 902
868static int handle_ecc(struct mtd_info *mtd, struct denali_nand_info *denali, 903static int denali_sw_ecc_fixup(struct mtd_info *mtd,
869 uint8_t *buf, unsigned long *uncor_ecc_flags) 904 struct denali_nand_info *denali,
905 unsigned long *uncor_ecc_flags, uint8_t *buf)
870{ 906{
871 unsigned int bitflips = 0; 907 unsigned int bitflips = 0;
872 unsigned int max_bitflips = 0; 908 unsigned int max_bitflips = 0;
@@ -1070,12 +1106,12 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
1070 uint8_t *buf, int oob_required, int page) 1106 uint8_t *buf, int oob_required, int page)
1071{ 1107{
1072 struct denali_nand_info *denali = mtd_to_denali(mtd); 1108 struct denali_nand_info *denali = mtd_to_denali(mtd);
1073
1074 dma_addr_t addr = denali->buf.dma_buf; 1109 dma_addr_t addr = denali->buf.dma_buf;
1075 size_t size = mtd->writesize + mtd->oobsize; 1110 size_t size = mtd->writesize + mtd->oobsize;
1076
1077 uint32_t irq_status; 1111 uint32_t irq_status;
1078 uint32_t irq_mask = INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR; 1112 uint32_t irq_mask = denali->caps & DENALI_CAP_HW_ECC_FIXUP ?
1113 INTR__DMA_CMD_COMP | INTR__ECC_UNCOR_ERR :
1114 INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
1079 unsigned long uncor_ecc_flags = 0; 1115 unsigned long uncor_ecc_flags = 0;
1080 int stat = 0; 1116 int stat = 0;
1081 1117
@@ -1101,8 +1137,10 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
1101 1137
1102 memcpy(buf, denali->buf.buf, mtd->writesize); 1138 memcpy(buf, denali->buf.buf, mtd->writesize);
1103 1139
1104 if (irq_status & INTR__ECC_ERR) 1140 if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
1105 stat = handle_ecc(mtd, denali, buf, &uncor_ecc_flags); 1141 stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
1142 else if (irq_status & INTR__ECC_ERR)
1143 stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
1106 denali_enable_dma(denali, false); 1144 denali_enable_dma(denali, false);
1107 1145
1108 if (stat < 0) 1146 if (stat < 0)
diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h
index 483c0e988f33..e532956b7177 100644
--- a/drivers/mtd/nand/denali.h
+++ b/drivers/mtd/nand/denali.h
@@ -20,6 +20,7 @@
20#ifndef __DENALI_H__ 20#ifndef __DENALI_H__
21#define __DENALI_H__ 21#define __DENALI_H__
22 22
23#include <linux/bitops.h>
23#include <linux/mtd/nand.h> 24#include <linux/mtd/nand.h>
24 25
25#define DEVICE_RESET 0x0 26#define DEVICE_RESET 0x0
@@ -218,8 +219,10 @@
218 219
219#define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50)) 220#define INTR_STATUS(__bank) (0x410 + ((__bank) * 0x50))
220#define INTR_EN(__bank) (0x420 + ((__bank) * 0x50)) 221#define INTR_EN(__bank) (0x420 + ((__bank) * 0x50))
221#define INTR__ECC_TRANSACTION_DONE 0x0001 222/* bit[1:0] is used differently depending on IP version */
222#define INTR__ECC_ERR 0x0002 223#define INTR__ECC_UNCOR_ERR 0x0001 /* new IP */
224#define INTR__ECC_TRANSACTION_DONE 0x0001 /* old IP */
225#define INTR__ECC_ERR 0x0002 /* old IP */
223#define INTR__DMA_CMD_COMP 0x0004 226#define INTR__DMA_CMD_COMP 0x0004
224#define INTR__TIME_OUT 0x0008 227#define INTR__TIME_OUT 0x0008
225#define INTR__PROGRAM_FAIL 0x0010 228#define INTR__PROGRAM_FAIL 0x0010
@@ -259,6 +262,11 @@
259#define ERR_CORRECTION_INFO__ERROR_TYPE 0x4000 262#define ERR_CORRECTION_INFO__ERROR_TYPE 0x4000
260#define ERR_CORRECTION_INFO__LAST_ERR_INFO 0x8000 263#define ERR_CORRECTION_INFO__LAST_ERR_INFO 0x8000
261 264
265#define ECC_COR_INFO(bank) (0x650 + (bank) / 2 * 0x10)
266#define ECC_COR_INFO__SHIFT(bank) ((bank) % 2 * 8)
267#define ECC_COR_INFO__MAX_ERRORS 0x007f
268#define ECC_COR_INFO__UNCOR_ERR 0x0080
269
262#define DMA_ENABLE 0x700 270#define DMA_ENABLE 0x700
263#define DMA_ENABLE__FLAG 0x0001 271#define DMA_ENABLE__FLAG 0x0001
264 272
@@ -338,6 +346,8 @@ struct denali_nand_info {
338 unsigned int caps; 346 unsigned int caps;
339}; 347};
340 348
349#define DENALI_CAP_HW_ECC_FIXUP BIT(0)
350
341extern int denali_init(struct denali_nand_info *denali); 351extern int denali_init(struct denali_nand_info *denali);
342extern void denali_remove(struct denali_nand_info *denali); 352extern void denali_remove(struct denali_nand_info *denali);
343 353