From patchwork Wed Aug 10 08:50:59 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh KUMAR X-Patchwork-Id: 1052472 Received: from lists.sourceforge.net (lists.sourceforge.net [216.34.181.88]) by demeter1.kernel.org (8.14.4/8.14.4) with ESMTP id p7A9uZmo011208 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Wed, 10 Aug 2011 09:56:57 GMT Received: from localhost ([127.0.0.1] helo=sfs-ml-3.v29.ch3.sourceforge.com) by sfs-ml-3.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1Qr5Wc-0002Mw-PX; Wed, 10 Aug 2011 09:56:34 +0000 Received: from sog-mx-4.v43.ch3.sourceforge.com ([172.29.43.194] helo=mx.sourceforge.net) by sfs-ml-3.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1Qr5Wb-0002Mk-AZ for spi-devel-general@lists.sourceforge.net; Wed, 10 Aug 2011 09:56:33 +0000 X-ACL-Warn: Received: from eu1sys200aog117.obsmtp.com ([207.126.144.143]) by sog-mx-4.v43.ch3.sourceforge.com with smtps (TLSv1:AES256-SHA:256) (Exim 4.76) id 1Qr5Wa-0000s0-4h for spi-devel-general@lists.sourceforge.net; Wed, 10 Aug 2011 09:56:33 +0000 Received: from beta.dmz-ap.st.com ([138.198.100.35]) (using TLSv1) by eu1sys200aob117.postini.com ([207.126.147.11]) with SMTP ID DSNKTkJVyC114D6ZPu8hrjoSAIFphYHFi2ge@postini.com; Wed, 10 Aug 2011 09:56:31 UTC Received: from zeta.dmz-ap.st.com (ns6.st.com [138.198.234.13]) by beta.dmz-ap.st.com (STMicroelectronics) with ESMTP id 58C6E126; Wed, 10 Aug 2011 08:51:11 +0000 (GMT) Received: from Webmail-ap.st.com (eapex1hubcas1.st.com [10.80.176.8]) by zeta.dmz-ap.st.com (STMicroelectronics) with ESMTP id 394DEC1A; Wed, 10 Aug 2011 08:51:11 +0000 (GMT) Received: from localhost (10.199.88.156) by Webmail-ap.st.com (10.80.176.7) with Microsoft SMTP Server (TLS) id 8.2.234.1; Wed, 10 Aug 2011 16:51:10 +0800 From: Viresh Kumar To: Subject: [PATCH V2 6/6] spi/spi-pl022: Request/free DMA channels as and when required. Date: Wed, 10 Aug 2011 14:20:59 +0530 Message-ID: <566c0525199f498f04422d4c3b2ddd7466648c20.1312965742.git.viresh.kumar@st.com> X-Mailer: git-send-email 1.7.2.2 In-Reply-To: References: MIME-Version: 1.0 X-Spam-Score: -0.3 (/) X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -0.3 AWL AWL: From: address is in the auto white-list X-Headers-End: 1Qr5Wa-0000s0-4h Cc: pratyush.anand@st.com, viresh.kumar@st.com, rajeev-dlh.kumar@st.com, bhavna.yadav@st.com, bhupesh.sharma@st.com, armando.visconti@st.com, linus.walleij@linaro.org, vipin.kumar@st.com, shiraz.hashim@st.com, amit.virdi@st.com, vipulkumar.samar@st.com, deepak.sikri@st.com, spi-devel-general@lists.sourceforge.net, linux-arm-kernel@lists.infradead.org X-BeenThere: spi-devel-general@lists.sourceforge.net X-Mailman-Version: 2.1.9 Precedence: list List-Id: Linux SPI core/device drivers discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: spi-devel-general-bounces@lists.sourceforge.net X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Wed, 10 Aug 2011 09:56:57 +0000 (UTC) Currently we request DMA channels at probe time and free them at remove. They are always occupied, irrespective of their usage. They must be allocated when they are required and must be freed after we are done with transfers. So that they can be used by other users. Signed-off-by: Viresh Kumar Tested-by: Linus Walleij --- drivers/spi/spi-pl022.c | 98 +++++++++++++++++++++++++++++++++------------- 1 files changed, 70 insertions(+), 28 deletions(-) diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 01e84e3..a596b96 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -389,6 +389,7 @@ struct pl022 { struct sg_table sgt_rx; struct sg_table sgt_tx; char *dummypage; + dma_cap_mask_t mask; #endif }; @@ -1093,16 +1094,33 @@ err_alloc_rx_sg: static int __init pl022_dma_probe(struct pl022 *pl022) { - dma_cap_mask_t mask; + /* set dma mask */ + dma_cap_zero(pl022->mask); + dma_cap_set(DMA_SLAVE, pl022->mask); - /* Try to acquire a generic DMA engine slave channel */ - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); + pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!pl022->dummypage) { + dev_err(&pl022->adev->dev, + "Failed to work in dma mode, work without dma!\n"); + return -ENODEV; + } + + return 0; +} + +static int pl022_alloc_dmachan(struct pl022 *pl022) +{ /* - * We need both RX and TX channels to do DMA, else do none - * of them. + * Both channels should be allocated or not allocated. It is wrong if + * only one allocated */ - pl022->dma_rx_channel = dma_request_channel(mask, + if (pl022->dma_rx_channel && pl022->dma_tx_channel) + return 0; + + BUG_ON(pl022->dma_rx_channel || pl022->dma_tx_channel); + + /* We need both RX and TX channels to do DMA, else do none of them. */ + pl022->dma_rx_channel = dma_request_channel(pl022->mask, pl022->master_info->dma_filter, pl022->master_info->dma_rx_param); if (!pl022->dma_rx_channel) { @@ -1110,7 +1128,7 @@ static int __init pl022_dma_probe(struct pl022 *pl022) goto err_no_rxchan; } - pl022->dma_tx_channel = dma_request_channel(mask, + pl022->dma_tx_channel = dma_request_channel(pl022->mask, pl022->master_info->dma_filter, pl022->master_info->dma_tx_param); if (!pl022->dma_tx_channel) { @@ -1118,20 +1136,12 @@ static int __init pl022_dma_probe(struct pl022 *pl022) goto err_no_txchan; } - pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!pl022->dummypage) { - dev_dbg(&pl022->adev->dev, "no DMA dummypage!\n"); - goto err_no_dummypage; - } - - dev_info(&pl022->adev->dev, "setup for DMA on RX %s, TX %s\n", + dev_dbg(&pl022->adev->dev, "setup for DMA on RX %s, TX %s\n", dma_chan_name(pl022->dma_rx_channel), dma_chan_name(pl022->dma_tx_channel)); return 0; -err_no_dummypage: - dma_release_channel(pl022->dma_tx_channel); err_no_txchan: dma_release_channel(pl022->dma_rx_channel); pl022->dma_rx_channel = NULL; @@ -1143,22 +1153,30 @@ err_no_rxchan: static void terminate_dma(struct pl022 *pl022) { - struct dma_chan *rxchan = pl022->dma_rx_channel; - struct dma_chan *txchan = pl022->dma_tx_channel; - - dmaengine_terminate_all(rxchan); - dmaengine_terminate_all(txchan); - unmap_free_dma_scatter(pl022); + if (pl022->dma_rx_channel) + dmaengine_terminate_all(pl022->dma_rx_channel); + if (pl022->dma_tx_channel) + dmaengine_terminate_all(pl022->dma_tx_channel); } -static void pl022_dma_remove(struct pl022 *pl022) +static void pl022_free_dmachan(struct pl022 *pl022) { - if (pl022->busy) - terminate_dma(pl022); if (pl022->dma_tx_channel) dma_release_channel(pl022->dma_tx_channel); if (pl022->dma_rx_channel) dma_release_channel(pl022->dma_rx_channel); + + pl022->dma_rx_channel = pl022->dma_tx_channel = NULL; +} + +static void pl022_dma_remove(struct pl022 *pl022) +{ + if (pl022->busy) { + terminate_dma(pl022); + unmap_free_dma_scatter(pl022); + } + + pl022_free_dmachan(pl022); kfree(pl022->dummypage); } @@ -1176,6 +1194,19 @@ static inline int pl022_dma_probe(struct pl022 *pl022) static inline void pl022_dma_remove(struct pl022 *pl022) { } + +static inline int pl022_alloc_dmachan(struct pl022 *pl022) +{ + return 0; +} + +static inline void terminate_dma(struct pl022 *pl022) +{ +} + +static void pl022_free_dmachan(struct pl022 *pl022) +{ +} #endif /** @@ -1401,16 +1432,20 @@ static void do_interrupt_dma_transfer(struct pl022 *pl022) } /* If we're using DMA, set up DMA here */ if (pl022->cur_chip->enable_dma) { + int status = pl022_alloc_dmachan(pl022); + if (status) + goto switch_to_irq_mode; + /* Configure DMA transfer */ if (configure_dma(pl022)) { dev_dbg(&pl022->adev->dev, "configuration of DMA failed, fall back to interrupt mode\n"); - goto err_config_dma; + goto switch_to_irq_mode; } /* Disable interrupts in DMA mode, IRQ from DMA controller */ irqflags = DISABLE_ALL_INTERRUPTS; } -err_config_dma: +switch_to_irq_mode: /* Enable SSP, turn on interrupts */ writew((readw(SSP_CR1(pl022->virtbase)) | SSP_CR1_MASK_SSE), SSP_CR1(pl022->virtbase)); @@ -1513,7 +1548,14 @@ static void pump_messages(struct work_struct *work) spin_lock_irqsave(&pl022->queue_lock, flags); if (list_empty(&pl022->queue) || !pl022->running) { pl022->busy = false; + spin_unlock_irqrestore(&pl022->queue_lock, flags); + + /* free dma channels */ + if (pl022->master_info->enable_dma) { + terminate_dma(pl022); + pl022_free_dmachan(pl022); + } return; } /* Make sure we are not already running a message */