RE: [PATCH v2 2/2] one-bit-adc-dac: Add initial version of one bit ADC-DAC

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

 



Hi Jonathan,

The additional functionality it is a little bit unclear to me.

> -----Original Message-----
> From: Jonathan Cameron <jic23@xxxxxxxxxx>
> Sent: Sunday, January 16, 2022 1:52 PM
> To: Pop, Cristian <Cristian.Pop@xxxxxxxxxx>
> Cc: linux-iio@xxxxxxxxxxxxxxx; linux-gpio@xxxxxxxxxxxxxxx; linux-
> kernel@xxxxxxxxxxxxxxx; devicetree@xxxxxxxxxxxxxxx; robh+dt@xxxxxxxxxx
> Subject: Re: [PATCH v2 2/2] one-bit-adc-dac: Add initial version of one bit
> ADC-DAC
> 
> [External]
> 
> On Tue, 11 Jan 2022 13:59:19 +0200
> Cristian Pop <cristian.pop@xxxxxxxxxx> wrote:
> 
> > This allows remote reading and writing of the GPIOs. This is useful in
> > application that run on another PC, at system level, where multiple
> > iio devices and GPIO devices are integrated together.
> Hi Cristian,
> 
> So I fully understand why this can be useful, but it is a software only
> construction so I'm not convinced that it makes sense to necessarily
> configure it via device tree.   A better fit may well be configfs.
> (note I always meant to add configfs controls for iio_hwmon but haven't
> found the time to do it yet...)
> 
> As it currently stands, doing only polled / sysfs reads this driver doesn't do
> enough to justify its existence. You could just do all this in userspace using
> the existing gpio interfaces.  So to be useful I'd expect it to at least do
> triggered buffer capture.

What do you mean by triggered buffer capture? Should I save previous GPIO
states into a buffer? What should I do with them?

A useful use case that I see:
- Is to register a function callback (in kernel space, maybe user space that
  should be used by a different driver), and called in the interrupt handler
  when the state of an input GPIO changes.
- Output to a GPIO from a buffer using a clock, obtaining a PWM like signal.

> 
> When we have talked about handling digital signals int he past we discussed
> whether the IIO channel description would also benefit from tighter packing
> than the current minimum of a byte per channel.  Perhaps we don't
> technically 'need' it here but if we do add it in future it will be hard to retrofit
> into this driver without big userspace ABI changes (i.e. breaking all existing
> users).
> 
> I've not repeated stuff Andy got it in his review (assuming I remembered it
> was something Andy raised).
> 
> Conclusion:
> 1) Creation interface needs a rethink or strong argument why it belongs in
> DT.
> 2) Driver needs to do more to justify it's existence.
> 
> Jonathan
> 
> >
> > Signed-off-by: Cristian Pop <cristian.pop@xxxxxxxxxx>
> > ---
> >  drivers/iio/addac/Kconfig           |   8 +
> >  drivers/iio/addac/Makefile          |   1 +
> >  drivers/iio/addac/one-bit-adc-dac.c | 229
> > ++++++++++++++++++++++++++++
> >  3 files changed, 238 insertions(+)
> >  create mode 100644 drivers/iio/addac/one-bit-adc-dac.c
> >
> > diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig
> > index 138492362f20..5f311f4a747e 100644
> > --- a/drivers/iio/addac/Kconfig
> > +++ b/drivers/iio/addac/Kconfig
> > @@ -17,4 +17,12 @@ config AD74413R
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called ad74413r.
> >
> > +config ONE_BIT_ADC_DAC
> > +	tristate "ONE_BIT_ADC_DAC driver"
> > +	help
> > +	  Say yes here to build support for ONE_BIT_ADC_DAC driver.
> 
> Needs a lot more detail here.  Though naming it to be explicitly about GPIO to
> IIO binding would help.
> 
> > +
> > +	  To compile this driver as a module, choose M here: the
> > +	  module will be called one-bit-adc-dac.
> > +
> >  endmenu
> > diff --git a/drivers/iio/addac/Makefile b/drivers/iio/addac/Makefile
> > index cfd4bbe64ad3..0a33f0706b55 100644
> > --- a/drivers/iio/addac/Makefile
> > +++ b/drivers/iio/addac/Makefile
> > @@ -5,3 +5,4 @@
> >
> >  # When adding new entries keep the list in alphabetical order
> >  obj-$(CONFIG_AD74413R) += ad74413r.o
> > +obj-$(CONFIG_ONE_BIT_ADC_DAC) += one-bit-adc-dac.o
> > diff --git a/drivers/iio/addac/one-bit-adc-dac.c
> > b/drivers/iio/addac/one-bit-adc-dac.c
> > new file mode 100644
> > index 000000000000..5680de594429
> > --- /dev/null
> > +++ b/drivers/iio/addac/one-bit-adc-dac.c
> > @@ -0,0 +1,229 @@
> > +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
> > +/*
> > + * one-bit-adc-dac
> 
> Improve description here as well.  It's really just a wrapper for a gpio in an IIO
> channel, so just say that.  Fine to say the representation is 1 bit ADC or DAC
> channels as well, but I think the GPIO part will be more obvious to the casual
> reader.
> 
> > + *
> > + * Copyright 2022 Analog Devices Inc.
> > + */
> > +
> > +#include <linux/device.h>
> > +#include <linux/module.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/gpio/consumer.h>
> > +
> > +enum ch_direction {
> > +	CH_IN,
> > +	CH_OUT,
> > +};
> > +
> > +struct one_bit_adc_dac_state {
> > +	int			in_num_ch;
> > +	int			out_num_ch;
> > +	struct platform_device  *pdev;
> 
> Not used after probe so don't keep it around.
> 
> > +	struct gpio_descs       *in_gpio_descs;
> > +	struct gpio_descs       *out_gpio_descs;
> > +	const char		**labels;
> > +};
> > +
> > +static int one_bit_adc_dac_read_raw(struct iio_dev *indio_dev,
> > +	const struct iio_chan_spec *chan, int *val, int *val2, long info) {
> > +	struct one_bit_adc_dac_state *st = iio_priv(indio_dev);
> > +	struct gpio_descs *descs;
> > +
> > +	switch (info) {
> > +	case IIO_CHAN_INFO_RAW:
> > +		if (chan->output)
> > +			descs = st->out_gpio_descs;
> > +		else
> > +			descs = st->in_gpio_descs;
> > +		*val = gpiod_get_value_cansleep(descs->desc[chan-
> >channel]);
> > +
> > +		return IIO_VAL_INT;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int one_bit_adc_dac_write_raw(struct iio_dev *indio_dev,
> > +			    struct iio_chan_spec const *chan,
> > +			    int val,
> > +			    int val2,
> > +			    long info)
> > +{
> > +	struct one_bit_adc_dac_state *st = iio_priv(indio_dev);
> > +	int channel = chan->channel;
> > +
> > +	if (!chan->output)
> > +		return 0;
> 
> -EINVAL;  It's an error that should be reported to userspace..
> 
> > +
> > +	switch (info) {
> > +	case IIO_CHAN_INFO_RAW:
> > +		gpiod_set_value_cansleep(st->out_gpio_descs-
> >desc[channel], val);
> > +
> > +		return 0;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int one_bit_adc_dac_read_label(struct iio_dev *indio_dev,
> > +	const struct iio_chan_spec *chan, char *label) {
> > +	struct one_bit_adc_dac_state *st = iio_priv(indio_dev);
> > +	int ch;
> > +
> > +	if (chan->output)
> > +		ch = chan->channel + st->in_num_ch;
> > +	else
> > +		ch = chan->channel;
> > +
> > +	return sprintf(label, "%s\n", st->labels[ch]); }
> > +
> > +static const struct iio_info one_bit_adc_dac_info = {
> > +	.read_raw = &one_bit_adc_dac_read_raw,
> > +	.write_raw = &one_bit_adc_dac_write_raw,
> > +	.read_label = &one_bit_adc_dac_read_label, };
> > +
> > +static int one_bit_adc_dac_set_ch(struct iio_chan_spec *channels,
> > +					int num_ch,
> > +					enum ch_direction direction)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < num_ch; i++) {
> > +		channels[i].type = IIO_VOLTAGE;
> > +		channels[i].indexed = 1;
> > +		channels[i].channel = i;
> > +		channels[i].info_mask_separate =
> BIT(IIO_CHAN_INFO_RAW);
> > +		channels[i].output = direction;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int one_bit_adc_dac_set_channel_label(struct iio_dev *indio_dev,
> > +						struct iio_chan_spec
> *channels,
> > +						int num_channels)
> > +{
> > +	struct device *device = indio_dev->dev.parent;
> > +	struct one_bit_adc_dac_state *st = iio_priv(indio_dev);
> > +	struct fwnode_handle *fwnode;
> > +	struct fwnode_handle *child;
> > +	struct iio_chan_spec *chan;
> > +	const char *label;
> > +	int crt_ch = 0, child_num, i = 0;
> > +
> > +	fwnode = dev_fwnode(device);
> > +	child_num = device_get_child_node_count(device);
> > +
> > +	st->labels = devm_kzalloc(device, sizeof(*st->labels) * child_num,
> GFP_KERNEL);
> > +	if (!st->labels)
> > +		return -ENOMEM;
> > +
> > +	i = child_num;
> > +	fwnode_for_each_child_node(fwnode, child) {
> > +		if (fwnode_property_read_u32(child, "reg", &crt_ch))
> > +			continue;
> > +
> > +		if (crt_ch >= num_channels)
> > +			continue;
> > +
> > +		if (fwnode_property_read_string(child, "label", &label))
> > +			continue;
> > +
> > +		chan = &channels[crt_ch];
> ? Not used.
> 
> > +		st->labels[--i] = label;
> 
> I've no idea how this works...  Should be looking for the chan->channel value
> as that's what your read uses to index.
> 
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int one_bit_adc_dac_parse_dt(struct iio_dev *indio_dev) {
> > +	struct one_bit_adc_dac_state *st = iio_priv(indio_dev);
> > +	struct iio_chan_spec *channels;
> > +	int ret, in_num_ch = 0, out_num_ch = 0;
> > +
> > +	st->in_gpio_descs = devm_gpiod_get_array_optional(&st->pdev-
> >dev, "in", GPIOD_IN);
> > +	if (IS_ERR(st->in_gpio_descs))
> > +		return PTR_ERR(st->in_gpio_descs);
> > +
> > +	if (st->in_gpio_descs)
> > +		in_num_ch = st->in_gpio_descs->ndescs;
> > +
> > +	st->out_gpio_descs = devm_gpiod_get_array_optional(&st->pdev-
> >dev, "out", GPIOD_OUT_LOW);
> > +	if (IS_ERR(st->out_gpio_descs))
> > +		return PTR_ERR(st->out_gpio_descs);
> > +
> > +	if (st->out_gpio_descs)
> > +		out_num_ch = st->out_gpio_descs->ndescs;
> > +	st->in_num_ch = in_num_ch;
> > +	st->out_num_ch = out_num_ch;
> > +
> > +	channels = devm_kcalloc(indio_dev->dev.parent, in_num_ch +
> out_num_ch,
> > +				sizeof(*channels), GFP_KERNEL);
> > +	if (!channels)
> > +		return -ENOMEM;
> > +
> > +	ret = one_bit_adc_dac_set_ch(&channels[0], in_num_ch, CH_IN);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = one_bit_adc_dac_set_ch(&channels[in_num_ch],
> out_num_ch, CH_OUT);
> > +	if (ret)
> > +		return ret;
> > +
> > +	ret = one_bit_adc_dac_set_channel_label(indio_dev, channels,
> in_num_ch + out_num_ch);
> > +	if (ret)
> > +		return ret;
> > +
> > +	indio_dev->channels = channels;
> > +	indio_dev->num_channels = in_num_ch + out_num_ch;
> > +
> > +	return 0;
> > +}
> > +
> > +static int one_bit_adc_dac_probe(struct platform_device *pdev) {
> > +	struct iio_dev *indio_dev;
> > +	struct one_bit_adc_dac_state *st;
> > +	int ret;
> > +
> > +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
> > +	if (!indio_dev)
> > +		return -ENOMEM;
> > +
> > +	st = iio_priv(indio_dev);
> > +	st->pdev = pdev;
> > +	indio_dev->name = "one-bit-adc-dac";
> > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > +	indio_dev->info = &one_bit_adc_dac_info;
> > +
> > +	ret = one_bit_adc_dac_parse_dt(indio_dev);
> > +	if (ret)
> > +		return ret;
> > +
> > +	return devm_iio_device_register(indio_dev->dev.parent,
> indio_dev); }
> > +
> > +static const struct of_device_id one_bit_adc_dac_dt_match[] = {
> > +	{ .compatible = "one-bit-adc-dac" },
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(of, one_bit_adc_dac_dt_match);
> > +
> > +static struct platform_driver one_bit_adc_dac_driver = {
> > +	.driver = {
> > +		.name = "one-bit-adc-dac",
> > +		.of_match_table = one_bit_adc_dac_dt_match,
> > +	},
> > +	.probe = one_bit_adc_dac_probe,
> > +};
> > +module_platform_driver(one_bit_adc_dac_driver);
> > +
> > +MODULE_AUTHOR("Cristian Pop <cristian.pop@xxxxxxxxxx>");
> > +MODULE_DESCRIPTION("One bit ADC DAC converter");
> MODULE_LICENSE("Dual
> > +BSD/GPL");
> > \ No newline at end of file
> 
> Make sure to eyeball your patches before sending as this sort of thing should
> be caught at that stage...
> 





[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