From patchwork Thu Jan 19 13:34:12 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fabrice Gasnier X-Patchwork-Id: 9525777 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id EE9B7601AE for ; Thu, 19 Jan 2017 13:37:02 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C4BFB2840B for ; Thu, 19 Jan 2017 13:37:02 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B870628480; Thu, 19 Jan 2017 13:37:02 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.1 required=2.0 tests=BAYES_00,RDNS_NONE autolearn=no version=3.3.1 Received: from bombadil.infradead.org (unknown [65.50.211.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 199872840B for ; Thu, 19 Jan 2017 13:37:02 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1cUCtg-00012t-Mj; Thu, 19 Jan 2017 13:37:00 +0000 Received: from mx08-00178001.pphosted.com ([91.207.212.93] helo=mx07-00178001.pphosted.com) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1cUCs5-0005lO-JZ for linux-arm-kernel@lists.infradead.org; Thu, 19 Jan 2017 13:35:24 +0000 Received: from pps.filterd (m0046660.ppops.net [127.0.0.1]) by mx08-00178001.pphosted.com (8.16.0.11/8.16.0.11) with SMTP id v0JDV9Mj012442; Thu, 19 Jan 2017 14:34:46 +0100 Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx08-.pphosted.com with ESMTP id 27ya0b4e9y-1 (version=TLSv1 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=NOT); Thu, 19 Jan 2017 14:34:46 +0100 Received: from zeta.dmz-eu.st.com (zeta.dmz-eu.st.com [164.129.230.9]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id D8D4A3D; Thu, 19 Jan 2017 13:34:43 +0000 (GMT) Received: from Webmail-eu.st.com (sfhdag5node3.st.com [10.75.127.15]) by zeta.dmz-eu.st.com (STMicroelectronics) with ESMTP id A4F9B2BF4; Thu, 19 Jan 2017 13:34:43 +0000 (GMT) Received: from localhost (10.75.127.46) by SFHDAG5NODE3.st.com (10.75.127.15) with Microsoft SMTP Server (TLS) id 15.0.1178.4; Thu, 19 Jan 2017 14:34:43 +0100 From: Fabrice Gasnier To: , , , , , Subject: [PATCH 5/7] iio: adc: stm32: add optional dma support Date: Thu, 19 Jan 2017 14:34:12 +0100 Message-ID: <1484832854-6314-6-git-send-email-fabrice.gasnier@st.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1484832854-6314-1-git-send-email-fabrice.gasnier@st.com> References: <1484832854-6314-1-git-send-email-fabrice.gasnier@st.com> MIME-Version: 1.0 X-Originating-IP: [10.75.127.46] X-ClientProxiedBy: SFHDAG2NODE2.st.com (10.75.127.5) To SFHDAG5NODE3.st.com (10.75.127.15) X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-01-19_04:, , signatures=0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170119_053521_975460_ECD4FB29 X-CRM114-Status: GOOD ( 21.59 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: mark.rutland@arm.com, benjamin.gaignard@linaro.org, lars@metafoo.de, alexandre.torgue@st.com, linux-iio@vger.kernel.org, pmeerw@pmeerw.net, mcoquelin.stm32@gmail.com, knaack.h@gmx.de, fabrice.gasnier@st.com, benjamin.gaignard@st.com 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 Add optional DMA support to STM32 ADC. Use dma cyclic mode with at least two periods. Signed-off-by: Fabrice Gasnier --- drivers/iio/adc/Kconfig | 2 + drivers/iio/adc/stm32-adc-core.c | 1 + drivers/iio/adc/stm32-adc-core.h | 2 + drivers/iio/adc/stm32-adc.c | 209 ++++++++++++++++++++++++++++++++++++--- 4 files changed, 202 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 9a7b090..2a2ef78 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -444,12 +444,14 @@ config ROCKCHIP_SARADC config STM32_ADC_CORE tristate "STMicroelectronics STM32 adc core" depends on ARCH_STM32 || COMPILE_TEST + depends on HAS_DMA depends on OF depends on REGULATOR select IIO_BUFFER select MFD_STM32_TIMERS select IIO_STM32_TIMER_TRIGGER select IIO_TRIGGERED_BUFFER + select IRQ_WORK help Select this option to enable the core driver for STMicroelectronics STM32 analog-to-digital converter (ADC). diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 4214b0c..22b7c93 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -201,6 +201,7 @@ static int stm32_adc_probe(struct platform_device *pdev) priv->common.base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->common.base)) return PTR_ERR(priv->common.base); + priv->common.phys_base = res->start; priv->vref = devm_regulator_get(&pdev->dev, "vref"); if (IS_ERR(priv->vref)) { diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h index 081fa5f..2ec7abb 100644 --- a/drivers/iio/adc/stm32-adc-core.h +++ b/drivers/iio/adc/stm32-adc-core.h @@ -42,10 +42,12 @@ /** * struct stm32_adc_common - stm32 ADC driver common data (for all instances) * @base: control registers base cpu addr + * @phys_base: control registers base physical addr * @vref_mv: vref voltage (mv) */ struct stm32_adc_common { void __iomem *base; + phys_addr_t phys_base; int vref_mv; }; diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 9753c39..3439f4c 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include #include @@ -29,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +72,8 @@ #define STM32F4_EXTSEL_SHIFT 24 #define STM32F4_EXTSEL_MASK GENMASK(27, 24) #define STM32F4_EOCS BIT(10) +#define STM32F4_DDS BIT(9) +#define STM32F4_DMA BIT(8) #define STM32F4_ADON BIT(0) /* STM32F4_ADC_SQR1 - bit fields */ @@ -165,6 +170,11 @@ struct stm32_adc_trig_info { * @bufi: data buffer index * @num_conv: expected number of scan conversions * @exten: external trigger config (enable/polarity) + * @work: irq work used to call trigger poll routine + * @dma_chan: dma channel + * @rx_buf: dma rx buffer cpu address + * @rx_dma_buf: dma rx buffer bus address + * @rx_buf_sz: dma rx buffer size */ struct stm32_adc { struct stm32_adc_common *common; @@ -177,6 +187,11 @@ struct stm32_adc { int bufi; int num_conv; enum stm32_adc_exten exten; + struct irq_work work; + struct dma_chan *dma_chan; + u8 *rx_buf; + dma_addr_t rx_dma_buf; + int rx_buf_sz; }; /** @@ -332,10 +347,20 @@ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc) /** * stm32_adc_start_conv() - Start conversions for regular channels. * @adc: stm32 adc instance + * + * Start conversions for regular channels. + * Also take care of normal or DMA mode. DMA is used in circular mode for + * regular conversions, in IIO buffer modes. Rely on rx_buf as raw + * read doesn't use dma, but direct DR read. */ static void stm32_adc_start_conv(struct stm32_adc *adc) { stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN); + + if (adc->rx_buf) + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, + STM32F4_DMA | STM32F4_DDS); + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON); /* Wait for Power-up time (tSTAB from datasheet) */ @@ -353,6 +378,10 @@ static void stm32_adc_stop_conv(struct stm32_adc *adc) stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN); stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON); + + if (adc->rx_buf) + stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, + STM32F4_DMA | STM32F4_DDS); } /** @@ -689,19 +718,138 @@ static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev, .driver_module = THIS_MODULE, }; +static int stm32_adc_dma_residue(struct stm32_adc *adc) +{ + struct dma_tx_state state; + enum dma_status status; + + if (!adc->rx_buf) + return 0; + + status = dmaengine_tx_status(adc->dma_chan, + adc->dma_chan->cookie, + &state); + if (status == DMA_IN_PROGRESS) { + /* Residue is size in bytes from end of buffer */ + int i = adc->rx_buf_sz - state.residue; + int size; + + /* Return available bytes */ + if (i >= adc->bufi) + size = i - adc->bufi; + else + size = adc->rx_buf_sz - adc->bufi + i; + + return size; + } + + return 0; +} + +static void stm32_adc_dma_irq_work(struct irq_work *work) +{ + struct stm32_adc *adc = container_of(work, struct stm32_adc, work); + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + + /** + * iio_trigger_poll calls generic_handle_irq(). So, it requires hard + * irq context, and cannot be called directly from dma callback, + * dma cb has to schedule this work instead. + */ + iio_trigger_poll(indio_dev->trig); +} + +static void stm32_adc_dma_buffer_done(void *data) +{ + struct stm32_adc *adc = data; + + /* invoques iio_trigger_poll() from hard irq context */ + irq_work_queue(&adc->work); +} + static int stm32_adc_buffer_preenable(struct iio_dev *indio_dev) { struct stm32_adc *adc = iio_priv(indio_dev); + struct dma_async_tx_descriptor *desc; + struct dma_slave_config config; + dma_cookie_t cookie; + int ret, size, watermark; /* Reset adc buffer index */ adc->bufi = 0; - /* Allocate adc buffer */ - adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL); - if (!adc->buffer) + if (!adc->dma_chan) { + /* Allocate adc buffer */ + adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL); + if (!adc->buffer) + return -ENOMEM; + + return 0; + } + + /* + * Allocate at least twice the buffer size for dma cyclic transfers, so + * we can work with at least two dma periods. There should be : + * - always one buffer (period) dma is working on + * - one buffer (period) driver can push with iio_trigger_poll(). + */ + size = indio_dev->buffer->bytes_per_datum * indio_dev->buffer->length; + size = max(indio_dev->scan_bytes * 2, size); + + adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev, + PAGE_ALIGN(size), &adc->rx_dma_buf, + GFP_KERNEL); + if (!adc->rx_buf) return -ENOMEM; + adc->rx_buf_sz = size; + watermark = indio_dev->buffer->bytes_per_datum + * indio_dev->buffer->watermark; + watermark = max(indio_dev->scan_bytes, watermark); + watermark = rounddown(watermark, indio_dev->scan_bytes); + + dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, size, + watermark); + + /* Configure DMA channel to read data register */ + memset(&config, 0, sizeof(config)); + config.src_addr = (dma_addr_t)adc->common->phys_base; + config.src_addr += adc->offset + STM32F4_ADC_DR; + config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + ret = dmaengine_slave_config(adc->dma_chan, &config); + if (ret) + goto config_err; + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(adc->dma_chan, + adc->rx_dma_buf, + size, watermark, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) { + ret = -ENODEV; + goto config_err; + } + + desc->callback = stm32_adc_dma_buffer_done; + desc->callback_param = adc; + + cookie = dmaengine_submit(desc); + if (dma_submit_error(cookie)) { + ret = dma_submit_error(cookie); + goto config_err; + } + + /* Issue pending DMA requests */ + dma_async_issue_pending(adc->dma_chan); return 0; + +config_err: + dma_free_coherent(adc->dma_chan->device->dev, PAGE_ALIGN(size), + adc->rx_buf, adc->rx_dma_buf); + + return ret; } static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev) @@ -719,7 +867,8 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev) if (ret < 0) return ret; - stm32_adc_conv_irq_enable(adc); + if (!adc->dma_chan) + stm32_adc_conv_irq_enable(adc); stm32_adc_start_conv(adc); return 0; @@ -731,7 +880,8 @@ static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev) int ret; stm32_adc_stop_conv(adc); - stm32_adc_conv_irq_disable(adc); + if (!adc->dma_chan) + stm32_adc_conv_irq_disable(adc); ret = iio_triggered_buffer_predisable(indio_dev); if (ret < 0) @@ -748,7 +898,16 @@ static int stm32_adc_buffer_postdisable(struct iio_dev *indio_dev) { struct stm32_adc *adc = iio_priv(indio_dev); - kfree(adc->buffer); + if (!adc->dma_chan) { + kfree(adc->buffer); + } else { + dmaengine_terminate_all(adc->dma_chan); + irq_work_sync(&adc->work); + dma_free_coherent(adc->dma_chan->device->dev, + PAGE_ALIGN(adc->rx_buf_sz), + adc->rx_buf, adc->rx_dma_buf); + adc->rx_buf = NULL; + } adc->buffer = NULL; return 0; @@ -769,15 +928,31 @@ static irqreturn_t stm32_adc_trigger_handler(int irq, void *p) dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi); - /* reset buffer index */ - adc->bufi = 0; - iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer, - pf->timestamp); + if (!adc->dma_chan) { + /* reset buffer index */ + adc->bufi = 0; + iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer, + pf->timestamp); + } else { + int residue = stm32_adc_dma_residue(adc); + + while (residue >= indio_dev->scan_bytes) { + adc->buffer = (u16 *)&adc->rx_buf[adc->bufi]; + iio_push_to_buffers_with_timestamp(indio_dev, + adc->buffer, + pf->timestamp); + residue -= indio_dev->scan_bytes; + adc->bufi += indio_dev->scan_bytes; + if (adc->bufi >= adc->rx_buf_sz) + adc->bufi = 0; + } + } iio_trigger_notify_done(indio_dev->trig); /* re-enable eoc irq */ - stm32_adc_conv_irq_enable(adc); + if (!adc->dma_chan) + stm32_adc_conv_irq_enable(adc); return IRQ_HANDLED; } @@ -910,13 +1085,17 @@ static int stm32_adc_probe(struct platform_device *pdev) if (ret < 0) goto err_clk_disable; + adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx"); + if (adc->dma_chan) + init_irq_work(&adc->work, stm32_adc_dma_irq_work); + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, &stm32_adc_trigger_handler, &stm32_adc_buffer_setup_ops); if (ret) { dev_err(&pdev->dev, "buffer setup failed\n"); - goto err_clk_disable; + goto err_dma_disable; } ret = iio_device_register(indio_dev); @@ -930,6 +1109,10 @@ static int stm32_adc_probe(struct platform_device *pdev) err_buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); +err_dma_disable: + if (adc->dma_chan) + dma_release_channel(adc->dma_chan); + err_clk_disable: clk_disable_unprepare(adc->clk); @@ -943,6 +1126,8 @@ static int stm32_adc_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); + if (adc->dma_chan) + dma_release_channel(adc->dma_chan); clk_disable_unprepare(adc->clk); return 0;