]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/mtd/nand/nand_base.c
mtd: nand: Pass the CS line to ->setup_data_interface()
[karo-tx-linux.git] / drivers / mtd / nand / nand_base.c
index 36258e69a3897b085fb94029df6cabde3f9d4354..3317acf0ce04d5575cbf755e01ffcf0c97d56052 100644 (file)
@@ -139,6 +139,74 @@ const struct mtd_ooblayout_ops nand_ooblayout_lp_ops = {
 };
 EXPORT_SYMBOL_GPL(nand_ooblayout_lp_ops);
 
+/*
+ * Support the old "large page" layout used for 1-bit Hamming ECC where ECC
+ * are placed at a fixed offset.
+ */
+static int nand_ooblayout_ecc_lp_hamming(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;
+
+       switch (mtd->oobsize) {
+       case 64:
+               oobregion->offset = 40;
+               break;
+       case 128:
+               oobregion->offset = 80;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       oobregion->length = ecc->total;
+       if (oobregion->offset + oobregion->length > mtd->oobsize)
+               return -ERANGE;
+
+       return 0;
+}
+
+static int nand_ooblayout_free_lp_hamming(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;
+       int ecc_offset = 0;
+
+       if (section < 0 || section > 1)
+               return -ERANGE;
+
+       switch (mtd->oobsize) {
+       case 64:
+               ecc_offset = 40;
+               break;
+       case 128:
+               ecc_offset = 80;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (section == 0) {
+               oobregion->offset = 2;
+               oobregion->length = ecc_offset - 2;
+       } else {
+               oobregion->offset = ecc_offset + ecc->total;
+               oobregion->length = mtd->oobsize - oobregion->offset;
+       }
+
+       return 0;
+}
+
+const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = {
+       .ecc = nand_ooblayout_ecc_lp_hamming,
+       .free = nand_ooblayout_free_lp_hamming,
+};
+
 static int check_offs_len(struct mtd_info *mtd,
                                        loff_t ofs, uint64_t len)
 {
@@ -974,12 +1042,13 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
 /**
  * nand_reset_data_interface - Reset data interface and timings
  * @chip: The NAND chip
+ * @chipnr: Internal die id
  *
  * Reset the Data interface and timings to ONFI mode 0.
  *
  * Returns 0 for success or negative error code otherwise.
  */
-static int nand_reset_data_interface(struct nand_chip *chip)
+static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
 {
        struct mtd_info *mtd = nand_to_mtd(chip);
        const struct nand_data_interface *conf;
@@ -1003,7 +1072,7 @@ static int nand_reset_data_interface(struct nand_chip *chip)
         */
 
        conf = nand_get_default_data_interface();
-       ret = chip->setup_data_interface(mtd, conf, false);
+       ret = chip->setup_data_interface(mtd, chipnr, conf);
        if (ret)
                pr_err("Failed to configure data interface to SDR timing mode 0\n");
 
@@ -1013,6 +1082,7 @@ static int nand_reset_data_interface(struct nand_chip *chip)
 /**
  * nand_setup_data_interface - Setup the best data interface and timings
  * @chip: The NAND chip
+ * @chipnr: Internal die id
  *
  * Find and configure the best data interface and NAND timings supported by
  * the chip and the driver.
@@ -1022,7 +1092,7 @@ static int nand_reset_data_interface(struct nand_chip *chip)
  *
  * Returns 0 for success or negative error code otherwise.
  */
-static int nand_setup_data_interface(struct nand_chip *chip)
+static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
 {
        struct mtd_info *mtd = nand_to_mtd(chip);
        int ret;
@@ -1046,7 +1116,7 @@ static int nand_setup_data_interface(struct nand_chip *chip)
                        goto err;
        }
 
-       ret = chip->setup_data_interface(mtd, chip->data_interface, false);
+       ret = chip->setup_data_interface(mtd, chipnr, chip->data_interface);
 err:
        return ret;
 }
@@ -1097,8 +1167,10 @@ static int nand_init_data_interface(struct nand_chip *chip)
                if (ret)
                        continue;
 
-               ret = chip->setup_data_interface(mtd, chip->data_interface,
-                                                true);
+               /* Pass -1 to only */
+               ret = chip->setup_data_interface(mtd,
+                                                NAND_DATA_IFACE_CHECK_ONLY,
+                                                chip->data_interface);
                if (!ret) {
                        chip->onfi_timing_mode_default = mode;
                        break;
@@ -1125,7 +1197,7 @@ int nand_reset(struct nand_chip *chip, int chipnr)
        struct mtd_info *mtd = nand_to_mtd(chip);
        int ret;
 
-       ret = nand_reset_data_interface(chip);
+       ret = nand_reset_data_interface(chip, chipnr);
        if (ret)
                return ret;
 
@@ -1138,7 +1210,7 @@ int nand_reset(struct nand_chip *chip, int chipnr)
        chip->select_chip(mtd, -1);
 
        chip->select_chip(mtd, chipnr);
-       ret = nand_setup_data_interface(chip);
+       ret = nand_setup_data_interface(chip, chipnr);
        chip->select_chip(mtd, -1);
        if (ret)
                return ret;
@@ -1353,7 +1425,10 @@ static int nand_check_erased_buf(void *buf, int len, int bitflips_threshold)
 
        for (; len >= sizeof(long);
             len -= sizeof(long), bitmap += sizeof(long)) {
-               weight = hweight_long(*((unsigned long *)bitmap));
+               unsigned long d = *((unsigned long *)bitmap);
+               if (d == ~0UL)
+                       continue;
+               weight = hweight_long(d);
                bitflips += BITS_PER_LONG - weight;
                if (unlikely(bitflips > bitflips_threshold))
                        return -EBADMSG;
@@ -1456,14 +1531,15 @@ EXPORT_SYMBOL(nand_check_erased_ecc_chunk);
  *
  * Not for syndrome calculating ECC controllers, which use a special oob layout.
  */
-static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
-                             uint8_t *buf, int oob_required, int page)
+int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+                      uint8_t *buf, int oob_required, int page)
 {
        chip->read_buf(mtd, buf, mtd->writesize);
        if (oob_required)
                chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
        return 0;
 }
+EXPORT_SYMBOL(nand_read_page_raw);
 
 /**
  * nand_read_page_raw_syndrome - [INTERN] read raw page data without ecc
@@ -1954,7 +2030,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
                if (!aligned)
                        use_bufpoi = 1;
                else if (chip->options & NAND_USE_BOUNCE_BUFFER)
-                       use_bufpoi = !virt_addr_valid(buf);
+                       use_bufpoi = !virt_addr_valid(buf) ||
+                                    !IS_ALIGNED((unsigned long)buf,
+                                                chip->buf_align);
                else
                        use_bufpoi = 0;
 
@@ -1993,8 +2071,6 @@ read_retry:
                                break;
                        }
 
-                       max_bitflips = max_t(unsigned int, max_bitflips, ret);
-
                        /* Transfer not aligned data */
                        if (use_bufpoi) {
                                if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
@@ -2045,6 +2121,7 @@ read_retry:
                        }
 
                        buf += bytes;
+                       max_bitflips = max_t(unsigned int, max_bitflips, ret);
                } else {
                        memcpy(buf, chip->buffers->databuf + col, bytes);
                        buf += bytes;
@@ -2400,8 +2477,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
  *
  * Not for syndrome calculating ECC controllers, which use a special oob layout.
  */
-static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
-                              const uint8_t *buf, int oob_required, int page)
+int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+                       const uint8_t *buf, int oob_required, int page)
 {
        chip->write_buf(mtd, buf, mtd->writesize);
        if (oob_required)
@@ -2409,6 +2486,7 @@ static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
 
        return 0;
 }
+EXPORT_SYMBOL(nand_write_page_raw);
 
 /**
  * nand_write_page_raw_syndrome - [INTERN] raw page write function
@@ -2811,7 +2889,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
                if (part_pagewr)
                        use_bufpoi = 1;
                else if (chip->options & NAND_USE_BOUNCE_BUFFER)
-                       use_bufpoi = !virt_addr_valid(buf);
+                       use_bufpoi = !virt_addr_valid(buf) ||
+                                    !IS_ALIGNED((unsigned long)buf,
+                                                chip->buf_align);
                else
                        use_bufpoi = 0;
 
@@ -3347,6 +3427,25 @@ static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip,
        return 0;
 }
 
+/**
+ * nand_onfi_get_set_features_notsupp - set/get features stub returning
+ *                                     -ENOTSUPP
+ * @mtd: MTD device structure
+ * @chip: nand chip info structure
+ * @addr: feature address.
+ * @subfeature_param: the subfeature parameters, a four bytes array.
+ *
+ * Should be used by NAND controller drivers that do not support the SET/GET
+ * FEATURES operations.
+ */
+int nand_onfi_get_set_features_notsupp(struct mtd_info *mtd,
+                                      struct nand_chip *chip, int addr,
+                                      u8 *subfeature_param)
+{
+       return -ENOTSUPP;
+}
+EXPORT_SYMBOL(nand_onfi_get_set_features_notsupp);
+
 /**
  * nand_suspend - [MTD Interface] Suspend the NAND flash
  * @mtd: MTD device structure
@@ -3430,6 +3529,8 @@ static void nand_set_defaults(struct nand_chip *chip)
                nand_hw_control_init(chip->controller);
        }
 
+       if (!chip->buf_align)
+               chip->buf_align = 1;
 }
 
 /* Sanitize ONFI strings so we can safely print them */
@@ -4104,6 +4205,7 @@ static const char * const nand_ecc_modes[] = {
        [NAND_ECC_HW]           = "hw",
        [NAND_ECC_HW_SYNDROME]  = "hw_syndrome",
        [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
+       [NAND_ECC_ON_DIE]       = "on-die",
 };
 
 static int of_get_nand_ecc_mode(struct device_node *np)
@@ -4298,7 +4400,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
         * For the other dies, nand_reset() will automatically switch to the
         * best mode for us.
         */
-       ret = nand_setup_data_interface(chip);
+       ret = nand_setup_data_interface(chip, 0);
        if (ret)
                return ret;
 
@@ -4496,7 +4598,7 @@ int nand_scan_tail(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
        struct nand_ecc_ctrl *ecc = &chip->ecc;
-       struct nand_buffers *nbuf;
+       struct nand_buffers *nbuf = NULL;
        int ret;
 
        /* New bad blocks should be marked in OOB, flash-based BBT, or both */
@@ -4510,13 +4612,28 @@ int nand_scan_tail(struct mtd_info *mtd)
        }
 
        if (!(chip->options & NAND_OWN_BUFFERS)) {
-               nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
-                               + mtd->oobsize * 3, GFP_KERNEL);
+               nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL);
                if (!nbuf)
                        return -ENOMEM;
-               nbuf->ecccalc = (uint8_t *)(nbuf + 1);
-               nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
-               nbuf->databuf = nbuf->ecccode + mtd->oobsize;
+
+               nbuf->ecccalc = kmalloc(mtd->oobsize, GFP_KERNEL);
+               if (!nbuf->ecccalc) {
+                       ret = -ENOMEM;
+                       goto err_free;
+               }
+
+               nbuf->ecccode = kmalloc(mtd->oobsize, GFP_KERNEL);
+               if (!nbuf->ecccode) {
+                       ret = -ENOMEM;
+                       goto err_free;
+               }
+
+               nbuf->databuf = kmalloc(mtd->writesize + mtd->oobsize,
+                                       GFP_KERNEL);
+               if (!nbuf->databuf) {
+                       ret = -ENOMEM;
+                       goto err_free;
+               }
 
                chip->buffers = nbuf;
        } else {
@@ -4539,7 +4656,7 @@ int nand_scan_tail(struct mtd_info *mtd)
                        break;
                case 64:
                case 128:
-                       mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+                       mtd_set_ooblayout(mtd, &nand_ooblayout_lp_hamming_ops);
                        break;
                default:
                        WARN(1, "No oob scheme defined for oobsize %d\n",
@@ -4629,6 +4746,18 @@ int nand_scan_tail(struct mtd_info *mtd)
                }
                break;
 
+       case NAND_ECC_ON_DIE:
+               if (!ecc->read_page || !ecc->write_page) {
+                       WARN(1, "No ECC functions supplied; on-die ECC not possible\n");
+                       ret = -EINVAL;
+                       goto err_free;
+               }
+               if (!ecc->read_oob)
+                       ecc->read_oob = nand_read_oob_std;
+               if (!ecc->write_oob)
+                       ecc->write_oob = nand_write_oob_std;
+               break;
+
        case NAND_ECC_NONE:
                pr_warn("NAND_ECC_NONE selected by board driver. This is not recommended!\n");
                ecc->read_page = nand_read_page_raw;
@@ -4756,8 +4885,12 @@ 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);
+       if (nbuf) {
+               kfree(nbuf->databuf);
+               kfree(nbuf->ecccode);
+               kfree(nbuf->ecccalc);
+               kfree(nbuf);
+       }
        return ret;
 }
 EXPORT_SYMBOL(nand_scan_tail);
@@ -4808,8 +4941,12 @@ void nand_cleanup(struct nand_chip *chip)
 
        /* Free bad block table memory */
        kfree(chip->bbt);
-       if (!(chip->options & NAND_OWN_BUFFERS))
+       if (!(chip->options & NAND_OWN_BUFFERS) && chip->buffers) {
+               kfree(chip->buffers->databuf);
+               kfree(chip->buffers->ecccode);
+               kfree(chip->buffers->ecccalc);
                kfree(chip->buffers);
+       }
 
        /* Free bad block descriptor memory */
        if (chip->badblock_pattern && chip->badblock_pattern->options