Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 69db4aa4 authored by Brian Norris's avatar Brian Norris
Browse files

Merge tag '4.9/mtd-pairing-scheme' of github.com:linux-nand/linux

Introduction of the MTD pairing scheme concept.
parents f5b88de2 477b0229
Loading
Loading
Loading
Loading
+104 −0
Original line number Original line Diff line number Diff line
@@ -375,6 +375,110 @@ static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,
	return NOTIFY_DONE;
	return NOTIFY_DONE;
}
}


/**
 * mtd_wunit_to_pairing_info - get pairing information of a wunit
 * @mtd: pointer to new MTD device info structure
 * @wunit: write unit we are interested in
 * @info: returned pairing information
 *
 * Retrieve pairing information associated to the wunit.
 * This is mainly useful when dealing with MLC/TLC NANDs where pages can be
 * paired together, and where programming a page may influence the page it is
 * paired with.
 * The notion of page is replaced by the term wunit (write-unit) to stay
 * consistent with the ->writesize field.
 *
 * The @wunit argument can be extracted from an absolute offset using
 * mtd_offset_to_wunit(). @info is filled with the pairing information attached
 * to @wunit.
 *
 * From the pairing info the MTD user can find all the wunits paired with
 * @wunit using the following loop:
 *
 * for (i = 0; i < mtd_pairing_groups(mtd); i++) {
 *	info.pair = i;
 *	mtd_pairing_info_to_wunit(mtd, &info);
 *	...
 * }
 */
int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
			      struct mtd_pairing_info *info)
{
	int npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd);

	if (wunit < 0 || wunit >= npairs)
		return -EINVAL;

	if (mtd->pairing && mtd->pairing->get_info)
		return mtd->pairing->get_info(mtd, wunit, info);

	info->group = 0;
	info->pair = wunit;

	return 0;
}
EXPORT_SYMBOL_GPL(mtd_wunit_to_pairing_info);

/**
 * mtd_wunit_to_pairing_info - get wunit from pairing information
 * @mtd: pointer to new MTD device info structure
 * @info: pairing information struct
 *
 * Returns a positive number representing the wunit associated to the info
 * struct, or a negative error code.
 *
 * This is the reverse of mtd_wunit_to_pairing_info(), and can help one to
 * iterate over all wunits of a given pair (see mtd_wunit_to_pairing_info()
 * doc).
 *
 * It can also be used to only program the first page of each pair (i.e.
 * page attached to group 0), which allows one to use an MLC NAND in
 * software-emulated SLC mode:
 *
 * info.group = 0;
 * npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd);
 * for (info.pair = 0; info.pair < npairs; info.pair++) {
 *	wunit = mtd_pairing_info_to_wunit(mtd, &info);
 *	mtd_write(mtd, mtd_wunit_to_offset(mtd, blkoffs, wunit),
 *		  mtd->writesize, &retlen, buf + (i * mtd->writesize));
 * }
 */
int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
			      const struct mtd_pairing_info *info)
{
	int ngroups = mtd_pairing_groups(mtd);
	int npairs = mtd_wunit_per_eb(mtd) / ngroups;

	if (!info || info->pair < 0 || info->pair >= npairs ||
	    info->group < 0 || info->group >= ngroups)
		return -EINVAL;

	if (mtd->pairing && mtd->pairing->get_wunit)
		return mtd->pairing->get_wunit(mtd, info);

	return info->pair;
}
EXPORT_SYMBOL_GPL(mtd_pairing_info_to_wunit);

/**
 * mtd_pairing_groups - get the number of pairing groups
 * @mtd: pointer to new MTD device info structure
 *
 * Returns the number of pairing groups.
 *
 * This number is usually equal to the number of bits exposed by a single
 * cell, and can be used in conjunction with mtd_pairing_info_to_wunit()
 * to iterate over all pages of a given pair.
 */
int mtd_pairing_groups(struct mtd_info *mtd)
{
	if (!mtd->pairing || !mtd->pairing->ngroups)
		return 1;

	return mtd->pairing->ngroups;
}
EXPORT_SYMBOL_GPL(mtd_pairing_groups);

/**
/**
 *	add_mtd_device - register an MTD device
 *	add_mtd_device - register an MTD device
 *	@mtd: pointer to new MTD device info structure
 *	@mtd: pointer to new MTD device info structure
+1 −0
Original line number Original line Diff line number Diff line
@@ -409,6 +409,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
	slave->mtd.oobsize = master->oobsize;
	slave->mtd.oobsize = master->oobsize;
	slave->mtd.oobavail = master->oobavail;
	slave->mtd.oobavail = master->oobavail;
	slave->mtd.subpage_sft = master->subpage_sft;
	slave->mtd.subpage_sft = master->subpage_sft;
	slave->mtd.pairing = master->pairing;


	slave->mtd.name = name;
	slave->mtd.name = name;
	slave->mtd.owner = master->owner;
	slave->mtd.owner = master->owner;
+107 −0
Original line number Original line Diff line number Diff line
@@ -127,6 +127,82 @@ struct mtd_ooblayout_ops {
		    struct mtd_oob_region *oobfree);
		    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 module;	/* only needed for owner field in mtd_info */


struct mtd_info {
struct mtd_info {
@@ -188,6 +264,9 @@ struct mtd_info {
	/* OOB layout description */
	/* OOB layout description */
	const struct mtd_ooblayout_ops *ooblayout;
	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. */
	/* the ecc step size. */
	unsigned int 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;
	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,
static inline void mtd_set_of_node(struct mtd_info *mtd,
				   struct device_node *np)
				   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;
	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_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,
int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
	      void **virt, resource_size_t *phys);
	      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);
	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)
static inline int mtd_has_oob(const struct mtd_info *mtd)
{
{
	return mtd->_read_oob && mtd->_write_oob;
	return mtd->_read_oob && mtd->_write_oob;