]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - include/linux/mtd/mtd.h
Merge branches 'pm-core', 'pm-qos', 'pm-domains' and 'pm-opp'
[linux.git] / include / linux / mtd / mtd.h
index 29a1706122035b74a52e77bfa3a1f6b60f115ee2..eebdc63cf6af94a5ea9a5c4703c416f87e0079a8 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/uio.h>
 #include <linux/notifier.h>
 #include <linux/device.h>
+#include <linux/of.h>
 
 #include <mtd/mtd-abi.h>
 
@@ -127,6 +128,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 +265,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;
 
@@ -243,6 +323,7 @@ struct mtd_info {
        int (*_block_isreserved) (struct mtd_info *mtd, loff_t ofs);
        int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
        int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
+       int (*_max_bad_blocks) (struct mtd_info *mtd, loff_t ofs, size_t len);
        int (*_suspend) (struct mtd_info *mtd);
        void (*_resume) (struct mtd_info *mtd);
        void (*_reboot) (struct mtd_info *mtd);
@@ -296,10 +377,18 @@ 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)
 {
        mtd->dev.of_node = np;
+       if (!mtd->name)
+               of_property_read_string(np, "label", &mtd->name);
 }
 
 static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
@@ -312,6 +401,23 @@ 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;
 }
 
+static inline int mtd_max_bad_blocks(struct mtd_info *mtd,
+                                    loff_t ofs, size_t len)
+{
+       if (!mtd->_max_bad_blocks)
+               return -ENOTSUPP;
+
+       if (mtd->size < (len + ofs) || ofs < 0)
+               return -EINVAL;
+
+       return mtd->_max_bad_blocks(mtd, ofs, len);
+}
+
+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 +503,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;