From patchwork Wed Mar 30 15:39:48 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris BREZILLON X-Patchwork-Id: 33739 Received: from mail.tu-berlin.de ([130.149.7.33]) by www.linuxtv.org with esmtp (Exim 4.84_2) (envelope-from ) id 1alIE5-0001RW-TF; Wed, 30 Mar 2016 15:40:09 +0000 X-tubIT-Incoming-IP: 209.132.180.67 Received: from vger.kernel.org ([209.132.180.67]) by mail.tu-berlin.de (exim-4.76/mailfrontend-7) with esmtp id 1alIE3-0001mI-1y; Wed, 30 Mar 2016 17:40:09 +0200 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754592AbcC3PkB (ORCPT + 1 other); Wed, 30 Mar 2016 11:40:01 -0400 Received: from down.free-electrons.com ([37.187.137.238]:60510 "EHLO mail.free-electrons.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1752688AbcC3PkA (ORCPT ); Wed, 30 Mar 2016 11:40:00 -0400 Received: by mail.free-electrons.com (Postfix, from userid 110) id D519C17EA; Wed, 30 Mar 2016 17:39:57 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on mail.free-electrons.com X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=ALL_TRUSTED,SHORTCIRCUIT shortcircuit=ham autolearn=disabled version=3.4.0 Received: from localhost.localdomain (LMontsouris-657-1-184-87.w90-63.abo.wanadoo.fr [90.63.216.87]) by mail.free-electrons.com (Postfix) with ESMTPSA id 2A72E17EB; Wed, 30 Mar 2016 17:39:57 +0200 (CEST) From: Boris Brezillon To: David Woodhouse , Brian Norris , linux-mtd@lists.infradead.org, Andrew Morton , Dave Gordon Cc: Mark Brown , linux-spi@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Maxime Ripard , Chen-Yu Tsai , linux-sunxi@googlegroups.com, Vinod Koul , Dan Williams , dmaengine@vger.kernel.org, Mauro Carvalho Chehab , Hans Verkuil , Laurent Pinchart , linux-media@vger.kernel.org, Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , devicetree@vger.kernel.org, Boris Brezillon , Richard Weinberger Subject: [PATCH v2 1/7] mtd: nand: sunxi: move some ECC related operations to their own functions Date: Wed, 30 Mar 2016 17:39:48 +0200 Message-Id: <1459352394-22810-2-git-send-email-boris.brezillon@free-electrons.com> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1459352394-22810-1-git-send-email-boris.brezillon@free-electrons.com> References: <1459352394-22810-1-git-send-email-boris.brezillon@free-electrons.com> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-PMX-Version: 6.0.0.2142326, Antispam-Engine: 2.7.2.2107409, Antispam-Data: 2016.3.30.153316 X-PMX-Spam: Gauge=IIIIIIII, Probability=8%, Report=' MULTIPLE_RCPTS 0.1, HTML_00_01 0.05, HTML_00_10 0.05, NO_URI_HTTPS 0, REFERENCES 0, SINGLE_URI_IN_BODY 0, URI_ENDS_IN_HTML 0, __ANY_URI 0, __CP_URI_IN_BODY 0, __HAS_FROM 0, __HAS_MSGID 0, __HAS_X_MAILER 0, __HAS_X_MAILING_LIST 0, __IN_REP_TO 0, __MIME_TEXT_ONLY 0, __MULTIPLE_RCPTS_CC_X2 0, __MULTIPLE_RCPTS_TO_X5 0, __REFERENCES 0, __SANE_MSGID 0, __SINGLE_URI_TEXT 0, __SUBJ_ALPHA_END 0, __TO_MALFORMED_2 0, __URI_IN_BODY 0, __URI_NO_WWW 0, __URI_NS , __URI_WITH_PATH 0' In order to support DMA operations in a clean way we need to extract some of the logic coded in sunxi_nfc_hw_ecc_read/write_page() into their own function. Signed-off-by: Boris Brezillon --- drivers/mtd/nand/sunxi_nand.c | 163 ++++++++++++++++++++++++++++-------------- 1 file changed, 108 insertions(+), 55 deletions(-) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index a71905c..f6ea0fb 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -776,6 +776,92 @@ static inline void sunxi_nfc_user_data_to_buf(u32 user_data, u8 *buf) buf[3] = user_data >> 24; } +static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) +{ + return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); +} + +static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct mtd_info *mtd, u8 *oob, + int step, bool bbm, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + + sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)), + oob); + + /* De-randomize the Bad Block Marker. */ + if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) + sunxi_nfc_randomize_bbm(mtd, page, oob); +} + +static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct mtd_info *mtd, + const u8 *oob, int step, + bool bbm, int page) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + u8 user_data[4]; + + /* Randomize the Bad Block Marker. */ + if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) { + memcpy(user_data, oob, sizeof(user_data)); + sunxi_nfc_randomize_bbm(mtd, page, user_data); + oob = user_data; + } + + writel(sunxi_nfc_buf_to_user_data(oob), + nfc->regs + NFC_REG_USER_DATA(step)); +} + +static void sunxi_nfc_hw_ecc_update_stats(struct mtd_info *mtd, + unsigned int *max_bitflips, int ret) +{ + if (ret < 0) { + mtd->ecc_stats.failed++; + } else { + mtd->ecc_stats.corrected += ret; + *max_bitflips = max_t(unsigned int, *max_bitflips, ret); + } +} + +static int sunxi_nfc_hw_ecc_correct(struct mtd_info *mtd, u8 *data, u8 *oob, + int step, u32 status, bool *erased) +{ + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct nand_ecc_ctrl *ecc = &nand->ecc; + u32 tmp; + + *erased = false; + + if (status & NFC_ECC_ERR(step)) + return -EBADMSG; + + if (status & NFC_ECC_PAT_FOUND(step)) { + u8 pattern; + + if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) { + pattern = 0x0; + } else { + pattern = 0xff; + *erased = true; + } + + if (data) + memset(data, pattern, ecc->size); + + if (oob) + memset(oob, pattern, ecc->bytes + 4); + + return 0; + } + + tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step)); + + return NFC_ECC_ERR_CNT(step, tmp); +} + static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, u8 *data, int data_off, u8 *oob, int oob_off, @@ -787,7 +873,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct nand_ecc_ctrl *ecc = &nand->ecc; int raw_mode = 0; - u32 status; + bool erased; int ret; if (*cur_off != data_off) @@ -813,27 +899,13 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, *cur_off = oob_off + ecc->bytes + 4; - status = readl(nfc->regs + NFC_REG_ECC_ST); - if (status & NFC_ECC_PAT_FOUND(0)) { - u8 pattern = 0xff; - - if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) - pattern = 0x0; - - memset(data, pattern, ecc->size); - memset(oob, pattern, ecc->bytes + 4); - + ret = sunxi_nfc_hw_ecc_correct(mtd, data, oob, 0, + readl(nfc->regs + NFC_REG_ECC_ST), + &erased); + if (erased) return 1; - } - - ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(0))); - - memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page); - - if (status & NFC_ECC_ERR(0)) { + if (ret < 0) { /* * Re-read the data with the randomizer disabled to identify * bitflips in erased pages. @@ -841,35 +913,32 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, if (nand->options & NAND_NEED_SCRAMBLING) { nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); nand->read_buf(mtd, data, ecc->size); - nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - nand->read_buf(mtd, oob, ecc->bytes + 4); + } else { + memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, + ecc->size); } + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + nand->read_buf(mtd, oob, ecc->bytes + 4); + ret = nand_check_erased_ecc_chunk(data, ecc->size, oob, ecc->bytes + 4, NULL, 0, ecc->strength); if (ret >= 0) raw_mode = 1; } else { - /* - * The engine protects 4 bytes of OOB data per chunk. - * Retrieve the corrected OOB bytes. - */ - sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(0)), - oob); + memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); - /* De-randomize the Bad Block Marker. */ - if (bbm && nand->options & NAND_NEED_SCRAMBLING) - sunxi_nfc_randomize_bbm(mtd, page, oob); - } + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); + sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, + true, page); - if (ret < 0) { - mtd->ecc_stats.failed++; - } else { - mtd->ecc_stats.corrected += ret; - *max_bitflips = max_t(unsigned int, *max_bitflips, ret); + sunxi_nfc_hw_ecc_get_prot_oob_bytes(mtd, oob, 0, + bbm, page); } + sunxi_nfc_hw_ecc_update_stats(mtd, max_bitflips, ret); + return raw_mode; } @@ -898,11 +967,6 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, *cur_off = mtd->oobsize + mtd->writesize; } -static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) -{ - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); -} - static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, const u8 *data, int data_off, const u8 *oob, int oob_off, @@ -919,19 +983,6 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, sunxi_nfc_randomizer_write_buf(mtd, data, ecc->size, false, page); - /* Fill OOB data in */ - if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) { - u8 user_data[4]; - - memcpy(user_data, oob, 4); - sunxi_nfc_randomize_bbm(mtd, page, user_data); - writel(sunxi_nfc_buf_to_user_data(user_data), - nfc->regs + NFC_REG_USER_DATA(0)); - } else { - writel(sunxi_nfc_buf_to_user_data(oob), - nfc->regs + NFC_REG_USER_DATA(0)); - } - if (data_off + ecc->size != oob_off) nand->cmdfunc(mtd, NAND_CMD_RNDIN, oob_off, -1); @@ -940,6 +991,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, return ret; sunxi_nfc_randomizer_enable(mtd); + sunxi_nfc_hw_ecc_set_prot_oob_bytes(mtd, oob, 0, bbm, page); + writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR | NFC_ECC_OP, nfc->regs + NFC_REG_CMD);