diff options
Diffstat (limited to 'target/linux/sunxi/patches-4.1/113-mtd-nand-add-pst.patch')
-rw-r--r-- | target/linux/sunxi/patches-4.1/113-mtd-nand-add-pst.patch | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/target/linux/sunxi/patches-4.1/113-mtd-nand-add-pst.patch b/target/linux/sunxi/patches-4.1/113-mtd-nand-add-pst.patch new file mode 100644 index 0000000000..efab359d84 --- /dev/null +++ b/target/linux/sunxi/patches-4.1/113-mtd-nand-add-pst.patch @@ -0,0 +1,265 @@ +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(+) + +diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c +index a30b67f..8a5d12e 100644 +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -1102,6 +1102,138 @@ int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) + 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_info *mtd, loff_t to, + 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_info *mtd, loff_t to, + 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, struct erase_info *instr, + 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, struct erase_info *instr, + 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; +diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h +index 5616f51..4f7ca8d 100644 +--- 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; + |