From patchwork Tue Aug 9 11:05:23 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh KUMAR X-Patchwork-Id: 1049082 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 p79B6Bej001670 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Tue, 9 Aug 2011 11:06:32 GMT Received: from localhost ([127.0.0.1] helo=sfs-ml-1.v29.ch3.sourceforge.com) by sfs-ml-1.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1Qqk8R-00064V-ON; Tue, 09 Aug 2011 11:06:11 +0000 Received: from sog-mx-4.v43.ch3.sourceforge.com ([172.29.43.194] helo=mx.sourceforge.net) by sfs-ml-1.v29.ch3.sourceforge.com with esmtp (Exim 4.76) (envelope-from ) id 1Qqk8Q-00064L-7f for spi-devel-general@lists.sourceforge.net; Tue, 09 Aug 2011 11:06:10 +0000 X-ACL-Warn: Received: from eu1sys200aog119.obsmtp.com ([207.126.144.147]) by sog-mx-4.v43.ch3.sourceforge.com with smtps (TLSv1:AES256-SHA:256) (Exim 4.76) id 1Qqk8N-0005U2-M3 for spi-devel-general@lists.sourceforge.net; Tue, 09 Aug 2011 11:06:10 +0000 Received: from beta.dmz-ap.st.com ([138.198.100.35]) (using TLSv1) by eu1sys200aob119.postini.com ([207.126.147.11]) with SMTP ID DSNKTkEUlNZV5mexJnPBdeRef9ZcQm6lc6No@postini.com; Tue, 09 Aug 2011 11:06:07 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 6576D159; Tue, 9 Aug 2011 11:05:45 +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 55DDFC7F; Tue, 9 Aug 2011 11:05:43 +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; Tue, 9 Aug 2011 19:05:42 +0800 From: Viresh Kumar To: Subject: [PATCH 6/6] spi/spi-pl022: Request/free DMA channels as and when required. Date: Tue, 9 Aug 2011 16:35:23 +0530 Message-ID: <68a2f54a8c2cc542278f3fe6678c555716b8f4d9.1312887860.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: 1Qqk8N-0005U2-M3 Cc: vipin.kumar@st.com, viresh.kumar@st.com, rajeev-dlh.kumar@st.com, bhavna.yadav@st.com, bhupesh.sharma@st.com, pratyush.anand@st.com, armando.visconti@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]); Tue, 09 Aug 2011 11:06:32 +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 --- 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 e2333e7..3653f00 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 */