From: yassin <yassinjaf...@gmail.com>

Signed-off-by: Roy Spliet <r.spl...@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;
 };
 
-- 
2.4.2


-- 


IMAGINE IT >> MAKE IT

Meet us online at Twitter <http://twitter.com/ultimaker>, Facebook 
<http://facebook.com/ultimaker>, Google+ <http://google.com/+Ultimaker>

www.ultimaker.com

-- 
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to linux-sunxi+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to