From patchwork Thu Oct 18 05:32:15 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Huang Shijie X-Patchwork-Id: 1608301 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork2.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork2.kernel.org (Postfix) with ESMTP id D2E8ADFB34 for ; Thu, 18 Oct 2012 06:11:15 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1TOjEX-0003nc-0q; Thu, 18 Oct 2012 06:05:29 +0000 Received: from ch1ehsobe006.messaging.microsoft.com ([216.32.181.186] helo=ch1outboundpool.messaging.microsoft.com) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1TOjEN-0003n6-ET; Thu, 18 Oct 2012 06:05:20 +0000 Received: from mail84-ch1-R.bigfish.com (10.43.68.228) by CH1EHSOBE011.bigfish.com (10.43.70.61) with Microsoft SMTP Server id 14.1.225.23; Thu, 18 Oct 2012 06:05:16 +0000 Received: from mail84-ch1 (localhost [127.0.0.1]) by mail84-ch1-R.bigfish.com (Postfix) with ESMTP id 7C55132019D; Thu, 18 Oct 2012 06:05:16 +0000 (UTC) X-Forefront-Antispam-Report: CIP:70.37.183.190; KIP:(null); UIP:(null); IPV:NLI; H:mail.freescale.net; RD:none; EFVD:NLI X-SpamScore: 3 X-BigFish: VS3(zzzz1202h1d1ah1d2ah1082kzz8275bhz2dh2a8h668h839hd24he5bhf0ah107ah1288h12a5h12a9h12bdh12e5h1354h137ah139eh13b6h1441h1155h) Received: from mail84-ch1 (localhost.localdomain [127.0.0.1]) by mail84-ch1 (MessageSwitch) id 1350540314619574_24426; Thu, 18 Oct 2012 06:05:14 +0000 (UTC) Received: from CH1EHSMHS032.bigfish.com (snatpool2.int.messaging.microsoft.com [10.43.68.232]) by mail84-ch1.bigfish.com (Postfix) with ESMTP id 91F93160049; Thu, 18 Oct 2012 06:05:14 +0000 (UTC) Received: from mail.freescale.net (70.37.183.190) by CH1EHSMHS032.bigfish.com (10.43.70.32) with Microsoft SMTP Server (TLS) id 14.1.225.23; Thu, 18 Oct 2012 06:05:14 +0000 Received: from az84smr01.freescale.net (10.64.34.197) by 039-SN1MMR1-004.039d.mgd.msft.net (10.84.1.14) with Microsoft SMTP Server (TLS) id 14.2.309.3; Thu, 18 Oct 2012 06:05:13 +0000 Received: from localhost.localdomain (shlinux2.ap.freescale.net [10.192.224.44]) by az84smr01.freescale.net (8.14.3/8.14.0) with ESMTP id q9I653wp007503; Wed, 17 Oct 2012 23:05:04 -0700 From: Huang Shijie To: Subject: [PATCH] dma: add new DMA control commands Date: Thu, 18 Oct 2012 13:32:15 +0800 Message-ID: <1350538335-29026-1-git-send-email-b32955@freescale.com> X-Mailer: git-send-email 1.7.0.4 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.186 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, tiwai@suse.de, artem.bityutskiy@linux.intel.com, perex@perex.cz, linux-mtd@lists.infradead.org, linux-i2c@vger.kernel.org, Huang Shijie , marex@denx.de, linux@arm.linux.org.uk, cjb@laptop.org, lrg@ti.com, ben-linux@fluff.org, linux-arm-kernel@lists.infradead.org, broonie@opensource.wolfsonmicro.com, linux-mmc@vger.kernel.org, w.sang@pengutronix.de, linux-kernel@vger.kernel.org, Huang Shijie , djbw@fb.com, khali@linux-fr.org, shawn.guo@linaro.org, dwmw2@infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org From: Huang Shijie [1] Why add these new DMA control commands? In mx6q, the gpmi-nand driver is the only user of the APBH-DMA. The dma clock is enabled when we have successfully requested a DMA channel. So even when the gpmi-nand driver does not work, the dma clock(apbh-dma) still runs in high speed (198MHz). To save some power, it is better to disable the dma clock when the dma device, such as gpmi-nand, is not working anymore. When the dma device becomes work again, enable the dma clock again. [2] add new DMA control commands: DMA_START/DMA_END DMA_START: do some preprations to start the DMA engine, such as enable the necessary clocks. DMA_END: do some works to end the DMA engine, such as disable the necessary clocks. [3] This patch does not change any logic in i2c-mxs driver and mxs-pcm driver. But for gpmi-nand driver, we will enable the the clock only when we select the nand chip, and want to do some real jobs with the nand chip. For mxs-mmc driver, disable the dma clock in the suspend; and enable the dma clock in the resume. Signed-off-by: Huang Shijie --- drivers/dma/mxs-dma.c | 17 +++++++++++++++++ drivers/i2c/busses/i2c-mxs.c | 10 +++++++++- drivers/mmc/host/mxs-mmc.c | 19 ++++++++++++++++--- drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 10 ++++++++++ drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 10 ++++++---- include/linux/dmaengine.h | 6 ++++++ sound/soc/mxs/mxs-pcm.c | 12 ++++++++++++ 7 files changed, 76 insertions(+), 8 deletions(-) diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 9f02e79..89286f4 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -384,6 +384,8 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan) /* the descriptor is ready */ async_tx_ack(&mxs_chan->desc); + clk_disable_unprepare(mxs_dma->clk); + return 0; err_clk: @@ -399,6 +401,14 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; + int ret; + + ret = clk_prepare_enable(mxs_dma->clk); + if (ret) { + dev_err(mxs_dma->dma_device.dev, + "failed in enabling the dma clock\n"); + return; + } mxs_dma_disable_chan(mxs_chan); @@ -597,9 +607,13 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); + struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; int ret = 0; switch (cmd) { + case DMA_START: + ret = clk_prepare_enable(mxs_dma->clk); + break; case DMA_TERMINATE_ALL: mxs_dma_reset_chan(mxs_chan); mxs_dma_disable_chan(mxs_chan); @@ -610,6 +624,9 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, case DMA_RESUME: mxs_dma_resume_chan(mxs_chan); break; + case DMA_END: + clk_disable_unprepare(mxs_dma->clk); + break; default: ret = -ENOSYS; } diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 1f58197..da1e881 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -643,6 +643,12 @@ static int __devinit mxs_i2c_probe(struct platform_device *pdev) dev_err(dev, "Failed to request dma\n"); return -ENODEV; } + err = dmaengine_device_control(i2c->dmach, DMA_START, 0); + if (err) { + dma_release_channel(i2c->dmach); + dev_err(dev, "Failed to start dma\n"); + return err; + } } platform_set_drvdata(pdev, i2c); @@ -680,8 +686,10 @@ static int __devexit mxs_i2c_remove(struct platform_device *pdev) if (ret) return -EBUSY; - if (i2c->dmach) + if (i2c->dmach) { + dmaengine_device_control(i2c->dmach, DMA_END, 0); dma_release_channel(i2c->dmach); + } writel(MXS_I2C_CTRL0_SFTRST, i2c->regs + MXS_I2C_CTRL0_SET); diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 80d1e6d..aa91830 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -676,6 +676,9 @@ static int mxs_mmc_probe(struct platform_device *pdev) "%s: failed to request dma\n", __func__); goto out_clk_put; } + ret = dmaengine_device_control(ssp->dmach, DMA_START, 0); + if (ret) + goto out_free_dma; /* set mmc core parameters */ mmc->ops = &mxs_mmc_ops; @@ -717,18 +720,20 @@ static int mxs_mmc_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0, DRIVER_NAME, host); if (ret) - goto out_free_dma; + goto out_end_free_dma; spin_lock_init(&host->lock); ret = mmc_add_host(mmc); if (ret) - goto out_free_dma; + goto out_end_free_dma; dev_info(mmc_dev(host->mmc), "initialized\n"); return 0; +out_end_free_dma: + dmaengine_device_control(ssp->dmach, DMA_END, 0); out_free_dma: if (ssp->dmach) dma_release_channel(ssp->dmach); @@ -750,8 +755,10 @@ static int mxs_mmc_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - if (ssp->dmach) + if (ssp->dmach) { + dmaengine_device_control(ssp->dmach, DMA_END, 0); dma_release_channel(ssp->dmach); + } clk_disable_unprepare(ssp->clk); clk_put(ssp->clk); @@ -772,6 +779,7 @@ static int mxs_mmc_suspend(struct device *dev) ret = mmc_suspend_host(mmc); clk_disable_unprepare(ssp->clk); + dmaengine_device_control(ssp->dmach, DMA_END, 0); return ret; } @@ -784,8 +792,13 @@ static int mxs_mmc_resume(struct device *dev) int ret = 0; clk_prepare_enable(ssp->clk); + ret = dmaengine_device_control(ssp->dmach, DMA_START, 0); + if (ret) + return ret; ret = mmc_resume_host(mmc); + if (ret) + dmaengine_device_control(ssp->dmach, DMA_END, 0); return ret; } diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 3502acc..20ed3f3 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -958,6 +958,7 @@ void gpmi_begin(struct gpmi_nand_data *this) uint32_t reg; unsigned int dll_wait_time_in_us; struct gpmi_nfc_hardware_timing hw; + struct dma_chan *channel = get_dma_chan(this); int ret; /* Enable the clock. */ @@ -967,6 +968,12 @@ void gpmi_begin(struct gpmi_nand_data *this) goto err_out; } + ret = dmaengine_device_control(channel, DMA_START, 0); + if (ret) { + gpmi_disable_clk(this); + goto err_out; + } + /* Only initialize the timing once */ if (this->flags & GPMI_TIMING_INIT_OK) return; @@ -1035,7 +1042,10 @@ err_out: void gpmi_end(struct gpmi_nand_data *this) { + struct dma_chan *channel = get_dma_chan(this); + gpmi_disable_clk(this); + dmaengine_device_control(channel, DMA_END, 0); } /* Clears a BCH interrupt. */ diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index e2c56fc..5694d03 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -815,12 +815,14 @@ static void gpmi_select_chip(struct mtd_info *mtd, int chipnr) struct nand_chip *chip = mtd->priv; struct gpmi_nand_data *this = chip->priv; - if ((this->current_chip < 0) && (chipnr >= 0)) + if ((this->current_chip < 0) && (chipnr >= 0)) { + /* set the current_chip before we call gpmi_begin(). */ + this->current_chip = chipnr; gpmi_begin(this); - else if ((this->current_chip >= 0) && (chipnr < 0)) + } else if ((this->current_chip >= 0) && (chipnr < 0)) { gpmi_end(this); - - this->current_chip = chipnr; + this->current_chip = chipnr; + } } static void gpmi_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index d3201e4..79f864a 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -199,6 +199,8 @@ enum dma_ctrl_flags { /** * enum dma_ctrl_cmd - DMA operations that can optionally be exercised * on a running channel. + * @DMA_START: do some preprations to start the DMA engine, such as enable the + * necessary clocks. * @DMA_TERMINATE_ALL: terminate all ongoing transfers * @DMA_PAUSE: pause ongoing transfers * @DMA_RESUME: resume paused transfer @@ -209,13 +211,17 @@ enum dma_ctrl_flags { * command. * @FSLDMA_EXTERNAL_START: this command will put the Freescale DMA controller * into external start mode. + * @DMA_END: do some works to end the DMA engine, such as disable the + * necessary clocks. */ enum dma_ctrl_cmd { + DMA_START, DMA_TERMINATE_ALL, DMA_PAUSE, DMA_RESUME, DMA_SLAVE_CONFIG, FSLDMA_EXTERNAL_START, + DMA_END, }; /** diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index f82d766..cfcc30f 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -92,6 +92,7 @@ static int snd_mxs_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct mxs_pcm_dma_data *pcm_dma_data; + struct dma_chan *chan; int ret; pcm_dma_data = kzalloc(sizeof(*pcm_dma_data), GFP_KERNEL); @@ -107,6 +108,14 @@ static int snd_mxs_open(struct snd_pcm_substream *substream) return ret; } + chan = snd_dmaengine_pcm_get_chan(substream); + ret = dmaengine_device_control(chan, DMA_START, (unsigned long)0); + if (ret) { + snd_dmaengine_pcm_close(substream); + kfree(pcm_dma_data); + return ret; + } + snd_soc_set_runtime_hwparams(substream, &snd_mxs_hardware); snd_dmaengine_pcm_set_data(substream, pcm_dma_data); @@ -117,7 +126,10 @@ static int snd_mxs_open(struct snd_pcm_substream *substream) static int snd_mxs_close(struct snd_pcm_substream *substream) { struct mxs_pcm_dma_data *pcm_dma_data = snd_dmaengine_pcm_get_data(substream); + struct dma_chan *chan; + chan = snd_dmaengine_pcm_get_chan(substream); + dmaengine_device_control(chan, DMA_END, 0); snd_dmaengine_pcm_close(substream); kfree(pcm_dma_data);