| #include <common.h> |
| #include <errno.h> |
| #include <clock.h> |
| #include <linux/mtd/mtd.h> |
| #include <linux/mtd/nand.h> |
| #include <linux/err.h> |
| #include <linux/mtd/nand_ecc.h> |
| #include <asm/byteorder.h> |
| #include <asm/io.h> |
| #include <malloc.h> |
| |
| #include "nand.h" |
| |
| /** |
| * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function |
| * @mtd: mtd info structure |
| * @chip: nand chip info structure |
| * @buf: buffer to store read data |
| * |
| * Not for syndrome calculating ecc controllers which need a special oob layout |
| */ |
| static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, |
| uint8_t *buf) |
| { |
| int i, eccsize = chip->ecc.size; |
| int eccbytes = chip->ecc.bytes; |
| int eccsteps = chip->ecc.steps; |
| uint8_t *p = buf; |
| uint8_t *ecc_calc = chip->buffers->ecccalc; |
| uint8_t *ecc_code = chip->buffers->ecccode; |
| uint32_t *eccpos = chip->ecc.layout->eccpos; |
| |
| for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { |
| chip->ecc.hwctl(mtd, NAND_ECC_READ); |
| chip->read_buf(mtd, p, eccsize); |
| chip->ecc.calculate(mtd, p, &ecc_calc[i]); |
| } |
| chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); |
| |
| for (i = 0; i < chip->ecc.total; i++) |
| ecc_code[i] = chip->oob_poi[eccpos[i]]; |
| |
| eccsteps = chip->ecc.steps; |
| p = buf; |
| |
| for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { |
| int stat; |
| |
| stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); |
| if (stat < 0) |
| mtd->ecc_stats.failed++; |
| else |
| mtd->ecc_stats.corrected += stat; |
| } |
| return 0; |
| } |
| |
| /** |
| * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function |
| * @mtd: mtd info structure |
| * @chip: nand chip info structure |
| * @buf: data buffer |
| */ |
| #ifdef CONFIG_NAND_WRITE |
| static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, |
| const uint8_t *buf) |
| { |
| int i, eccsize = chip->ecc.size; |
| int eccbytes = chip->ecc.bytes; |
| int eccsteps = chip->ecc.steps; |
| uint8_t *ecc_calc = chip->buffers->ecccalc; |
| const uint8_t *p = buf; |
| uint32_t *eccpos = chip->ecc.layout->eccpos; |
| |
| for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { |
| chip->ecc.hwctl(mtd, NAND_ECC_WRITE); |
| chip->write_buf(mtd, p, eccsize); |
| chip->ecc.calculate(mtd, p, &ecc_calc[i]); |
| } |
| |
| for (i = 0; i < chip->ecc.total; i++) |
| chip->oob_poi[eccpos[i]] = ecc_calc[i]; |
| |
| chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); |
| } |
| #endif |
| |
| void nand_init_ecc_hw(struct nand_chip *chip) |
| { |
| /* Use standard hwecc read page function ? */ |
| if (!chip->ecc.read_page) |
| chip->ecc.read_page = nand_read_page_hwecc; |
| #ifdef CONFIG_NAND_READ_OOB |
| if (!chip->ecc.read_oob) |
| chip->ecc.read_oob = nand_read_oob_std; |
| #endif |
| #ifdef CONFIG_NAND_WRITE |
| if (!chip->ecc.write_oob) |
| chip->ecc.write_oob = nand_write_oob_std; |
| if (!chip->ecc.write_page) |
| chip->ecc.write_page = nand_write_page_hwecc; |
| #endif |
| } |