2016-10-05 09:07:23

by Mugunthan V N

[permalink] [raw]
Subject: [PATCH v3 0/4] Add DMA support for ti_am335x_adc driver

The ADC has a 64 word depth fifo length which holds the ADC data
till the CPU reads. So when a user program needs a large ADC data
to operate on, then it has to do multiple reads to get its
buffer. Currently if the application asks for 4 samples per
channel with all 8 channels are enabled, kernel can provide only
3 samples per channel when all 8 channels are enabled (logs at
[1]). So with DMA support user can request for large number of
samples at a time (logs at [1]).

Tested the patch on AM437x-gp-evm and AM335x Boneblack with the
patch [2] to enable ADC and pushed a branch for testing [3]

Changes from v2:
* Fixed an issue on handling of error return from dma_request_chan().

Changes from Initial version:
* Changed DMA api from dma_request_slave_channel_compat() to
dma_request_chan()
* Changed variable names to have more clear information as per
comments from Peter.

[1] - http://pastebin.ubuntu.com/23278405/
[2] - http://pastebin.ubuntu.com/23211494/
[3] - git://git.ti.com/~mugunthanvnm/ti-linux-kernel/linux.git iio-dma-v2

Mugunthan V N (4):
mfd: ti_am335x_tscadc: store physical address
drivers: iio: ti_am335x_adc: add dma support
ARM: dts: am33xx: add DMA properties for tscadc
ARM: dts: am4372: add DMA properties for tscadc

arch/arm/boot/dts/am33xx.dtsi | 2 +
arch/arm/boot/dts/am4372.dtsi | 2 +
drivers/iio/adc/ti_am335x_adc.c | 148 ++++++++++++++++++++++++++++++++++-
drivers/mfd/ti_am335x_tscadc.c | 1 +
include/linux/mfd/ti_am335x_tscadc.h | 8 ++
5 files changed, 158 insertions(+), 3 deletions(-)

--
2.10.0.372.g6fe1b14


2016-10-05 09:07:30

by Mugunthan V N

[permalink] [raw]
Subject: [PATCH v3 1/4] mfd: ti_am335x_tscadc: store physical address

store the physical address of the device in its priv to use it
for DMA addressing in the client drivers.

Signed-off-by: Mugunthan V N <[email protected]>
---
drivers/mfd/ti_am335x_tscadc.c | 1 +
include/linux/mfd/ti_am335x_tscadc.h | 1 +
2 files changed, 2 insertions(+)

diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index c8f027b..0f3fab4 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -183,6 +183,7 @@ static int ti_tscadc_probe(struct platform_device *pdev)
tscadc->irq = err;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tscadc->tscadc_phys_base = res->start;
tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tscadc->tscadc_base))
return PTR_ERR(tscadc->tscadc_base);
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index 7f55b8b..e45a208 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -155,6 +155,7 @@ struct ti_tscadc_dev {
struct device *dev;
struct regmap *regmap;
void __iomem *tscadc_base;
+ phys_addr_t tscadc_phys_base;
int irq;
int used_cells; /* 1-2 */
int tsc_wires;
--
2.10.0.372.g6fe1b14

2016-10-05 09:08:00

by Mugunthan V N

[permalink] [raw]
Subject: [PATCH v3 3/4] ARM: dts: am33xx: add DMA properties for tscadc

Add DMA properties for tscadc

Signed-off-by: Mugunthan V N <[email protected]>
---
arch/arm/boot/dts/am33xx.dtsi | 2 ++
1 file changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 98748c6..6d607b8 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -917,6 +917,8 @@
interrupts = <16>;
ti,hwmods = "adc_tsc";
status = "disabled";
+ dmas = <&edma 53 0>, <&edma 57 0>;
+ dma-names = "fifo0", "fifo1";

tsc {
compatible = "ti,am3359-tsc";
--
2.10.0.372.g6fe1b14

2016-10-05 09:08:14

by Mugunthan V N

[permalink] [raw]
Subject: [PATCH v3 4/4] ARM: dts: am4372: add DMA properties for tscadc

Add DMA properties for tscadc

Signed-off-by: Mugunthan V N <[email protected]>
---
arch/arm/boot/dts/am4372.dtsi | 2 ++
1 file changed, 2 insertions(+)

diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index 0fadae5..6094d17 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -867,6 +867,8 @@
clocks = <&adc_tsc_fck>;
clock-names = "fck";
status = "disabled";
+ dmas = <&edma 53 0>, <&edma 57 0>;
+ dma-names = "fifo0", "fifo1";

tsc {
compatible = "ti,am3359-tsc";
--
2.10.0.372.g6fe1b14

2016-10-05 09:07:58

by Mugunthan V N

[permalink] [raw]
Subject: [PATCH v3 2/4] drivers: iio: ti_am335x_adc: add dma support

This patch adds the required pieces to ti_am335x_adc driver for
DMA support

Signed-off-by: Mugunthan V N <[email protected]>
---
drivers/iio/adc/ti_am335x_adc.c | 148 ++++++++++++++++++++++++++++++++++-
include/linux/mfd/ti_am335x_tscadc.h | 7 ++
2 files changed, 152 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index c3cfacca..ad9dec3 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -30,10 +30,28 @@
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>

+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+
+#define DMA_BUFFER_SIZE SZ_2K
+
+struct tiadc_dma {
+ struct dma_slave_config conf;
+ struct dma_chan *chan;
+ dma_addr_t addr;
+ dma_cookie_t cookie;
+ u8 *buf;
+ int current_period;
+ int period_size;
+ u8 fifo_thresh;
+};
+
struct tiadc_device {
struct ti_tscadc_dev *mfd_tscadc;
+ struct tiadc_dma dma;
struct mutex fifo1_lock; /* to protect fifo access */
int channels;
+ int total_ch_enabled;
u8 channel_line[8];
u8 channel_step[8];
int buffer_en_ch_steps;
@@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
return IRQ_HANDLED;
}

+static void tiadc_dma_rx_complete(void *param)
+{
+ struct iio_dev *indio_dev = param;
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ u8 *data;
+ int i;
+
+ data = dma->buf + dma->current_period * dma->period_size;
+ dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
+
+ for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
+ iio_push_to_buffers(indio_dev, data);
+ data += indio_dev->scan_bytes;
+ }
+}
+
+static int tiadc_start_dma(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ struct dma_async_tx_descriptor *desc;
+
+ dma->current_period = 0; /* We start to fill period 0 */
+ /*
+ * Make the fifo thresh as the multiple of total number of
+ * channels enabled, so make sure that cyclic DMA period
+ * length is also a multiple of total number of channels
+ * enabled. This ensures that no invalid data is reported
+ * to the stack via iio_push_to_buffers().
+ */
+ dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
+ adc_dev->total_ch_enabled) - 1;
+ /* Make sure that period length is multiple of fifo thresh level */
+ dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
+ (dma->fifo_thresh + 1) * sizeof(u16));
+
+ dma->conf.src_maxburst = dma->fifo_thresh + 1;
+ dmaengine_slave_config(dma->chan, &dma->conf);
+
+ desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
+ dma->period_size * 2,
+ dma->period_size, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc)
+ return -EBUSY;
+
+ desc->callback = tiadc_dma_rx_complete;
+ desc->callback_param = indio_dev;
+
+ dma->cookie = dmaengine_submit(desc);
+
+ dma_async_issue_pending(dma->chan);
+
+ tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
+ tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
+ tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
+
+ return 0;
+}
+
static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
@@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ unsigned int irq_enable;
unsigned int enb = 0;
u8 bit;

tiadc_step_config(indio_dev);
- for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
+ for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
+ adc_dev->total_ch_enabled++;
+ }
adc_dev->buffer_en_ch_steps = enb;

+ if (dma->chan)
+ tiadc_start_dma(indio_dev);
+
am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);

tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
- tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
- | IRQENB_FIFO1OVRRUN);
+
+ irq_enable = IRQENB_FIFO1OVRRUN;
+ if (!dma->chan)
+ irq_enable |= IRQENB_FIFO1THRES;
+ tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);

return 0;
}
@@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
int fifo1count, i, read;

tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
adc_dev->buffer_en_ch_steps = 0;
+ adc_dev->total_ch_enabled = 0;
+ if (dma->chan) {
+ tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
+ dmaengine_terminate_async(dma->chan);
+ }

/* Flush FIFO of leftover data in the time it takes to disable adc */
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
@@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = {
.driver_module = THIS_MODULE,
};

+static int tiadc_request_dma(struct platform_device *pdev,
+ struct tiadc_device *adc_dev)
+{
+ struct tiadc_dma *dma = &adc_dev->dma;
+ dma_cap_mask_t mask;
+
+ /* Default slave configuration parameters */
+ dma->conf.direction = DMA_DEV_TO_MEM;
+ dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_CYCLIC, mask);
+
+ /* Get a channel for RX */
+ dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
+ if (IS_ERR(dma->chan)) {
+ int ret = PTR_ERR(dma->chan);
+
+ dma->chan = NULL;
+ return ret;
+ }
+
+ /* RX buffer */
+ dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+ &dma->addr, GFP_KERNEL);
+ if (!dma->buf)
+ goto err;
+
+ return 0;
+err:
+ dma_release_channel(dma->chan);
+ return -ENOMEM;
+}
+
static int tiadc_parse_dt(struct platform_device *pdev,
struct tiadc_device *adc_dev)
{
@@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev)

platform_set_drvdata(pdev, indio_dev);

+ err = tiadc_request_dma(pdev, adc_dev);
+ if (err && err == -EPROBE_DEFER)
+ goto err_dma;
+
return 0;

+err_dma:
+ iio_device_unregister(indio_dev);
err_buffer_unregister:
tiadc_iio_buffered_hardware_remove(indio_dev);
err_free_channels:
@@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
u32 step_en;

+ if (dma->chan) {
+ dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+ dma->buf, dma->addr);
+ dma_release_channel(dma->chan);
+ }
iio_device_unregister(indio_dev);
tiadc_iio_buffered_hardware_remove(indio_dev);
tiadc_channels_remove(indio_dev);
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index e45a208..b9a53e0 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -23,6 +23,8 @@
#define REG_IRQENABLE 0x02C
#define REG_IRQCLR 0x030
#define REG_IRQWAKEUP 0x034
+#define REG_DMAENABLE_SET 0x038
+#define REG_DMAENABLE_CLEAR 0x03c
#define REG_CTRL 0x040
#define REG_ADCFSM 0x044
#define REG_CLKDIV 0x04C
@@ -36,6 +38,7 @@
#define REG_FIFO0THR 0xE8
#define REG_FIFO1CNT 0xF0
#define REG_FIFO1THR 0xF4
+#define REG_DMA1REQ 0xF8
#define REG_FIFO0 0x100
#define REG_FIFO1 0x200

@@ -126,6 +129,10 @@
#define FIFOREAD_DATA_MASK (0xfff << 0)
#define FIFOREAD_CHNLID_MASK (0xf << 16)

+/* DMA ENABLE/CLEAR Register */
+#define DMA_FIFO0 BIT(0)
+#define DMA_FIFO1 BIT(1)
+
/* Sequencer Status */
#define SEQ_STATUS BIT(5)
#define CHARGE_STEP 0x11
--
2.10.0.372.g6fe1b14

2016-10-05 09:27:14

by Peter Ujfalusi

[permalink] [raw]
Subject: Re: [PATCH v3 2/4] drivers: iio: ti_am335x_adc: add dma support

On 10/05/16 12:04, Mugunthan V N wrote:
> This patch adds the required pieces to ti_am335x_adc driver for
> DMA support
>
> Signed-off-by: Mugunthan V N <[email protected]>
> ---
> drivers/iio/adc/ti_am335x_adc.c | 148 ++++++++++++++++++++++++++++++++++-
> include/linux/mfd/ti_am335x_tscadc.h | 7 ++
> 2 files changed, 152 insertions(+), 3 deletions(-)

Reviewed-by: Peter Ujfalusi <[email protected]>

>
> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
> index c3cfacca..ad9dec3 100644
> --- a/drivers/iio/adc/ti_am335x_adc.c
> +++ b/drivers/iio/adc/ti_am335x_adc.c
> @@ -30,10 +30,28 @@
> #include <linux/iio/buffer.h>
> #include <linux/iio/kfifo_buf.h>
>
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +
> +#define DMA_BUFFER_SIZE SZ_2K
> +
> +struct tiadc_dma {
> + struct dma_slave_config conf;
> + struct dma_chan *chan;
> + dma_addr_t addr;
> + dma_cookie_t cookie;
> + u8 *buf;
> + int current_period;
> + int period_size;
> + u8 fifo_thresh;
> +};
> +
> struct tiadc_device {
> struct ti_tscadc_dev *mfd_tscadc;
> + struct tiadc_dma dma;
> struct mutex fifo1_lock; /* to protect fifo access */
> int channels;
> + int total_ch_enabled;
> u8 channel_line[8];
> u8 channel_step[8];
> int buffer_en_ch_steps;
> @@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
> return IRQ_HANDLED;
> }
>
> +static void tiadc_dma_rx_complete(void *param)
> +{
> + struct iio_dev *indio_dev = param;
> + struct tiadc_device *adc_dev = iio_priv(indio_dev);
> + struct tiadc_dma *dma = &adc_dev->dma;
> + u8 *data;
> + int i;
> +
> + data = dma->buf + dma->current_period * dma->period_size;
> + dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
> +
> + for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
> + iio_push_to_buffers(indio_dev, data);
> + data += indio_dev->scan_bytes;
> + }
> +}
> +
> +static int tiadc_start_dma(struct iio_dev *indio_dev)
> +{
> + struct tiadc_device *adc_dev = iio_priv(indio_dev);
> + struct tiadc_dma *dma = &adc_dev->dma;
> + struct dma_async_tx_descriptor *desc;
> +
> + dma->current_period = 0; /* We start to fill period 0 */
> + /*
> + * Make the fifo thresh as the multiple of total number of
> + * channels enabled, so make sure that cyclic DMA period
> + * length is also a multiple of total number of channels
> + * enabled. This ensures that no invalid data is reported
> + * to the stack via iio_push_to_buffers().
> + */
> + dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
> + adc_dev->total_ch_enabled) - 1;
> + /* Make sure that period length is multiple of fifo thresh level */
> + dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
> + (dma->fifo_thresh + 1) * sizeof(u16));
> +
> + dma->conf.src_maxburst = dma->fifo_thresh + 1;
> + dmaengine_slave_config(dma->chan, &dma->conf);
> +
> + desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
> + dma->period_size * 2,
> + dma->period_size, DMA_DEV_TO_MEM,
> + DMA_PREP_INTERRUPT);
> + if (!desc)
> + return -EBUSY;
> +
> + desc->callback = tiadc_dma_rx_complete;
> + desc->callback_param = indio_dev;
> +
> + dma->cookie = dmaengine_submit(desc);
> +
> + dma_async_issue_pending(dma->chan);
> +
> + tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
> + tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
> + tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
> +
> + return 0;
> +}
> +
> static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
> {
> struct tiadc_device *adc_dev = iio_priv(indio_dev);
> @@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
> static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
> {
> struct tiadc_device *adc_dev = iio_priv(indio_dev);
> + struct tiadc_dma *dma = &adc_dev->dma;
> + unsigned int irq_enable;
> unsigned int enb = 0;
> u8 bit;
>
> tiadc_step_config(indio_dev);
> - for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
> + for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
> enb |= (get_adc_step_bit(adc_dev, bit) << 1);
> + adc_dev->total_ch_enabled++;
> + }
> adc_dev->buffer_en_ch_steps = enb;
>
> + if (dma->chan)
> + tiadc_start_dma(indio_dev);
> +
> am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
>
> tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
> | IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
> - tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
> - | IRQENB_FIFO1OVRRUN);
> +
> + irq_enable = IRQENB_FIFO1OVRRUN;
> + if (!dma->chan)
> + irq_enable |= IRQENB_FIFO1THRES;
> + tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);
>
> return 0;
> }
> @@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
> static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
> {
> struct tiadc_device *adc_dev = iio_priv(indio_dev);
> + struct tiadc_dma *dma = &adc_dev->dma;
> int fifo1count, i, read;
>
> tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
> IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
> am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
> adc_dev->buffer_en_ch_steps = 0;
> + adc_dev->total_ch_enabled = 0;
> + if (dma->chan) {
> + tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
> + dmaengine_terminate_async(dma->chan);
> + }
>
> /* Flush FIFO of leftover data in the time it takes to disable adc */
> fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
> @@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = {
> .driver_module = THIS_MODULE,
> };
>
> +static int tiadc_request_dma(struct platform_device *pdev,
> + struct tiadc_device *adc_dev)
> +{
> + struct tiadc_dma *dma = &adc_dev->dma;
> + dma_cap_mask_t mask;
> +
> + /* Default slave configuration parameters */
> + dma->conf.direction = DMA_DEV_TO_MEM;
> + dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
> + dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_CYCLIC, mask);
> +
> + /* Get a channel for RX */
> + dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
> + if (IS_ERR(dma->chan)) {
> + int ret = PTR_ERR(dma->chan);
> +
> + dma->chan = NULL;
> + return ret;
> + }
> +
> + /* RX buffer */
> + dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
> + &dma->addr, GFP_KERNEL);
> + if (!dma->buf)
> + goto err;
> +
> + return 0;
> +err:
> + dma_release_channel(dma->chan);
> + return -ENOMEM;
> +}
> +
> static int tiadc_parse_dt(struct platform_device *pdev,
> struct tiadc_device *adc_dev)
> {
> @@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev)
>
> platform_set_drvdata(pdev, indio_dev);
>
> + err = tiadc_request_dma(pdev, adc_dev);
> + if (err && err == -EPROBE_DEFER)
> + goto err_dma;
> +
> return 0;
>
> +err_dma:
> + iio_device_unregister(indio_dev);
> err_buffer_unregister:
> tiadc_iio_buffered_hardware_remove(indio_dev);
> err_free_channels:
> @@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev)
> {
> struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> struct tiadc_device *adc_dev = iio_priv(indio_dev);
> + struct tiadc_dma *dma = &adc_dev->dma;
> u32 step_en;
>
> + if (dma->chan) {
> + dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
> + dma->buf, dma->addr);
> + dma_release_channel(dma->chan);
> + }
> iio_device_unregister(indio_dev);
> tiadc_iio_buffered_hardware_remove(indio_dev);
> tiadc_channels_remove(indio_dev);
> diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
> index e45a208..b9a53e0 100644
> --- a/include/linux/mfd/ti_am335x_tscadc.h
> +++ b/include/linux/mfd/ti_am335x_tscadc.h
> @@ -23,6 +23,8 @@
> #define REG_IRQENABLE 0x02C
> #define REG_IRQCLR 0x030
> #define REG_IRQWAKEUP 0x034
> +#define REG_DMAENABLE_SET 0x038
> +#define REG_DMAENABLE_CLEAR 0x03c
> #define REG_CTRL 0x040
> #define REG_ADCFSM 0x044
> #define REG_CLKDIV 0x04C
> @@ -36,6 +38,7 @@
> #define REG_FIFO0THR 0xE8
> #define REG_FIFO1CNT 0xF0
> #define REG_FIFO1THR 0xF4
> +#define REG_DMA1REQ 0xF8
> #define REG_FIFO0 0x100
> #define REG_FIFO1 0x200
>
> @@ -126,6 +129,10 @@
> #define FIFOREAD_DATA_MASK (0xfff << 0)
> #define FIFOREAD_CHNLID_MASK (0xf << 16)
>
> +/* DMA ENABLE/CLEAR Register */
> +#define DMA_FIFO0 BIT(0)
> +#define DMA_FIFO1 BIT(1)
> +
> /* Sequencer Status */
> #define SEQ_STATUS BIT(5)
> #define CHARGE_STEP 0x11
>


--
P?ter

2016-10-09 08:30:53

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 2/4] drivers: iio: ti_am335x_adc: add dma support

On 05/10/16 10:04, Mugunthan V N wrote:
> This patch adds the required pieces to ti_am335x_adc driver for
> DMA support
>
> Signed-off-by: Mugunthan V N <[email protected]>
Hi,

Just the one question inline. I'll also need an Ack from Lee as
this touches code in mfd (as does the previous patch).
We have obviously missed this merge window, so no particular rush.

Otherwise, looking very nice indeed. Got to get my BBB out and play
with this now ;)

Jonathan
> ---
> drivers/iio/adc/ti_am335x_adc.c | 148 ++++++++++++++++++++++++++++++++++-
> include/linux/mfd/ti_am335x_tscadc.h | 7 ++
> 2 files changed, 152 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
> index c3cfacca..ad9dec3 100644
> --- a/drivers/iio/adc/ti_am335x_adc.c
> +++ b/drivers/iio/adc/ti_am335x_adc.c
> @@ -30,10 +30,28 @@
> #include <linux/iio/buffer.h>
> #include <linux/iio/kfifo_buf.h>
>
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +
> +#define DMA_BUFFER_SIZE SZ_2K
> +
> +struct tiadc_dma {
> + struct dma_slave_config conf;
> + struct dma_chan *chan;
> + dma_addr_t addr;
> + dma_cookie_t cookie;
> + u8 *buf;
> + int current_period;
> + int period_size;
> + u8 fifo_thresh;
> +};
> +
> struct tiadc_device {
> struct ti_tscadc_dev *mfd_tscadc;
> + struct tiadc_dma dma;
> struct mutex fifo1_lock; /* to protect fifo access */
> int channels;
> + int total_ch_enabled;
> u8 channel_line[8];
> u8 channel_step[8];
> int buffer_en_ch_steps;
> @@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
> return IRQ_HANDLED;
> }
>
> +static void tiadc_dma_rx_complete(void *param)
> +{
> + struct iio_dev *indio_dev = param;
> + struct tiadc_device *adc_dev = iio_priv(indio_dev);
> + struct tiadc_dma *dma = &adc_dev->dma;
> + u8 *data;
> + int i;
> +
> + data = dma->buf + dma->current_period * dma->period_size;
> + dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
> +
> + for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
> + iio_push_to_buffers(indio_dev, data);
> + data += indio_dev->scan_bytes;
> + }
Hmm. Another case for the mooted iio_push_to_buffers_multi. Guess
we should move on with that one ;)
> +}
> +
> +static int tiadc_start_dma(struct iio_dev *indio_dev)
> +{
> + struct tiadc_device *adc_dev = iio_priv(indio_dev);
> + struct tiadc_dma *dma = &adc_dev->dma;
> + struct dma_async_tx_descriptor *desc;
> +
> + dma->current_period = 0; /* We start to fill period 0 */
> + /*
> + * Make the fifo thresh as the multiple of total number of
> + * channels enabled, so make sure that cyclic DMA period
> + * length is also a multiple of total number of channels
> + * enabled. This ensures that no invalid data is reported
> + * to the stack via iio_push_to_buffers().
> + */
> + dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
> + adc_dev->total_ch_enabled) - 1;
> + /* Make sure that period length is multiple of fifo thresh level */
> + dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
> + (dma->fifo_thresh + 1) * sizeof(u16));
> +
> + dma->conf.src_maxburst = dma->fifo_thresh + 1;
> + dmaengine_slave_config(dma->chan, &dma->conf);
> +
> + desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
> + dma->period_size * 2,
> + dma->period_size, DMA_DEV_TO_MEM,
> + DMA_PREP_INTERRUPT);
> + if (!desc)
> + return -EBUSY;
> +
> + desc->callback = tiadc_dma_rx_complete;
> + desc->callback_param = indio_dev;
> +
> + dma->cookie = dmaengine_submit(desc);
> +
> + dma_async_issue_pending(dma->chan);
> +
> + tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
> + tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
> + tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
> +
> + return 0;
> +}
> +
> static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
> {
> struct tiadc_device *adc_dev = iio_priv(indio_dev);
> @@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
> static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
> {
> struct tiadc_device *adc_dev = iio_priv(indio_dev);
> + struct tiadc_dma *dma = &adc_dev->dma;
> + unsigned int irq_enable;
> unsigned int enb = 0;
> u8 bit;
>
> tiadc_step_config(indio_dev);
> - for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
> + for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
> enb |= (get_adc_step_bit(adc_dev, bit) << 1);
> + adc_dev->total_ch_enabled++;
> + }
> adc_dev->buffer_en_ch_steps = enb;
>
> + if (dma->chan)
> + tiadc_start_dma(indio_dev);
> +
> am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
>
> tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
> | IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
> - tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
> - | IRQENB_FIFO1OVRRUN);
> +
> + irq_enable = IRQENB_FIFO1OVRRUN;
> + if (!dma->chan)
> + irq_enable |= IRQENB_FIFO1THRES;
This changes the non dma behaviour as we no longer set IRQENB_FIFO1THRES.
Why? Was it wrong before?
> + tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);
>
> return 0;
> }
> @@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
> static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
> {
> struct tiadc_device *adc_dev = iio_priv(indio_dev);
> + struct tiadc_dma *dma = &adc_dev->dma;
> int fifo1count, i, read;
>
> tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
> IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
> am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
> adc_dev->buffer_en_ch_steps = 0;
> + adc_dev->total_ch_enabled = 0;
> + if (dma->chan) {
> + tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
> + dmaengine_terminate_async(dma->chan);
> + }
>
> /* Flush FIFO of leftover data in the time it takes to disable adc */
> fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
> @@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = {
> .driver_module = THIS_MODULE,
> };
>
> +static int tiadc_request_dma(struct platform_device *pdev,
> + struct tiadc_device *adc_dev)
> +{
> + struct tiadc_dma *dma = &adc_dev->dma;
> + dma_cap_mask_t mask;
> +
> + /* Default slave configuration parameters */
> + dma->conf.direction = DMA_DEV_TO_MEM;
> + dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
> + dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_CYCLIC, mask);
> +
> + /* Get a channel for RX */
> + dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
> + if (IS_ERR(dma->chan)) {
> + int ret = PTR_ERR(dma->chan);
> +
> + dma->chan = NULL;
> + return ret;
> + }
> +
> + /* RX buffer */
> + dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
> + &dma->addr, GFP_KERNEL);
> + if (!dma->buf)
> + goto err;
> +
> + return 0;
> +err:
> + dma_release_channel(dma->chan);
> + return -ENOMEM;
> +}
> +
> static int tiadc_parse_dt(struct platform_device *pdev,
> struct tiadc_device *adc_dev)
> {
> @@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev)
>
> platform_set_drvdata(pdev, indio_dev);
>
> + err = tiadc_request_dma(pdev, adc_dev);
> + if (err && err == -EPROBE_DEFER)
> + goto err_dma;
> +
> return 0;
>
> +err_dma:
> + iio_device_unregister(indio_dev);
> err_buffer_unregister:
> tiadc_iio_buffered_hardware_remove(indio_dev);
> err_free_channels:
> @@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev)
> {
> struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> struct tiadc_device *adc_dev = iio_priv(indio_dev);
> + struct tiadc_dma *dma = &adc_dev->dma;
> u32 step_en;
>
> + if (dma->chan) {
> + dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
> + dma->buf, dma->addr);
> + dma_release_channel(dma->chan);
> + }
> iio_device_unregister(indio_dev);
> tiadc_iio_buffered_hardware_remove(indio_dev);
> tiadc_channels_remove(indio_dev);
> diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
> index e45a208..b9a53e0 100644
> --- a/include/linux/mfd/ti_am335x_tscadc.h
> +++ b/include/linux/mfd/ti_am335x_tscadc.h
> @@ -23,6 +23,8 @@
> #define REG_IRQENABLE 0x02C
> #define REG_IRQCLR 0x030
> #define REG_IRQWAKEUP 0x034
> +#define REG_DMAENABLE_SET 0x038
> +#define REG_DMAENABLE_CLEAR 0x03c
> #define REG_CTRL 0x040
> #define REG_ADCFSM 0x044
> #define REG_CLKDIV 0x04C
> @@ -36,6 +38,7 @@
> #define REG_FIFO0THR 0xE8
> #define REG_FIFO1CNT 0xF0
> #define REG_FIFO1THR 0xF4
> +#define REG_DMA1REQ 0xF8
> #define REG_FIFO0 0x100
> #define REG_FIFO1 0x200
>
> @@ -126,6 +129,10 @@
> #define FIFOREAD_DATA_MASK (0xfff << 0)
> #define FIFOREAD_CHNLID_MASK (0xf << 16)
>
> +/* DMA ENABLE/CLEAR Register */
> +#define DMA_FIFO0 BIT(0)
> +#define DMA_FIFO1 BIT(1)
> +
> /* Sequencer Status */
> #define SEQ_STATUS BIT(5)
> #define CHARGE_STEP 0x11
>

2016-10-09 08:35:01

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 3/4] ARM: dts: am33xx: add DMA properties for tscadc

On 05/10/16 10:04, Mugunthan V N wrote:
> Add DMA properties for tscadc
>
> Signed-off-by: Mugunthan V N <[email protected]>
Do the binding docs need updating to reflect this? I can't immediately
find the relevant doc to check!

Jonathan
> ---
> arch/arm/boot/dts/am33xx.dtsi | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
> index 98748c6..6d607b8 100644
> --- a/arch/arm/boot/dts/am33xx.dtsi
> +++ b/arch/arm/boot/dts/am33xx.dtsi
> @@ -917,6 +917,8 @@
> interrupts = <16>;
> ti,hwmods = "adc_tsc";
> status = "disabled";
> + dmas = <&edma 53 0>, <&edma 57 0>;
> + dma-names = "fifo0", "fifo1";
>
> tsc {
> compatible = "ti,am3359-tsc";
>

2016-10-19 22:56:40

by Mugunthan V N

[permalink] [raw]
Subject: Re: [PATCH v3 2/4] drivers: iio: ti_am335x_adc: add dma support

On Sunday 09 October 2016 02:00 PM, Jonathan Cameron wrote:
> On 05/10/16 10:04, Mugunthan V N wrote:
>> This patch adds the required pieces to ti_am335x_adc driver for
>> DMA support
>>
>> Signed-off-by: Mugunthan V N <[email protected]>
> Hi,
>
> Just the one question inline. I'll also need an Ack from Lee as
> this touches code in mfd (as does the previous patch).
> We have obviously missed this merge window, so no particular rush.
>
> Otherwise, looking very nice indeed. Got to get my BBB out and play
> with this now ;)
>
> Jonathan
>> ---
>> drivers/iio/adc/ti_am335x_adc.c | 148 ++++++++++++++++++++++++++++++++++-
>> include/linux/mfd/ti_am335x_tscadc.h | 7 ++
>> 2 files changed, 152 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
>> index c3cfacca..ad9dec3 100644
>> --- a/drivers/iio/adc/ti_am335x_adc.c
>> +++ b/drivers/iio/adc/ti_am335x_adc.c
>> @@ -30,10 +30,28 @@
>> #include <linux/iio/buffer.h>
>> #include <linux/iio/kfifo_buf.h>
>>
>> +#include <linux/dmaengine.h>
>> +#include <linux/dma-mapping.h>
>> +
>> +#define DMA_BUFFER_SIZE SZ_2K
>> +
>> +struct tiadc_dma {
>> + struct dma_slave_config conf;
>> + struct dma_chan *chan;
>> + dma_addr_t addr;
>> + dma_cookie_t cookie;
>> + u8 *buf;
>> + int current_period;
>> + int period_size;
>> + u8 fifo_thresh;
>> +};
>> +
>> struct tiadc_device {
>> struct ti_tscadc_dev *mfd_tscadc;
>> + struct tiadc_dma dma;
>> struct mutex fifo1_lock; /* to protect fifo access */
>> int channels;
>> + int total_ch_enabled;
>> u8 channel_line[8];
>> u8 channel_step[8];
>> int buffer_en_ch_steps;
>> @@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
>> return IRQ_HANDLED;
>> }
>>
>> +static void tiadc_dma_rx_complete(void *param)
>> +{
>> + struct iio_dev *indio_dev = param;
>> + struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> + u8 *data;
>> + int i;
>> +
>> + data = dma->buf + dma->current_period * dma->period_size;
>> + dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
>> +
>> + for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
>> + iio_push_to_buffers(indio_dev, data);
>> + data += indio_dev->scan_bytes;
>> + }
> Hmm. Another case for the mooted iio_push_to_buffers_multi. Guess
> we should move on with that one ;)
>> +}
>> +
>> +static int tiadc_start_dma(struct iio_dev *indio_dev)
>> +{
>> + struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> + struct dma_async_tx_descriptor *desc;
>> +
>> + dma->current_period = 0; /* We start to fill period 0 */
>> + /*
>> + * Make the fifo thresh as the multiple of total number of
>> + * channels enabled, so make sure that cyclic DMA period
>> + * length is also a multiple of total number of channels
>> + * enabled. This ensures that no invalid data is reported
>> + * to the stack via iio_push_to_buffers().
>> + */
>> + dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
>> + adc_dev->total_ch_enabled) - 1;
>> + /* Make sure that period length is multiple of fifo thresh level */
>> + dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
>> + (dma->fifo_thresh + 1) * sizeof(u16));
>> +
>> + dma->conf.src_maxburst = dma->fifo_thresh + 1;
>> + dmaengine_slave_config(dma->chan, &dma->conf);
>> +
>> + desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
>> + dma->period_size * 2,
>> + dma->period_size, DMA_DEV_TO_MEM,
>> + DMA_PREP_INTERRUPT);
>> + if (!desc)
>> + return -EBUSY;
>> +
>> + desc->callback = tiadc_dma_rx_complete;
>> + desc->callback_param = indio_dev;
>> +
>> + dma->cookie = dmaengine_submit(desc);
>> +
>> + dma_async_issue_pending(dma->chan);
>> +
>> + tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
>> + tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
>> + tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
>> +
>> + return 0;
>> +}
>> +
>> static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
>> {
>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> @@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
>> static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
>> {
>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> + unsigned int irq_enable;
>> unsigned int enb = 0;
>> u8 bit;
>>
>> tiadc_step_config(indio_dev);
>> - for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
>> + for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
>> enb |= (get_adc_step_bit(adc_dev, bit) << 1);
>> + adc_dev->total_ch_enabled++;
>> + }
>> adc_dev->buffer_en_ch_steps = enb;
>>
>> + if (dma->chan)
>> + tiadc_start_dma(indio_dev);
>> +
>> am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
>>
>> tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
>> | IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
>> - tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
>> - | IRQENB_FIFO1OVRRUN);
>> +
>> + irq_enable = IRQENB_FIFO1OVRRUN;
>> + if (!dma->chan)
>> + irq_enable |= IRQENB_FIFO1THRES;
> This changes the non dma behaviour as we no longer set IRQENB_FIFO1THRES.
> Why? Was it wrong before?

In non DMA case, dms->chan will be NULL and IRQENB_FIFO1THRES interrupt
will be enabled to pop the ADC samples.

Regards
Mugunthan V N

>> + tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);
>>
>> return 0;
>> }
>> @@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
>> static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
>> {
>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> int fifo1count, i, read;
>>
>> tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
>> IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
>> am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
>> adc_dev->buffer_en_ch_steps = 0;
>> + adc_dev->total_ch_enabled = 0;
>> + if (dma->chan) {
>> + tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
>> + dmaengine_terminate_async(dma->chan);
>> + }
>>
>> /* Flush FIFO of leftover data in the time it takes to disable adc */
>> fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
>> @@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = {
>> .driver_module = THIS_MODULE,
>> };
>>
>> +static int tiadc_request_dma(struct platform_device *pdev,
>> + struct tiadc_device *adc_dev)
>> +{
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> + dma_cap_mask_t mask;
>> +
>> + /* Default slave configuration parameters */
>> + dma->conf.direction = DMA_DEV_TO_MEM;
>> + dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
>> + dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
>> +
>> + dma_cap_zero(mask);
>> + dma_cap_set(DMA_CYCLIC, mask);
>> +
>> + /* Get a channel for RX */
>> + dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
>> + if (IS_ERR(dma->chan)) {
>> + int ret = PTR_ERR(dma->chan);
>> +
>> + dma->chan = NULL;
>> + return ret;
>> + }
>> +
>> + /* RX buffer */
>> + dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
>> + &dma->addr, GFP_KERNEL);
>> + if (!dma->buf)
>> + goto err;
>> +
>> + return 0;
>> +err:
>> + dma_release_channel(dma->chan);
>> + return -ENOMEM;
>> +}
>> +
>> static int tiadc_parse_dt(struct platform_device *pdev,
>> struct tiadc_device *adc_dev)
>> {
>> @@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev)
>>
>> platform_set_drvdata(pdev, indio_dev);
>>
>> + err = tiadc_request_dma(pdev, adc_dev);
>> + if (err && err == -EPROBE_DEFER)
>> + goto err_dma;
>> +
>> return 0;
>>
>> +err_dma:
>> + iio_device_unregister(indio_dev);
>> err_buffer_unregister:
>> tiadc_iio_buffered_hardware_remove(indio_dev);
>> err_free_channels:
>> @@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev)
>> {
>> struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> u32 step_en;
>>
>> + if (dma->chan) {
>> + dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
>> + dma->buf, dma->addr);
>> + dma_release_channel(dma->chan);
>> + }
>> iio_device_unregister(indio_dev);
>> tiadc_iio_buffered_hardware_remove(indio_dev);
>> tiadc_channels_remove(indio_dev);
>> diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
>> index e45a208..b9a53e0 100644
>> --- a/include/linux/mfd/ti_am335x_tscadc.h
>> +++ b/include/linux/mfd/ti_am335x_tscadc.h
>> @@ -23,6 +23,8 @@
>> #define REG_IRQENABLE 0x02C
>> #define REG_IRQCLR 0x030
>> #define REG_IRQWAKEUP 0x034
>> +#define REG_DMAENABLE_SET 0x038
>> +#define REG_DMAENABLE_CLEAR 0x03c
>> #define REG_CTRL 0x040
>> #define REG_ADCFSM 0x044
>> #define REG_CLKDIV 0x04C
>> @@ -36,6 +38,7 @@
>> #define REG_FIFO0THR 0xE8
>> #define REG_FIFO1CNT 0xF0
>> #define REG_FIFO1THR 0xF4
>> +#define REG_DMA1REQ 0xF8
>> #define REG_FIFO0 0x100
>> #define REG_FIFO1 0x200
>>
>> @@ -126,6 +129,10 @@
>> #define FIFOREAD_DATA_MASK (0xfff << 0)
>> #define FIFOREAD_CHNLID_MASK (0xf << 16)
>>
>> +/* DMA ENABLE/CLEAR Register */
>> +#define DMA_FIFO0 BIT(0)
>> +#define DMA_FIFO1 BIT(1)
>> +
>> /* Sequencer Status */
>> #define SEQ_STATUS BIT(5)
>> #define CHARGE_STEP 0x11
>>
>

2016-10-22 17:47:47

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 2/4] drivers: iio: ti_am335x_adc: add dma support

On 19/10/16 09:52, Mugunthan V N wrote:
> On Sunday 09 October 2016 02:00 PM, Jonathan Cameron wrote:
>> On 05/10/16 10:04, Mugunthan V N wrote:
>>> This patch adds the required pieces to ti_am335x_adc driver for
>>> DMA support
>>>
>>> Signed-off-by: Mugunthan V N <[email protected]>
>> Hi,
>>
>> Just the one question inline. I'll also need an Ack from Lee as
>> this touches code in mfd (as does the previous patch).
>> We have obviously missed this merge window, so no particular rush.
Question answered below. So just need that Ack from Lee.


>>
>> Otherwise, looking very nice indeed. Got to get my BBB out and play
>> with this now ;)
>>
>> Jonathan
>>> ---
>>> drivers/iio/adc/ti_am335x_adc.c | 148 ++++++++++++++++++++++++++++++++++-
>>> include/linux/mfd/ti_am335x_tscadc.h | 7 ++
>>> 2 files changed, 152 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
>>> index c3cfacca..ad9dec3 100644
>>> --- a/drivers/iio/adc/ti_am335x_adc.c
>>> +++ b/drivers/iio/adc/ti_am335x_adc.c
>>> @@ -30,10 +30,28 @@
>>> #include <linux/iio/buffer.h>
>>> #include <linux/iio/kfifo_buf.h>
>>>
>>> +#include <linux/dmaengine.h>
>>> +#include <linux/dma-mapping.h>
>>> +
>>> +#define DMA_BUFFER_SIZE SZ_2K
>>> +
>>> +struct tiadc_dma {
>>> + struct dma_slave_config conf;
>>> + struct dma_chan *chan;
>>> + dma_addr_t addr;
>>> + dma_cookie_t cookie;
>>> + u8 *buf;
>>> + int current_period;
>>> + int period_size;
>>> + u8 fifo_thresh;
>>> +};
>>> +
>>> struct tiadc_device {
>>> struct ti_tscadc_dev *mfd_tscadc;
>>> + struct tiadc_dma dma;
>>> struct mutex fifo1_lock; /* to protect fifo access */
>>> int channels;
>>> + int total_ch_enabled;
>>> u8 channel_line[8];
>>> u8 channel_step[8];
>>> int buffer_en_ch_steps;
>>> @@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
>>> return IRQ_HANDLED;
>>> }
>>>
>>> +static void tiadc_dma_rx_complete(void *param)
>>> +{
>>> + struct iio_dev *indio_dev = param;
>>> + struct tiadc_device *adc_dev = iio_priv(indio_dev);
>>> + struct tiadc_dma *dma = &adc_dev->dma;
>>> + u8 *data;
>>> + int i;
>>> +
>>> + data = dma->buf + dma->current_period * dma->period_size;
>>> + dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
>>> +
>>> + for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
>>> + iio_push_to_buffers(indio_dev, data);
>>> + data += indio_dev->scan_bytes;
>>> + }
>> Hmm. Another case for the mooted iio_push_to_buffers_multi. Guess
>> we should move on with that one ;)
>>> +}
>>> +
>>> +static int tiadc_start_dma(struct iio_dev *indio_dev)
>>> +{
>>> + struct tiadc_device *adc_dev = iio_priv(indio_dev);
>>> + struct tiadc_dma *dma = &adc_dev->dma;
>>> + struct dma_async_tx_descriptor *desc;
>>> +
>>> + dma->current_period = 0; /* We start to fill period 0 */
>>> + /*
>>> + * Make the fifo thresh as the multiple of total number of
>>> + * channels enabled, so make sure that cyclic DMA period
>>> + * length is also a multiple of total number of channels
>>> + * enabled. This ensures that no invalid data is reported
>>> + * to the stack via iio_push_to_buffers().
>>> + */
>>> + dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
>>> + adc_dev->total_ch_enabled) - 1;
>>> + /* Make sure that period length is multiple of fifo thresh level */
>>> + dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
>>> + (dma->fifo_thresh + 1) * sizeof(u16));
>>> +
>>> + dma->conf.src_maxburst = dma->fifo_thresh + 1;
>>> + dmaengine_slave_config(dma->chan, &dma->conf);
>>> +
>>> + desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
>>> + dma->period_size * 2,
>>> + dma->period_size, DMA_DEV_TO_MEM,
>>> + DMA_PREP_INTERRUPT);
>>> + if (!desc)
>>> + return -EBUSY;
>>> +
>>> + desc->callback = tiadc_dma_rx_complete;
>>> + desc->callback_param = indio_dev;
>>> +
>>> + dma->cookie = dmaengine_submit(desc);
>>> +
>>> + dma_async_issue_pending(dma->chan);
>>> +
>>> + tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
>>> + tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
>>> + tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
>>> {
>>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>>> @@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
>>> static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
>>> {
>>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>>> + struct tiadc_dma *dma = &adc_dev->dma;
>>> + unsigned int irq_enable;
>>> unsigned int enb = 0;
>>> u8 bit;
>>>
>>> tiadc_step_config(indio_dev);
>>> - for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
>>> + for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
>>> enb |= (get_adc_step_bit(adc_dev, bit) << 1);
>>> + adc_dev->total_ch_enabled++;
>>> + }
>>> adc_dev->buffer_en_ch_steps = enb;
>>>
>>> + if (dma->chan)
>>> + tiadc_start_dma(indio_dev);
>>> +
>>> am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
>>>
>>> tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
>>> | IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
>>> - tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
>>> - | IRQENB_FIFO1OVRRUN);
>>> +
>>> + irq_enable = IRQENB_FIFO1OVRRUN;
>>> + if (!dma->chan)
>>> + irq_enable |= IRQENB_FIFO1THRES;
>> This changes the non dma behaviour as we no longer set IRQENB_FIFO1THRES.
>> Why? Was it wrong before?
>
> In non DMA case, dms->chan will be NULL and IRQENB_FIFO1THRES interrupt
> will be enabled to pop the ADC samples.
>
oops. Thanks for pointing out how I'd misread this!
So just after the ack from Lee now.
Jonathan
> Regards
> Mugunthan V N
>
>>> + tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);
>>>
>>> return 0;
>>> }
>>> @@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
>>> static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
>>> {
>>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>>> + struct tiadc_dma *dma = &adc_dev->dma;
>>> int fifo1count, i, read;
>>>
>>> tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
>>> IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
>>> am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
>>> adc_dev->buffer_en_ch_steps = 0;
>>> + adc_dev->total_ch_enabled = 0;
>>> + if (dma->chan) {
>>> + tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
>>> + dmaengine_terminate_async(dma->chan);
>>> + }
>>>
>>> /* Flush FIFO of leftover data in the time it takes to disable adc */
>>> fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
>>> @@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = {
>>> .driver_module = THIS_MODULE,
>>> };
>>>
>>> +static int tiadc_request_dma(struct platform_device *pdev,
>>> + struct tiadc_device *adc_dev)
>>> +{
>>> + struct tiadc_dma *dma = &adc_dev->dma;
>>> + dma_cap_mask_t mask;
>>> +
>>> + /* Default slave configuration parameters */
>>> + dma->conf.direction = DMA_DEV_TO_MEM;
>>> + dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
>>> + dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
>>> +
>>> + dma_cap_zero(mask);
>>> + dma_cap_set(DMA_CYCLIC, mask);
>>> +
>>> + /* Get a channel for RX */
>>> + dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
>>> + if (IS_ERR(dma->chan)) {
>>> + int ret = PTR_ERR(dma->chan);
>>> +
>>> + dma->chan = NULL;
>>> + return ret;
>>> + }
>>> +
>>> + /* RX buffer */
>>> + dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
>>> + &dma->addr, GFP_KERNEL);
>>> + if (!dma->buf)
>>> + goto err;
>>> +
>>> + return 0;
>>> +err:
>>> + dma_release_channel(dma->chan);
>>> + return -ENOMEM;
>>> +}
>>> +
>>> static int tiadc_parse_dt(struct platform_device *pdev,
>>> struct tiadc_device *adc_dev)
>>> {
>>> @@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev)
>>>
>>> platform_set_drvdata(pdev, indio_dev);
>>>
>>> + err = tiadc_request_dma(pdev, adc_dev);
>>> + if (err && err == -EPROBE_DEFER)
>>> + goto err_dma;
>>> +
>>> return 0;
>>>
>>> +err_dma:
>>> + iio_device_unregister(indio_dev);
>>> err_buffer_unregister:
>>> tiadc_iio_buffered_hardware_remove(indio_dev);
>>> err_free_channels:
>>> @@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev)
>>> {
>>> struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>>> + struct tiadc_dma *dma = &adc_dev->dma;
>>> u32 step_en;
>>>
>>> + if (dma->chan) {
>>> + dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
>>> + dma->buf, dma->addr);
>>> + dma_release_channel(dma->chan);
>>> + }
>>> iio_device_unregister(indio_dev);
>>> tiadc_iio_buffered_hardware_remove(indio_dev);
>>> tiadc_channels_remove(indio_dev);
>>> diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
>>> index e45a208..b9a53e0 100644
>>> --- a/include/linux/mfd/ti_am335x_tscadc.h
>>> +++ b/include/linux/mfd/ti_am335x_tscadc.h
>>> @@ -23,6 +23,8 @@
>>> #define REG_IRQENABLE 0x02C
>>> #define REG_IRQCLR 0x030
>>> #define REG_IRQWAKEUP 0x034
>>> +#define REG_DMAENABLE_SET 0x038
>>> +#define REG_DMAENABLE_CLEAR 0x03c
>>> #define REG_CTRL 0x040
>>> #define REG_ADCFSM 0x044
>>> #define REG_CLKDIV 0x04C
>>> @@ -36,6 +38,7 @@
>>> #define REG_FIFO0THR 0xE8
>>> #define REG_FIFO1CNT 0xF0
>>> #define REG_FIFO1THR 0xF4
>>> +#define REG_DMA1REQ 0xF8
>>> #define REG_FIFO0 0x100
>>> #define REG_FIFO1 0x200
>>>
>>> @@ -126,6 +129,10 @@
>>> #define FIFOREAD_DATA_MASK (0xfff << 0)
>>> #define FIFOREAD_CHNLID_MASK (0xf << 16)
>>>
>>> +/* DMA ENABLE/CLEAR Register */
>>> +#define DMA_FIFO0 BIT(0)
>>> +#define DMA_FIFO1 BIT(1)
>>> +
>>> /* Sequencer Status */
>>> #define SEQ_STATUS BIT(5)
>>> #define CHARGE_STEP 0x11
>>>
>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>

2016-11-05 17:33:21

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 2/4] drivers: iio: ti_am335x_adc: add dma support

On 05/10/16 10:26, Peter Ujfalusi wrote:
> On 10/05/16 12:04, Mugunthan V N wrote:
>> This patch adds the required pieces to ti_am335x_adc driver for
>> DMA support
>>
>> Signed-off-by: Mugunthan V N <[email protected]>
Applied to a special branch in iio.git that I'll pull into togreg and then
push out as testing in a few minutes for the autobuilders to play with it.
(the special branch is incase Lee needs to later pull this into mfd as well).

Jonathan
>> ---
>> drivers/iio/adc/ti_am335x_adc.c | 148 ++++++++++++++++++++++++++++++++++-
>> include/linux/mfd/ti_am335x_tscadc.h | 7 ++
>> 2 files changed, 152 insertions(+), 3 deletions(-)
>
> Reviewed-by: Peter Ujfalusi <[email protected]>
>
>>
>> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
>> index c3cfacca..ad9dec3 100644
>> --- a/drivers/iio/adc/ti_am335x_adc.c
>> +++ b/drivers/iio/adc/ti_am335x_adc.c
>> @@ -30,10 +30,28 @@
>> #include <linux/iio/buffer.h>
>> #include <linux/iio/kfifo_buf.h>
>>
>> +#include <linux/dmaengine.h>
>> +#include <linux/dma-mapping.h>
>> +
>> +#define DMA_BUFFER_SIZE SZ_2K
>> +
>> +struct tiadc_dma {
>> + struct dma_slave_config conf;
>> + struct dma_chan *chan;
>> + dma_addr_t addr;
>> + dma_cookie_t cookie;
>> + u8 *buf;
>> + int current_period;
>> + int period_size;
>> + u8 fifo_thresh;
>> +};
>> +
>> struct tiadc_device {
>> struct ti_tscadc_dev *mfd_tscadc;
>> + struct tiadc_dma dma;
>> struct mutex fifo1_lock; /* to protect fifo access */
>> int channels;
>> + int total_ch_enabled;
>> u8 channel_line[8];
>> u8 channel_step[8];
>> int buffer_en_ch_steps;
>> @@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
>> return IRQ_HANDLED;
>> }
>>
>> +static void tiadc_dma_rx_complete(void *param)
>> +{
>> + struct iio_dev *indio_dev = param;
>> + struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> + u8 *data;
>> + int i;
>> +
>> + data = dma->buf + dma->current_period * dma->period_size;
>> + dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
>> +
>> + for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
>> + iio_push_to_buffers(indio_dev, data);
>> + data += indio_dev->scan_bytes;
>> + }
>> +}
>> +
>> +static int tiadc_start_dma(struct iio_dev *indio_dev)
>> +{
>> + struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> + struct dma_async_tx_descriptor *desc;
>> +
>> + dma->current_period = 0; /* We start to fill period 0 */
>> + /*
>> + * Make the fifo thresh as the multiple of total number of
>> + * channels enabled, so make sure that cyclic DMA period
>> + * length is also a multiple of total number of channels
>> + * enabled. This ensures that no invalid data is reported
>> + * to the stack via iio_push_to_buffers().
>> + */
>> + dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
>> + adc_dev->total_ch_enabled) - 1;
>> + /* Make sure that period length is multiple of fifo thresh level */
>> + dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
>> + (dma->fifo_thresh + 1) * sizeof(u16));
>> +
>> + dma->conf.src_maxburst = dma->fifo_thresh + 1;
>> + dmaengine_slave_config(dma->chan, &dma->conf);
>> +
>> + desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
>> + dma->period_size * 2,
>> + dma->period_size, DMA_DEV_TO_MEM,
>> + DMA_PREP_INTERRUPT);
>> + if (!desc)
>> + return -EBUSY;
>> +
>> + desc->callback = tiadc_dma_rx_complete;
>> + desc->callback_param = indio_dev;
>> +
>> + dma->cookie = dmaengine_submit(desc);
>> +
>> + dma_async_issue_pending(dma->chan);
>> +
>> + tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
>> + tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
>> + tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
>> +
>> + return 0;
>> +}
>> +
>> static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
>> {
>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> @@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
>> static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
>> {
>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> + unsigned int irq_enable;
>> unsigned int enb = 0;
>> u8 bit;
>>
>> tiadc_step_config(indio_dev);
>> - for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
>> + for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
>> enb |= (get_adc_step_bit(adc_dev, bit) << 1);
>> + adc_dev->total_ch_enabled++;
>> + }
>> adc_dev->buffer_en_ch_steps = enb;
>>
>> + if (dma->chan)
>> + tiadc_start_dma(indio_dev);
>> +
>> am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
>>
>> tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
>> | IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
>> - tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
>> - | IRQENB_FIFO1OVRRUN);
>> +
>> + irq_enable = IRQENB_FIFO1OVRRUN;
>> + if (!dma->chan)
>> + irq_enable |= IRQENB_FIFO1THRES;
>> + tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);
>>
>> return 0;
>> }
>> @@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
>> static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
>> {
>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> int fifo1count, i, read;
>>
>> tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
>> IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
>> am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
>> adc_dev->buffer_en_ch_steps = 0;
>> + adc_dev->total_ch_enabled = 0;
>> + if (dma->chan) {
>> + tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
>> + dmaengine_terminate_async(dma->chan);
>> + }
>>
>> /* Flush FIFO of leftover data in the time it takes to disable adc */
>> fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
>> @@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = {
>> .driver_module = THIS_MODULE,
>> };
>>
>> +static int tiadc_request_dma(struct platform_device *pdev,
>> + struct tiadc_device *adc_dev)
>> +{
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> + dma_cap_mask_t mask;
>> +
>> + /* Default slave configuration parameters */
>> + dma->conf.direction = DMA_DEV_TO_MEM;
>> + dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
>> + dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
>> +
>> + dma_cap_zero(mask);
>> + dma_cap_set(DMA_CYCLIC, mask);
>> +
>> + /* Get a channel for RX */
>> + dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
>> + if (IS_ERR(dma->chan)) {
>> + int ret = PTR_ERR(dma->chan);
>> +
>> + dma->chan = NULL;
>> + return ret;
>> + }
>> +
>> + /* RX buffer */
>> + dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
>> + &dma->addr, GFP_KERNEL);
>> + if (!dma->buf)
>> + goto err;
>> +
>> + return 0;
>> +err:
>> + dma_release_channel(dma->chan);
>> + return -ENOMEM;
>> +}
>> +
>> static int tiadc_parse_dt(struct platform_device *pdev,
>> struct tiadc_device *adc_dev)
>> {
>> @@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev)
>>
>> platform_set_drvdata(pdev, indio_dev);
>>
>> + err = tiadc_request_dma(pdev, adc_dev);
>> + if (err && err == -EPROBE_DEFER)
>> + goto err_dma;
>> +
>> return 0;
>>
>> +err_dma:
>> + iio_device_unregister(indio_dev);
>> err_buffer_unregister:
>> tiadc_iio_buffered_hardware_remove(indio_dev);
>> err_free_channels:
>> @@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev)
>> {
>> struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> struct tiadc_device *adc_dev = iio_priv(indio_dev);
>> + struct tiadc_dma *dma = &adc_dev->dma;
>> u32 step_en;
>>
>> + if (dma->chan) {
>> + dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
>> + dma->buf, dma->addr);
>> + dma_release_channel(dma->chan);
>> + }
>> iio_device_unregister(indio_dev);
>> tiadc_iio_buffered_hardware_remove(indio_dev);
>> tiadc_channels_remove(indio_dev);
>> diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
>> index e45a208..b9a53e0 100644
>> --- a/include/linux/mfd/ti_am335x_tscadc.h
>> +++ b/include/linux/mfd/ti_am335x_tscadc.h
>> @@ -23,6 +23,8 @@
>> #define REG_IRQENABLE 0x02C
>> #define REG_IRQCLR 0x030
>> #define REG_IRQWAKEUP 0x034
>> +#define REG_DMAENABLE_SET 0x038
>> +#define REG_DMAENABLE_CLEAR 0x03c
>> #define REG_CTRL 0x040
>> #define REG_ADCFSM 0x044
>> #define REG_CLKDIV 0x04C
>> @@ -36,6 +38,7 @@
>> #define REG_FIFO0THR 0xE8
>> #define REG_FIFO1CNT 0xF0
>> #define REG_FIFO1THR 0xF4
>> +#define REG_DMA1REQ 0xF8
>> #define REG_FIFO0 0x100
>> #define REG_FIFO1 0x200
>>
>> @@ -126,6 +129,10 @@
>> #define FIFOREAD_DATA_MASK (0xfff << 0)
>> #define FIFOREAD_CHNLID_MASK (0xf << 16)
>>
>> +/* DMA ENABLE/CLEAR Register */
>> +#define DMA_FIFO0 BIT(0)
>> +#define DMA_FIFO1 BIT(1)
>> +
>> /* Sequencer Status */
>> #define SEQ_STATUS BIT(5)
>> #define CHARGE_STEP 0x11
>>
>
>

2016-11-05 17:35:06

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 3/4] ARM: dts: am33xx: add DMA properties for tscadc

On 05/10/16 10:04, Mugunthan V N wrote:
> Add DMA properties for tscadc
>
> Signed-off-by: Mugunthan V N <[email protected]>
The support in the driver is now working it's way through iio.git towards
linux-next. I'm guessing this and the next patch will ultimately go through
arm-soc.

Shout if you'd rather I took them through the iio tree.

Thanks,

Jonathan
> ---
> arch/arm/boot/dts/am33xx.dtsi | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
> index 98748c6..6d607b8 100644
> --- a/arch/arm/boot/dts/am33xx.dtsi
> +++ b/arch/arm/boot/dts/am33xx.dtsi
> @@ -917,6 +917,8 @@
> interrupts = <16>;
> ti,hwmods = "adc_tsc";
> status = "disabled";
> + dmas = <&edma 53 0>, <&edma 57 0>;
> + dma-names = "fifo0", "fifo1";
>
> tsc {
> compatible = "ti,am3359-tsc";
>

2016-11-09 22:23:57

by Tony Lindgren

[permalink] [raw]
Subject: Re: [PATCH v3 3/4] ARM: dts: am33xx: add DMA properties for tscadc

* Jonathan Cameron <[email protected]> [161105 10:35]:
> On 05/10/16 10:04, Mugunthan V N wrote:
> > Add DMA properties for tscadc
> >
> > Signed-off-by: Mugunthan V N <[email protected]>
> The support in the driver is now working it's way through iio.git towards
> linux-next. I'm guessing this and the next patch will ultimately go through
> arm-soc.
>
> Shout if you'd rather I took them through the iio tree.

Applying the dts related patches 3 and 4 into omap-for-v4.10/dt thanks.

Tony