mtd: nand: set ECC algorithm to Hamming on fallback
[deliverable/linux.git] / drivers / mtd / nand / nand_base.c
index f2c8ff398d6cb46f74396fbf055fc4de0e3ac7b1..2a049576ab31f0f025e68f4b89037935938ec531 100644 (file)
 #include <linux/mtd/nand_bch.h>
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
-#include <linux/leds.h>
 #include <linux/io.h>
 #include <linux/mtd/partitions.h>
-#include <linux/of_mtd.h>
+#include <linux/of.h>
+
+static int nand_get_device(struct mtd_info *mtd, int new_state);
+
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+                            struct mtd_oob_ops *ops);
 
 /* Define default oob placement schemes for large and small page devices */
-static struct nand_ecclayout nand_oob_8 = {
-       .eccbytes = 3,
-       .eccpos = {0, 1, 2},
-       .oobfree = {
-               {.offset = 3,
-                .length = 2},
-               {.offset = 6,
-                .length = 2} }
-};
+static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
 
-static struct nand_ecclayout nand_oob_16 = {
-       .eccbytes = 6,
-       .eccpos = {0, 1, 2, 3, 6, 7},
-       .oobfree = {
-               {.offset = 8,
-                . length = 8} }
-};
+       if (section > 1)
+               return -ERANGE;
 
-static struct nand_ecclayout nand_oob_64 = {
-       .eccbytes = 24,
-       .eccpos = {
-                  40, 41, 42, 43, 44, 45, 46, 47,
-                  48, 49, 50, 51, 52, 53, 54, 55,
-                  56, 57, 58, 59, 60, 61, 62, 63},
-       .oobfree = {
-               {.offset = 2,
-                .length = 38} }
-};
+       if (!section) {
+               oobregion->offset = 0;
+               oobregion->length = 4;
+       } else {
+               oobregion->offset = 6;
+               oobregion->length = ecc->total - 4;
+       }
 
-static struct nand_ecclayout nand_oob_128 = {
-       .eccbytes = 48,
-       .eccpos = {
-                  80, 81, 82, 83, 84, 85, 86, 87,
-                  88, 89, 90, 91, 92, 93, 94, 95,
-                  96, 97, 98, 99, 100, 101, 102, 103,
-                  104, 105, 106, 107, 108, 109, 110, 111,
-                  112, 113, 114, 115, 116, 117, 118, 119,
-                  120, 121, 122, 123, 124, 125, 126, 127},
-       .oobfree = {
-               {.offset = 2,
-                .length = 78} }
+       return 0;
+}
+
+static int nand_ooblayout_free_sp(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       if (section > 1)
+               return -ERANGE;
+
+       if (mtd->oobsize == 16) {
+               if (section)
+                       return -ERANGE;
+
+               oobregion->length = 8;
+               oobregion->offset = 8;
+       } else {
+               oobregion->length = 2;
+               if (!section)
+                       oobregion->offset = 3;
+               else
+                       oobregion->offset = 6;
+       }
+
+       return 0;
+}
+
+const struct mtd_ooblayout_ops nand_ooblayout_sp_ops = {
+       .ecc = nand_ooblayout_ecc_sp,
+       .free = nand_ooblayout_free_sp,
 };
+EXPORT_SYMBOL_GPL(nand_ooblayout_sp_ops);
 
-static int nand_get_device(struct mtd_info *mtd, int new_state);
+static int nand_ooblayout_ecc_lp(struct mtd_info *mtd, int section,
+                                struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
 
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
-                            struct mtd_oob_ops *ops);
+       if (section)
+               return -ERANGE;
 
-/*
- * For devices which display every fart in the system on a separate LED. Is
- * compiled away when LED support is disabled.
- */
-DEFINE_LED_TRIGGER(nand_led_trigger);
+       oobregion->length = ecc->total;
+       oobregion->offset = mtd->oobsize - oobregion->length;
+
+       return 0;
+}
+
+static int nand_ooblayout_free_lp(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *oobregion)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+       if (section)
+               return -ERANGE;
+
+       oobregion->length = mtd->oobsize - ecc->total - 2;
+       oobregion->offset = 2;
+
+       return 0;
+}
+
+const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
+       .ecc = nand_ooblayout_ecc_lp,
+       .free = nand_ooblayout_free_lp,
+};
+EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
 
 static int check_offs_len(struct mtd_info *mtd,
                                        loff_t ofs, uint64_t len)
@@ -313,13 +348,12 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
  * nand_block_bad - [DEFAULT] Read bad block marker from the chip
  * @mtd: MTD device structure
  * @ofs: offset from device start
- * @getchip: 0, if the chip is already selected
  *
  * Check, if the block is bad.
  */
-static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+static int nand_block_bad(struct mtd_info *mtd, loff_t ofs)
 {
-       int page, chipnr, res = 0, i = 0;
+       int page, res = 0, i = 0;
        struct nand_chip *chip = mtd_to_nand(mtd);
        u16 bad;
 
@@ -328,15 +362,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
 
        page = (int)(ofs >> chip->page_shift) & chip->pagemask;
 
-       if (getchip) {
-               chipnr = (int)(ofs >> chip->chip_shift);
-
-               nand_get_device(mtd, FL_READING);
-
-               /* Select the NAND device */
-               chip->select_chip(mtd, chipnr);
-       }
-
        do {
                if (chip->options & NAND_BUSWIDTH_16) {
                        chip->cmdfunc(mtd, NAND_CMD_READOOB,
@@ -361,11 +386,6 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
                i++;
        } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
 
-       if (getchip) {
-               chip->select_chip(mtd, -1);
-               nand_release_device(mtd);
-       }
-
        return res;
 }
 
@@ -503,19 +523,17 @@ static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
  * nand_block_checkbad - [GENERIC] Check if a block is marked bad
  * @mtd: MTD device structure
  * @ofs: offset from device start
- * @getchip: 0, if the chip is already selected
  * @allowbbt: 1, if its allowed to access the bbt area
  *
  * Check, if the block is bad. Either by reading the bad block table or
  * calling of the scan function.
  */
-static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
-                              int allowbbt)
+static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
 
        if (!chip->bbt)
-               return chip->block_bad(mtd, ofs, getchip);
+               return chip->block_bad(mtd, ofs);
 
        /* Return info from the table */
        return nand_isbad_bbt(mtd, ofs, allowbbt);
@@ -557,19 +575,16 @@ void nand_wait_ready(struct mtd_info *mtd)
        if (in_interrupt() || oops_in_progress)
                return panic_nand_wait_ready(mtd, timeo);
 
-       led_trigger_event(nand_led_trigger, LED_FULL);
        /* Wait until command is processed or timeout occurs */
        timeo = jiffies + msecs_to_jiffies(timeo);
        do {
                if (chip->dev_ready(mtd))
-                       goto out;
+                       return;
                cond_resched();
        } while (time_before(jiffies, timeo));
 
-       pr_warn_ratelimited(
-               "timeout while waiting for chip to become ready\n");
-out:
-       led_trigger_event(nand_led_trigger, LED_OFF);
+       if (!chip->dev_ready(mtd))
+               pr_warn_ratelimited("timeout while waiting for chip to become ready\n");
 }
 EXPORT_SYMBOL_GPL(nand_wait_ready);
 
@@ -902,8 +917,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
        int status;
        unsigned long timeo = 400;
 
-       led_trigger_event(nand_led_trigger, LED_FULL);
-
        /*
         * Apply this short delay always to ensure that we do wait tWB in any
         * case on any machine.
@@ -927,7 +940,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
                        cond_resched();
                } while (time_before(jiffies, timeo));
        }
-       led_trigger_event(nand_led_trigger, LED_OFF);
 
        status = (int)chip->read_byte(mtd);
        /* This can happen if in case of timeout or buggy dev_ready */
@@ -1309,13 +1321,12 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
 static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
                                uint8_t *buf, int oob_required, int page)
 {
-       int i, eccsize = chip->ecc.size;
+       int i, eccsize = chip->ecc.size, ret;
        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;
        unsigned int max_bitflips = 0;
 
        chip->ecc.read_page_raw(mtd, chip, buf, 1, page);
@@ -1323,8 +1334,10 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 
-       for (i = 0; i < chip->ecc.total; i++)
-               ecc_code[i] = chip->oob_poi[eccpos[i]];
+       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        eccsteps = chip->ecc.steps;
        p = buf;
@@ -1356,14 +1369,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
                        uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
                        int page)
 {
-       int start_step, end_step, num_steps;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
+       int start_step, end_step, num_steps, ret;
        uint8_t *p;
        int data_col_addr, i, gaps = 0;
        int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
        int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
-       int index;
+       int index, section = 0;
        unsigned int max_bitflips = 0;
+       struct mtd_oob_region oobregion = { };
 
        /* Column address within the page aligned to ECC size (256bytes) */
        start_step = data_offs / chip->ecc.size;
@@ -1391,12 +1404,13 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
         * The performance is faster if we position offsets according to
         * ecc.pos. Let's make sure that there are no gaps in ECC positions.
         */
-       for (i = 0; i < eccfrag_len - 1; i++) {
-               if (eccpos[i + index] + 1 != eccpos[i + index + 1]) {
-                       gaps = 1;
-                       break;
-               }
-       }
+       ret = mtd_ooblayout_find_eccregion(mtd, index, &section, &oobregion);
+       if (ret)
+               return ret;
+
+       if (oobregion.length < eccfrag_len)
+               gaps = 1;
+
        if (gaps) {
                chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
                chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -1405,20 +1419,23 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
                 * Send the command to read the particular ECC bytes take care
                 * about buswidth alignment in read_buf.
                 */
-               aligned_pos = eccpos[index] & ~(busw - 1);
+               aligned_pos = oobregion.offset & ~(busw - 1);
                aligned_len = eccfrag_len;
-               if (eccpos[index] & (busw - 1))
+               if (oobregion.offset & (busw - 1))
                        aligned_len++;
-               if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
+               if ((oobregion.offset + (num_steps * chip->ecc.bytes)) &
+                   (busw - 1))
                        aligned_len++;
 
                chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
-                                       mtd->writesize + aligned_pos, -1);
+                             mtd->writesize + aligned_pos, -1);
                chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
        }
 
-       for (i = 0; i < eccfrag_len; i++)
-               chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
+       ret = mtd_ooblayout_get_eccbytes(mtd, chip->buffers->ecccode,
+                                        chip->oob_poi, index, eccfrag_len);
+       if (ret)
+               return ret;
 
        p = bufpoi + data_col_addr;
        for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
@@ -1459,13 +1476,12 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
 static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
                                uint8_t *buf, int oob_required, int page)
 {
-       int i, eccsize = chip->ecc.size;
+       int i, eccsize = chip->ecc.size, ret;
        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;
        unsigned int max_bitflips = 0;
 
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
@@ -1475,8 +1491,10 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
        }
        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]];
+       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        eccsteps = chip->ecc.steps;
        p = buf;
@@ -1521,12 +1539,11 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
 static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
        struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
 {
-       int i, eccsize = chip->ecc.size;
+       int i, eccsize = chip->ecc.size, ret;
        int eccbytes = chip->ecc.bytes;
        int eccsteps = chip->ecc.steps;
        uint8_t *p = buf;
        uint8_t *ecc_code = chip->buffers->ecccode;
-       uint32_t *eccpos = chip->ecc.layout->eccpos;
        uint8_t *ecc_calc = chip->buffers->ecccalc;
        unsigned int max_bitflips = 0;
 
@@ -1535,8 +1552,10 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
        chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
        chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
 
-       for (i = 0; i < chip->ecc.total; i++)
-               ecc_code[i] = chip->oob_poi[eccpos[i]];
+       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
                int stat;
@@ -1637,14 +1656,17 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
 
 /**
  * nand_transfer_oob - [INTERN] Transfer oob to client buffer
- * @chip: nand chip structure
+ * @mtd: mtd info structure
  * @oob: oob destination address
  * @ops: oob ops structure
  * @len: size of oob to transfer
  */
-static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
+static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob,
                                  struct mtd_oob_ops *ops, size_t len)
 {
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       int ret;
+
        switch (ops->mode) {
 
        case MTD_OPS_PLACE_OOB:
@@ -1652,31 +1674,12 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
                memcpy(oob, chip->oob_poi + ops->ooboffs, len);
                return oob + len;
 
-       case MTD_OPS_AUTO_OOB: {
-               struct nand_oobfree *free = chip->ecc.layout->oobfree;
-               uint32_t boffs = 0, roffs = ops->ooboffs;
-               size_t bytes = 0;
-
-               for (; free->length && len; free++, len -= bytes) {
-                       /* Read request not from offset 0? */
-                       if (unlikely(roffs)) {
-                               if (roffs >= free->length) {
-                                       roffs -= free->length;
-                                       continue;
-                               }
-                               boffs = free->offset + roffs;
-                               bytes = min_t(size_t, len,
-                                             (free->length - roffs));
-                               roffs = 0;
-                       } else {
-                               bytes = min_t(size_t, len, free->length);
-                               boffs = free->offset;
-                       }
-                       memcpy(oob, chip->oob_poi + boffs, bytes);
-                       oob += bytes;
-               }
-               return oob;
-       }
+       case MTD_OPS_AUTO_OOB:
+               ret = mtd_ooblayout_get_databytes(mtd, oob, chip->oob_poi,
+                                                 ops->ooboffs, len);
+               BUG_ON(ret);
+               return oob + len;
+
        default:
                BUG();
        }
@@ -1723,8 +1726,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
        int ret = 0;
        uint32_t readlen = ops->len;
        uint32_t oobreadlen = ops->ooblen;
-       uint32_t max_oobsize = ops->mode == MTD_OPS_AUTO_OOB ?
-               mtd->oobavail : mtd->oobsize;
+       uint32_t max_oobsize = mtd_oobavail(mtd, ops);
 
        uint8_t *bufpoi, *oob, *buf;
        int use_bufpoi;
@@ -1811,7 +1813,7 @@ read_retry:
                                int toread = min(oobreadlen, max_oobsize);
 
                                if (toread) {
-                                       oob = nand_transfer_oob(chip,
+                                       oob = nand_transfer_oob(mtd,
                                                oob, ops, toread);
                                        oobreadlen -= toread;
                                }
@@ -1924,13 +1926,13 @@ static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
  * @chip: nand chip info structure
  * @page: page number to read
  */
-static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
-                            int page)
+int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
 {
        chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
        chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
        return 0;
 }
+EXPORT_SYMBOL(nand_read_oob_std);
 
 /**
  * nand_read_oob_syndrome - [REPLACEABLE] OOB data read function for HW ECC
@@ -1939,8 +1941,8 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
  * @chip: nand chip info structure
  * @page: page number to read
  */
-static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
-                                 int page)
+int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+                          int page)
 {
        int length = mtd->oobsize;
        int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
@@ -1968,6 +1970,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
 
        return 0;
 }
+EXPORT_SYMBOL(nand_read_oob_syndrome);
 
 /**
  * nand_write_oob_std - [REPLACEABLE] the most common OOB data write function
@@ -1975,8 +1978,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
  * @chip: nand chip info structure
  * @page: page number to write
  */
-static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
-                             int page)
+int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int page)
 {
        int status = 0;
        const uint8_t *buf = chip->oob_poi;
@@ -1991,6 +1993,7 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
 
        return status & NAND_STATUS_FAIL ? -EIO : 0;
 }
+EXPORT_SYMBOL(nand_write_oob_std);
 
 /**
  * nand_write_oob_syndrome - [REPLACEABLE] OOB data write function for HW ECC
@@ -1999,8 +2002,8 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
  * @chip: nand chip info structure
  * @page: page number to write
  */
-static int nand_write_oob_syndrome(struct mtd_info *mtd,
-                                  struct nand_chip *chip, int page)
+int nand_write_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+                           int page)
 {
        int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
        int eccsize = chip->ecc.size, length = mtd->oobsize;
@@ -2050,6 +2053,7 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd,
 
        return status & NAND_STATUS_FAIL ? -EIO : 0;
 }
+EXPORT_SYMBOL(nand_write_oob_syndrome);
 
 /**
  * nand_do_read_oob - [INTERN] NAND read out-of-band
@@ -2075,10 +2079,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
 
        stats = mtd->ecc_stats;
 
-       if (ops->mode == MTD_OPS_AUTO_OOB)
-               len = chip->ecc.layout->oobavail;
-       else
-               len = mtd->oobsize;
+       len = mtd_oobavail(mtd, ops);
 
        if (unlikely(ops->ooboffs >= len)) {
                pr_debug("%s: attempt to start read outside oob\n",
@@ -2112,7 +2113,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
                        break;
 
                len = min(len, readlen);
-               buf = nand_transfer_oob(chip, buf, ops, len);
+               buf = nand_transfer_oob(mtd, buf, ops, len);
 
                if (chip->options & NAND_NEED_READRDY) {
                        /* Apply delay or wait for ready/busy pin */
@@ -2271,19 +2272,20 @@ static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
                                 const uint8_t *buf, int oob_required,
                                 int page)
 {
-       int i, eccsize = chip->ecc.size;
+       int i, eccsize = chip->ecc.size, ret;
        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;
 
        /* Software ECC calculation */
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, 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];
+       ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        return chip->ecc.write_page_raw(mtd, chip, buf, 1, page);
 }
@@ -2300,12 +2302,11 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
                                  const uint8_t *buf, int oob_required,
                                  int page)
 {
-       int i, eccsize = chip->ecc.size;
+       int i, eccsize = chip->ecc.size, ret;
        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);
@@ -2313,8 +2314,10 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
        }
 
-       for (i = 0; i < chip->ecc.total; i++)
-               chip->oob_poi[eccpos[i]] = ecc_calc[i];
+       ret = mtd_ooblayout_set_eccbytes(mtd, ecc_calc, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
 
        chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
 
@@ -2342,11 +2345,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
        int ecc_size      = chip->ecc.size;
        int ecc_bytes     = chip->ecc.bytes;
        int ecc_steps     = chip->ecc.steps;
-       uint32_t *eccpos  = chip->ecc.layout->eccpos;
        uint32_t start_step = offset / ecc_size;
        uint32_t end_step   = (offset + data_len - 1) / ecc_size;
        int oob_bytes       = mtd->oobsize / ecc_steps;
-       int step, i;
+       int step, ret;
 
        for (step = 0; step < ecc_steps; step++) {
                /* configure controller for WRITE access */
@@ -2374,8 +2376,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd,
        /* copy calculated ECC for whole page to chip->buffer->oob */
        /* this include masked-value(0xFF) for unwritten subpages */
        ecc_calc = chip->buffers->ecccalc;
-       for (i = 0; i < chip->ecc.total; i++)
-               chip->oob_poi[eccpos[i]] = ecc_calc[i];
+       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->write_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -2512,6 +2516,7 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
                              struct mtd_oob_ops *ops)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
+       int ret;
 
        /*
         * Initialise to all 0xFF, to avoid the possibility of left over OOB
@@ -2526,31 +2531,12 @@ static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
                memcpy(chip->oob_poi + ops->ooboffs, oob, len);
                return oob + len;
 
-       case MTD_OPS_AUTO_OOB: {
-               struct nand_oobfree *free = chip->ecc.layout->oobfree;
-               uint32_t boffs = 0, woffs = ops->ooboffs;
-               size_t bytes = 0;
-
-               for (; free->length && len; free++, len -= bytes) {
-                       /* Write request not from offset 0? */
-                       if (unlikely(woffs)) {
-                               if (woffs >= free->length) {
-                                       woffs -= free->length;
-                                       continue;
-                               }
-                               boffs = free->offset + woffs;
-                               bytes = min_t(size_t, len,
-                                             (free->length - woffs));
-                               woffs = 0;
-                       } else {
-                               bytes = min_t(size_t, len, free->length);
-                               boffs = free->offset;
-                       }
-                       memcpy(chip->oob_poi + boffs, oob, bytes);
-                       oob += bytes;
-               }
-               return oob;
-       }
+       case MTD_OPS_AUTO_OOB:
+               ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi,
+                                                 ops->ooboffs, len);
+               BUG_ON(ret);
+               return oob + len;
+
        default:
                BUG();
        }
@@ -2575,8 +2561,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
        uint32_t writelen = ops->len;
 
        uint32_t oobwritelen = ops->ooblen;
-       uint32_t oobmaxlen = ops->mode == MTD_OPS_AUTO_OOB ?
-                               mtd->oobavail : mtd->oobsize;
+       uint32_t oobmaxlen = mtd_oobavail(mtd, ops);
 
        uint8_t *oob = ops->oobbuf;
        uint8_t *buf = ops->datbuf;
@@ -2766,10 +2751,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
        pr_debug("%s: to = 0x%08x, len = %i\n",
                         __func__, (unsigned int)to, (int)ops->ooblen);
 
-       if (ops->mode == MTD_OPS_AUTO_OOB)
-               len = chip->ecc.layout->oobavail;
-       else
-               len = mtd->oobsize;
+       len = mtd_oobavail(mtd, ops);
 
        /* Do not allow write past end of page */
        if ((ops->ooboffs + ops->ooblen) > len) {
@@ -2957,7 +2939,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
        while (len) {
                /* Check if we have a bad block, we do not erase bad blocks! */
                if (nand_block_checkbad(mtd, ((loff_t) page) <<
-                                       chip->page_shift, 0, allowbbt)) {
+                                       chip->page_shift, allowbbt)) {
                        pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
                                    __func__, page);
                        instr->state = MTD_ERASE_FAILED;
@@ -3044,7 +3026,20 @@ static void nand_sync(struct mtd_info *mtd)
  */
 static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
 {
-       return nand_block_checkbad(mtd, offs, 1, 0);
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       int chipnr = (int)(offs >> chip->chip_shift);
+       int ret;
+
+       /* Select the NAND device */
+       nand_get_device(mtd, FL_READING);
+       chip->select_chip(mtd, chipnr);
+
+       ret = nand_block_checkbad(mtd, offs, 0);
+
+       chip->select_chip(mtd, -1);
+       nand_release_device(mtd);
+
+       return ret;
 }
 
 /**
@@ -3976,10 +3971,102 @@ ident_done:
        return type;
 }
 
+static const char * const nand_ecc_modes[] = {
+       [NAND_ECC_NONE]         = "none",
+       [NAND_ECC_SOFT]         = "soft",
+       [NAND_ECC_HW]           = "hw",
+       [NAND_ECC_HW_SYNDROME]  = "hw_syndrome",
+       [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
+       [NAND_ECC_SOFT_BCH]     = "soft_bch",
+};
+
+static int of_get_nand_ecc_mode(struct device_node *np)
+{
+       const char *pm;
+       int err, i;
+
+       err = of_property_read_string(np, "nand-ecc-mode", &pm);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++)
+               if (!strcasecmp(pm, nand_ecc_modes[i]))
+                       return i;
+
+       return -ENODEV;
+}
+
+static int of_get_nand_ecc_algo(struct device_node *np)
+{
+       const char *pm;
+       int err;
+
+       /*
+        * TODO: Read ECC algo OF property and map it to enum nand_ecc_algo.
+        * It's not implemented yet as currently NAND subsystem ignores
+        * algorithm explicitly set this way. Once it's handled we should
+        * document & support new property.
+        */
+
+       /*
+        * For backward compatibility we also read "nand-ecc-mode" checking
+        * for some obsoleted values that were specifying ECC algorithm.
+        */
+       err = of_property_read_string(np, "nand-ecc-mode", &pm);
+       if (err < 0)
+               return err;
+
+       if (!strcasecmp(pm, "soft"))
+               return NAND_ECC_HAMMING;
+       else if (!strcasecmp(pm, "soft_bch"))
+               return NAND_ECC_BCH;
+
+       return -ENODEV;
+}
+
+static int of_get_nand_ecc_step_size(struct device_node *np)
+{
+       int ret;
+       u32 val;
+
+       ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
+       return ret ? ret : val;
+}
+
+static int of_get_nand_ecc_strength(struct device_node *np)
+{
+       int ret;
+       u32 val;
+
+       ret = of_property_read_u32(np, "nand-ecc-strength", &val);
+       return ret ? ret : val;
+}
+
+static int of_get_nand_bus_width(struct device_node *np)
+{
+       u32 val;
+
+       if (of_property_read_u32(np, "nand-bus-width", &val))
+               return 8;
+
+       switch (val) {
+       case 8:
+       case 16:
+               return val;
+       default:
+               return -EIO;
+       }
+}
+
+static bool of_get_nand_on_flash_bbt(struct device_node *np)
+{
+       return of_property_read_bool(np, "nand-on-flash-bbt");
+}
+
 static int nand_dt_init(struct nand_chip *chip)
 {
        struct device_node *dn = nand_get_flash_node(chip);
-       int ecc_mode, ecc_strength, ecc_step;
+       int ecc_mode, ecc_algo, ecc_strength, ecc_step;
 
        if (!dn)
                return 0;
@@ -3991,6 +4078,7 @@ static int nand_dt_init(struct nand_chip *chip)
                chip->bbt_options |= NAND_BBT_USE_FLASH;
 
        ecc_mode = of_get_nand_ecc_mode(dn);
+       ecc_algo = of_get_nand_ecc_algo(dn);
        ecc_strength = of_get_nand_ecc_strength(dn);
        ecc_step = of_get_nand_ecc_step_size(dn);
 
@@ -4003,6 +4091,9 @@ static int nand_dt_init(struct nand_chip *chip)
        if (ecc_mode >= 0)
                chip->ecc.mode = ecc_mode;
 
+       if (ecc_algo >= 0)
+               chip->ecc.algo = ecc_algo;
+
        if (ecc_strength >= 0)
                chip->ecc.strength = ecc_strength;
 
@@ -4124,14 +4215,15 @@ static bool nand_ecc_strength_good(struct mtd_info *mtd)
  */
 int nand_scan_tail(struct mtd_info *mtd)
 {
-       int i;
        struct nand_chip *chip = mtd_to_nand(mtd);
        struct nand_ecc_ctrl *ecc = &chip->ecc;
        struct nand_buffers *nbuf;
+       int ret;
 
        /* New bad blocks should be marked in OOB, flash-based BBT, or both */
-       BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
-                       !(chip->bbt_options & NAND_BBT_USE_FLASH));
+       if (WARN_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
+                  !(chip->bbt_options & NAND_BBT_USE_FLASH)))
+               return -EINVAL;
 
        if (!(chip->options & NAND_OWN_BUFFERS)) {
                nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
@@ -4154,24 +4246,21 @@ int nand_scan_tail(struct mtd_info *mtd)
        /*
         * If no default placement scheme is given, select an appropriate one.
         */
-       if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) {
+       if (!mtd->ooblayout && (ecc->mode != NAND_ECC_SOFT_BCH)) {
                switch (mtd->oobsize) {
                case 8:
-                       ecc->layout = &nand_oob_8;
-                       break;
                case 16:
-                       ecc->layout = &nand_oob_16;
+                       mtd_set_ooblayout(mtd, &nand_ooblayout_sp_ops);
                        break;
                case 64:
-                       ecc->layout = &nand_oob_64;
-                       break;
                case 128:
-                       ecc->layout = &nand_oob_128;
+                       mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
                        break;
                default:
-                       pr_warn("No oob scheme defined for oobsize %d\n",
-                                  mtd->oobsize);
-                       BUG();
+                       WARN(1, "No oob scheme defined for oobsize %d\n",
+                               mtd->oobsize);
+                       ret = -EINVAL;
+                       goto err_free;
                }
        }
 
@@ -4187,8 +4276,9 @@ int nand_scan_tail(struct mtd_info *mtd)
        case NAND_ECC_HW_OOB_FIRST:
                /* Similar to NAND_ECC_HW, but a separate read_page handle */
                if (!ecc->calculate || !ecc->correct || !ecc->hwctl) {
-                       pr_warn("No ECC functions supplied; hardware ECC not possible\n");
-                       BUG();
+                       WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
+                       ret = -EINVAL;
+                       goto err_free;
                }
                if (!ecc->read_page)
                        ecc->read_page = nand_read_page_hwecc_oob_first;
@@ -4218,8 +4308,9 @@ int nand_scan_tail(struct mtd_info *mtd)
                     ecc->read_page == nand_read_page_hwecc ||
                     !ecc->write_page ||
                     ecc->write_page == nand_write_page_hwecc)) {
-                       pr_warn("No ECC functions supplied; hardware ECC not possible\n");
-                       BUG();
+                       WARN(1, "No ECC functions supplied; hardware ECC not possible\n");
+                       ret = -EINVAL;
+                       goto err_free;
                }
                /* Use standard syndrome read/write page function? */
                if (!ecc->read_page)
@@ -4237,14 +4328,16 @@ int nand_scan_tail(struct mtd_info *mtd)
 
                if (mtd->writesize >= ecc->size) {
                        if (!ecc->strength) {
-                               pr_warn("Driver must set ecc.strength when using hardware ECC\n");
-                               BUG();
+                               WARN(1, "Driver must set ecc.strength when using hardware ECC\n");
+                               ret = -EINVAL;
+                               goto err_free;
                        }
                        break;
                }
                pr_warn("%d byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
                        ecc->size, mtd->writesize);
                ecc->mode = NAND_ECC_SOFT;
+               ecc->algo = NAND_ECC_HAMMING;
 
        case NAND_ECC_SOFT:
                ecc->calculate = nand_calculate_ecc;
@@ -4264,8 +4357,9 @@ int nand_scan_tail(struct mtd_info *mtd)
 
        case NAND_ECC_SOFT_BCH:
                if (!mtd_nand_has_bch()) {
-                       pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n");
-                       BUG();
+                       WARN(1, "CONFIG_MTD_NAND_ECC_BCH not enabled\n");
+                       ret = -EINVAL;
+                       goto err_free;
                }
                ecc->calculate = nand_bch_calculate_ecc;
                ecc->correct = nand_bch_correct_data;
@@ -4286,14 +4380,28 @@ int nand_scan_tail(struct mtd_info *mtd)
                        ecc->strength = 4;
                }
 
+               /*
+                * if no ecc placement scheme was provided pickup the default
+                * large page one.
+                */
+               if (!mtd->ooblayout) {
+                       /* handle large page devices only */
+                       if (mtd->oobsize < 64) {
+                               WARN(1, "OOB layout is required when using software BCH on small pages\n");
+                               ret = -EINVAL;
+                               goto err_free;
+                       }
+
+                       mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+               }
+
                /* See nand_bch_init() for details. */
-               ecc->bytes = DIV_ROUND_UP(
-                               ecc->strength * fls(8 * ecc->size), 8);
-               ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes,
-                                              &ecc->layout);
+               ecc->bytes = 0;
+               ecc->priv = nand_bch_init(mtd);
                if (!ecc->priv) {
-                       pr_warn("BCH ECC initialization failed!\n");
-                       BUG();
+                       WARN(1, "BCH ECC initialization failed!\n");
+                       ret = -EINVAL;
+                       goto err_free;
                }
                break;
 
@@ -4311,8 +4419,9 @@ int nand_scan_tail(struct mtd_info *mtd)
                break;
 
        default:
-               pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode);
-               BUG();
+               WARN(1, "Invalid NAND_ECC_MODE %d\n", ecc->mode);
+               ret = -EINVAL;
+               goto err_free;
        }
 
        /* For many systems, the standard OOB write also works for raw */
@@ -4321,20 +4430,9 @@ int nand_scan_tail(struct mtd_info *mtd)
        if (!ecc->write_oob_raw)
                ecc->write_oob_raw = ecc->write_oob;
 
-       /*
-        * The number of bytes available for a client to place data into
-        * the out of band area.
-        */
-       ecc->layout->oobavail = 0;
-       for (i = 0; ecc->layout->oobfree[i].length
-                       && i < ARRAY_SIZE(ecc->layout->oobfree); i++)
-               ecc->layout->oobavail += ecc->layout->oobfree[i].length;
-       mtd->oobavail = ecc->layout->oobavail;
-
-       /* ECC sanity check: warn if it's too weak */
-       if (!nand_ecc_strength_good(mtd))
-               pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
-                       mtd->name);
+       /* propagate ecc info to mtd_info */
+       mtd->ecc_strength = ecc->strength;
+       mtd->ecc_step_size = ecc->size;
 
        /*
         * Set the number of read / write steps for one page depending on ECC
@@ -4342,11 +4440,27 @@ int nand_scan_tail(struct mtd_info *mtd)
         */
        ecc->steps = mtd->writesize / ecc->size;
        if (ecc->steps * ecc->size != mtd->writesize) {
-               pr_warn("Invalid ECC parameters\n");
-               BUG();
+               WARN(1, "Invalid ECC parameters\n");
+               ret = -EINVAL;
+               goto err_free;
        }
        ecc->total = ecc->steps * ecc->bytes;
 
+       /*
+        * The number of bytes available for a client to place data into
+        * the out of band area.
+        */
+       ret = mtd_ooblayout_count_freebytes(mtd);
+       if (ret < 0)
+               ret = 0;
+
+       mtd->oobavail = ret;
+
+       /* ECC sanity check: warn if it's too weak */
+       if (!nand_ecc_strength_good(mtd))
+               pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
+                       mtd->name);
+
        /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */
        if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) {
                switch (ecc->steps) {
@@ -4403,10 +4517,6 @@ int nand_scan_tail(struct mtd_info *mtd)
        mtd->_block_markbad = nand_block_markbad;
        mtd->writebufsize = mtd->writesize;
 
-       /* propagate ecc info to mtd_info */
-       mtd->ecclayout = ecc->layout;
-       mtd->ecc_strength = ecc->strength;
-       mtd->ecc_step_size = ecc->size;
        /*
         * Initialize bitflip_threshold to its default prior scan_bbt() call.
         * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be
@@ -4421,6 +4531,10 @@ int nand_scan_tail(struct mtd_info *mtd)
 
        /* Build bad block table */
        return chip->scan_bbt(mtd);
+err_free:
+       if (!(chip->options & NAND_OWN_BUFFERS))
+               kfree(chip->buffers);
+       return ret;
 }
 EXPORT_SYMBOL(nand_scan_tail);
 
@@ -4488,20 +4602,6 @@ void nand_release(struct mtd_info *mtd)
 }
 EXPORT_SYMBOL_GPL(nand_release);
 
-static int __init nand_base_init(void)
-{
-       led_trigger_register_simple("nand-disk", &nand_led_trigger);
-       return 0;
-}
-
-static void __exit nand_base_exit(void)
-{
-       led_trigger_unregister_simple(nand_led_trigger);
-}
-
-module_init(nand_base_init);
-module_exit(nand_base_exit);
-
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
 MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
This page took 0.041192 seconds and 5 git commands to generate.