|
@@ -127,6 +127,82 @@ struct mtd_ooblayout_ops {
|
|
|
struct mtd_oob_region *oobfree);
|
|
|
};
|
|
|
|
|
|
+/**
|
|
|
+ * struct mtd_pairing_info - page pairing information
|
|
|
+ *
|
|
|
+ * @pair: pair id
|
|
|
+ * @group: group id
|
|
|
+ *
|
|
|
+ * The term "pair" is used here, even though TLC NANDs might group pages by 3
|
|
|
+ * (3 bits in a single cell). A pair should regroup all pages that are sharing
|
|
|
+ * the same cell. Pairs are then indexed in ascending order.
|
|
|
+ *
|
|
|
+ * @group is defining the position of a page in a given pair. It can also be
|
|
|
+ * seen as the bit position in the cell: page attached to bit 0 belongs to
|
|
|
+ * group 0, page attached to bit 1 belongs to group 1, etc.
|
|
|
+ *
|
|
|
+ * Example:
|
|
|
+ * The H27UCG8T2BTR-BC datasheet describes the following pairing scheme:
|
|
|
+ *
|
|
|
+ * group-0 group-1
|
|
|
+ *
|
|
|
+ * pair-0 page-0 page-4
|
|
|
+ * pair-1 page-1 page-5
|
|
|
+ * pair-2 page-2 page-8
|
|
|
+ * ...
|
|
|
+ * pair-127 page-251 page-255
|
|
|
+ *
|
|
|
+ *
|
|
|
+ * Note that the "group" and "pair" terms were extracted from Samsung and
|
|
|
+ * Hynix datasheets, and might be referenced under other names in other
|
|
|
+ * datasheets (Micron is describing this concept as "shared pages").
|
|
|
+ */
|
|
|
+struct mtd_pairing_info {
|
|
|
+ int pair;
|
|
|
+ int group;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * struct mtd_pairing_scheme - page pairing scheme description
|
|
|
+ *
|
|
|
+ * @ngroups: number of groups. Should be related to the number of bits
|
|
|
+ * per cell.
|
|
|
+ * @get_info: converts a write-unit (page number within an erase block) into
|
|
|
+ * mtd_pairing information (pair + group). This function should
|
|
|
+ * fill the info parameter based on the wunit index or return
|
|
|
+ * -EINVAL if the wunit parameter is invalid.
|
|
|
+ * @get_wunit: converts pairing information into a write-unit (page) number.
|
|
|
+ * This function should return the wunit index pointed by the
|
|
|
+ * pairing information described in the info argument. It should
|
|
|
+ * return -EINVAL, if there's no wunit corresponding to the
|
|
|
+ * passed pairing information.
|
|
|
+ *
|
|
|
+ * See mtd_pairing_info documentation for a detailed explanation of the
|
|
|
+ * pair and group concepts.
|
|
|
+ *
|
|
|
+ * The mtd_pairing_scheme structure provides a generic solution to represent
|
|
|
+ * NAND page pairing scheme. Instead of exposing two big tables to do the
|
|
|
+ * write-unit <-> (pair + group) conversions, we ask the MTD drivers to
|
|
|
+ * implement the ->get_info() and ->get_wunit() functions.
|
|
|
+ *
|
|
|
+ * MTD users will then be able to query these information by using the
|
|
|
+ * mtd_pairing_info_to_wunit() and mtd_wunit_to_pairing_info() helpers.
|
|
|
+ *
|
|
|
+ * @ngroups is here to help MTD users iterating over all the pages in a
|
|
|
+ * given pair. This value can be retrieved by MTD users using the
|
|
|
+ * mtd_pairing_groups() helper.
|
|
|
+ *
|
|
|
+ * Examples are given in the mtd_pairing_info_to_wunit() and
|
|
|
+ * mtd_wunit_to_pairing_info() documentation.
|
|
|
+ */
|
|
|
+struct mtd_pairing_scheme {
|
|
|
+ int ngroups;
|
|
|
+ int (*get_info)(struct mtd_info *mtd, int wunit,
|
|
|
+ struct mtd_pairing_info *info);
|
|
|
+ int (*get_wunit)(struct mtd_info *mtd,
|
|
|
+ const struct mtd_pairing_info *info);
|
|
|
+};
|
|
|
+
|
|
|
struct module; /* only needed for owner field in mtd_info */
|
|
|
|
|
|
struct mtd_info {
|
|
@@ -188,6 +264,9 @@ struct mtd_info {
|
|
|
/* OOB layout description */
|
|
|
const struct mtd_ooblayout_ops *ooblayout;
|
|
|
|
|
|
+ /* NAND pairing scheme, only provided for MLC/TLC NANDs */
|
|
|
+ const struct mtd_pairing_scheme *pairing;
|
|
|
+
|
|
|
/* the ecc step size. */
|
|
|
unsigned int ecc_step_size;
|
|
|
|
|
@@ -296,6 +375,12 @@ static inline void mtd_set_ooblayout(struct mtd_info *mtd,
|
|
|
mtd->ooblayout = ooblayout;
|
|
|
}
|
|
|
|
|
|
+static inline void mtd_set_pairing_scheme(struct mtd_info *mtd,
|
|
|
+ const struct mtd_pairing_scheme *pairing)
|
|
|
+{
|
|
|
+ mtd->pairing = pairing;
|
|
|
+}
|
|
|
+
|
|
|
static inline void mtd_set_of_node(struct mtd_info *mtd,
|
|
|
struct device_node *np)
|
|
|
{
|
|
@@ -312,6 +397,11 @@ static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops)
|
|
|
return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
|
|
|
}
|
|
|
|
|
|
+int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
|
|
|
+ struct mtd_pairing_info *info);
|
|
|
+int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
|
|
|
+ const struct mtd_pairing_info *info);
|
|
|
+int mtd_pairing_groups(struct mtd_info *mtd);
|
|
|
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr);
|
|
|
int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
|
|
|
void **virt, resource_size_t *phys);
|
|
@@ -397,6 +487,23 @@ static inline uint32_t mtd_mod_by_ws(uint64_t sz, struct mtd_info *mtd)
|
|
|
return do_div(sz, mtd->writesize);
|
|
|
}
|
|
|
|
|
|
+static inline int mtd_wunit_per_eb(struct mtd_info *mtd)
|
|
|
+{
|
|
|
+ return mtd->erasesize / mtd->writesize;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int mtd_offset_to_wunit(struct mtd_info *mtd, loff_t offs)
|
|
|
+{
|
|
|
+ return mtd_div_by_ws(mtd_mod_by_eb(offs, mtd), mtd);
|
|
|
+}
|
|
|
+
|
|
|
+static inline loff_t mtd_wunit_to_offset(struct mtd_info *mtd, loff_t base,
|
|
|
+ int wunit)
|
|
|
+{
|
|
|
+ return base + (wunit * mtd->writesize);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static inline int mtd_has_oob(const struct mtd_info *mtd)
|
|
|
{
|
|
|
return mtd->_read_oob && mtd->_write_oob;
|