summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/mtd/mtdchar.c4
-rw-r--r--drivers/mtd/mtdconcat.c2
-rw-r--r--drivers/mtd/mtdcore.c165
-rw-r--r--drivers/mtd/mtdpart.c23
-rw-r--r--include/linux/mtd/mtd.h32
5 files changed, 174 insertions, 52 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index cd64ab76dd7b..3fad2c7425b0 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -888,7 +888,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct nand_oobinfo oi;
- if (!mtd->ecclayout)
+ if (!mtd->ooblayout)
return -EOPNOTSUPP;
ret = get_oobinfo(mtd, &oi);
@@ -982,7 +982,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct nand_ecclayout_user *usrlay;
- if (!mtd->ecclayout)
+ if (!mtd->ooblayout)
return -EOPNOTSUPP;
usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c
index 481565e5fbaa..d573606b91c2 100644
--- a/drivers/mtd/mtdconcat.c
+++ b/drivers/mtd/mtdconcat.c
@@ -777,7 +777,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
}
- mtd_set_ecclayout(&concat->mtd, subdev[0]->ecclayout);
+ mtd_set_ooblayout(&concat->mtd, subdev[0]->ooblayout);
concat->num_subdev = num_devs;
concat->mtd.name = name;
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 0290c41e44fc..134ed2f7b919 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -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
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index f53d9d72b23a..1f13e32556f8 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -317,6 +317,27 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
return res;
}
+static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct mtd_part *part = mtd_to_part(mtd);
+
+ return mtd_ooblayout_ecc(part->master, section, oobregion);
+}
+
+static int part_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct mtd_part *part = mtd_to_part(mtd);
+
+ return mtd_ooblayout_free(part->master, section, oobregion);
+}
+
+static const struct mtd_ooblayout_ops part_ooblayout_ops = {
+ .ecc = part_ooblayout_ecc,
+ .free = part_ooblayout_free,
+};
+
static inline void free_partition(struct mtd_part *p)
{
kfree(p->mtd.name);
@@ -533,7 +554,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
part->name);
}
- mtd_set_ecclayout(&slave->mtd, master->ecclayout);
+ mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops);
slave->mtd.ecc_step_size = master->ecc_step_size;
slave->mtd.ecc_strength = master->ecc_strength;
slave->mtd.bitflip_threshold = master->bitflip_threshold;
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h
index e62da8462493..177bf314ad70 100644
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -101,6 +101,9 @@ struct mtd_oob_ops {
* similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained
* for export to user-space via the ECCGETLAYOUT ioctl.
* nand_ecclayout should be expandable in the future simply by the above macros.
+ *
+ * This structure is now deprecated, you should use struct nand_ecclayout_ops
+ * to describe your OOB layout.
*/
struct nand_ecclayout {
__u32 eccbytes;
@@ -123,6 +126,22 @@ struct mtd_oob_region {
u32 length;
};
+/*
+ * struct mtd_ooblayout_ops - NAND OOB layout operations
+ * @ecc: function returning an ECC region in the OOB area.
+ * Should return -ERANGE if %section exceeds the total number of
+ * ECC sections.
+ * @free: function returning a free region in the OOB area.
+ * Should return -ERANGE if %section exceeds the total number of
+ * free sections.
+ */
+struct mtd_ooblayout_ops {
+ int (*ecc)(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobecc);
+ int (*free)(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobfree);
+};
+
struct module; /* only needed for owner field in mtd_info */
struct mtd_info {
@@ -181,9 +200,12 @@ struct mtd_info {
const char *name;
int index;
- /* ECC layout structure pointer - read only! */
+ /* [Deprecated] ECC layout structure pointer - read only! */
struct nand_ecclayout *ecclayout;
+ /* OOB layout description */
+ const struct mtd_ooblayout_ops *ooblayout;
+
/* the ecc step size. */
unsigned int ecc_step_size;
@@ -286,10 +308,12 @@ int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
int mtd_ooblayout_count_freebytes(struct mtd_info *mtd);
int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd);
-static inline void mtd_set_ecclayout(struct mtd_info *mtd,
- struct nand_ecclayout *ecclayout)
+void mtd_set_ecclayout(struct mtd_info *mtd, struct nand_ecclayout *ecclayout);
+
+static inline void mtd_set_ooblayout(struct mtd_info *mtd,
+ const struct mtd_ooblayout_ops *ooblayout)
{
- mtd->ecclayout = ecclayout;
+ mtd->ooblayout = ooblayout;
}
static inline void mtd_set_of_node(struct mtd_info *mtd,