
On Fri, 5 Jun 2015 13:52:37 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
Ditto (this work hasn't been accepted in Linux).
From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.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 dbeb092..1c514a0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1185,6 +1185,138 @@ EXPORT_SYMBOL(nand_lock); #endif
/**
- 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 * 8; i++) {
if (!(buf[i / 8] &
(1 << (i % 8)))) {
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 * 8; i++) {
if (!(buf[i / 8] &
(1 << (i % 8)))) {
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
@@ -2521,6 +2653,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; uint8_t *wbuf = buf;
int subpage;
WATCHDOG_RESET(); /* Partial page write? */
@@ -2547,6 +2680,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;
@@ -2804,6 +2945,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,
@@ -2880,6 +3022,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 0cdb3b9..ef6a783 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -547,6 +547,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.
@@ -660,6 +678,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.
@@ -750,6 +769,8 @@ struct nand_chip {
struct nand_bbt_descr *badblock_pattern;
- uint8_t *pst;
- void *priv;
};