From patchwork Wed Apr 8 14:44:37 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Agner X-Patchwork-Id: 459305 X-Patchwork-Delegate: scottwood@freescale.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 0055C1401AD for ; Thu, 9 Apr 2015 00:46:06 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="verification failed; unprotected key" header.d=agner.ch header.i=@agner.ch header.b=Muv0JbR2; dkim-adsp=none (unprotected policy); dkim-atps=neutral Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id B6F21B37BD; Wed, 8 Apr 2015 16:45:44 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id J6azlx6NrwNX; Wed, 8 Apr 2015 16:45:44 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 50821A74D3; Wed, 8 Apr 2015 16:45:17 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 631E9A742B for ; Wed, 8 Apr 2015 16:44:52 +0200 (CEST) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id JeypvtKlrJWc for ; Wed, 8 Apr 2015 16:44:52 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail.kmu-office.ch (mail.kmu-office.ch [178.209.48.109]) by theia.denx.de (Postfix) with ESMTP id 500B0A741C for ; Wed, 8 Apr 2015 16:44:46 +0200 (CEST) Received: from trochilidae.toradex.int (unknown [46.140.72.82]) by mail.kmu-office.ch (Postfix) with ESMTPSA id D1F345C0E58; Wed, 8 Apr 2015 16:43:52 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=agner.ch; s=dkim; t=1428504232; bh=6Gq7xPoOBDXtg9gjz8iji0cyl/2pe0xyAMa8UpqnTSM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Muv0JbR2pn3mb0RPTskLbvAxDxJbqY0XMM8d+zAP6K0RyWc8cQym6HQTfU6yIg04o CZE1HHTlSgOoT+6gXbtpMRcaOA69OMmlQoMtqCRDvb+RbmLo5i9lis/bHDgTMX+gEA eqQjvwPnFaUjQpsEJ+Actkn0QU0wUh9gVGLeoUoM= From: Stefan Agner To: scottwood@freescale.com, trini@konsulko.com Date: Wed, 8 Apr 2015 16:44:37 +0200 Message-Id: <1428504281-30214-5-git-send-email-stefan@agner.ch> X-Mailer: git-send-email 2.3.5 In-Reply-To: <1428504281-30214-1-git-send-email-stefan@agner.ch> References: <1428504281-30214-1-git-send-email-stefan@agner.ch> Cc: u-boot@lists.denx.de, bpringlemeir@nbsps.com Subject: [U-Boot] [PATCH v2 4/8] mtd: vf610_nfc: implement OOB only read X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Implement read of OOB area only. When using column and sector size properties, only parts of the page can be read. However, this works only when hardware ECC is disabled, otherwise the ECC engine would ruin the data in the buffer. To allow OOB only reads, three points had to be addressed: - Set ECC mode per command. - Handle NAND_CMD_READOOB seperate. Make sure column and sector size is correctly set up, while disabling ECC. - Now, the OOB data end up at the beginning of the buffer. Remove the special handling of OOB (spareonly). Especially bad block scans benefit from this change. On a 512MiB SLC NAND device, the bad block scan took 1.5s less than before. Signed-off-by: Stefan Agner --- drivers/mtd/nand/vf610_nfc.c | 99 ++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 59 deletions(-) diff --git a/drivers/mtd/nand/vf610_nfc.c b/drivers/mtd/nand/vf610_nfc.c index 16485f5..5d72b4a 100644 --- a/drivers/mtd/nand/vf610_nfc.c +++ b/drivers/mtd/nand/vf610_nfc.c @@ -145,8 +145,6 @@ struct vf610_nfc { struct nand_chip chip; void __iomem *regs; uint column; - int spareonly; - int page_sz; /* Status and ID are in alternate locations. */ int alt_buf; #define ALT_BUF_ID 1 @@ -319,8 +317,8 @@ static void vf610_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) { if (column != -1) { struct vf610_nfc *nfc = mtd_to_nfc(mtd); - if (nfc->chip.options | NAND_BUSWIDTH_16) - column = column/2; + if (nfc->chip.options & NAND_BUSWIDTH_16) + column = column / 2; vf610_nfc_set_field(mtd, NFC_COL_ADDR, COL_ADDR_MASK, COL_ADDR_SHIFT, column); } @@ -329,6 +327,13 @@ static void vf610_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) ROW_ADDR_SHIFT, page); } +static inline void vf610_nfc_ecc_mode(struct mtd_info *mtd, int ecc_mode) +{ + vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, + CONFIG_ECC_MODE_MASK, + CONFIG_ECC_MODE_SHIFT, ecc_mode); +} + static inline void vf610_nfc_transfer_size(void __iomem *regbase, int size) { __raw_writel(size, regbase + NFC_SECTOR_SIZE); @@ -339,10 +344,10 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, int column, int page) { struct vf610_nfc *nfc = mtd_to_nfc(mtd); + int page_sz = nfc->chip.options & NAND_BUSWIDTH_16 ? 1 : 0; - nfc->column = max(column, 0); - nfc->spareonly = 0; - nfc->alt_buf = 0; + nfc->column = max(column, 0); + nfc->alt_buf = 0; switch (command) { case NAND_CMD_SEQIN: @@ -354,23 +359,36 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, */ return; case NAND_CMD_PAGEPROG: - vf610_nfc_transfer_size(nfc->regs, nfc->page_sz); + page_sz += mtd->writesize + mtd->oobsize; + vf610_nfc_transfer_size(nfc->regs, page_sz); vf610_nfc_send_commands(nfc->regs, NAND_CMD_SEQIN, command, PROGRAM_PAGE_CMD_CODE); + vf610_nfc_ecc_mode(mtd, ECC_45_BYTE); break; case NAND_CMD_RESET: vf610_nfc_transfer_size(nfc->regs, 0); vf610_nfc_send_command(nfc->regs, command, RESET_CMD_CODE); break; + case NAND_CMD_READOOB: - nfc->spareonly = 1; + page_sz += mtd->oobsize; + column = mtd->writesize; + vf610_nfc_transfer_size(nfc->regs, page_sz); + vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0, + NAND_CMD_READSTART, READ_PAGE_CMD_CODE); + vf610_nfc_addr_cycle(mtd, column, page); + vf610_nfc_ecc_mode(mtd, ECC_BYPASS); + break; + case NAND_CMD_READ0: + page_sz += mtd->writesize + mtd->oobsize; column = 0; - vf610_nfc_transfer_size(nfc->regs, nfc->page_sz); + vf610_nfc_transfer_size(nfc->regs, page_sz); vf610_nfc_send_commands(nfc->regs, NAND_CMD_READ0, NAND_CMD_READSTART, READ_PAGE_CMD_CODE); vf610_nfc_addr_cycle(mtd, column, page); + vf610_nfc_ecc_mode(mtd, ECC_45_BYTE); break; case NAND_CMD_ERASE1: @@ -399,46 +417,25 @@ static void vf610_nfc_command(struct mtd_info *mtd, unsigned command, vf610_nfc_done(mtd); } -static inline void vf610_nfc_read_spare(struct mtd_info *mtd, void *buf, - int len) -{ - struct vf610_nfc *nfc = mtd_to_nfc(mtd); - - len = min(mtd->oobsize, (uint)len); - if (len > 0) - vf610_nfc_memcpy(buf, nfc->regs + mtd->writesize, len); -} - /* Read data from NFC buffers */ static void vf610_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) { struct vf610_nfc *nfc = mtd_to_nfc(mtd); uint c = nfc->column; - uint l; - /* Handle main area */ - if (!nfc->spareonly) { - l = min((uint)len, mtd->writesize - c); - nfc->column += l; - - if (!nfc->alt_buf) - vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, - l); - else - if (nfc->alt_buf & ALT_BUF_ID) - *buf = vf610_nfc_get_id(mtd, c); - else - *buf = vf610_nfc_get_status(mtd); - - buf += l; - len -= l; + switch (nfc->alt_buf) { + case ALT_BUF_ID: + *buf = vf610_nfc_get_id(mtd, c); + break; + case ALT_BUF_STAT: + *buf = vf610_nfc_get_status(mtd); + break; + default: + vf610_nfc_memcpy(buf, nfc->regs + NFC_MAIN_AREA(0) + c, len); + break; } - /* Handle spare area access */ - if (len) { - nfc->column += len; - vf610_nfc_read_spare(mtd, buf, len); - } + nfc->column += len; } /* Write data to NFC buffers */ @@ -629,17 +626,9 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr) if (cfg.flash_bbt) chip->bbt_options = NAND_BBT_USE_FLASH | NAND_BBT_CREATE; - /* Default to software ECC until flash ID. */ - vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_ECC_MODE_MASK, - CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); - chip->bbt_td = &bbt_main_descr; chip->bbt_md = &bbt_mirror_descr; - nfc->page_sz = PAGE_2K + OOB_64; - nfc->page_sz += cfg.width == 16 ? 1 : 0; - /* Set configuration register. */ vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_ADDR_AUTO_INCR_BIT); vf610_nfc_clear(mtd, NFC_FLASH_CONFIG, CONFIG_BUFNO_AUTO_INCR_BIT); @@ -667,15 +656,12 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr) chip->ecc.mode = NAND_ECC_SOFT; /* default */ - nfc->page_sz = mtd->writesize + mtd->oobsize; - /* Single buffer only, max 256 OOB minus ECC status */ - if (nfc->page_sz > PAGE_2K + 256 - 8) { + if (mtd->writesize + mtd->oobsize > PAGE_2K + 256 - 8) { dev_err(nfc->dev, "Unsupported flash size\n"); err = -ENXIO; goto error; } - nfc->page_sz += cfg.width == 16 ? 1 : 0; if (cfg.hardware_ecc) { if (mtd->writesize != PAGE_2K && mtd->oobsize < 64) { @@ -696,11 +682,6 @@ static int vf610_nfc_nand_init(int devnum, void __iomem *addr) chip->ecc.size = PAGE_2K; chip->ecc.strength = 24; - /* set ECC mode to 45 bytes OOB with 24 bits correction */ - vf610_nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_ECC_MODE_MASK, - CONFIG_ECC_MODE_SHIFT, ECC_45_BYTE); - /* Enable ECC_STATUS */ vf610_nfc_set(mtd, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT); }