mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
Introduction of the MTD pairing scheme concept.
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJX2rbJAAoJEGXtNgF+CLcAnGMP/06Eydb50SiBv7YQRVJTF8Rk K0lPQOJBXX+32xXJYKAj0UIRNsd5yxdH1tmWJFOxUV4etI1K9CtlCMLYoLgqPrPf SVXMBtfZvWmK8D5Mee9BroQIlSOQnzbYO3zAFY8pV8SFZ8y9SMXvPM6Z/2tTlMvK uds1x0t1DHGXLcAwZo4Ctvtxy+x4wS1RmfdMQhWYGSlbAqDzbjwK3Dceq6JmzJ1i UxoYHqXdjAF6h67twADHre5vMwdDgYEgEHZHomXtWpHFslWYpWCQUWLLSnWG3h4d ia3Q2xTFPxKg90PKurGrLffwfo0KnpwieqLYqHZ70d9pgepgqv2N547kPeYPNKIS VdbDqakY7V4P6OZ+ykUJ3VcfDPRXh0b0+V9AVyGPIXH4m03YXlGsKigN1tYHid/K I3DeZqcCX35/Fc4iSD9k+HYlIplx6Ijo3JgpnRVlircczloKk+00mZ2THV5/pYcn vfgxgjeKaTKeG+zcSehfmYFZO1SuvhSFO4/qXLgYd8LWav8QWADQ8H3C2nn/ZlMk krb9S0iAQE9nmAY1ZPUz0hreaaYCEZVst/KAJWAkGW0Laj29gW6PAj/lQb+fG5d4 4aSn4qnPrXQ7P76NAihbtzYGCTFwtlhoBXDXhv3A5aoqI5QaMI6QQ+LkYJMRAn0s HY4ExJEeC/lUe5QiF0Q7 =GnV0 -----END PGP SIGNATURE----- Merge tag '4.9/mtd-pairing-scheme' of github.com:linux-nand/linux Introduction of the MTD pairing scheme concept.
This commit is contained in:
commit
69db4aa44f
@ -375,6 +375,110 @@ static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,
|
||||
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
|
||||
* @mtd: pointer to new MTD device info structure
|
||||
|
@ -409,6 +409,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
|
||||
slave->mtd.oobsize = master->oobsize;
|
||||
slave->mtd.oobavail = master->oobavail;
|
||||
slave->mtd.subpage_sft = master->subpage_sft;
|
||||
slave->mtd.pairing = master->pairing;
|
||||
|
||||
slave->mtd.name = name;
|
||||
slave->mtd.owner = master->owner;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user