123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- From bec69bb8e85151729014d859106dcc3fe652b1d4 Mon Sep 17 00:00:00 2001
- From: Boris BREZILLON <boris.brezillon@free-electrons.com>
- Date: Mon, 28 Jul 2014 14:45:40 +0200
- Subject: [PATCH] mtd: nand: Add page status table (pst)
- Page status table is an byte array storing pages status.
- It defines 3 status:
- - unknown: the page has not been read yet and we do not know its current
- state
- - empty: the page contains only FFs
- - filled: the page has been filled with data
- Care must be taken: an empty page does not mean it can be written, because
- it might have already been written with only FFs.
- These page status are useful to check wether the controller should try to
- correct errors (using ECC) or a derandomize data (using a randomizer
- block).
- Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
- Signed-off-by: Hans de Goede <hdegoede@redhat.com>
- ---
- drivers/mtd/nand/nand_base.c | 154 +++++++++++++++++++++++++++++++++++++++++++
- include/linux/mtd/nand.h | 21 ++++++
- 2 files changed, 175 insertions(+)
- --- a/drivers/mtd/nand/nand_base.c
- +++ b/drivers/mtd/nand/nand_base.c
- @@ -1102,6 +1102,138 @@ out:
- EXPORT_SYMBOL(nand_lock);
-
- /**
- + * nand_page_is_empty - check wether a NAND page contains only FFs
- + * @mtd: mtd info
- + * @data: data buffer
- + * @oob: oob buffer
- + *
- + * Reads the data stored in the databuf buffer and check if it contains only
- + * FFs.
- + *
- + * Return true if it does else return false.
- + */
- +bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob)
- +{
- + u8 *buf;
- + int length;
- + u32 pattern = 0xffffffff;
- + int bitflips = 0;
- + int cnt;
- +
- + buf = data;
- + length = mtd->writesize;
- + while (length) {
- + cnt = length < sizeof(pattern) ? length : sizeof(pattern);
- + if (memcmp(&pattern, buf, cnt)) {
- + int i;
- + for (i = 0; i < cnt * BITS_PER_BYTE; i++) {
- + if (!(buf[i / BITS_PER_BYTE] &
- + (1 << (i % BITS_PER_BYTE)))) {
- + bitflips++;
- + if (bitflips > mtd->ecc_strength)
- + return false;
- + }
- + }
- + }
- +
- + buf += sizeof(pattern);
- + length -= sizeof(pattern);
- + }
- +
- + buf = oob;
- + length = mtd->oobsize;
- + while (length) {
- + cnt = length < sizeof(pattern) ? length : sizeof(pattern);
- + if (memcmp(&pattern, buf, cnt)) {
- + int i;
- + for (i = 0; i < cnt * BITS_PER_BYTE; i++) {
- + if (!(buf[i / BITS_PER_BYTE] &
- + (1 << (i % BITS_PER_BYTE)))) {
- + bitflips++;
- + if (bitflips > mtd->ecc_strength)
- + return false;
- + }
- + }
- + }
- +
- + buf += sizeof(pattern);
- + length -= sizeof(pattern);
- + }
- +
- + return true;
- +}
- +EXPORT_SYMBOL(nand_page_is_empty);
- +
- +/**
- + * nand_page_get_status - retrieve page status from the page status table (pst)
- + * @mtd: mtd info
- + * @page: page you want to get status on
- + *
- + * Return the page status.
- + */
- +int nand_page_get_status(struct mtd_info *mtd, int page)
- +{
- + struct nand_chip *chip = mtd->priv;
- + u8 shift = (page % 4) * 2;
- + uint64_t offset = page / 4;
- + int ret = NAND_PAGE_STATUS_UNKNOWN;
- +
- + if (chip->pst)
- + ret = (chip->pst[offset] >> shift) & 0x3;
- +
- + return ret;
- +}
- +EXPORT_SYMBOL(nand_page_get_status);
- +
- +/**
- + * nand_page_set_status - assign page status from in the page status table
- + * @mtd: mtd info
- + * @page: page you want to get status on
- + * @status: new status to assign
- + */
- +void nand_page_set_status(struct mtd_info *mtd, int page,
- + enum nand_page_status status)
- +{
- + struct nand_chip *chip = mtd->priv;
- + u8 shift;
- + uint64_t offset;
- +
- + if (!chip->pst)
- + return;
- +
- + shift = (page % 4) * 2;
- + offset = page / 4;
- + chip->pst[offset] &= ~(0x3 << shift);
- + chip->pst[offset] |= (status & 0x3) << shift;
- +}
- +EXPORT_SYMBOL(nand_page_set_status);
- +
- +/**
- + * nand_pst_create - create a page status table
- + * @mtd: mtd info
- + *
- + * Allocate a page status table and assign it to the mtd device.
- + *
- + * Returns 0 in case of success or -ERRNO in case of error.
- + */
- +int nand_pst_create(struct mtd_info *mtd)
- +{
- + struct nand_chip *chip = mtd->priv;
- +
- + if (chip->pst)
- + return 0;
- +
- + chip->pst = kzalloc(mtd->size >>
- + (chip->page_shift + mtd->subpage_sft + 2),
- + GFP_KERNEL);
- + if (!chip->pst)
- + return -ENOMEM;
- +
- + return 0;
- +}
- +EXPORT_SYMBOL(nand_pst_create);
- +
- +/**
- * nand_read_page_raw - [INTERN] read raw page data without ecc
- * @mtd: mtd info structure
- * @chip: nand chip info structure
- @@ -2539,6 +2671,7 @@ static int nand_do_write_ops(struct mtd_
- uint8_t *wbuf = buf;
- int use_bufpoi;
- int part_pagewr = (column || writelen < (mtd->writesize - 1));
- + int subpage;
-
- if (part_pagewr)
- use_bufpoi = 1;
- @@ -2574,6 +2707,14 @@ static int nand_do_write_ops(struct mtd_
- if (ret)
- break;
-
- + for (subpage = column / chip->subpagesize;
- + subpage < (column + writelen) / chip->subpagesize;
- + subpage++)
- + nand_page_set_status(mtd,
- + (page << mtd->subpage_sft) +
- + subpage,
- + NAND_PAGE_FILLED);
- +
- writelen -= bytes;
- if (!writelen)
- break;
- @@ -2979,6 +3120,7 @@ int nand_erase_nand(struct mtd_info *mtd
- int page, status, pages_per_block, ret, chipnr;
- struct nand_chip *chip = mtd->priv;
- loff_t len;
- + int i;
-
- pr_debug("%s: start = 0x%012llx, len = %llu\n",
- __func__, (unsigned long long)instr->addr,
- @@ -3051,6 +3193,18 @@ int nand_erase_nand(struct mtd_info *mtd
- goto erase_exit;
- }
-
- + for (i = 0; i < pages_per_block; i++) {
- + int subpage;
- + for (subpage = 0;
- + subpage < 1 << mtd->subpage_sft;
- + subpage++) {
- + nand_page_set_status(mtd,
- + ((page + i) << mtd->subpage_sft) +
- + subpage,
- + NAND_PAGE_EMPTY);
- + }
- + }
- +
- /* Increment page address and decrement length */
- len -= (1ULL << chip->phys_erase_shift);
- page += pages_per_block;
- --- a/include/linux/mtd/nand.h
- +++ b/include/linux/mtd/nand.h
- @@ -521,6 +521,24 @@ struct nand_ecc_ctrl {
- int page);
- };
-
- +/*
- + * Constants for page status
- + */
- +enum nand_page_status {
- + NAND_PAGE_STATUS_UNKNOWN,
- + NAND_PAGE_EMPTY,
- + NAND_PAGE_FILLED,
- +};
- +
- +bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob);
- +
- +int nand_page_get_status(struct mtd_info *mtd, int page);
- +
- +void nand_page_set_status(struct mtd_info *mtd, int page,
- + enum nand_page_status status);
- +
- +int nand_pst_create(struct mtd_info *mtd);
- +
- /**
- * struct nand_buffers - buffer structure for read/write
- * @ecccalc: buffer pointer for calculated ECC, size is oobsize.
- @@ -630,6 +648,7 @@ struct nand_buffers {
- * @bbt_md: [REPLACEABLE] bad block table mirror descriptor
- * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial
- * bad block scan.
- + * @pst: [INTERN] page status table
- * @controller: [REPLACEABLE] a pointer to a hardware controller
- * structure which is shared among multiple independent
- * devices.
- @@ -718,6 +737,8 @@ struct nand_chip {
-
- struct nand_bbt_descr *badblock_pattern;
-
- + uint8_t *pst;
- +
- struct list_head partitions;
- struct mutex part_lock;
-
|