|
@@ -1035,49 +1035,15 @@ EXPORT_SYMBOL_GPL(mtd_write_oob);
|
|
|
int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
|
|
|
struct mtd_oob_region *oobecc)
|
|
|
{
|
|
|
- int eccbyte = 0, cursection = 0, length = 0, eccpos = 0;
|
|
|
-
|
|
|
memset(oobecc, 0, sizeof(*oobecc));
|
|
|
|
|
|
if (!mtd || section < 0)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (!mtd->ecclayout)
|
|
|
+ if (!mtd->ooblayout || !mtd->ooblayout->ecc)
|
|
|
return -ENOTSUPP;
|
|
|
|
|
|
- /*
|
|
|
- * This logic allows us to reuse the ->ecclayout information and
|
|
|
- * expose them as ECC regions (as done for the OOB free regions).
|
|
|
- *
|
|
|
- * TODO: this should be dropped as soon as we get rid of the
|
|
|
- * ->ecclayout field.
|
|
|
- */
|
|
|
- for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) {
|
|
|
- eccpos = mtd->ecclayout->eccpos[eccbyte];
|
|
|
-
|
|
|
- if (eccbyte < mtd->ecclayout->eccbytes - 1) {
|
|
|
- int neccpos = mtd->ecclayout->eccpos[eccbyte + 1];
|
|
|
-
|
|
|
- if (eccpos + 1 == neccpos) {
|
|
|
- length++;
|
|
|
- continue;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (section == cursection)
|
|
|
- break;
|
|
|
-
|
|
|
- length = 0;
|
|
|
- cursection++;
|
|
|
- }
|
|
|
-
|
|
|
- if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes)
|
|
|
- return -ERANGE;
|
|
|
-
|
|
|
- oobecc->length = length + 1;
|
|
|
- oobecc->offset = eccpos - length;
|
|
|
-
|
|
|
- return 0;
|
|
|
+ return mtd->ooblayout->ecc(mtd, section, oobecc);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
|
|
|
|
|
@@ -1106,16 +1072,10 @@ int mtd_ooblayout_free(struct mtd_info *mtd, int section,
|
|
|
if (!mtd || section < 0)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (!mtd->ecclayout)
|
|
|
+ if (!mtd->ooblayout || !mtd->ooblayout->free)
|
|
|
return -ENOTSUPP;
|
|
|
|
|
|
- if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE)
|
|
|
- return -ERANGE;
|
|
|
-
|
|
|
- oobfree->offset = mtd->ecclayout->oobfree[section].offset;
|
|
|
- oobfree->length = mtd->ecclayout->oobfree[section].length;
|
|
|
-
|
|
|
- return 0;
|
|
|
+ return mtd->ooblayout->free(mtd, section, oobfree);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
|
|
|
|
|
@@ -1416,6 +1376,123 @@ int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes);
|
|
|
|
|
|
+/**
|
|
|
+ * mtd_ecclayout_ecc - Default ooblayout_ecc iterator implementation
|
|
|
+ * @mtd: MTD device structure
|
|
|
+ * @section: ECC section. Depending on the layout you may have all the ECC
|
|
|
+ * bytes stored in a single contiguous section, or one section
|
|
|
+ * per ECC chunk (and sometime several sections for a single ECC
|
|
|
+ * ECC chunk)
|
|
|
+ * @oobecc: OOB region struct filled with the appropriate ECC position
|
|
|
+ * information
|
|
|
+ *
|
|
|
+ * This function is just a wrapper around the mtd->ecclayout field and is
|
|
|
+ * here to ease the transition to the mtd_ooblayout_ops approach.
|
|
|
+ * All it does is convert the layout->eccpos information into proper oob
|
|
|
+ * region definitions.
|
|
|
+ *
|
|
|
+ * Returns zero on success, a negative error code otherwise.
|
|
|
+ */
|
|
|
+static int mtd_ecclayout_ecc(struct mtd_info *mtd, int section,
|
|
|
+ struct mtd_oob_region *oobecc)
|
|
|
+{
|
|
|
+ int eccbyte = 0, cursection = 0, length = 0, eccpos = 0;
|
|
|
+
|
|
|
+ if (!mtd->ecclayout)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * This logic allows us to reuse the ->ecclayout information and
|
|
|
+ * expose them as ECC regions (as done for the OOB free regions).
|
|
|
+ *
|
|
|
+ * TODO: this should be dropped as soon as we get rid of the
|
|
|
+ * ->ecclayout field.
|
|
|
+ */
|
|
|
+ for (eccbyte = 0; eccbyte < mtd->ecclayout->eccbytes; eccbyte++) {
|
|
|
+ eccpos = mtd->ecclayout->eccpos[eccbyte];
|
|
|
+
|
|
|
+ if (eccbyte < mtd->ecclayout->eccbytes - 1) {
|
|
|
+ int neccpos = mtd->ecclayout->eccpos[eccbyte + 1];
|
|
|
+
|
|
|
+ if (eccpos + 1 == neccpos) {
|
|
|
+ length++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (section == cursection)
|
|
|
+ break;
|
|
|
+
|
|
|
+ length = 0;
|
|
|
+ cursection++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cursection != section || eccbyte >= mtd->ecclayout->eccbytes)
|
|
|
+ return -ERANGE;
|
|
|
+
|
|
|
+ oobecc->length = length + 1;
|
|
|
+ oobecc->offset = eccpos - length;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * mtd_ecclayout_ecc - Default ooblayout_free iterator implementation
|
|
|
+ * @mtd: MTD device structure
|
|
|
+ * @section: Free section. Depending on the layout you may have all the free
|
|
|
+ * bytes stored in a single contiguous section, or one section
|
|
|
+ * per ECC chunk (and sometime several sections for a single ECC
|
|
|
+ * ECC chunk)
|
|
|
+ * @oobfree: OOB region struct filled with the appropriate free position
|
|
|
+ * information
|
|
|
+ *
|
|
|
+ * This function is just a wrapper around the mtd->ecclayout field and is
|
|
|
+ * here to ease the transition to the mtd_ooblayout_ops approach.
|
|
|
+ * All it does is convert the layout->oobfree information into proper oob
|
|
|
+ * region definitions.
|
|
|
+ *
|
|
|
+ * Returns zero on success, a negative error code otherwise.
|
|
|
+ */
|
|
|
+static int mtd_ecclayout_free(struct mtd_info *mtd, int section,
|
|
|
+ struct mtd_oob_region *oobfree)
|
|
|
+{
|
|
|
+ struct nand_ecclayout *layout = mtd->ecclayout;
|
|
|
+
|
|
|
+ if (!layout)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ if (section >= MTD_MAX_OOBFREE_ENTRIES_LARGE ||
|
|
|
+ !layout->oobfree[section].length)
|
|
|
+ return -ERANGE;
|
|
|
+
|
|
|
+ oobfree->offset = layout->oobfree[section].offset;
|
|
|
+ oobfree->length = layout->oobfree[section].length;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct mtd_ooblayout_ops mtd_ecclayout_wrapper_ops = {
|
|
|
+ .ecc = mtd_ecclayout_ecc,
|
|
|
+ .free = mtd_ecclayout_free,
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * mtd_set_ecclayout - Attach an ecclayout to an MTD device
|
|
|
+ * @mtd: MTD device structure
|
|
|
+ * @ecclayout: The ecclayout to attach to the device
|
|
|
+ *
|
|
|
+ * Returns zero on success, a negative error code otherwise.
|
|
|
+ */
|
|
|
+void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout)
|
|
|
+{
|
|
|
+ if (!mtd || !ecclayout)
|
|
|
+ return;
|
|
|
+
|
|
|
+ mtd->ecclayout = ecclayout;
|
|
|
+ mtd_set_ooblayout(mtd, &mtd_ecclayout_wrapper_ops);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(mtd_set_ecclayout);
|
|
|
+
|
|
|
/*
|
|
|
* Method to access the protection register area, present in some flash
|
|
|
* devices. The user data is one time programmable but the factory data is read
|