Re: [alsa-devel] [PATCH 4/7] IIO: add STM32 DFSDM ADC support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 23 Jan 2017, Arnaud Pouliquen wrote:

> Add driver to handle Sigma Delta ADC conversion for ADC
> connected to DFSDM IP.

mostly trivial comments below
 
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen@xxxxxx>
> ---
>  drivers/iio/adc/Kconfig           |   9 +
>  drivers/iio/adc/Makefile          |   1 +
>  drivers/iio/adc/stm32-dfsdm-adc.c | 676 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 686 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index e0b3c09..4b2b886 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -441,6 +441,15 @@ config STM32_ADC
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called stm32-adc.
>  
> +config STM32_DFSDM_ADC
> +	tristate "STMicroelectronics STM32 DFSDM ADC driver"
> +	depends on (ARCH_STM32 && OF && MFD_STM32_DFSDM) || COMPILE_TEST
> +	help
> +	  Say yes here to build the driver for the STMicroelectronics
> +	  STM32 analog-to-digital converter with Digital filter.
> +	  This driver can also be built as a module. If so, the module
> +	  will be called stm32_dfsdm_adc.
> +
>  config STX104
>  	tristate "Apex Embedded Systems STX104 driver"
>  	depends on X86 && ISA_BUS_API
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 8e02a94..aed42c6 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_STX104) += stx104.o
>  obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>  obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> new file mode 100644
> index 0000000..727d6b1
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -0,0 +1,676 @@
> +/*
> + * This file is part of STM32 DFSDM ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Arnaud Pouliquen <arnaud.pouliquen@xxxxxx>.
> + *
> + * License type: GPLv2
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE.
> + * See the GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/irq_work.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/iio.h>
> +
> +#include <linux/mfd/stm32-dfsdm.h>
> +
> +#define DFSDM_ADC_MAX_RESOLUTION 24
> +#define DFSDM_ADC_STORAGE_BITS   32
> +
> +#define DFSDM_MAX_CH_OFFSET BIT(24)
> +#define DFSDM_MAX_CH_SHIFT 24
> +
> +#define DFSDM_TIMEOUT_US 100000

this only use of the constant is in the line below where it is divided by 
1000

> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
> +
> +#define CH_ID_FROM_IDX(i) (adc->inputs[i].id)

in IIO we like consistent prefixes for macros and all declarations

e.g. STM32_DFSDM_ or just DFSDM_


> +#define CH_CFG_FROM_IDX(i) (&adc->inputs_cfg[i])
> +
> +struct stm32_dfsdm_adc {
> +	struct device *dev;
> +	struct stm32_dfsdm *dfsdm;
> +	struct list_head adc_list;
> +
> +	/* Filter */
> +	unsigned int fl_id;
> +	struct stm32_dfsdm_sinc_filter sinc;
> +	unsigned int int_oversampling;
> +
> +	/* Channels */
> +	struct stm32_dfsdm_channel *inputs;
> +	struct stm32_dfsdm_ch_cfg *inputs_cfg;
> +
> +	/* Raw mode*/
> +	struct completion completion;
> +	struct stm32_dfsdm_regular reg_params;
> +	u32 *buffer;
> +};
> +
> +static const char * const stm32_dfsdm_adc_sinc_order[] = {
> +	[0] = "FastSinc",
> +	[1] = "Sinc1",
> +	[2] = "Sinc2",
> +	[3] = "Sinc3",
> +	[4] = "Sinc4",
> +	[5] = "Sinc5",
> +};
> +
> +static inline const struct iio_chan_spec *get_ch_from_id(

no inline, let the compiler decide, here and elsewhere
consistent prefix please

> +					struct iio_dev *indio_dev, int ch_id)
> +{
> +	int i;
> +
> +	for (i = 0; i < indio_dev->num_channels; i++) {
> +		if (ch_id == indio_dev->channels[i].channel)

channel is a channel number, not an id; the parameter chould be more 
appropriately named

> +			return &indio_dev->channels[i];
> +	}
> +
> +	return NULL;
> +}
> +
> +/*
> + * Filter attributes

single line comment style? here and elsewhere, matter of taste

> + */
> +
> +static int stm32_dfsdm_adc_set_sinc(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan,
> +				    unsigned int val)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
> +		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
> +
> +	adc->sinc.order = val;
> +
> +	return 0;
> +}
> +
> +static int stm32_dfsdm_adc_get_sinc(struct iio_dev *indio_dev,
> +				    const struct iio_chan_spec *chan)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	dev_dbg(&indio_dev->dev, "%s: %s\n", __func__,
> +		stm32_dfsdm_adc_sinc_order[adc->sinc.order]);
> +
> +	return adc->sinc.order;
> +}
> +
> +static const struct iio_enum stm32_dfsdm_adc_fl_sinc_order = {
> +	.items = stm32_dfsdm_adc_sinc_order,
> +	.num_items = ARRAY_SIZE(stm32_dfsdm_adc_sinc_order),
> +	.get = stm32_dfsdm_adc_get_sinc,
> +	.set = stm32_dfsdm_adc_set_sinc,
> +};
> +
> +static ssize_t stm32_dfsdm_adc_get_int_os(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adc->int_oversampling);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_int_os(struct iio_dev *indio_dev,
> +					  uintptr_t priv,
> +					  const struct iio_chan_spec *chan,
> +					  const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);

we want an uint here for val?

> +	if (ret)
> +		return ret;
> +
> +	if ((!val) || (val > DFSDM_MAX_INT_OVERSAMPLING)) {

what if val is negative?

> +		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
> +			DFSDM_MAX_INT_OVERSAMPLING);

\n missing

> +		return -EINVAL;
> +	}
> +	adc->int_oversampling = val;
> +
> +	return len;
> +}
> +
> +static ssize_t stm32_dfsdm_adc_get_fl_os(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adc->sinc.oversampling);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_fl_os(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					const struct iio_chan_spec *chan,
> +					const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if ((!val) || (val > DFSDM_MAX_FL_OVERSAMPLING)) {

parenthesis not strictly needed, matter of taste

> +		dev_err(&indio_dev->dev, "invalid oversampling (0 or > %#x)",
> +			DFSDM_MAX_FL_OVERSAMPLING);
> +		return -EINVAL;
> +	}
> +	adc->sinc.oversampling = val;
> +
> +	return len;
> +}
> +
> +/*
> + * Data bit shifting attribute
> + */
> +static ssize_t stm32_dfsdm_adc_get_shift(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 char *buf)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", ch_cfg->right_bit_shift);
> +}
> +
> +static ssize_t stm32_dfsdm_adc_set_shift(struct iio_dev *indio_dev,
> +					 uintptr_t priv,
> +					 const struct iio_chan_spec *chan,
> +					 const char *buf, size_t len)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	int ret, val;
> +
> +	ret = kstrtoint(buf, 0, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val > DFSDM_MAX_CH_SHIFT) {
> +		dev_err(&indio_dev->dev, "invalid shift value (> %#x)",
> +			DFSDM_MAX_CH_SHIFT);
> +		return -EINVAL;
> +	}
> +	ch_cfg->right_bit_shift = val;
> +
> +	return len;
> +}
> +
> +static const struct iio_chan_spec_ext_info stm32_dfsdm_adc_ext_info[] = {
> +	/* sinc_filter_order: Configure Sinc filter order */
> +	IIO_ENUM("sinc_filter_order", IIO_SHARED_BY_TYPE,
> +		 &stm32_dfsdm_adc_fl_sinc_order),
> +	IIO_ENUM_AVAILABLE("sinc_filter_order", &stm32_dfsdm_adc_fl_sinc_order),
> +	/* filter oversampling: Post filter oversampling ratio */
> +	{
> +		.name = "sinc_filter_oversampling_ratio",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = stm32_dfsdm_adc_get_fl_os,
> +		.write = stm32_dfsdm_adc_set_fl_os,
> +	},
> +	/* data_right_bit_shift : Filter output data shifting */

no space before :, here and elsewhere

> +	{
> +		.name = "data_right_bit_shift",
> +		.shared = IIO_SEPARATE,
> +		.read = stm32_dfsdm_adc_get_shift,
> +		.write = stm32_dfsdm_adc_set_shift,
> +	},
> +
> +	/*
> +	 * averaging_length : Mean windows of data from filter.
> +	 * Defines how many filter data will be summed to one data output
> +	 */
> +	{
> +		.name = "integrator_oversampling",
> +		.shared = IIO_SHARED_BY_TYPE,
> +		.read = stm32_dfsdm_adc_get_int_os,
> +		.write = stm32_dfsdm_adc_set_int_os,
> +	},
> +	{},
> +};
> +
> +/*
> + * Filter event routine called under IRQ context
> + */
> +static void stm32_dfsdm_event_cb(struct stm32_dfsdm *dfsdm, int flt_id,
> +				 enum stm32_dfsdm_events ev, unsigned int param,
> +				 void *context)
> +{
> +	struct stm32_dfsdm_adc *adc = context;
> +	unsigned int ch_id;
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	switch (ev) {
> +	case DFSDM_EVENT_REG_EOC:
> +		stm32_dfsdm_read_fl_conv(adc->dfsdm, flt_id, adc->buffer,
> +					 &ch_id, DFSDM_FILTER_REG_CONV);
> +		complete(&adc->completion);
> +		break;
> +	case DFSDM_EVENT_REG_XRUN:
> +		dev_err(adc->dev, "%s: underrun detected for filter %d\n",
> +			__func__, flt_id);
> +		break;
> +	default:
> +		dev_err(adc->dev, "%s: event %#x not implemented\n",
> +			__func__, ev);
> +		break;
> +	}
> +}
> +
> +static inline void stm32_dfsdm_adc_fl_config(struct stm32_dfsdm_adc *adc,
> +					     u32 channel_mask,
> +					     struct stm32_dfsdm_filter *filter)
> +{
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	filter->event.cb = stm32_dfsdm_event_cb;
> +	filter->event.context = adc;
> +
> +	filter->sinc_params = adc->sinc;
> +
> +	filter->int_oversampling = adc->int_oversampling;
> +}
> +
> +static int stm32_dfsdm_adc_start_raw_conv(struct stm32_dfsdm_adc *adc,
> +					  const struct iio_chan_spec *chan)
> +{
> +	struct stm32_dfsdm_filter filter;
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
> +	int ret;
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	memset(&filter, 0, sizeof(filter));
> +	filter.reg_params = &adc->reg_params;
> +
> +	if (!filter.reg_params)
> +		return -ENOMEM;
> +
> +	filter.reg_params->ch_src = ch_id;
> +
> +	stm32_dfsdm_adc_fl_config(adc, BIT(ch_id), &filter);
> +
> +	ret = stm32_dfsdm_configure_filter(adc->dfsdm, adc->fl_id, &filter);
> +	if (ret < 0) {
> +		dev_err(adc->dev, "Failed to configure filter\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_start_channel(adc->dfsdm, ch_id, ch_cfg);
> +	if (ret < 0)
> +		return ret;
> +
> +	stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id, DFSDM_FILTER_REG_CONV);
> +
> +	return 0;
> +}
> +
> +static void stm32_dfsdm_adc_stop_raw_conv(struct stm32_dfsdm_adc *adc,
> +					  const struct iio_chan_spec *chan)
> +{
> +	unsigned int ch_id = CH_ID_FROM_IDX(chan->scan_index);
> +
> +	dev_dbg(adc->dev, "%s:\n", __func__);
> +
> +	stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
> +	stm32_dfsdm_stop_channel(adc->dfsdm, ch_id);
> +}
> +
> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> +				   const struct iio_chan_spec *chan,
> +				   u32 *result)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	long timeout;
> +	int ret;
> +
> +	dev_dbg(&indio_dev->dev, "%s:\n", __func__);
> +
> +	reinit_completion(&adc->completion);
> +
> +	ret = stm32_dfsdm_register_fl_event(adc->dfsdm, adc->fl_id,
> +					    DFSDM_EVENT_REG_EOC, 0);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev, "Failed to register event\n");
> +		return ret;
> +	}
> +
> +	adc->buffer = result;
> +	ret = stm32_dfsdm_adc_start_raw_conv(adc, chan);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Failed to start conversion\n");
> +		goto free_event;
> +	}
> +
> +	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
> +							    DFSDM_TIMEOUT);
> +	if (timeout == 0) {
> +		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
> +		ret = -ETIMEDOUT;
> +	} else if (timeout < 0) {
> +		ret = timeout;
> +	} else {
> +		dev_dbg(&indio_dev->dev, "converted val %#x\n", *result);
> +		ret = IIO_VAL_INT;
> +	}
> +
> +	stm32_dfsdm_adc_stop_raw_conv(adc, chan);
> +
> +free_event:
> +	adc->buffer = NULL;
> +	stm32_dfsdm_unregister_fl_event(adc->dfsdm, adc->fl_id,
> +					DFSDM_EVENT_REG_EOC, 0);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
> +				struct iio_chan_spec const *chan, int *val,
> +				int *val2, long mask)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +	int ret = -EINVAL;
> +
> +	dev_dbg(&indio_dev->dev, "%s channel %d\n", __func__, chan->channel);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
> +		if (!ret)
> +			ret = IIO_VAL_INT;

_single_conv() already returns IIO_VAL_INT on success?

> +		break;
> +	case IIO_CHAN_INFO_OFFSET:
> +		*val = ch_cfg->offset;
> +		ret = IIO_VAL_INT;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
> +				 struct iio_chan_spec const *chan, int val,
> +				 int val2, long mask)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_ch_cfg *ch_cfg = CH_CFG_FROM_IDX(chan->scan_index);
> +
> +	dev_dbg(&indio_dev->dev, "%s channel%d", __func__, chan->channel);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_OFFSET:
> +		if (val > DFSDM_MAX_CH_OFFSET) {
> +			dev_err(&indio_dev->dev, "invalid offset (> %#lx)",
> +				DFSDM_MAX_CH_OFFSET);
> +			return -EINVAL;
> +		}
> +		ch_cfg->offset = val;
> +		break;

maybe return 0; directly here

> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct iio_info stm32_dfsdm_iio_info = {
> +	.read_raw = stm32_dfsdm_read_raw,
> +	.write_raw = stm32_dfsdm_write_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> +					 struct iio_chan_spec *chan,
> +					 int chan_idx)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	struct stm32_dfsdm_channel *dfsdm_ch = &adc->inputs[chan_idx];
> +	struct iio_chan_spec *ch = &chan[chan_idx];
> +	int ret;
> +	unsigned int alt_ch = 0;

initialization needed?

> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channels", chan_idx,
> +					 &ch->channel);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channels' for idx %d\n",

remove extra space at beginning

> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ret = of_property_read_string_index(indio_dev->dev.of_node,
> +					    "st,adc-channel-names", chan_idx,
> +					    &ch->datasheet_name);
> +	if (ret < 0) {
> +		dev_err(&indio_dev->dev,
> +			" error parsing 'st,adc-channel-names' for idx %d\n",
> +			chan_idx);
> +		return ret;
> +	}
> +
> +	ch->extend_name = ch->datasheet_name;
> +	ch->type = IIO_VOLTAGE;
> +	ch->indexed = 1;
> +	ch->scan_index = chan_idx;
> +	ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				 BIT(IIO_CHAN_INFO_OFFSET);
> +	ch->scan_type.sign = 'u';
> +	ch->scan_type.realbits = DFSDM_ADC_MAX_RESOLUTION;
> +	ch->scan_type.storagebits = DFSDM_ADC_STORAGE_BITS;
> +	ch->scan_type.shift = 8;

could compute _STORAGE_BITS - _MAX_RESOLUTION?

> +
> +	ch->ext_info = stm32_dfsdm_adc_ext_info;
> +
> +	of_property_read_u32_index(indio_dev->dev.of_node, "st,adc-alt-channel",
> +				   chan_idx, &alt_ch);

check retval?

> +	/* Select the previous channel if alternate field is defined*/

add space before */

> +	if (alt_ch) {
> +		if (!ch->channel)
> +			ch->channel = adc->dfsdm->max_channels;
> +		ch->channel -= 1;
> +		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_NEXT_CHANNEL_PINS;
> +	} else {
> +		dfsdm_ch->serial_if.pins = DFSDM_CHANNEL_SAME_CHANNEL_PINS;
> +	}
> +	dfsdm_ch->id = ch->channel;
> +
> +	dfsdm_ch->type.DataPacking = DFSDM_CHANNEL_STANDARD_MODE;
> +
> +	dfsdm_ch->type.source = DFSDM_CHANNEL_EXTERNAL_INPUTS;
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channel-types",
> +					 chan_idx, &dfsdm_ch->serial_if.type);
> +	if (ret < 0)
> +		dfsdm_ch->serial_if.type = DFSDM_CHANNEL_SPI_RISING;
> +
> +	ret = of_property_read_u32_index(indio_dev->dev.of_node,
> +					 "st,adc-channel-clk-src",
> +					 chan_idx,
> +					 &dfsdm_ch->serial_if.spi_clk);
> +
> +	if ((dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_RISING)  ||
> +	    (dfsdm_ch->serial_if.type == DFSDM_CHANNEL_MANCHESTER_FALLING) ||

inconsistent indentation

> +	    (ret < 0))
> +		dfsdm_ch->serial_if.spi_clk = DFSDM_CHANNEL_SPI_CLOCK_INTERNAL;
> +
> +	return stm32_dfsdm_get_channel(adc->dfsdm, dfsdm_ch);
> +}
> +
> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
> +{
> +	struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +	unsigned int num_ch;
> +	struct iio_chan_spec *channels;
> +	int ret, chan_idx;
> +
> +	num_ch = of_property_count_strings(indio_dev->dev.of_node,
> +					   "st,adc-channel-names");
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
> +				GFP_KERNEL);
> +	if (!channels)
> +		return -ENOMEM;
> +
> +	adc->inputs = devm_kcalloc(&indio_dev->dev, num_ch,
> +				   sizeof(*adc->inputs), GFP_KERNEL);
> +	if (!adc->inputs)
> +		return -ENOMEM;
> +
> +	adc->inputs_cfg = devm_kcalloc(&indio_dev->dev, num_ch,
> +				       sizeof(*adc->inputs_cfg), GFP_KERNEL);
> +	if (!adc->inputs_cfg)
> +		return -ENOMEM;
> +
> +	for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
> +		ret = stm32_dfsdm_adc_chan_init_one(indio_dev, channels,
> +						    chan_idx);
> +		if (ret < 0)
> +			goto ch_error;
> +	}
> +
> +	indio_dev->num_channels = num_ch;
> +	indio_dev->channels = channels;
> +
> +	return 0;
> +
> +ch_error:
> +	for (chan_idx--; chan_idx >= 0; chan_idx--)
> +		stm32_dfsdm_release_channel(adc->dfsdm,
> +					    adc->inputs[chan_idx].id);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct stm32_dfsdm_adc *adc;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct iio_dev *indio_dev;
> +	int ret, i;
> +
> +	if (!np)
> +		return -ENODEV;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
> +	if (IS_ERR(indio_dev)) {
> +		dev_err(dev, "%s: failed to allocate iio", __func__);

\n

> +		return PTR_ERR(indio_dev);
> +	}
> +
> +	indio_dev->name = np->name;
> +	indio_dev->dev.parent = dev;
> +	indio_dev->dev.of_node = np;
> +	indio_dev->info = &stm32_dfsdm_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	adc = iio_priv(indio_dev);
> +	if (IS_ERR(adc)) {
> +		dev_err(dev, "%s: failed to allocate adc", __func__);
> +		return PTR_ERR(adc);
> +	}
> +
> +	if (of_property_read_u32(np, "reg", &adc->fl_id)) {
> +		dev_err(&pdev->dev, "missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	adc->dev = &indio_dev->dev;
> +	adc->dfsdm = dev_get_drvdata(pdev->dev.parent);
> +
> +	ret = stm32_dfsdm_adc_chan_init(indio_dev);
> +	if (ret < 0) {
> +		dev_err(dev, "iio channels init failed\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_dfsdm_get_filter(adc->dfsdm, adc->fl_id);
> +	if (ret < 0)
> +		goto get_fl_err;
> +
> +	adc->int_oversampling = DFSDM_MIN_INT_OVERSAMPLING;
> +	adc->sinc.oversampling = DFSDM_MIN_FL_OVERSAMPLING;
> +
> +	init_completion(&adc->completion);
> +
> +	ret = devm_iio_device_register(dev, indio_dev);
> +	if (ret) {
> +		dev_err(adc->dev, "failed to register iio device\n");
> +		goto register_err;
> +	}
> +
> +	platform_set_drvdata(pdev, adc);
> +
> +	return 0;
> +
> +register_err:
> +	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
> +
> +get_fl_err:
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
> +
> +	return ret;
> +}
> +
> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev;
> +	struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
> +	int i;
> +
> +	indio_dev = iio_priv_to_dev(adc);
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		stm32_dfsdm_release_channel(adc->dfsdm, adc->inputs[i].id);
> +	stm32_dfsdm_release_filter(adc->dfsdm, adc->fl_id);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
> +	{ .compatible = "st,stm32-dfsdm-adc"},
> +	{}
> +};
> +
> +static struct platform_driver stm32_dfsdm_adc_driver = {
> +	.driver = {
> +		.name = "stm32-dfsdm-adc",
> +		.of_match_table = stm32_dfsdm_adc_match,
> +	},
> +	.probe = stm32_dfsdm_adc_probe,
> +	.remove = stm32_dfsdm_adc_remove,
> +};
> +module_platform_driver(stm32_dfsdm_adc_driver);
> +
> +MODULE_DESCRIPTION("STM32 sigma delta ADC");
> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@xxxxxx>");
> +MODULE_LICENSE("GPL v2");
> 

-- 

Peter Meerwald-Stadler
+43-664-2444418 (mobile)
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux