From patchwork Tue Feb 7 10:54:35 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Huang Shijie X-Patchwork-Id: 139900 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from merlin.infradead.org (unknown [IPv6:2001:4978:20e::2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 36A8D1007D2 for ; Tue, 7 Feb 2012 21:55:43 +1100 (EST) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1Ruih2-0007b9-34; Tue, 07 Feb 2012 10:54:36 +0000 Received: from ch1ehsobe005.messaging.microsoft.com ([216.32.181.185] helo=ch1outboundpool.messaging.microsoft.com) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1Ruigl-0007UN-Mv; Tue, 07 Feb 2012 10:54:21 +0000 Received: from mail166-ch1-R.bigfish.com (10.43.68.228) by CH1EHSOBE009.bigfish.com (10.43.70.59) with Microsoft SMTP Server id 14.1.225.23; Tue, 7 Feb 2012 10:54:17 +0000 Received: from mail166-ch1 (localhost [127.0.0.1]) by mail166-ch1-R.bigfish.com (Postfix) with ESMTP id DC5F4360242; Tue, 7 Feb 2012 10:54:16 +0000 (UTC) X-SpamScore: 3 X-BigFish: VS3(zzzz1202h1082kzz8275bhz2dh2a8h668h839h) X-Forefront-Antispam-Report: CIP:70.37.183.190; KIP:(null); UIP:(null); IPV:NLI; H:mail.freescale.net; RD:none; EFVD:NLI Received: from mail166-ch1 (localhost.localdomain [127.0.0.1]) by mail166-ch1 (MessageSwitch) id 1328612054157405_740; Tue, 7 Feb 2012 10:54:14 +0000 (UTC) Received: from CH1EHSMHS030.bigfish.com (snatpool3.int.messaging.microsoft.com [10.43.68.227]) by mail166-ch1.bigfish.com (Postfix) with ESMTP id 1FD0D2601C5; Tue, 7 Feb 2012 10:54:14 +0000 (UTC) Received: from mail.freescale.net (70.37.183.190) by CH1EHSMHS030.bigfish.com (10.43.70.30) with Microsoft SMTP Server (TLS) id 14.1.225.23; Tue, 7 Feb 2012 10:54:14 +0000 Received: from az33smr01.freescale.net (10.64.34.199) by 039-SN1MMR1-002.039d.mgd.msft.net (10.84.1.15) with Microsoft SMTP Server id 14.1.355.3; Tue, 7 Feb 2012 04:54:13 -0600 Received: from localhost.ap.freescale.net (udp161149uds.ap.freescale.net [10.192.242.25]) by az33smr01.freescale.net (8.13.1/8.13.0) with ESMTP id q17ArJEW003890; Tue, 7 Feb 2012 04:54:10 -0600 (CST) From: Huang Shijie To: Subject: [PATCH v3 2/2] mxs-dma : rewrite the last parameter of mxs_dma_prep_slave_sg() Date: Tue, 7 Feb 2012 18:54:35 +0800 Message-ID: <1328612075-5825-3-git-send-email-b32955@freescale.com> X-Mailer: git-send-email 1.7.3.2 In-Reply-To: <1328612075-5825-1-git-send-email-b32955@freescale.com> References: <1328612075-5825-1-git-send-email-b32955@freescale.com> MIME-Version: 1.0 X-OriginatorOrg: freescale.com X-Spam-Note: CRM114 invocation failed X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [216.32.181.185 listed in list.dnswl.org] -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] Cc: alsa-devel@alsa-project.org, artem.bityutskiy@intel.com, b29396@freescale.com, linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, w.sang@pengutronix.de, Huang Shijie , linux-mtd@lists.infradead.org, shawn.guo@linaro.org, shijie8@gmail.com, linux-arm-kernel@lists.infradead.org, LW@KARO-electronics.de X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org [1] Background : The GPMI does ECC read page operation with a DMA chain consist of three DMA Command Structures. The middle one of the chain is used to enable the BCH, and read out the NAND page. The WAIT4END(wait for command end) is a comunication signal between the GPMI and MXS-DMA. [2] The current DMA code sets the WAIT4END bit at the last one, such as: +-----+ +-----+ +-----+ | cmd | ------------> | cmd | ------------------> | cmd | +-----+ +-----+ +-----+ ^ | | set WAIT4END here This chain works fine in the mx23/mx28. [3] But in the new GPMI version (used in MX50/MX60), the WAIT4END bit should be set not only at the last DMA Command Structure, but also at the middle one, such as: +-----+ +-----+ +-----+ | cmd | ------------> | cmd | ------------------> | cmd | +-----+ +-----+ +-----+ ^ ^ | | | | set WAIT4END here too set WAIT4END here If we do not set WAIT4END, the BCH maybe stalls in "ECC reading page" state. In the next ECC write page operation, a DMA-timeout occurs. This has been catched in the MX6Q board. [4] In order to fix the bug, rewrite the last parameter of mxs_dma_prep_slave_sg(), and use the dma_ctrl_flags: --------------------------------------------------------- DMA_PREP_INTERRUPT : append a new DMA Command Structrue. DMA_CTRL_ACK : set the WAIT4END bit for this DMA Command Structure. --------------------------------------------------------- [5] changes to the relative drivers: <1> For mxs-mmc driver, just use the new flags, do not change any logic. <2> For gpmi-nand driver, add new field `gpmi_version` to distinguish different gpmi versions, and use the new flags. Signed-off-by: Huang Shijie --- drivers/dma/mxs-dma.c | 32 ++++++++++++++++++++++++++++---- drivers/mmc/host/mxs-mmc.c | 10 +++++----- drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 24 ++++++++++++++++++------ 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index c80fbed..7d7498b 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -349,10 +349,32 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan) clk_disable_unprepare(mxs_dma->clk); } +/* + * How to use the flags for ->device_prep_slave_sg() : + * [1] If there is only one DMA command in the DMA chain, the code should be: + * ...... + * ->device_prep_slave_sg(DMA_CTRL_ACK); + * ...... + * [2] If there are two DMA commands in the DMA chain, the code should be + * ...... + * ->device_prep_slave_sg(0); + * ...... + * ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + * ...... + * [3] If there are more than two DMA commands in the DMA chain, the code + * should be: + * ...... + * ->device_prep_slave_sg(0); // First + * ...... + * ->device_prep_slave_sg(DMA_PREP_INTERRUPT [| DMA_CTRL_ACK]); + * ...... + * ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK); // Last + * ...... + */ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, - unsigned long append) + unsigned long flags) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; @@ -360,6 +382,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( struct scatterlist *sg; int i, j; u32 *pio; + bool append = flags & DMA_PREP_INTERRUPT; int idx = append ? mxs_chan->desc_count : 0; if (mxs_chan->status == DMA_IN_PROGRESS && !append) @@ -386,7 +409,6 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ccw->bits |= CCW_CHAIN; ccw->bits &= ~CCW_IRQ; ccw->bits &= ~CCW_DEC_SEM; - ccw->bits &= ~CCW_WAIT4END; } else { idx = 0; } @@ -401,7 +423,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ccw->bits = 0; ccw->bits |= CCW_IRQ; ccw->bits |= CCW_DEC_SEM; - ccw->bits |= CCW_WAIT4END; + if (flags & DMA_CTRL_ACK) + ccw->bits |= CCW_WAIT4END; ccw->bits |= CCW_HALT_ON_TERM; ccw->bits |= CCW_TERM_FLUSH; ccw->bits |= BF_CCW(sg_len, PIO_NUM); @@ -432,7 +455,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ccw->bits &= ~CCW_CHAIN; ccw->bits |= CCW_IRQ; ccw->bits |= CCW_DEC_SEM; - ccw->bits |= CCW_WAIT4END; + if (flags & DMA_CTRL_ACK) + ccw->bits |= CCW_WAIT4END; } } } diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 1e18970..120669b 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -305,7 +305,7 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id) } static struct dma_async_tx_descriptor *mxs_mmc_prep_dma( - struct mxs_mmc_host *host, unsigned int append) + struct mxs_mmc_host *host, unsigned long flags) { struct dma_async_tx_descriptor *desc; struct mmc_data *data = host->data; @@ -325,7 +325,7 @@ static struct dma_async_tx_descriptor *mxs_mmc_prep_dma( } desc = host->dmach->device->device_prep_slave_sg(host->dmach, - sgl, sg_len, host->slave_dirn, append); + sgl, sg_len, host->slave_dirn, flags); if (desc) { desc->callback = mxs_mmc_dma_irq_callback; desc->callback_param = host; @@ -358,7 +358,7 @@ static void mxs_mmc_bc(struct mxs_mmc_host *host) host->ssp_pio_words[2] = cmd1; host->dma_dir = DMA_NONE; host->slave_dirn = DMA_TRANS_NONE; - desc = mxs_mmc_prep_dma(host, 0); + desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK); if (!desc) goto out; @@ -398,7 +398,7 @@ static void mxs_mmc_ac(struct mxs_mmc_host *host) host->ssp_pio_words[2] = cmd1; host->dma_dir = DMA_NONE; host->slave_dirn = DMA_TRANS_NONE; - desc = mxs_mmc_prep_dma(host, 0); + desc = mxs_mmc_prep_dma(host, DMA_CTRL_ACK); if (!desc) goto out; @@ -526,7 +526,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) host->data = data; host->dma_dir = dma_data_dir; host->slave_dirn = slave_dirn; - desc = mxs_mmc_prep_dma(host, 1); + desc = mxs_mmc_prep_dma(host, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) goto out; diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index cbcf022..81ddf59 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -850,7 +850,9 @@ int gpmi_send_command(struct gpmi_nand_data *this) sg_init_one(sgl, this->cmd_buffer, this->command_length); dma_map_sg(this->dev, sgl, 1, DMA_TO_DEVICE); desc = channel->device->device_prep_slave_sg(channel, - sgl, 1, DMA_MEM_TO_DEV, 1); + sgl, 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { pr_err("step 2 error\n"); return -1; @@ -892,7 +894,8 @@ int gpmi_send_data(struct gpmi_nand_data *this) /* [2] send DMA request */ prepare_data_dma(this, DMA_TO_DEVICE); desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_MEM_TO_DEV, 1); + 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -928,7 +931,8 @@ int gpmi_read_data(struct gpmi_nand_data *this) /* [2] : send DMA request */ prepare_data_dma(this, DMA_FROM_DEVICE); desc = channel->device->device_prep_slave_sg(channel, &this->data_sgl, - 1, DMA_DEV_TO_MEM, 1); + 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -975,7 +979,8 @@ int gpmi_send_page(struct gpmi_nand_data *this, desc = channel->device->device_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 0); + ARRAY_SIZE(pio), DMA_TRANS_NONE, + DMA_CTRL_ACK); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -995,6 +1000,7 @@ int gpmi_read_page(struct gpmi_nand_data *this, struct dma_async_tx_descriptor *desc; struct dma_chan *channel = get_dma_chan(this); int chip = this->current_chip; + unsigned long flags; u32 pio[6]; /* [1] Wait for the chip to report ready. */ @@ -1037,9 +1043,14 @@ int gpmi_read_page(struct gpmi_nand_data *this, pio[3] = geo->page_size; pio[4] = payload; pio[5] = auxiliary; + + /* Set DMA_CTRL_ACK for mx6q which uses the new GPMI version. */ + flags = DMA_PREP_INTERRUPT; + if (GPMI_IS_MX6Q(this)) + flags |= DMA_CTRL_ACK; desc = channel->device->device_prep_slave_sg(channel, (struct scatterlist *)pio, - ARRAY_SIZE(pio), DMA_TRANS_NONE, 1); + ARRAY_SIZE(pio), DMA_TRANS_NONE, flags); if (!desc) { pr_err("step 2 error\n"); return -1; @@ -1059,7 +1070,8 @@ int gpmi_read_page(struct gpmi_nand_data *this, pio[2] = 0; /* Set GPMI_HW_GPMI_ECCCTRL, disable the BCH. */ desc = channel->device->device_prep_slave_sg(channel, (struct scatterlist *)pio, 3, - DMA_TRANS_NONE, 1); + DMA_TRANS_NONE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { pr_err("step 3 error\n"); return -1;