]> 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 ed49a1d634b04487998c0296ecdaf681e9d4da3f..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
@@ -2401,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)
@@ -2410,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
@@ -3350,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
@@ -4109,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)
@@ -4303,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;
 
@@ -4559,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",
@@ -4649,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;