From patchwork Fri Oct 18 23:02:39 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ezequiel Garcia X-Patchwork-Id: 3070641 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id ED38EBF924 for ; Sat, 19 Oct 2013 00:16:11 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 08AA2204E7 for ; Sat, 19 Oct 2013 00:16:11 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E57AE2047C for ; Sat, 19 Oct 2013 00:16:09 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VXJ80-0003jP-Ea; Fri, 18 Oct 2013 23:06:45 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VXJ79-0001IB-Tj; Fri, 18 Oct 2013 23:05:51 +0000 Received: from top.free-electrons.com ([176.31.233.9] helo=mail.free-electrons.com) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VXJ58-0000yh-Mv; Fri, 18 Oct 2013 23:03:50 +0000 Received: by mail.free-electrons.com (Postfix, from userid 106) id E4EDA8A7; Sat, 19 Oct 2013 01:03:31 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-4.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from localhost.localdomain (unknown [190.2.98.212]) by mail.free-electrons.com (Postfix) with ESMTPA id 4E396890; Sat, 19 Oct 2013 01:03:28 +0200 (CEST) From: Ezequiel Garcia To: , Subject: [PATCH v2 12/27] mtd: nand: pxa3xx: Use a completion to signal device ready Date: Fri, 18 Oct 2013 20:02:39 -0300 Message-Id: <1382137374-21251-13-git-send-email-ezequiel.garcia@free-electrons.com> X-Mailer: git-send-email 1.8.1.5 In-Reply-To: <1382137374-21251-1-git-send-email-ezequiel.garcia@free-electrons.com> References: <1382137374-21251-1-git-send-email-ezequiel.garcia@free-electrons.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20131018_190347_246171_F142FC54 X-CRM114-Status: GOOD ( 19.02 ) X-Spam-Score: -1.7 (-) Cc: Lior Amsalem , Thomas Petazzoni , Jason Cooper , Tawfik Bayouk , Daniel Mack , Ezequiel Garcia , Gregory Clement , Brian Norris , Willy Tarreau X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Apparently, the expected behavior of the waitfunc() NAND chip call is to wait for the device to be READY (this is a standard chip line). However, the current implementation does almost nothing, which opens a possibility to issue a command to a non-ready device. Fix this by adding a new completion to wait for the ready event to arrive. Because the "is ready" flag is cleared from the controller status register, it's needed to store that state in the driver, and because the field is accesed from an interruption, the field needs to be of an atomic type. Signed-off-by: Ezequiel Garcia --- drivers/mtd/nand/pxa3xx_nand.c | 45 +++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 95e2ce3..1ceccb6 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -35,6 +35,7 @@ #include +#define NAND_DEV_READY_TIMEOUT 50 #define CHIP_DELAY_TIMEOUT (2 * HZ/10) #define NAND_STOP_DELAY (2 * HZ/50) #define PAGE_CHUNK_SIZE (2048) @@ -166,7 +167,7 @@ struct pxa3xx_nand_info { struct clk *clk; void __iomem *mmio_base; unsigned long mmio_phys; - struct completion cmd_complete; + struct completion cmd_complete, dev_ready; unsigned int buf_start; unsigned int buf_count; @@ -196,7 +197,13 @@ struct pxa3xx_nand_info { int use_ecc; /* use HW ECC ? */ int use_dma; /* use DMA ? */ int use_spare; /* use spare ? */ - int is_ready; + + /* + * The is_ready flag is accesed from several places, + * including an interruption hander. We need an atomic + * type to avoid races. + */ + atomic_t is_ready; unsigned int fifo_size; /* max. data size in the FIFO */ unsigned int data_size; /* data to be read from FIFO */ @@ -478,7 +485,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info) static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) { struct pxa3xx_nand_info *info = devid; - unsigned int status, is_completed = 0; + unsigned int status, is_completed = 0, is_ready = 0; unsigned int ready, cmd_done; if (info->cs == 0) { @@ -514,8 +521,9 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) is_completed = 1; } if (status & ready) { - info->is_ready = 1; + atomic_set(&info->is_ready, 1); info->state = STATE_READY; + is_ready = 1; } if (status & NDSR_WRCMDREQ) { @@ -544,6 +552,8 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) nand_writel(info, NDSR, status); if (is_completed) complete(&info->cmd_complete); + if (is_ready) + complete(&info->dev_ready); NORMAL_IRQ_EXIT: return IRQ_HANDLED; } @@ -574,7 +584,6 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, info->oob_size = 0; info->use_ecc = 0; info->use_spare = 1; - info->is_ready = 0; info->retcode = ERR_NONE; if (info->cs != 0) info->ndcb0 = NDCB0_CSEL; @@ -750,6 +759,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, exec_cmd = prepare_command_pool(info, command, column, page_addr); if (exec_cmd) { init_completion(&info->cmd_complete); + init_completion(&info->dev_ready); + atomic_set(&info->is_ready, 0); pxa3xx_nand_start(info); ret = wait_for_completion_timeout(&info->cmd_complete, @@ -862,21 +873,27 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) { struct pxa3xx_nand_host *host = mtd->priv; struct pxa3xx_nand_info *info = host->info_data; + int ret; + + /* Need to wait? */ + if (!atomic_read(&info->is_ready)) { + ret = wait_for_completion_timeout(&info->dev_ready, + CHIP_DELAY_TIMEOUT); + if (!ret) { + dev_err(&info->pdev->dev, "Ready time out!!!\n"); + return NAND_STATUS_FAIL; + } + } /* pxa3xx_nand_send_command has waited for command complete */ if (this->state == FL_WRITING || this->state == FL_ERASING) { if (info->retcode == ERR_NONE) return 0; - else { - /* - * any error make it return 0x01 which will tell - * the caller the erase and write fail - */ - return 0x01; - } + else + return NAND_STATUS_FAIL; } - return 0; + return NAND_STATUS_READY; } static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, @@ -1027,7 +1044,7 @@ static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info) return ret; chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0); - if (info->is_ready) + if (atomic_read(&info->is_ready)) return 0; return -ENODEV;