aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand/raw/omap2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/nand/raw/omap2.c')
-rw-r--r--drivers/mtd/nand/raw/omap2.c229
1 files changed, 141 insertions, 88 deletions
diff --git a/drivers/mtd/nand/raw/omap2.c b/drivers/mtd/nand/raw/omap2.c
index c75e7a0b101f..b1839eef5b65 100644
--- a/drivers/mtd/nand/raw/omap2.c
+++ b/drivers/mtd/nand/raw/omap2.c
@@ -131,7 +131,7 @@
#define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */
#define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */
-#define BADBLOCK_MARKER_LENGTH 2
+#define BBM_LEN 2
static u_char bch16_vector[] = {0xf5, 0x24, 0x1c, 0xd0, 0x61, 0xb3, 0xf1, 0x55,
0x2e, 0x2c, 0x86, 0xa3, 0xed, 0x36, 0x1b, 0x78,
@@ -171,6 +171,10 @@ struct omap_nand_info {
struct device *elm_dev;
/* NAND ready gpio */
struct gpio_desc *ready_gpiod;
+ unsigned int neccpg;
+ unsigned int nsteps_per_eccpg;
+ unsigned int eccpg_size;
+ unsigned int eccpg_bytes;
};
static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
@@ -1355,7 +1359,7 @@ static int omap_elm_correct_data(struct nand_chip *chip, u_char *data,
{
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
struct nand_ecc_ctrl *ecc = &info->nand.ecc;
- int eccsteps = info->nand.ecc.steps;
+ int eccsteps = info->nsteps_per_eccpg;
int i , j, stat = 0;
int eccflag, actual_eccbytes;
struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
@@ -1525,24 +1529,37 @@ static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf,
int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
- int ret;
+ struct omap_nand_info *info = mtd_to_omap(mtd);
uint8_t *ecc_calc = chip->ecc.calc_buf;
+ unsigned int eccpg;
+ int ret;
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
- /* Enable GPMC ecc engine */
- chip->ecc.hwctl(chip, NAND_ECC_WRITE);
+ for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
+ /* Enable GPMC ecc engine */
+ chip->ecc.hwctl(chip, NAND_ECC_WRITE);
- /* Write data */
- chip->legacy.write_buf(chip, buf, mtd->writesize);
+ /* Write data */
+ chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
+ info->eccpg_size);
- /* Update ecc vector from GPMC result registers */
- omap_calculate_ecc_bch_multi(mtd, buf, &ecc_calc[0]);
+ /* Update ecc vector from GPMC result registers */
+ ret = omap_calculate_ecc_bch_multi(mtd,
+ buf + (eccpg * info->eccpg_size),
+ ecc_calc);
+ if (ret)
+ return ret;
- ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc,
+ chip->oob_poi,
+ eccpg * info->eccpg_bytes,
+ info->eccpg_bytes);
+ if (ret)
+ return ret;
+ }
/* Write ecc vector to OOB area */
chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
@@ -1566,12 +1583,13 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
u8 *ecc_calc = chip->ecc.calc_buf;
int ecc_size = chip->ecc.size;
int ecc_bytes = chip->ecc.bytes;
- int ecc_steps = chip->ecc.steps;
u32 start_step = offset / ecc_size;
u32 end_step = (offset + data_len - 1) / ecc_size;
+ unsigned int eccpg;
int step, ret = 0;
/*
@@ -1580,36 +1598,48 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
* ECC is calculated for all subpages but we choose
* only what we want.
*/
- nand_prog_page_begin_op(chip, page, 0, NULL, 0);
-
- /* Enable GPMC ECC engine */
- chip->ecc.hwctl(chip, NAND_ECC_WRITE);
-
- /* Write data */
- chip->legacy.write_buf(chip, buf, mtd->writesize);
+ ret = nand_prog_page_begin_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
- for (step = 0; step < ecc_steps; step++) {
- /* mask ECC of un-touched subpages by padding 0xFF */
- if (step < start_step || step > end_step)
- memset(ecc_calc, 0xff, ecc_bytes);
- else
- ret = _omap_calculate_ecc_bch(mtd, buf, ecc_calc, step);
+ for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
+ /* Enable GPMC ECC engine */
+ chip->ecc.hwctl(chip, NAND_ECC_WRITE);
+
+ /* Write data */
+ chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
+ info->eccpg_size);
+
+ for (step = 0; step < info->nsteps_per_eccpg; step++) {
+ unsigned int base_step = eccpg * info->nsteps_per_eccpg;
+ const u8 *bufoffs = buf + (eccpg * info->eccpg_size);
+
+ /* Mask ECC of un-touched subpages with 0xFFs */
+ if ((step + base_step) < start_step ||
+ (step + base_step) > end_step)
+ memset(ecc_calc + (step * ecc_bytes), 0xff,
+ ecc_bytes);
+ else
+ ret = _omap_calculate_ecc_bch(mtd,
+ bufoffs + (step * ecc_size),
+ ecc_calc + (step * ecc_bytes),
+ step);
+
+ if (ret)
+ return ret;
+ }
+ /*
+ * Copy the calculated ECC for the whole page including the
+ * masked values (0xFF) corresponding to unwritten subpages.
+ */
+ ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi,
+ eccpg * info->eccpg_bytes,
+ info->eccpg_bytes);
if (ret)
return ret;
-
- buf += ecc_size;
- ecc_calc += ecc_bytes;
}
- /* copy calculated ECC for whole page to chip->buffer->oob */
- /* this include masked-value(0xFF) for unwritten subpages */
- ecc_calc = chip->ecc.calc_buf;
- ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
-
/* write OOB buffer to NAND device */
chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
@@ -1634,40 +1664,60 @@ static int omap_read_page_bch(struct nand_chip *chip, uint8_t *buf,
int oob_required, int page)
{
struct mtd_info *mtd = nand_to_mtd(chip);
+ struct omap_nand_info *info = mtd_to_omap(mtd);
uint8_t *ecc_calc = chip->ecc.calc_buf;
uint8_t *ecc_code = chip->ecc.code_buf;
+ unsigned int max_bitflips = 0, eccpg;
int stat, ret;
- unsigned int max_bitflips = 0;
-
- nand_read_page_op(chip, page, 0, NULL, 0);
- /* Enable GPMC ecc engine */
- chip->ecc.hwctl(chip, NAND_ECC_READ);
+ ret = nand_read_page_op(chip, page, 0, NULL, 0);
+ if (ret)
+ return ret;
- /* Read data */
- chip->legacy.read_buf(chip, buf, mtd->writesize);
+ for (eccpg = 0; eccpg < info->neccpg; eccpg++) {
+ /* Enable GPMC ecc engine */
+ chip->ecc.hwctl(chip, NAND_ECC_READ);
- /* Read oob bytes */
- nand_change_read_column_op(chip,
- mtd->writesize + BADBLOCK_MARKER_LENGTH,
- chip->oob_poi + BADBLOCK_MARKER_LENGTH,
- chip->ecc.total, false);
+ /* Read data */
+ ret = nand_change_read_column_op(chip, eccpg * info->eccpg_size,
+ buf + (eccpg * info->eccpg_size),
+ info->eccpg_size, false);
+ if (ret)
+ return ret;
- /* Calculate ecc bytes */
- omap_calculate_ecc_bch_multi(mtd, buf, ecc_calc);
+ /* Read oob bytes */
+ ret = nand_change_read_column_op(chip,
+ mtd->writesize + BBM_LEN +
+ (eccpg * info->eccpg_bytes),
+ chip->oob_poi + BBM_LEN +
+ (eccpg * info->eccpg_bytes),
+ info->eccpg_bytes, false);
+ if (ret)
+ return ret;
- ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
- chip->ecc.total);
- if (ret)
- return ret;
+ /* Calculate ecc bytes */
+ ret = omap_calculate_ecc_bch_multi(mtd,
+ buf + (eccpg * info->eccpg_size),
+ ecc_calc);
+ if (ret)
+ return ret;
- stat = chip->ecc.correct(chip, buf, ecc_code, ecc_calc);
+ ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code,
+ chip->oob_poi,
+ eccpg * info->eccpg_bytes,
+ info->eccpg_bytes);
+ if (ret)
+ return ret;
- if (stat < 0) {
- mtd->ecc_stats.failed++;
- } else {
- mtd->ecc_stats.corrected += stat;
- max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ stat = chip->ecc.correct(chip,
+ buf + (eccpg * info->eccpg_size),
+ ecc_code, ecc_calc);
+ if (stat < 0) {
+ mtd->ecc_stats.failed++;
+ } else {
+ mtd->ecc_stats.corrected += stat;
+ max_bitflips = max_t(unsigned int, max_bitflips, stat);
+ }
}
return max_bitflips;
@@ -1820,7 +1870,7 @@ static int omap_ooblayout_ecc(struct mtd_info *mtd, int section,
{
struct omap_nand_info *info = mtd_to_omap(mtd);
struct nand_chip *chip = &info->nand;
- int off = BADBLOCK_MARKER_LENGTH;
+ int off = BBM_LEN;
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
!(chip->options & NAND_BUSWIDTH_16))
@@ -1840,7 +1890,7 @@ static int omap_ooblayout_free(struct mtd_info *mtd, int section,
{
struct omap_nand_info *info = mtd_to_omap(mtd);
struct nand_chip *chip = &info->nand;
- int off = BADBLOCK_MARKER_LENGTH;
+ int off = BBM_LEN;
if (info->ecc_opt == OMAP_ECC_HAM1_CODE_HW &&
!(chip->options & NAND_BUSWIDTH_16))
@@ -1870,7 +1920,7 @@ static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
struct nand_device *nand = mtd_to_nanddev(mtd);
unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
- int off = BADBLOCK_MARKER_LENGTH;
+ int off = BBM_LEN;
if (section >= nsteps)
return -ERANGE;
@@ -1891,7 +1941,7 @@ static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
struct nand_device *nand = mtd_to_nanddev(mtd);
unsigned int nsteps = nanddev_get_ecc_nsteps(nand);
unsigned int ecc_bytes = nanddev_get_ecc_bytes_per_step(nand);
- int off = BADBLOCK_MARKER_LENGTH;
+ int off = BBM_LEN;
if (section)
return -ERANGE;
@@ -1920,7 +1970,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
struct mtd_info *mtd = nand_to_mtd(chip);
struct omap_nand_info *info = mtd_to_omap(mtd);
struct device *dev = &info->pdev->dev;
- int min_oobbytes = BADBLOCK_MARKER_LENGTH;
+ int min_oobbytes = BBM_LEN;
+ int elm_bch_strength = -1;
int oobbytes_per_step;
dma_cap_mask_t mask;
int err;
@@ -2074,12 +2125,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = chip->ecc.bytes;
-
- err = elm_config(info->elm_dev, BCH4_ECC,
- mtd->writesize / chip->ecc.size,
- chip->ecc.size, chip->ecc.bytes);
- if (err < 0)
- return err;
+ elm_bch_strength = BCH4_ECC;
break;
case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
@@ -2116,13 +2162,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = chip->ecc.bytes;
-
- err = elm_config(info->elm_dev, BCH8_ECC,
- mtd->writesize / chip->ecc.size,
- chip->ecc.size, chip->ecc.bytes);
- if (err < 0)
- return err;
-
+ elm_bch_strength = BCH8_ECC;
break;
case OMAP_ECC_BCH16_CODE_HW:
@@ -2138,19 +2178,32 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
chip->ecc.write_subpage = omap_write_subpage_bch;
mtd_set_ooblayout(mtd, &omap_ooblayout_ops);
oobbytes_per_step = chip->ecc.bytes;
-
- err = elm_config(info->elm_dev, BCH16_ECC,
- mtd->writesize / chip->ecc.size,
- chip->ecc.size, chip->ecc.bytes);
- if (err < 0)
- return err;
-
+ elm_bch_strength = BCH16_ECC;
break;
default:
dev_err(dev, "Invalid or unsupported ECC scheme\n");
return -EINVAL;
}
+ if (elm_bch_strength >= 0) {
+ chip->ecc.steps = mtd->writesize / chip->ecc.size;
+ info->neccpg = chip->ecc.steps / ERROR_VECTOR_MAX;
+ if (info->neccpg) {
+ info->nsteps_per_eccpg = ERROR_VECTOR_MAX;
+ } else {
+ info->neccpg = 1;
+ info->nsteps_per_eccpg = chip->ecc.steps;
+ }
+ info->eccpg_size = info->nsteps_per_eccpg * chip->ecc.size;
+ info->eccpg_bytes = info->nsteps_per_eccpg * chip->ecc.bytes;
+
+ err = elm_config(info->elm_dev, elm_bch_strength,
+ info->nsteps_per_eccpg, chip->ecc.size,
+ chip->ecc.bytes);
+ if (err < 0)
+ return err;
+ }
+
/* Check if NAND device's OOB is enough to store ECC signatures */
min_oobbytes += (oobbytes_per_step *
(mtd->writesize / chip->ecc.size));