mtd: gpmi: add subpage read support

Linux-MTD Mailing List linux-mtd at lists.infradead.org
Sat Apr 5 02:59:03 EDT 2014


Gitweb:     http://git.infradead.org/?p=mtd-2.6.git;a=commit;h=b8e2931d168724ca95d275f4f825636bff82a9e8
Commit:     b8e2931d168724ca95d275f4f825636bff82a9e8
Parent:     4a57d670a9edb56d7cbc8d9075c7b242e0670ecf
Author:     Huang Shijie <b32955 at freescale.com>
AuthorDate: Fri Jan 3 11:01:42 2014 +0800
Committer:  Brian Norris <computersforpeace at gmail.com>
CommitDate: Mon Mar 10 22:42:31 2014 -0700

    mtd: gpmi: add subpage read support
    
    1) Why add the subpage read support?
      The page size of the nand chip becomes larger and larger, the imx6 has to
      supports the 16K page or even bigger page. But sometimes, the upper layer only
      needs a small part of the page, such as 512 bytes or less.
    
      For example, ubiattach may only read 64 bytes per page.
    
    2) We only enable the subpage read support when it meets the conditions:
       <1> the chip is imx6 (or later chips) which can supports large nand page.
       <2> the size of ECC parity is byte aligned.
           If the size of ECC parity is not byte aligned, the calling of NAND_CMD_RNDOUT
           will fail.
    
    3) What does this patch do?
       This patch will fake a virtual small page for the subpage read, and call the
       gpmi_ecc_read_page() to do the real work.
    
       In order to fake a virtual small page, the patch changes the BCH registers and
       the bch_geometry{}. After the subpage read finished, we will restore them back.
    
    4) Performace:
        4.1) Tested with Toshiba TC58NVG2S0F(4096 + 224) with the following command:
             #ubiattach /dev/ubi_ctrl -m 4
    
           The detail information of /dev/mtd4 shows below:
           --------------------------------------------------------------
           #mtdinfo /dev/mtd4
            mtd4
            Name:                           test
            Type:                           nand
            Eraseblock size:                262144 bytes, 256.0 KiB
            Amount of eraseblocks:          1856 (486539264 bytes, 464.0 MiB)
            Minimum input/output unit size: 4096 bytes
            Sub-page size:                  4096 bytes
            OOB size:                       224 bytes
            Character device major/minor:   90:8
            Bad blocks are allowed:         true
            Device is writable:             true
           --------------------------------------------------------------
    
        4.2) Before this patch:
           --------------------------------------------------------------
           [   94.530495] UBI: attaching mtd4 to ubi0
           [   98.928850] UBI: scanning is finished
           [   98.953594] UBI: attached mtd4 (name "test", size 464 MiB) to ubi0
           [   98.958562] UBI: PEB size: 262144 bytes (256 KiB), LEB size: 253952 bytes
           [   98.964076] UBI: min./max. I/O unit sizes: 4096/4096, sub-page size 4096
           [   98.969518] UBI: VID header offset: 4096 (aligned 4096), data offset: 8192
           [   98.975128] UBI: good PEBs: 1856, bad PEBs: 0, corrupted PEBs: 0
           [   98.979843] UBI: user volume: 1, internal volumes: 1, max. volumes count: 128
           [   98.985878] UBI: max/mean erase counter: 2/1, WL threshold: 4096, image sequence number: 2024916145
           [   98.993635] UBI: available PEBs: 0, total reserved PEBs: 1856, PEBs reserved for bad PEB handling: 40
           [   99.001807] UBI: background thread "ubi_bgt0d" started, PID 831
           --------------------------------------------------------------
           The attach time is about 98.9 - 94.5 = 4.4s
    
        4.3) After this patch:
           --------------------------------------------------------------
           [  286.464906] UBI: attaching mtd4 to ubi0
           [  289.186129] UBI: scanning is finished
           [  289.211416] UBI: attached mtd4 (name "test", size 464 MiB) to ubi0
           [  289.216360] UBI: PEB size: 262144 bytes (256 KiB), LEB size: 253952 bytes
           [  289.221858] UBI: min./max. I/O unit sizes: 4096/4096, sub-page size 4096
           [  289.227293] UBI: VID header offset: 4096 (aligned 4096), data offset: 8192
           [  289.232878] UBI: good PEBs: 1856, bad PEBs: 0, corrupted PEBs: 0
           [  289.237628] UBI: user volume: 0, internal volumes: 1, max. volumes count: 128
           [  289.243553] UBI: max/mean erase counter: 1/1, WL threshold: 4096, image sequence number: 2024916145
           [  289.251348] UBI: available PEBs: 1812, total reserved PEBs: 44, PEBs reserved for bad PEB handling: 40
           [  289.259417] UBI: background thread "ubi_bgt0d" started, PID 847
           --------------------------------------------------------------
           The attach time is about 289.18 - 286.46 = 2.7s
    
         4.4) The conclusion:
           We achieve (4.4 - 2.7) / 4.4 = 38.6% faster in the ubiattach.
    
    Signed-off-by: Huang Shijie <b32955 at freescale.com>
    Signed-off-by: Brian Norris <computersforpeace at gmail.com>
---
 drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 96 ++++++++++++++++++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index 5aaa7f5..bb77f75 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -27,6 +27,7 @@
 #include <linux/of_device.h>
 #include <linux/of_mtd.h>
 #include "gpmi-nand.h"
+#include "bch-regs.h"
 
 /* Resource names for the GPMI NAND driver. */
 #define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME  "gpmi-nand"
@@ -1049,6 +1050,90 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 	return max_bitflips;
 }
 
+/* Fake a virtual small page for the subpage read */
+static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+			uint32_t offs, uint32_t len, uint8_t *buf, int page)
+{
+	struct gpmi_nand_data *this = chip->priv;
+	void __iomem *bch_regs = this->resources.bch_regs;
+	struct bch_geometry old_geo = this->bch_geometry;
+	struct bch_geometry *geo = &this->bch_geometry;
+	int size = chip->ecc.size; /* ECC chunk size */
+	int meta, n, page_size;
+	u32 r1_old, r2_old, r1_new, r2_new;
+	unsigned int max_bitflips;
+	int first, last, marker_pos;
+	int ecc_parity_size;
+	int col = 0;
+
+	/* The size of ECC parity */
+	ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
+
+	/* Align it with the chunk size */
+	first = offs / size;
+	last = (offs + len - 1) / size;
+
+	/*
+	 * Find the chunk which contains the Block Marker. If this chunk is
+	 * in the range of [first, last], we have to read out the whole page.
+	 * Why? since we had swapped the data at the position of Block Marker
+	 * to the metadata which is bound with the chunk 0.
+	 */
+	marker_pos = geo->block_mark_byte_offset / size;
+	if (last >= marker_pos && first <= marker_pos) {
+		dev_dbg(this->dev, "page:%d, first:%d, last:%d, marker at:%d\n",
+				page, first, last, marker_pos);
+		return gpmi_ecc_read_page(mtd, chip, buf, 0, page);
+	}
+
+	meta = geo->metadata_size;
+	if (first) {
+		col = meta + (size + ecc_parity_size) * first;
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
+
+		meta = 0;
+		buf = buf + first * size;
+	}
+
+	/* Save the old environment */
+	r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0);
+	r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+	/* change the BCH registers and bch_geometry{} */
+	n = last - first + 1;
+	page_size = meta + (size + ecc_parity_size) * n;
+
+	r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS |
+			BM_BCH_FLASH0LAYOUT0_META_SIZE);
+	r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1)
+			| BF_BCH_FLASH0LAYOUT0_META_SIZE(meta);
+	writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+	r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE;
+	r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size);
+	writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+	geo->ecc_chunk_count = n;
+	geo->payload_size = n * size;
+	geo->page_size = page_size;
+	geo->auxiliary_status_offset = ALIGN(meta, 4);
+
+	dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n",
+		page, offs, len, col, first, n, page_size);
+
+	/* Read the subpage now */
+	this->swap_block_mark = false;
+	max_bitflips = gpmi_ecc_read_page(mtd, chip, buf, 0, page);
+
+	/* Restore */
+	writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0);
+	writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1);
+	this->bch_geometry = old_geo;
+	this->swap_block_mark = true;
+
+	return max_bitflips;
+}
+
 static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
 				const uint8_t *buf, int oob_required)
 {
@@ -1566,6 +1651,17 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
 	ecc->layout	= &gpmi_hw_ecclayout;
 
 	/*
+	 * We only enable the subpage read when:
+	 *  (1) the chip is imx6, and
+	 *  (2) the size of the ECC parity is byte aligned.
+	 */
+	if (GPMI_IS_MX6Q(this) &&
+		((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
+		ecc->read_subpage = gpmi_ecc_read_subpage;
+		chip->options |= NAND_SUBPAGE_READ;
+	}
+
+	/*
 	 * Can we enable the extra features? such as EDO or Sync mode.
 	 *
 	 * We do not check the return value now. That's means if we fail in



More information about the linux-mtd-cvs mailing list