mtd: nand: set ECC algorithm to Hamming on fallback
[deliverable/linux.git] / drivers / mtd / nand / nand_base.c
index e8332ea457392d52292cce15557ee0fe652ca724..2a049576ab31f0f025e68f4b89037935938ec531 100644 (file)
@@ -45,7 +45,7 @@
 #include <linux/bitops.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);
 
@@ -3971,6 +3971,98 @@ 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);
@@ -4151,13 +4243,6 @@ int nand_scan_tail(struct mtd_info *mtd)
        /* Set the internal oob buffer location, just after the page data */
        chip->oob_poi = chip->buffers->databuf + mtd->writesize;
 
-       /*
-        * Set the provided ECC layout. If ecc->layout is NULL, the MTD core
-        * will just leave mtd->ooblayout to NULL, if it's not NULL, it will
-        * set ->ooblayout to the default ecclayout wrapper.
-        */
-       mtd_set_ecclayout(mtd, ecc->layout);
-
        /*
         * If no default placement scheme is given, select an appropriate one.
         */
@@ -4252,6 +4337,7 @@ int nand_scan_tail(struct mtd_info *mtd)
                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;
@@ -4294,6 +4380,21 @@ 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 = 0;
                ecc->priv = nand_bch_init(mtd);
This page took 0.027386 seconds and 5 git commands to generate.