From patchwork Tue Jun 13 07:02:49 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Boris Brezillon X-Patchwork-Id: 774985 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [65.50.211.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3wn12D0d7Qz9s71 for ; Tue, 13 Jun 2017 17:03:32 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="kfS0984w"; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Subject:To:From:Date:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=XMA2r4bZOmAUb+lbgvFC3ENrW+/iMoX4hmA5lDhFROE=; b=kfS0984w3dyF7r ha/xTAYvb/K5YKHbLKQlBX2VqPdxyWXG8M0aJJH7T3/DwzGqcsgqcQZ37IAT0w3V/ioNUCN+qUrhk NoMhRDSPRDlF52HhfpKM1YviGcrOpcmzuxt9NJDebu1x6jg9GYiFqmN/oWukd9kL/Q1UPCywLgmZt am+nGDsR2Ie6bz9TFKgZltVQiMRbz8rfymo9Uyix51FlQfVx0xE/LfFVp25qCUm9wJoZqmLf0j2DS Rgvf/wIbai/ESfwUfJ0H212C5J9Iy1uKs/c4u/ac5TLNVXMrSvcjX+S5VdVB9oE1Ky0YPH/OCB45P 74ckjON+lLq/pQaK4dKw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1dKfrN-0005U8-4p; Tue, 13 Jun 2017 07:03:29 +0000 Received: from mail.free-electrons.com ([62.4.15.54]) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1dKfrH-0005Sc-MD for linux-mtd@lists.infradead.org; Tue, 13 Jun 2017 07:03:26 +0000 Received: by mail.free-electrons.com (Postfix, from userid 110) id 66B6521DAC; Tue, 13 Jun 2017 09:03:00 +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, URIBL_BLOCKED shortcircuit=ham autolearn=disabled version=3.4.0 Received: from bbrezillon (unknown [91.160.177.164]) by mail.free-electrons.com (Postfix) with ESMTPSA id 0E8E221293; Tue, 13 Jun 2017 09:02:50 +0200 (CEST) Date: Tue, 13 Jun 2017 09:02:49 +0200 From: Boris Brezillon To: Masahiro Yamada Subject: Re: [PATCH v6 00/18] mtd: nand: denali: Denali NAND IP patch bomb Message-ID: <20170613090249.6a247389@bbrezillon> In-Reply-To: <1497330250-17348-1-git-send-email-yamada.masahiro@socionext.com> References: <1497330250-17348-1-git-send-email-yamada.masahiro@socionext.com> X-Mailer: Claws Mail 3.14.1 (GTK+ 2.24.31; x86_64-pc-linux-gnu) MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170613_000324_168277_CFE8E1A5 X-CRM114-Status: GOOD ( 33.88 ) X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Cyrille Pitchen , Richard Weinberger , Marek Vasut , David Woodhouse , Chuanxiao Dong , linux-kernel@vger.kernel.org, Dinh Nguyen , linux-mtd@lists.infradead.org, Masami Hiramatsu , Artem Bityutskiy , Jassi Brar , Brian Norris , Enrico Jorns Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Le Tue, 13 Jun 2017 14:03:52 +0900, Masahiro Yamada a écrit : > This patch series intends to solve various problems. > > [1] The driver just retrieves the OOB area as-is > whereas the controller uses syndrome page layout. > [2] ONFi devices are not working > [3] It can not read Bad Block Marker > > Outstanding changes are: > - Fix raw/oob callbacks for syndrome page layout > - Implement setup_data_interface() callback > - Fix/implement more commands for ONFi devices > - Allow to skip the driver internal bounce buffer > - Support PIO in case DMA is not supported > - Switch from ->cmdfunc over to ->cmd_ctrl > > 18 patches were merged by v2. > 11 patches were merged by v3. > 2 patches were merged by v4. > 5 patches were merged by v5. > Here is the rest of the series. > > v1: https://lkml.org/lkml/2016/11/26/144 > v2: https://lkml.org/lkml/2017/3/22/804 > v3: https://lkml.org/lkml/2017/3/30/90 > v4: https://lkml.org/lkml/2017/6/5/1005 > > Masahiro Yamada (18): > mtd: nand: denali: set NAND_ECC_CUSTOM_PAGE_ACCESS > mtd: nand: denali: remove unneeded find_valid_banks() > mtd: nand: denali: handle timing parameters by setup_data_interface() > mtd: nand: denali: rework interrupt handling > mtd: nand: denali: fix NAND_CMD_STATUS handling > mtd: nand: denali: fix NAND_CMD_PARAM handling AFAICT, patch 5 and 6 are unneeded... > mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc ... because you're anyway switching to ->cmd_ctrl() in patch 7, which fixes the problem you were addressing in patch 5 and 6. Please squash those 3 patches into a single one and adjust your commit message accordingly (explaining that it fixes STATUS and PARAM handling). See below if you need an example. BTW, I also implemented ->read/write_buf_word() since the core may one day call these functions, and the default implementations used by the core when these hooks are NULL are not appropriate in your case. --->8--- From 136727ba7b453ca1567c711037230aa6ec0f124a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 13 Jun 2017 14:03:57 +0900 Subject: [PATCH] mtd: nand: denali: switch over to cmd_ctrl instead of cmdfunc The NAND_CMD_SET_FEATURES support is missing from denali_cmdfunc(). Besides, we see /* TODO: Read OOB data */ comment line. It would be possible to add more commands along with the current implementation, but having ->cmd_ctrl() seems a better approach from the discussion with Boris [1]. Rely on the default ->cmdfunc() from the framework and implement the driver's own ->cmd_ctrl(). We are also implementing ->read/write_buf/byte/word(). Also add ->write_byte(), which is needed for write direction commands. Then, we can drop nand_onfi_get_set_features_notsupp from this driver. This transition also fixes NAND_CMD_STATUS and NAND_CMD_PARAM handling. NAND_CMD_STATUS was just faked by the implementation, and the only valid bit returned in this case was the WP bit. NAND_CMD_PARAM was completely broken: not only the command sent on the bus was NAND_CMD_STATUS instead of NAND_CMD_PARAM, but the driver was only reading 8 bytes of data, while the parameter page is contains several hundreds of bytes. [1] https://lkml.org/lkml/2017/3/15/97 Signed-off-by: Masahiro Yamada Signed-off-by: Boris Brezillon --- drivers/mtd/nand/denali.c | 204 +++++++++++++++++++++------------------------- drivers/mtd/nand/denali.h | 2 - 2 files changed, 95 insertions(+), 111 deletions(-) diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index d7e7555a3d73..633faf2da1f4 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -85,43 +85,6 @@ static void index_addr(struct denali_nand_info *denali, iowrite32(data, denali->flash_mem + 0x10); } -/* Perform an indexed read of the device */ -static void index_addr_read_data(struct denali_nand_info *denali, - uint32_t address, uint32_t *pdata) -{ - iowrite32(address, denali->flash_mem); - *pdata = ioread32(denali->flash_mem + 0x10); -} - -/* - * We need to buffer some data for some of the NAND core routines. - * The operations manage buffering that data. - */ -static void reset_buf(struct denali_nand_info *denali) -{ - denali->buf.head = denali->buf.tail = 0; -} - -static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte) -{ - denali->buf.buf[denali->buf.tail++] = byte; -} - -/* reads the status of the device */ -static void read_status(struct denali_nand_info *denali) -{ - uint32_t cmd; - - /* initialize the data buffer to store status */ - reset_buf(denali); - - cmd = ioread32(denali->flash_reg + WRITE_PROTECT); - if (cmd) - write_byte_to_buf(denali, NAND_STATUS_WP); - else - write_byte_to_buf(denali, 0); -} - /* Reset the flash controller */ static uint16_t denali_nand_reset(struct denali_nand_info *denali) { @@ -267,20 +230,16 @@ static uint32_t denali_wait_for_irq(struct denali_nand_info *denali, return denali->irq_status; } -/* resets a specific device connected to the core */ -static void reset_bank(struct denali_nand_info *denali) +static uint32_t denali_check_irq(struct denali_nand_info *denali) { + unsigned long flags; uint32_t irq_status; - denali_reset_irq(denali); - - iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET); - - irq_status = denali_wait_for_irq(denali, - INTR__RST_COMP | INTR__TIME_OUT); + spin_lock_irqsave(&denali->irq_lock, flags); + irq_status = denali->irq_status; + spin_unlock_irqrestore(&denali->irq_lock, flags); - if (!(irq_status & INTR__RST_COMP)) - dev_err(denali->dev, "reset bank failed.\n"); + return irq_status; } /* @@ -301,6 +260,82 @@ static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en, iowrite32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG); } +static void denali_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + int i; + + for (i = 0; i < len; i++) { + iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, + denali->flash_mem); + buf[i] = ioread32(denali->flash_mem + 0x10); + } +} + +static u8 denali_read_byte(struct mtd_info *mtd) +{ + u8 ret; + + denali_read_buf(mtd, &ret, 1); + + return ret; +} + +static u16 denali_read_word(struct mtd_info *mtd) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, denali->flash_mem); + + return ioread32(denali->flash_mem + 0x10); +} + +static void denali_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + int i; + + for (i = 0; i < len; i++) { + iowrite32(MODE_11 | BANK(denali->flash_bank) | 2, + denali->flash_mem); + iowrite32(buf[i], denali->flash_mem + 0x10); + } +} + +static void denali_write_byte(struct mtd_info *mtd, uint8_t byte) +{ + denali_write_buf(mtd, &byte, 1); +} + +static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t type; + + if (ctrl & NAND_CLE) + type = 0; + else if (ctrl & NAND_ALE) + type = 1; + else + return; + + /* + * Some commands are followed by chip->dev_ready or chip->waitfunc. + * irq_status must be cleared here to catch the R/B# interrupt later. + */ + if (ctrl & NAND_CTRL_CHANGE) + denali_reset_irq(denali); + + index_addr(denali, MODE_11 | BANK(denali->flash_bank) | type, dat); +} + +static int denali_dev_ready(struct mtd_info *mtd) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + return !!(denali_check_irq(denali) & INTR__INT_ACT); +} + /* * sends a pipeline command operation to the controller. See the Denali NAND * controller's user guide for more information (section 4.2.3.6). @@ -843,17 +878,6 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, return 0; } -static uint8_t denali_read_byte(struct mtd_info *mtd) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - uint8_t result = 0xff; - - if (denali->buf.head < denali->buf.tail) - result = denali->buf.buf[denali->buf.head++]; - - return result; -} - static void denali_select_chip(struct mtd_info *mtd, int chip) { struct denali_nand_info *denali = mtd_to_denali(mtd); @@ -863,7 +887,13 @@ static void denali_select_chip(struct mtd_info *mtd, int chip) static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) { - return 0; + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t irq_status; + + /* R/B# pin transitioned from low to high? */ + irq_status = denali_wait_for_irq(denali, INTR__INT_ACT); + + return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL; } static int denali_erase(struct mtd_info *mtd, int page) @@ -884,45 +914,6 @@ static int denali_erase(struct mtd_info *mtd, int page) return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL; } -static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, - int page) -{ - struct denali_nand_info *denali = mtd_to_denali(mtd); - uint32_t addr, id; - int i; - - switch (cmd) { - case NAND_CMD_STATUS: - read_status(denali); - break; - case NAND_CMD_READID: - case NAND_CMD_PARAM: - reset_buf(denali); - /* - * sometimes ManufactureId read from register is not right - * e.g. some of Micron MT29F32G08QAA MLC NAND chips - * So here we send READID cmd to NAND insteand - */ - addr = MODE_11 | BANK(denali->flash_bank); - index_addr(denali, addr | 0, 0x90); - index_addr(denali, addr | 1, col); - for (i = 0; i < 8; i++) { - index_addr_read_data(denali, addr | 2, &id); - write_byte_to_buf(denali, id); - } - break; - case NAND_CMD_RESET: - reset_bank(denali); - break; - case NAND_CMD_READOOB: - /* TODO: Read OOB data */ - break; - default: - pr_err(": unsupported command received 0x%x\n", cmd); - break; - } -} - #define DIV_ROUND_DOWN_ULL(ll, d) \ ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) @@ -1238,12 +1229,6 @@ int denali_init(struct denali_nand_info *denali) struct mtd_info *mtd = nand_to_mtd(chip); int ret; - /* allocate a temporary buffer for nand_scan_ident() */ - denali->buf.buf = devm_kzalloc(denali->dev, PAGE_SIZE, - GFP_DMA | GFP_KERNEL); - if (!denali->buf.buf) - return -ENOMEM; - mtd->dev.parent = denali->dev; denali_hw_init(denali); denali_drv_init(denali); @@ -1267,11 +1252,14 @@ int denali_init(struct denali_nand_info *denali) /* register the driver with the NAND core subsystem */ chip->select_chip = denali_select_chip; - chip->cmdfunc = denali_cmdfunc; chip->read_byte = denali_read_byte; + chip->read_word = denali_read_word; + chip->read_buf = denali_read_buf; + chip->write_byte = denali_write_byte; + chip->write_buf = denali_write_buf; + chip->cmd_ctrl = denali_cmd_ctrl; + chip->dev_ready = denali_dev_ready; chip->waitfunc = denali_waitfunc; - chip->onfi_set_features = nand_onfi_get_set_features_notsupp; - chip->onfi_get_features = nand_onfi_get_set_features_notsupp; /* clk rate info is needed for setup_data_interface */ if (denali->clk_x_rate) @@ -1286,8 +1274,6 @@ int denali_init(struct denali_nand_info *denali) if (ret) goto disable_irq; - /* allocate the right size buffer now */ - devm_kfree(denali->dev, denali->buf.buf); denali->buf.buf = devm_kzalloc(denali->dev, mtd->writesize + mtd->oobsize, GFP_KERNEL); diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h index a0ac0f84f8b5..a84d8784ee98 100644 --- a/drivers/mtd/nand/denali.h +++ b/drivers/mtd/nand/denali.h @@ -306,8 +306,6 @@ #define MODE_11 0x0C000000 struct nand_buf { - int head; - int tail; uint8_t *buf; dma_addr_t dma_buf; };