* Re: [PATCH v3 8/8] iio: envelope-detector: ADC driver based on a DAC and a comparator
From: Peter Rosin @ 2016-10-30 14:26 UTC (permalink / raw)
To: Jonathan Cameron, linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
Rob Herring, Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Gleixner
In-Reply-To: <61cd873a-b6ff-50d5-1161-e9b1ad90af88-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On 2016-10-30 14:44, Jonathan Cameron wrote:
> On 23/10/16 23:39, Peter Rosin wrote:
>> The DAC is used to find the peak level of an alternating voltage input
>> signal by a binary search using the output of a comparator wired to
>> an interrupt pin. Like so:
>> _
>> | \
>> input +------>-------|+ \
>> | \
>> .-------. | }---.
>> | | | / |
>> | dac|-->--|- / |
>> | | |_/ |
>> | | |
>> | | |
>> | irq|------<-------'
>> | |
>> '-------'
>>
>> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> Looks good to me. I've cc'd Thomas Gleixner as the irq usage is obviously
> a bit unusual and I thought he may have some thoughts on it.
>
> Jonathan
>> ---
>> .../testing/sysfs-bus-iio-adc-envelope-detector | 36 ++
>> MAINTAINERS | 2 +
>> drivers/iio/adc/Kconfig | 10 +
>> drivers/iio/adc/Makefile | 1 +
>> drivers/iio/adc/envelope-detector.c | 422 +++++++++++++++++++++
>> 5 files changed, 471 insertions(+)
>> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
>> create mode 100644 drivers/iio/adc/envelope-detector.c
>>
>> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector b/Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
>> new file mode 100644
>> index 000000000000..2071f9bcfaa5
>> --- /dev/null
>> +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
>> @@ -0,0 +1,36 @@
>> +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_invert
>> +Date: October 2016
>> +KernelVersion: 4.9
>> +Contact: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
>> +Description:
>> + The DAC is used to find the peak level of an alternating
>> + voltage input signal by a binary search using the output
>> + of a comparator wired to an interrupt pin. Like so:
>> + _
>> + | \
>> + input +------>-------|+ \
>> + | \
>> + .-------. | }---.
>> + | | | / |
>> + | dac|-->--|- / |
>> + | | |_/ |
>> + | | |
>> + | | |
>> + | irq|------<-------'
>> + | |
>> + '-------'
>> + The boolean invert attribute (0/1) should be set when the
>> + input signal is centered around the maximum value of the
>> + dac instead of zero. The envelope detector will search
>> + from below in this case and will also invert the result.
>> + The edge/level of the interrupt is also switched to its
>> + opposite value.
>> +
>> +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_compare_interval
>> +Date: October 2016
>> +KernelVersion: 4.9
>> +Contact: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
>> +Description:
>> + Number of milliseconds to wait for the comparator in each
>> + step of the binary search for the input peak level. Needs
>> + to relate to the frequency of the input signal.
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index fca35d16037d..0cf3549e05e7 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -6123,7 +6123,9 @@ IIO ENVELOPE DETECTOR
>> M: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
>> L: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> S: Maintained
>> +F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
>> F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
>> +F: drivers/iio/adc/envelope-detector.c
>>
>> IIO SUBSYSTEM AND DRIVERS
>> M: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 7edcf3238620..d5c4a95855c2 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -195,6 +195,16 @@ config DA9150_GPADC
>> To compile this driver as a module, choose M here: the module will be
>> called berlin2-adc.
>>
>> +config ENVELOPE_DETECTOR
>> + tristate "Envelope detector using a DAC and a comparator"
>> + depends on OF
>> + help
>> + Say yes here to build support for an envelope detector using a DAC
>> + and a comparator.
>> +
>> + To compile this driver as a module, choose M here: the module will be
>> + called iio-envelope-detector.
While reading through your replies (thanks for your time!) I noticed
this; the module name should obviously be
s/iio-envelope-detector/envelope-detector/
I fixed that locally so if there is some other reason to respin, it'll
be included, but if not please squash that on the way in. Ok?
If you think I should respin instead, let me know...
Cheers,
Peter
>> +
>> config EXYNOS_ADC
>> tristate "Exynos ADC driver support"
>> depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index 7a40c04c311f..0d773c6a0578 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -20,6 +20,7 @@ obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
>> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
>> obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
>> obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
>> +obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
>> obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
>> obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
>> obj-$(CONFIG_HI8435) += hi8435.o
>> diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c
>> new file mode 100644
>> index 000000000000..fef15c0d7c9c
>> --- /dev/null
>> +++ b/drivers/iio/adc/envelope-detector.c
>> @@ -0,0 +1,422 @@
>> +/*
>> + * Driver for an envelope detector using a DAC and a comparator
>> + *
>> + * Copyright (C) 2016 Axentia Technologies AB
>> + *
>> + * Author: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
>> + *
>> + * 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.
>> + */
>> +
>> +/*
>> + * The DAC is used to find the peak level of an alternating voltage input
>> + * signal by a binary search using the output of a comparator wired to
>> + * an interrupt pin. Like so:
>> + * _
>> + * | \
>> + * input +------>-------|+ \
>> + * | \
>> + * .-------. | }---.
>> + * | | | / |
>> + * | dac|-->--|- / |
>> + * | | |_/ |
>> + * | | |
>> + * | | |
>> + * | irq|------<-------'
>> + * | |
>> + * '-------'
>> + */
>> +
>> +#include <linux/completion.h>
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/iio/consumer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irq.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/workqueue.h>
>> +
>> +struct envelope {
>> + spinlock_t comp_lock; /* protects comp */
>> + int comp;
>> +
>> + struct mutex read_lock; /* protects everything else */
>> +
>> + int comp_irq;
>> + u32 comp_irq_trigger;
>> + u32 comp_irq_trigger_inv;
>> +
>> + struct iio_channel *dac;
>> + struct delayed_work comp_timeout;
>> +
>> + unsigned int comp_interval;
>> + bool invert;
>> + u32 dac_max;
>> +
>> + int high;
>> + int level;
>> + int low;
>> +
>> + struct completion done;
>> +};
>> +
>> +/*
>> + * The envelope_detector_comp_latch function works together with the compare
>> + * interrupt service routine below (envelope_detector_comp_isr) as a latch
>> + * (one-bit memory) for if the interrupt has triggered since last calling
>> + * this function.
>> + * The ..._comp_isr function disables the interrupt so that the cpu does not
>> + * need to service a possible interrupt flood from the comparator when no-one
>> + * cares anyway, and this ..._comp_latch function reenables them again if
>> + * needed.
>> + */
>> +static int envelope_detector_comp_latch(struct envelope *env)
>> +{
>> + int comp;
>> +
>> + spin_lock_irq(&env->comp_lock);
>> + comp = env->comp;
>> + env->comp = 0;
>> + spin_unlock_irq(&env->comp_lock);
>> +
>> + if (!comp)
>> + return 0;
>> +
>> + /*
>> + * The irq was disabled, and is reenabled just now.
>> + * But there might have been a pending irq that
>> + * happened while the irq was disabled that fires
>> + * just as the irq is reenabled. That is not what
>> + * is desired.
>> + */
>> + enable_irq(env->comp_irq);
>> +
>> + /* So, synchronize this possibly pending irq... */
>> + synchronize_irq(env->comp_irq);
>> +
>> + /* ...and redo the whole dance. */
>> + spin_lock_irq(&env->comp_lock);
>> + comp = env->comp;
>> + env->comp = 0;
>> + spin_unlock_irq(&env->comp_lock);
>> +
>> + if (comp)
>> + enable_irq(env->comp_irq);
>> +
>> + return 1;
>> +}
>> +
>> +static irqreturn_t envelope_detector_comp_isr(int irq, void *ctx)
>> +{
>> + struct envelope *env = ctx;
>> +
>> + spin_lock(&env->comp_lock);
>> + env->comp = 1;
>> + disable_irq_nosync(env->comp_irq);
>> + spin_unlock(&env->comp_lock);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static void envelope_detector_setup_compare(struct envelope *env)
>> +{
>> + int ret;
>> +
>> + /*
>> + * Do a binary search for the peak input level, and stop
>> + * when that level is "trapped" between two adjacent DAC
>> + * values.
>> + * When invert is active, use the midpoint floor so that
>> + * env->level ends up as env->low when the termination
>> + * criteria below is fulfilled, and use the midpoint
>> + * ceiling when invert is not active so that env->level
>> + * ends up as env->high in that case.
>> + */
>> + env->level = (env->high + env->low + !env->invert) / 2;
>> +
>> + if (env->high == env->low + 1) {
>> + complete(&env->done);
>> + return;
>> + }
>> +
>> + /* Set a "safe" DAC level (if there is such a thing)... */
>> + ret = iio_write_channel_raw(env->dac, env->invert ? 0 : env->dac_max);
>> + if (ret < 0)
>> + goto err;
>> +
>> + /* ...clear the comparison result... */
>> + envelope_detector_comp_latch(env);
>> +
>> + /* ...set the real DAC level... */
>> + ret = iio_write_channel_raw(env->dac, env->level);
>> + if (ret < 0)
>> + goto err;
>> +
>> + /* ...and wait for a bit to see if the latch catches anything. */
>> + schedule_delayed_work(&env->comp_timeout,
>> + msecs_to_jiffies(env->comp_interval));
>> + return;
>> +
>> +err:
>> + env->level = ret;
>> + complete(&env->done);
>> +}
>> +
>> +static void envelope_detector_timeout(struct work_struct *work)
>> +{
>> + struct envelope *env = container_of(work, struct envelope,
>> + comp_timeout.work);
>> +
>> + /* Adjust low/high depending on the latch content... */
>> + if (!envelope_detector_comp_latch(env) ^ !env->invert)
>> + env->low = env->level;
>> + else
>> + env->high = env->level;
>> +
>> + /* ...and continue the search. */
>> + envelope_detector_setup_compare(env);
>> +}
>> +
>> +static int envelope_detector_read_raw(struct iio_dev *indio_dev,
>> + struct iio_chan_spec const *chan,
>> + int *val, int *val2, long mask)
>> +{
>> + struct envelope *env = iio_priv(indio_dev);
>> + int ret;
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_RAW:
>> + /*
>> + * When invert is active, start with high=max+1 and low=0
>> + * since we will end up with the low value when the
>> + * termination criteria is fulfilled (rounding down). And
>> + * start with high=max and low=-1 when invert is not active
>> + * since we will end up with the high value in that case.
>> + * This ensures that the returned value in both cases are
>> + * in the same range as the DAC and is a value that has not
>> + * triggered the comparator.
>> + */
>> + mutex_lock(&env->read_lock);
>> + env->high = env->dac_max + env->invert;
>> + env->low = -1 + env->invert;
>> + envelope_detector_setup_compare(env);
>> + wait_for_completion(&env->done);
>> + if (env->level < 0) {
>> + ret = env->level;
>> + goto err_unlock;
>> + }
>> + *val = env->invert ? env->dac_max - env->level : env->level;
>> + mutex_unlock(&env->read_lock);
>> +
>> + return IIO_VAL_INT;
>> +
>> + case IIO_CHAN_INFO_SCALE:
>> + return iio_read_channel_scale(env->dac, val, val2);
>> + }
>> +
>> + return -EINVAL;
>> +
>> +err_unlock:
>> + mutex_unlock(&env->read_lock);
>> + return ret;
>> +}
>> +
>> +static ssize_t envelope_show_invert(struct iio_dev *indio_dev,
>> + uintptr_t private,
>> + struct iio_chan_spec const *ch, char *buf)
>> +{
>> + struct envelope *env = iio_priv(indio_dev);
>> +
>> + return sprintf(buf, "%u\n", env->invert);
>> +}
>> +
>> +static ssize_t envelope_store_invert(struct iio_dev *indio_dev,
>> + uintptr_t private,
>> + struct iio_chan_spec const *ch,
>> + const char *buf, size_t len)
>> +{
>> + struct envelope *env = iio_priv(indio_dev);
>> + unsigned long invert;
>> + int ret;
>> + u32 trigger;
>> +
>> + ret = kstrtoul(buf, 0, &invert);
>> + if (ret < 0)
>> + return ret;
>> + if (invert > 1)
>> + return -EINVAL;
>> +
>> + trigger = invert ? env->comp_irq_trigger_inv : env->comp_irq_trigger;
>> +
>> + mutex_lock(&env->read_lock);
>> + if (invert != env->invert)
>> + ret = irq_set_irq_type(env->comp_irq, trigger);
>> + if (!ret) {
>> + env->invert = invert;
>> + ret = len;
>> + }
>> + mutex_unlock(&env->read_lock);
>> +
>> + return ret;
>> +}
>> +
>> +static ssize_t envelope_show_comp_interval(struct iio_dev *indio_dev,
>> + uintptr_t private,
>> + struct iio_chan_spec const *ch,
>> + char *buf)
>> +{
>> + struct envelope *env = iio_priv(indio_dev);
>> +
>> + return sprintf(buf, "%u\n", env->comp_interval);
>> +}
>> +
>> +static ssize_t envelope_store_comp_interval(struct iio_dev *indio_dev,
>> + uintptr_t private,
>> + struct iio_chan_spec const *ch,
>> + const char *buf, size_t len)
>> +{
>> + struct envelope *env = iio_priv(indio_dev);
>> + unsigned long interval;
>> + int ret;
>> +
>> + ret = kstrtoul(buf, 0, &interval);
>> + if (ret < 0)
>> + return ret;
>> + if (interval > 1000)
>> + return -EINVAL;
>> +
>> + mutex_lock(&env->read_lock);
>> + env->comp_interval = interval;
>> + mutex_unlock(&env->read_lock);
>> +
>> + return len;
>> +}
>> +
>> +static const struct iio_chan_spec_ext_info envelope_detector_ext_info[] = {
>> + { .name = "invert",
>> + .read = envelope_show_invert,
>> + .write = envelope_store_invert, },
>> + { .name = "compare_interval",
>> + .read = envelope_show_comp_interval,
>> + .write = envelope_store_comp_interval, },
>> + { /* sentinel */ }
>> +};
>> +
>> +static const struct iio_chan_spec envelope_detector_iio_channel = {
>> + .type = IIO_ALTVOLTAGE,
>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
>> + | BIT(IIO_CHAN_INFO_SCALE),
>> + .ext_info = envelope_detector_ext_info,
>> + .indexed = 1,
>> +};
>> +
>> +static const struct iio_info envelope_detector_info = {
>> + .read_raw = &envelope_detector_read_raw,
>> + .driver_module = THIS_MODULE,
>> +};
>> +
>> +static int envelope_detector_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct iio_dev *indio_dev;
>> + struct envelope *env;
>> + enum iio_chan_type type;
>> + int ret;
>> +
>> + indio_dev = devm_iio_device_alloc(dev, sizeof(*env));
>> + if (!indio_dev)
>> + return -ENOMEM;
>> +
>> + platform_set_drvdata(pdev, indio_dev);
>> + env = iio_priv(indio_dev);
>> + env->comp_interval = 50; /* some sensible default? */
>> +
>> + spin_lock_init(&env->comp_lock);
>> + mutex_init(&env->read_lock);
>> + init_completion(&env->done);
>> + INIT_DELAYED_WORK(&env->comp_timeout, envelope_detector_timeout);
>> +
>> + indio_dev->name = dev_name(dev);
>> + indio_dev->dev.parent = dev;
>> + indio_dev->dev.of_node = dev->of_node;
>> + indio_dev->info = &envelope_detector_info;
>> + indio_dev->channels = &envelope_detector_iio_channel;
>> + indio_dev->num_channels = 1;
>> +
>> + env->dac = devm_iio_channel_get(dev, "dac");
>> + if (IS_ERR(env->dac)) {
>> + if (PTR_ERR(env->dac) != -EPROBE_DEFER)
>> + dev_err(dev, "failed to get dac input channel\n");
>> + return PTR_ERR(env->dac);
>> + }
>> +
>> + env->comp_irq = platform_get_irq_byname(pdev, "comp");
>> + if (env->comp_irq < 0) {
>> + if (env->comp_irq != -EPROBE_DEFER)
>> + dev_err(dev, "failed to get compare interrupt\n");
>> + return env->comp_irq;
>> + }
>> +
>> + ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr,
>> + 0, "envelope-detector", env);
>> + if (ret) {
>> + if (ret != -EPROBE_DEFER)
>> + dev_err(dev, "failed to request interrupt\n");
>> + return ret;
>> + }
>> + env->comp_irq_trigger = irq_get_trigger_type(env->comp_irq);
>> + if (env->comp_irq_trigger & IRQF_TRIGGER_RISING)
>> + env->comp_irq_trigger_inv |= IRQF_TRIGGER_FALLING;
>> + if (env->comp_irq_trigger & IRQF_TRIGGER_FALLING)
>> + env->comp_irq_trigger_inv |= IRQF_TRIGGER_RISING;
>> + if (env->comp_irq_trigger & IRQF_TRIGGER_HIGH)
>> + env->comp_irq_trigger_inv |= IRQF_TRIGGER_LOW;
>> + if (env->comp_irq_trigger & IRQF_TRIGGER_LOW)
>> + env->comp_irq_trigger_inv |= IRQF_TRIGGER_HIGH;
>> +
>> + ret = iio_get_channel_type(env->dac, &type);
>> + if (ret < 0)
>> + return ret;
>> +
>> + if (type != IIO_VOLTAGE) {
>> + dev_err(dev, "dac is of the wrong type\n");
>> + return -EINVAL;
>> + }
>> +
>> + ret = iio_read_max_channel_raw(env->dac, &env->dac_max);
>> + if (ret < 0) {
>> + dev_err(dev, "dac does not indicate its raw maximum value\n");
>> + return ret;
>> + }
>> +
>> + return devm_iio_device_register(dev, indio_dev);
>> +}
>> +
>> +static const struct of_device_id envelope_detector_match[] = {
>> + { .compatible = "axentia,tse850-envelope-detector", },
>> + { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, envelope_detector_match);
>> +
>> +static struct platform_driver envelope_detector_driver = {
>> + .probe = envelope_detector_probe,
>> + .driver = {
>> + .name = "iio-envelope-detector",
>> + .of_match_table = envelope_detector_match,
>> + },
>> +};
>> +module_platform_driver(envelope_detector_driver);
>> +
>> +MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator");
>> +MODULE_AUTHOR("Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>");
>> +MODULE_LICENSE("GPL v2");
>>
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 05/10] iio: adc: stm32: add trigger polarity ext attr
From: Jonathan Cameron @ 2016-10-30 14:33 UTC (permalink / raw)
To: Fabrice Gasnier, linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-I+IVW8TIWO2tmTQ+vhA3Yw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg
In-Reply-To: <1477412722-24061-6-git-send-email-fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
On 25/10/16 17:25, Fabrice Gasnier wrote:
> Add trigger polarity extended attribute.
>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
ABI always needs documenting. Add docs also makes it easy to review
without diving into the code.
This will also need a convincing argument for why it doesn't belong in the
device tree.
J
> ---
> drivers/iio/adc/stm32/stm32-adc.c | 41 ++++++++++++++++++++++++++++++++++++-
> drivers/iio/adc/stm32/stm32-adc.h | 4 ++++
> drivers/iio/adc/stm32/stm32f4-adc.c | 12 +++++++++++
> 3 files changed, 56 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
> index 1a13450..9b4b459 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.c
> +++ b/drivers/iio/adc/stm32/stm32-adc.c
> @@ -207,7 +207,11 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
>
> /* trigger source */
> extsel = trig_info[ret].extsel;
> - exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
> +
> + /* default to rising edge if no polarity */
> + if (adc->exten == STM32_EXTEN_SWTRIG)
> + adc->exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
> + exten = adc->exten;
> }
>
> spin_lock_irqsave(&adc->lock, flags);
> @@ -221,6 +225,40 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
> return 0;
> }
>
> +static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan,
> + unsigned int type)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> +
> + adc->exten = type;
> +
> + return 0;
> +}
> +
> +static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> +
> + return adc->exten;
> +}
> +
> +static const char * const stm32_trig_pol_items[] = {
> + [STM32_EXTEN_SWTRIG] = "swtrig",
> + [STM32_EXTEN_HWTRIG_RISING_EDGE] = "rising-edge",
> + [STM32_EXTEN_HWTRIG_FALLING_EDGE] = "falling-edge",
> + [STM32_EXTEN_HWTRIG_BOTH_EDGES] = "both-edges",
> +};
> +
> +const struct iio_enum stm32_adc_trig_pol = {
> + .items = stm32_trig_pol_items,
> + .num_items = ARRAY_SIZE(stm32_trig_pol_items),
> + .get = stm32_adc_get_trig_pol,
> + .set = stm32_adc_set_trig_pol,
> +};
> +EXPORT_SYMBOL_GPL(stm32_adc_trig_pol);
> +
> /**
> * stm32_adc_conv_irq_enable() - Unmask end of conversion irq
> * @adc: stm32 adc instance
> @@ -893,6 +931,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
> chan->scan_type.realbits = adc->common->data->highres;
> chan->scan_type.storagebits = STM32_STORAGEBITS;
> chan->scan_type.shift = 0;
> + chan->ext_info = adc->common->data->ext_info;
> }
>
> static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
> index fe3568b..6c9b70d 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.h
> +++ b/drivers/iio/adc/stm32/stm32-adc.h
> @@ -234,6 +234,7 @@ struct stm32_adc_reginfo {
> * @ext_triggers: Reference to trigger info for regular channels
> * @jext_triggers: Reference to trigger info for injected channels
> * @adc_reginfo: stm32 ADC registers description
> + * @ext_info: Extended channel info
> * @highres: Max resolution
> * @max_clock_rate: Max input clock rate
> * @clk_sel: routine to select common clock and prescaler
> @@ -251,6 +252,7 @@ struct stm32_adc_ops {
> const struct stm32_adc_trig_info *ext_triggers;
> const struct stm32_adc_trig_info *jext_triggers;
> const struct stm32_adc_reginfo *adc_reginfo;
> + const struct iio_chan_spec_ext_info *ext_info;
> int highres;
> unsigned long max_clock_rate;
> int (*clk_sel)(struct stm32_adc *adc);
> @@ -273,6 +275,7 @@ struct stm32_adc_ops {
> * @id: ADC instance number (e.g. adc 1, 2 or 3)
> * @offset: ADC instance register offset in ADC block
> * @max_channels: Max channels number for this ADC.
> + * @exten: External trigger config (enable/polarity)
> * @extrig_list: External trigger list (for regular channel)
> * @completion: end of single conversion completion
> * @buffer: data buffer
> @@ -295,6 +298,7 @@ struct stm32_adc {
> int id;
> int offset;
> int max_channels;
> + enum stm32_adc_exten exten;
> struct list_head extrig_list;
> struct completion completion;
> u16 *buffer;
> diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
> index 4d7a2a8..e033a68 100644
> --- a/drivers/iio/adc/stm32/stm32f4-adc.c
> +++ b/drivers/iio/adc/stm32/stm32f4-adc.c
> @@ -555,11 +555,23 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
> return 0;
> }
>
> +static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
> + IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
> + {
> + .name = "trigger_pol_available",
> + .shared = IIO_SHARED_BY_ALL,
> + .read = iio_enum_available_read,
> + .private = (uintptr_t)&stm32_adc_trig_pol,
> + },
> + {},
> +};
> +
> static const struct stm32_adc_ops stm32f4_adc_ops = {
> .adc_info = stm32f4_adc_info,
> .ext_triggers = stm32f4_adc_ext_triggers,
> .jext_triggers = stm32f4_adc_jext_triggers,
> .adc_reginfo = &stm32f4_adc_reginfo,
> + .ext_info = stm32f4_adc_ext_info,
> .highres = 12,
> .max_clock_rate = 36000000,
> .clk_sel = stm32f4_adc_clk_sel,
>
^ permalink raw reply
* Re: [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios
From: Jonathan Cameron @ 2016-10-30 14:35 UTC (permalink / raw)
To: Fabrice Gasnier, linux-iio, linux-arm-kernel, devicetree,
linux-kernel
Cc: linux, robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue,
lars, knaack.h, pmeerw
In-Reply-To: <1477412722-24061-5-git-send-email-fabrice.gasnier@st.com>
On 25/10/16 17:25, Fabrice Gasnier wrote:
> STM32 ADC may use EXTi signals routed internally as trigger source
> for conversions. Configure them as interrupt to configure this path
> in HW to adc.
> Note: interrupt handler isn't required here, and corresponding interrupt
> can be kept masked at exti controller level.
>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
> drivers/iio/adc/stm32/stm32-adc.c | 45 +++++++++++++++++++++++++++++++++++++++
> drivers/iio/adc/stm32/stm32-adc.h | 3 +++
> 2 files changed, 48 insertions(+)
>
> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
> index 25d0307..1a13450 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.c
> +++ b/drivers/iio/adc/stm32/stm32-adc.c
> @@ -482,6 +482,12 @@ static irqreturn_t stm32_adc_common_isr(int irq, void *data)
> return ret;
> }
>
> +static irqreturn_t stm32_adc_exti_handler(int irq, void *data)
> +{
> + /* Exti handler should not be invoqued, and is not used */
invoked.
> + return IRQ_HANDLED;
> +}
> +
> /**
> * stm32_adc_validate_trigger() - validate trigger for stm32 adc
> * @indio_dev: IIO device
> @@ -1051,6 +1057,41 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
> }
> }
>
> +static int stm32_adc_exti_probe(struct stm32_adc_common *common)
> +{
> + int i, irq, ret;
> +
> + common->gpios = devm_gpiod_get_array_optional(common->dev, NULL,
> + GPIOD_IN);
> + if (!common->gpios)
> + return 0;
> +
> + for (i = 0; i < common->gpios->ndescs; i++) {
> + irq = gpiod_to_irq(common->gpios->desc[i]);
> + if (irq < 0) {
> + dev_err(common->dev, "gpio %d to irq failed\n", i);
> + return irq;
> + }
> +
> + ret = devm_request_irq(common->dev, irq, stm32_adc_exti_handler,
> + 0, dev_name(common->dev), common);
> + if (ret) {
> + dev_err(common->dev, "request IRQ %d failed\n", irq);
> + return ret;
> + }
> +
> + /*
> + * gpios are configured as interrupts, so exti trigger path is
> + * configured in HW. But getting interrupts when starting
> + * conversions is unused here, so mask it in on exti
> + * controller by default.
> + */
> + disable_irq(irq);
> + }
> +
> + return 0;
> +}
> +
> int stm32_adc_probe(struct platform_device *pdev)
> {
> struct device_node *np = pdev->dev.of_node, *child;
> @@ -1131,6 +1172,10 @@ int stm32_adc_probe(struct platform_device *pdev)
> goto err_clk_disable;
> }
>
> + ret = stm32_adc_exti_probe(common);
> + if (ret)
> + goto err_clk_disable;
> +
> /* Parse adc child nodes to retrieve master/slave instances data */
> for_each_available_child_of_node(np, child) {
> ret = stm32_adc_register(common, child);
> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
> index 01a0dda..fe3568b 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.h
> +++ b/drivers/iio/adc/stm32/stm32-adc.h
> @@ -23,6 +23,7 @@
> #define __STM32_ADC_H
>
> #include <linux/dmaengine.h>
> +#include <linux/gpio/consumer.h>
> #include <linux/irq_work.h>
>
> /*
> @@ -323,6 +324,7 @@ struct stm32_adc {
> * @aclk: common clock for the analog circuitry
> * @vref: regulator reference
> * @vref_mv: vref voltage (mv)
> + * @gpio_descs: gpio descriptor used to configure EXTi triggers
> * @lock: mutex
> */
> struct stm32_adc_common {
> @@ -335,6 +337,7 @@ struct stm32_adc_common {
> struct clk *aclk;
> struct regulator *vref;
> int vref_mv;
> + struct gpio_descs *gpios;
> struct mutex lock; /* read_raw lock */
> };
>
>
^ permalink raw reply
* Re: [PATCH v3 2/8] iio: inkern: add helpers to query available values from channels
From: Peter Meerwald-Stadler @ 2016-10-30 15:23 UTC (permalink / raw)
To: Jonathan Cameron, Peter Rosin
Cc: linux-kernel, Hartmut Knaack, Lars-Peter Clausen, Rob Herring,
Mark Rutland, linux-iio, devicetree
In-Reply-To: <c7848c84-960b-506f-f2e2-4aadfc5246f9@kernel.org>
> On 23/10/16 23:39, Peter Rosin wrote:
> > Specifically a helper for reading the available maximum raw value of a
> > channel and a helper for forwarding read_avail requests for raw values
> > from one iio driver to an iio channel that is consumed.
> >
> > These rather specific helpers are in turn built with generic helpers
> > making it easy to build more helpers for available values as needed.
> >
> > Signed-off-by: Peter Rosin <peda@axentia.se>
> Looks good to me. Just what I was after.
some comments below
> Jonathan
> > ---
> > drivers/iio/inkern.c | 97 ++++++++++++++++++++++++++++++++++++++++++++
> > include/linux/iio/consumer.h | 29 +++++++++++++
> > include/linux/iio/iio.h | 17 ++++++++
> > 3 files changed, 143 insertions(+)
> >
> > diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
> > index c4757e6367e7..74808f8a187a 100644
> > --- a/drivers/iio/inkern.c
> > +++ b/drivers/iio/inkern.c
> > @@ -703,6 +703,103 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
> > }
> > EXPORT_SYMBOL_GPL(iio_read_channel_scale);
> >
> > +static int iio_channel_read_avail(struct iio_channel *chan,
> > + const int **vals, int *type, int *length,
> > + enum iio_chan_info_enum info)
> > +{
> > + if (!iio_channel_has_available(chan->channel, info))
> > + return -EINVAL;
> > +
> > + return chan->indio_dev->info->read_avail(chan->indio_dev, chan->channel,
> > + vals, type, length, info);
> > +}
> > +
> > +int iio_read_avail_channel_raw(struct iio_channel *chan,
> > + const int **vals, int *type, int *length)
> > +{
> > + int ret;
> > +
> > + mutex_lock(&chan->indio_dev->info_exist_lock);
> > + if (!chan->indio_dev->info) {
> > + ret = -ENODEV;
> > + goto err_unlock;
> > + }
> > +
> > + ret = iio_channel_read_avail(chan,
> > + vals, type, length, IIO_CHAN_INFO_RAW);
> > +err_unlock:
> > + mutex_unlock(&chan->indio_dev->info_exist_lock);
> > +
> > + return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw);
> > +
> > +static int iio_channel_read_max(struct iio_channel *chan,
> > + int *val, int *val2, int *type,
> > + enum iio_chan_info_enum info)
> > +{
> > + int unused;
> > + const int *vals;
> > + int length;
> > + int ret;
> > +
> > + if (!val2)
> > + val2 = &unused;
> > +
> > + ret = iio_channel_read_avail(chan, &vals, type, &length, info);
> > + switch (ret) {
> > + case IIO_AVAIL_RANGE:
> > + switch (*type) {
> > + case IIO_VAL_INT:
> > + *val = vals[2];
> > + break;
> > + default:
> > + *val = vals[4];
> > + *val2 = vals[5];
> > + }
> > + return 0;
> > +
> > + case IIO_AVAIL_LIST:
> > + if (length <= 0)
> > + return -EINVAL;
> > + switch (*type) {
> > + case IIO_VAL_INT:
> > + *val = vals[--length];
> > + while (length) {
> > + if (vals[--length] > *val)
> > + *val = vals[length];
> > + }
> > + break;
> > + default:
> > + /* FIXME: learn about max for other iio values */
> > + return -EINVAL;
> > + }
> > + return 0;
> > +
> > + default:
> > + return ret;
> > + }
> > +}
> > +
> > +int iio_read_max_channel_raw(struct iio_channel *chan, int *val)
> > +{
> > + int ret;
> > + int type;
> > +
> > + mutex_lock(&chan->indio_dev->info_exist_lock);
> > + if (!chan->indio_dev->info) {
> > + ret = -ENODEV;
> > + goto err_unlock;
> > + }
> > +
> > + ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
> > +err_unlock:
> > + mutex_unlock(&chan->indio_dev->info_exist_lock);
> > +
> > + return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(iio_read_max_channel_raw);
> > +
> > int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
> > {
> > int ret = 0;
> > diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
> > index 9edccfba1ffb..baab5e45734f 100644
> > --- a/include/linux/iio/consumer.h
> > +++ b/include/linux/iio/consumer.h
> > @@ -226,6 +226,35 @@ int iio_read_channel_processed(struct iio_channel *chan, int *val);
> > int iio_write_channel_raw(struct iio_channel *chan, int val);
> >
> > /**
> > + * iio_read_max_channel_raw() - read maximum available raw value from a given
> > + * channel
> > + * @chan: The channel being queried.
> > + * @val: Value read back.
> > + *
> > + * Note raw reads from iio channels are in adc counts and hence
"Note: raw reads..." would be easier...
here and below
why is there no val2 here?
just reading the documentation ("maximum available raw value") I am not
sure what the function does: does it return the maximum value possible? or
the maximum value in some internal buffer? maximum value ever seen?
> > + * scale will need to be applied if standard units are required.
> > + */
> > +int iio_read_max_channel_raw(struct iio_channel *chan, int *val);
> > +
> > +/**
> > + * iio_read_avail_channel_raw() - read available raw values from a given channel
> > + * @chan: The channel being queried.
> > + * @vals: Available values read back.
no vals2?
> > + * @type: Type of available values in vals.
it is not clear what type is
> > + * @length: Number of entries in vals.
> > + *
> > + * Returns an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST.
> > + *
> > + * For ranges, three vals are always returned; min, step and max.
> > + * For lists, all the possible values are enumerated.
> > + *
> > + * Note raw available values from iio channels are in adc counts and
> > + * hence scale will need to be applied if standard units are required.
> > + */
> > +int iio_read_avail_channel_raw(struct iio_channel *chan,
> > + const int **vals, int *type, int *length);
> > +
> > +/**
> > * iio_get_channel_type() - get the type of a channel
> > * @channel: The channel being queried.
> > * @type: The type of the channel.
> > diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> > index 45b781084a4b..e565701d13ce 100644
> > --- a/include/linux/iio/iio.h
> > +++ b/include/linux/iio/iio.h
> > @@ -315,6 +315,23 @@ static inline bool iio_channel_has_info(const struct iio_chan_spec *chan,
> > (chan->info_mask_shared_by_all & BIT(type));
> > }
> >
> > +/**
> > + * iio_channel_has_available() - Checks if a channel has an available attribute
> > + * @chan: The channel to be queried
> > + * @type: Type of the available attribute to be checked
> > + *
> > + * Returns true if the channels supports reporting available values for the
channel
> > + * given attribute type, false otherwise.
> > + */
> > +static inline bool iio_channel_has_available(const struct iio_chan_spec *chan,
> > + enum iio_chan_info_enum type)
> > +{
> > + return (chan->info_mask_separate_available & BIT(type)) |
> > + (chan->info_mask_shared_by_type_available & BIT(type)) |
> > + (chan->info_mask_shared_by_dir_available & BIT(type)) |
> > + (chan->info_mask_shared_by_all_available & BIT(type));
> > +}
> > +
> > #define IIO_CHAN_SOFT_TIMESTAMP(_si) { \
> > .type = IIO_TIMESTAMP, \
> > .channel = -1, \
> >
>
--
Peter Meerwald-Stadler
+43-664-2444418 (mobile)
^ permalink raw reply
* Re: [PATCH 1/2] DT: binding: bcm2835-mbox: fix address typo in example
From: Andreas Färber @ 2016-10-30 15:26 UTC (permalink / raw)
To: Stefan Wahren, Rob Herring
Cc: Mark Rutland, Stephen Warren, Eric Anholt,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1477505640-26658-1-git-send-email-stefan.wahren-eS4NqCHxEME@public.gmane.org>
Am 26.10.2016 um 20:13 schrieb Stefan Wahren:
> The address of the mailbox node in the example has a typo.
> So fix it accordingly.
>
> Signed-off-by: Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org>
> Fixes: d4b5c782b9f4 ("dt/bindings: Add binding for the BCM2835 mailbox driver")
> ---
> .../bindings/mailbox/brcm,bcm2835-mbox.txt | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
Reviewed-by: Andreas Färber <afaerber-l3A5Bk7waGM@public.gmane.org>
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 02/10] iio: adc: Add stm32 support
From: Jonathan Cameron @ 2016-10-30 15:27 UTC (permalink / raw)
To: Fabrice Gasnier, linux-iio-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: linux-I+IVW8TIWO2tmTQ+vhA3Yw, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w,
alexandre.torgue-qxv4g6HH51o, lars-Qo5EllUWu/uELgA04lAiVw,
knaack.h-Mmb7MZpHnFY, pmeerw-jW+XmwGofnusTnJN9+BGXg
In-Reply-To: <1477412722-24061-3-git-send-email-fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
On 25/10/16 17:25, Fabrice Gasnier wrote:
> This patch adds support for STMicroelectronics STM32 MCU's analog to
> digital converter.
>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
Hi Fabrice,
Sometimes I hate SoC ADCs. For some reason the hardware designers seem to
try and throw everything and the kitchen sink at them. Discontinuous mode
as an example in this device. Not seen that particular piece of fun before
and glad to see you haven't 'yet' tried to support it!
Anyhow, the complexity of the hardware leads to an initially complex driver.
My first thought it that this would be easier to follow / review if we
built it up in smaller steps. Perhaps ditch the injected channel support
entirely in the first instance. I also wonder if you don't need to support
that whole thing (injected sampling) as another iio device entirely using the
same channels. That's kind of what it is from a data flow point of view
(we've had arbitary sequencers before with priorities - don't think anyone
ever decided the pain was worth supporting the complexity, but right answer
has always been multiple IIO devices).
You also have at least one layer of abstraction in here that serves no
current purpose. Please clear that out for now. It'll make the code
shorter and easier to follow. If/when other parts are introduced then
is the time to do that transistion to having the abstraction.
My first thought on the double / tripple adc handling is that you'd be better
off handling them as 3 separate devices then doing some 'unusual' trigger
handling to support the weird sequencing. Guessing you thought about that?
If so could you lay out your reasoning for the single driver instance approach.
I'm not arguing against it btw, merely want to understand your reasoning!
It would be tricky given one set of channels are selectable over 3 devices
and there are constraints to enforce (not sampling same channel on two ADCs
at the same time) but not impossible... Perhaps what you have here is
indeed simpler!
Whilst it's been a nasty job to review, I'm guessing writing it was
much worse ;) Pretty good starting point though might take a little while
to pin down the remaining questions on how best to handle this particular
monster.
Jonathan
> ---
> drivers/iio/adc/Kconfig | 2 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/stm32/Kconfig | 34 ++
> drivers/iio/adc/stm32/Makefile | 4 +
> drivers/iio/adc/stm32/stm32-adc.c | 999 ++++++++++++++++++++++++++++++++++++
> drivers/iio/adc/stm32/stm32-adc.h | 442 ++++++++++++++++
> drivers/iio/adc/stm32/stm32f4-adc.c | 574 +++++++++++++++++++++
> 7 files changed, 2056 insertions(+)
> create mode 100644 drivers/iio/adc/stm32/Kconfig
> create mode 100644 drivers/iio/adc/stm32/Makefile
> create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
> create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
> create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 7edcf32..5c96a55 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -583,4 +583,6 @@ config XILINX_XADC
> The driver can also be build as a module. If so, the module will be called
> xilinx-xadc.
>
> +source "drivers/iio/adc/stm32/Kconfig"
> +
> endmenu
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 7a40c04..a9dbf3a 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -13,6 +13,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
> obj-$(CONFIG_AD7793) += ad7793.o
> obj-$(CONFIG_AD7887) += ad7887.o
> obj-$(CONFIG_AD799X) += ad799x.o
> +obj-$(CONFIG_ARCH_STM32) += stm32/
> obj-$(CONFIG_AT91_ADC) += at91_adc.o
> obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
> new file mode 100644
> index 0000000..245d037
> --- /dev/null
> +++ b/drivers/iio/adc/stm32/Kconfig
> @@ -0,0 +1,34 @@
> +#
> +# STM32 familly ADC drivers
> +#
> +
> +config STM32_ADC
> + tristate
> + select REGULATOR
> + select REGULATOR_FIXED_VOLTAGE
> + select IIO_BUFFER
> + select IIO_TRIGGERED_BUFFER
> + help
> + Say yes here to build the driver for the STMicroelectronics
> + STM32 analog-to-digital converter (ADC).
> +
> + This driver can also be built as a module. If so, the module
> + will be called stm32-adc.
> +
> +config STM32F4_ADC
> + tristate "STMicroelectronics STM32F4 adc"
> + depends on ARCH_STM32 || COMPILE_TEST
> + depends on OF
> + select STM32_ADC
> + help
> + Say yes here to build support for STMicroelectronics stm32f4 Analog
> + to Digital Converter (ADC).
> +
> + This driver can also be built as a module. If so, the module
> + will be called stm32f4-adc.
> +
> +config STM32_ADC_DEBUG
> + bool "Enable debug for stm32 ADC drivers"
> + depends on STM32_ADC
> + help
> + Say "yes" to enable debug messages, on stm32 ADC drivers.
> diff --git a/drivers/iio/adc/stm32/Makefile b/drivers/iio/adc/stm32/Makefile
> new file mode 100644
> index 0000000..83e8154
> --- /dev/null
> +++ b/drivers/iio/adc/stm32/Makefile
> @@ -0,0 +1,4 @@
> +# Core
> +subdir-ccflags-$(CONFIG_STM32_ADC_DEBUG) := -DDEBUG
> +obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> +obj-$(CONFIG_STM32F4_ADC) += stm32f4-adc.o
> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
> new file mode 100644
> index 0000000..1e0850d
> --- /dev/null
> +++ b/drivers/iio/adc/stm32/stm32-adc.c
> @@ -0,0 +1,999 @@
> +/*
> + * This file is part of STM32 ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>.
> + *
> + * 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/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/events.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +#include "stm32-adc.h"
> +
> +/**
> + * stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
> + * @indio_dev: IIO device
> + * @scan_mask: channels to be converted
> + *
> + * Conversion sequence :
> + * Configure ADC scan sequence based on selected channels in scan_mask.
> + * Add channels to SQR or JSQR registers, from scan_mask LSB to MSB, then
> + * program sequence len.
> + *
> + * Note: This can be done only when ADC enabled and no conversion is ongoing.
> + */
> +static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
> + const unsigned long *scan_mask)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + const struct stm32_adc_regs *sq;
> + const struct iio_chan_spec *chan;
> + u32 val, bit;
> + int sq_max, i = 0;
> +
> + if (!stm32_adc_is_enabled(adc))
> + return -EBUSY;
> +
> + if (adc->injected) {
> + if (stm32_adc_injected_started(adc))
> + return -EBUSY;
> +
> + sq = adc->common->data->adc_reginfo->jsqr_reg;
> + sq_max = STM32_ADC_MAX_JSQ;
> + } else {
> + if (stm32_adc_regular_started(adc))
> + return -EBUSY;
> +
> + sq = adc->common->data->adc_reginfo->sqr_regs;
> + sq_max = STM32_ADC_MAX_SQ;
> + }
> +
> + /* Build sequence for regular or injected channels */
> + for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
> + chan = indio_dev->channels + bit;
> + /*
> + * Assign one channel per SQ/JSQ entry in regular/injected
> + * sequence, starting with SQ1/JSQ1.
> + */
> + i++;
> + if (i > sq_max)
> + return -EINVAL;
> +
> + dev_dbg(adc->common->dev, "%s chan %d to %s%d\n",
> + __func__, chan->channel, adc->injected ? "JSQ" : "SQ",
> + i);
> +
> + val = stm32_adc_readl(adc, sq[i].reg);
> + val &= ~sq[i].mask;
> + val |= (chan->channel << sq[i].shift) & sq[i].mask;
> + stm32_adc_writel(adc, sq[i].reg, val);
> + }
> +
> + if (!i)
> + return -EINVAL;
> +
> + /* Sequence len */
> + val = stm32_adc_readl(adc, sq[0].reg);
> + val &= ~sq[0].mask;
> + val |= ((i - 1) << sq[0].shift) & sq[0].mask;
> + stm32_adc_writel(adc, sq[0].reg, val);
> +
> + return 0;
> +}
> +
> +static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
> + const unsigned long *scan_mask)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + int ret;
> +
> + ret = stm32_adc_clk_sel(adc);
> + if (ret) {
> + dev_err(&indio_dev->dev, "Clock sel failed\n");
> + return ret;
> + }
> +
> + ret = stm32_adc_enable(adc);
> + if (ret) {
> + dev_err(&indio_dev->dev, "Failed to enable adc\n");
> + return ret;
> + }
> +
> + ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
> + if (ret) {
> + dev_err(&indio_dev->dev, "Failed to configure sequence\n");
> + goto err_dis;
> + }
It's horrible but to end up in the 'obvious' state I'd disable the adc
again assuming that doesn't kill the stuff that is configured.
> +
> + return 0;
> +
> +err_dis:
> + stm32_adc_disable(adc);
> +
> + return ret;
> +}
> +
> +/**
> + * stm32_adc_get_trig_index() - Get trigger index
> + * @indio_dev: IIO device
> + * @trig: trigger
> + *
> + * Returns trigger index, if trig matches one of the triggers registered by
> + * stm32 adc driver, -EINVAL otherwise.
> + */
> +static int stm32_adc_get_trig_index(struct iio_dev *indio_dev,
> + struct iio_trigger *trig)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + struct iio_trigger *tr;
> + int i = 0;
> +
> + list_for_each_entry(tr, &adc->extrig_list, alloc_list) {
> + if (tr == trig)
> + return i;
> + i++;
> + }
> +
> + return -EINVAL;
> +}
> +
> +/**
> + * stm32_adc_set_trig() - Set a regular or injected trigger
> + * @indio_dev: IIO device
> + * @trig: IIO trigger
> + *
> + * Set trigger source/polarity (e.g. SW, or HW with polarity) :
> + * - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw)
> + * - if HW trigger enabled, set source & polarity (trigger_pol / jtrigger_pol)
> + *
> + * Note: must be called when ADC is enabled
> + */
> +static int stm32_adc_set_trig(struct iio_dev *indio_dev,
> + struct iio_trigger *trig)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + const struct stm32_adc_trig_info *trig_info;
> + const struct stm32_adc_trig_reginfo *reginfo;
> + u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG;
> + unsigned long flags;
> + int ret;
> +
> + if (!stm32_adc_is_enabled(adc))
> + return -EBUSY;
> +
> + if (adc->injected) {
> + if (stm32_adc_injected_started(adc))
> + return -EBUSY;
> + reginfo = adc->common->data->adc_reginfo->jtrig_reginfo;
> + trig_info = adc->common->data->jext_triggers;
> + } else {
> + if (stm32_adc_regular_started(adc))
> + return -EBUSY;
> + reginfo = adc->common->data->adc_reginfo->trig_reginfo;
> + trig_info = adc->common->data->ext_triggers;
> + }
> +
> + if (trig) {
> + ret = stm32_adc_get_trig_index(indio_dev, trig);
> + if (ret < 0)
> + return ret;
> +
> + /* trigger source */
> + extsel = trig_info[ret].extsel;
> + exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
> + }
> +
> + spin_lock_irqsave(&adc->lock, flags);
> + val = stm32_adc_readl(adc, reginfo->reg);
> + val &= ~(reginfo->exten_mask | reginfo->extsel_mask);
> + val |= (exten << reginfo->exten_shift) & reginfo->exten_mask;
> + val |= (extsel << reginfo->extsel_shift) & reginfo->extsel_mask;
> + stm32_adc_writel(adc, reginfo->reg, val);
> + spin_unlock_irqrestore(&adc->lock, flags);
> +
> + return 0;
> +}
> +
> +/**
> + * stm32_adc_conv_irq_enable() - Unmask end of conversion irq
> + * @adc: stm32 adc instance
> + *
> + * Unmask either eoc or jeoc, depending on injected configuration.
> + */
> +static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
> +{
> + const struct stm32_adc_reginfo *reginfo =
> + adc->common->data->adc_reginfo;
> + u32 mask;
> +
> + if (adc->injected)
> + mask = reginfo->jeocie;
> + else
> + mask = reginfo->eocie;
> +
> + stm32_adc_set_bits(adc, reginfo->ier, mask);
> +}
> +
> +/**
> + * stm32_adc_conv_irq_disable() - Mask end of conversion irq
> + * @adc: stm32 adc instance
> + *
> + * Mask either eoc or jeoc, depending on injected configuration.
> + */
> +static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
> +{
> + const struct stm32_adc_reginfo *reginfo =
> + adc->common->data->adc_reginfo;
> + u32 mask;
> +
> + if (adc->injected)
> + mask = reginfo->jeocie;
> + else
> + mask = reginfo->eocie;
> +
> + stm32_adc_clr_bits(adc, reginfo->ier, mask);
> +}
> +
> +/**
> + * stm32_adc_single_conv() - perform a single conversion
> + * @indio_dev: IIO device
> + * @chan: IIO channel
> + * @result: conversion result
> + *
> + * The function performs a single conversion on a given channel, by
> + * by:
> + * - creating scan mask with only one channel
> + * - using SW trigger
> + * - then start single conv
> + */
> +static int stm32_adc_single_conv(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan,
> + int *val)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + unsigned long *scan_mask;
> + long timeout;
> + u16 result;
> + int ret;
> +
> + scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
> + GFP_KERNEL);
This is known maximum length... I'd just avoid the complexity of allocating
it like this - a comment would do the job to say it is the right length.
> + if (!scan_mask)
> + return -ENOMEM;
> +
> + set_bit(chan->scan_index, scan_mask);
> +
> + reinit_completion(&adc->completion);
> +
> + adc->bufi = 0;
> + adc->num_conv = 1;
> + adc->buffer = &result;
> +
> + ret = stm32_adc_conf_scan(indio_dev, scan_mask);
> + if (ret)
> + goto free;
> +
> + /* No HW trigger: conversion can be launched in SW */
> + ret = stm32_adc_set_trig(indio_dev, NULL);
Put it back again afterwards? Otherwise some nasty race conditions look
likely to me.. (userspace sets trigger and is about to enable the buffer
when along comes this code and changes it underneath).
> + if (ret) {
> + dev_err(&indio_dev->dev, "Can't set SW trigger\n");
> + goto adc_disable;
> + }
> +
> + stm32_adc_conv_irq_enable(adc);
> +
> + ret = stm32_adc_start_conv(adc);
> + if (ret) {
> + dev_err(&indio_dev->dev, "Failed to start single conv\n");
> + goto irq_disable;
> + }
> +
> + timeout = wait_for_completion_interruptible_timeout(
> + &adc->completion, STM32_ADC_TIMEOUT);
> + if (timeout == 0) {
> + dev_warn(&indio_dev->dev, "Conversion timed out!\n");
> + ret = -ETIMEDOUT;
> + } else if (timeout < 0) {
> + dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
> + ret = -EINTR;
> + } else {
> + *val = result & STM32_RESULT_MASK;
> + ret = IIO_VAL_INT;
> + }
> +
> + if (stm32_adc_stop_conv(adc))
> + dev_err(&indio_dev->dev, "stop failed\n");
> +
> +irq_disable:
> + stm32_adc_conv_irq_disable(adc);
> +
> +adc_disable:
> + stm32_adc_disable(adc);
> +
> +free:
> + kfree(scan_mask);
> + adc->buffer = NULL;
> +
> + return ret;
> +}
> +
> +static int stm32_adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + int *val, int *val2, long mask)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + int ret = -EINVAL;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> + if (chan->type == IIO_VOLTAGE)
> + ret = stm32_adc_single_conv(indio_dev, chan, val);
> + iio_device_release_direct_mode(indio_dev);
> + break;
> + case IIO_CHAN_INFO_SCALE:
> + *val = adc->common->vref_mv;
> + *val2 = chan->scan_type.realbits;
> + ret = IIO_VAL_FRACTIONAL_LOG2;
> + break;
> + default:
> + break;
> + }
> +
> + return ret;
> +}
> +
> +/**
> + * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
As this is kernel doc please document the parameter as well. Otherwise
we'll get a pile of warnings!
> + */
> +static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
> +{
> + struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> + const struct stm32_adc_reginfo *reginfo =
> + adc->common->data->adc_reginfo;
> + u32 mask, clr_mask, status = stm32_adc_readl(adc, reginfo->isr);
> +
> + if (adc->injected) {
> + mask = reginfo->jeoc;
> + clr_mask = mask;
> + } else {
> + mask = reginfo->eoc;
> + /* don't clear 'eoc' as it is cleared when reading 'dr' */
> + clr_mask = 0;
> + }
> +
> + /* clear irq */
> + stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
Want to do this in the non injected case? it's a noop isn't it?
> + status &= mask;
> +
> + /* Regular data */
> + if (status & reginfo->eoc) {
Hmm.. this is a little bit of 'missuse' of the standard trigger architecture
but as long as it's restricted to just this device I don't suppose we need
to care. Only reason we need it is to provide control of 'which' hardware
trigger is being used.
Guessing the DMA will almost always be turned on and will make this oddity
effectively disappear.
> + adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
> + if (iio_buffer_enabled(indio_dev)) {
> + adc->bufi++;
> + if (adc->bufi >= adc->num_conv) {
> + stm32_adc_conv_irq_disable(adc);
> + iio_trigger_poll(indio_dev->trig);
> + }
> + } else {
> + complete(&adc->completion);
> + }
> + }
> +
> + /* Injected data */
> + if (status & reginfo->jeoc) {
> + int i;
> +
> + for (i = 0; i < adc->num_conv; i++) {
> + adc->buffer[i] = stm32_adc_readl(adc, reginfo->jdr[i]);
> + adc->bufi++;
> + }
> +
> + if (iio_buffer_enabled(indio_dev)) {
> + stm32_adc_conv_irq_disable(adc);
> + iio_trigger_poll(indio_dev->trig);
> + } else {
> + complete(&adc->completion);
> + }
> + }
> +
> + /*
> + * In case end of conversion flags have been handled, this has been
> + * handled for this ADC instance
> + */
> + if (status)
> + return IRQ_HANDLED;
> +
> + /* This adc instance didn't trigger this interrupt */
> + return IRQ_NONE;
> +}
> +
> +/**
> + * stm32_adc_common_isr() - Common isr for the whole ADC block
> + *
> + * There is one IRQ for all ADCs in ADC block, check all instances.
> + */
> +static irqreturn_t stm32_adc_common_isr(int irq, void *data)
> +{
> + struct stm32_adc_common *common = data;
> + irqreturn_t ret = IRQ_NONE;
> + struct stm32_adc *adc;
> +
> + list_for_each_entry(adc, &common->adc_list, adc_list)
> + ret |= stm32_adc_isr(adc);
Hmm.. ret |= is rather fragile. Preferable to make the handling of NONE
vs IRQ_HANDLED explicit.
If you were to split the driver up as I suggested might make sense above,
then this would be done with an irq chip in a top level device (effectively
a very simple mfd).
> +
> + return ret;
> +}
> +
> +/**
> + * stm32_adc_validate_trigger() - validate trigger for stm32 adc
> + * @indio_dev: IIO device
> + * @trig: new trigger
> + *
> + * Returns: 0 if trig matches one of the triggers registered by stm32 adc
> + * driver, -EINVAL otherwise.
> + */
> +static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
> + struct iio_trigger *trig)
> +{
> + return stm32_adc_get_trig_index(indio_dev, trig) < 0 ? -EINVAL : 0;
> +}
> +
> +static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
> + const unsigned long *scan_mask)
I'm glad you kept this relatively simple compared to some of the
'fun' the hardware is capable of. Very wise!
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + int ret;
> + u32 bit;
> +
> + adc->num_conv = 0;
> + for_each_set_bit(bit, scan_mask, indio_dev->masklength)
> + adc->num_conv++;
> +
> + ret = stm32_adc_conf_scan(indio_dev, scan_mask);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}
> +
> +static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
> + const struct of_phandle_args *iiospec)
> +{
> + int i;
> +
> + for (i = 0; i < indio_dev->num_channels; i++)
> + if (indio_dev->channels[i].channel == iiospec->args[0])
> + return i;
> +
> + return -EINVAL;
> +}
> +
> +/**
> + * stm32_adc_debugfs_reg_access - read or write register value
> + *
> + * To read a value from an ADC register:
> + * echo [ADC reg offset] > direct_reg_access
> + * cat direct_reg_access
> + *
> + * To write a value in a ADC register:
> + * echo [ADC_reg_offset] [value] > direct_reg_access
> + */
> +static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
> + unsigned reg, unsigned writeval,
> + unsigned *readval)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> +
> + if (!readval)
> + stm32_adc_writel(adc, reg, writeval);
> + else
> + *readval = stm32_adc_readl(adc, reg);
> +
> + return 0;
> +}
> +
> +static const struct iio_info stm32_adc_iio_info = {
> + .read_raw = stm32_adc_read_raw,
> + .validate_trigger = stm32_adc_validate_trigger,
> + .update_scan_mode = stm32_adc_update_scan_mode,
> + .debugfs_reg_access = stm32_adc_debugfs_reg_access,
> + .of_xlate = stm32_adc_of_xlate,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static int stm32_adc_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> +
> + stm32_adc_disable(adc);
This is a surprise as postdisbale should balance preenable...
Ah, you have update scan mode enabling the adc. If you can balance it
better by moving that to preenable please do as it is more 'obviously' correct.
> +
> + return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
> + .postenable = &iio_triggered_buffer_postenable,
> + .predisable = &iio_triggered_buffer_predisable,
> + .postdisable = &stm32_adc_buffer_postdisable,
> +};
> +
> +static int stm32_adc_validate_device(struct iio_trigger *trig,
> + struct iio_dev *indio_dev)
> +{
> + struct iio_dev *indio = iio_trigger_get_drvdata(trig);
> +
> + return indio != indio_dev ? -EINVAL : 0;
> +}
> +
> +static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
> + bool state)
> +{
> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + int ret;
> +
> + if (state) {
> + /* Reset adc buffer index */
> + adc->bufi = 0;
> +
> + /* Allocate adc buffer */
> + adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
I'd be more cynical. It's not that big a memory allocation at worst.
Just put a big enough buffer in your adc structure and don't bother doing
it dynamically.
If you didn't want to do it, it should be in the preenable callback rather
than the trigger state one (for semantic reasons rather than because it's a
bug)
> + if (!adc->buffer)
> + return -ENOMEM;
> +
> + ret = stm32_adc_set_trig(indio_dev, trig);
> + if (ret) {
> + dev_err(&indio_dev->dev, "Can't set trigger\n");
> + goto err_buffer_free;
> + }
> +
> + stm32_adc_conv_irq_enable(adc);
> +
> + ret = stm32_adc_start_conv(adc);
> + if (ret) {
> + dev_err(&indio_dev->dev, "Failed to start\n");
> + goto err_irq_trig_disable;
> + }
> + } else {
> + ret = stm32_adc_stop_conv(adc);
> + if (ret < 0) {
> + dev_err(&indio_dev->dev, "Failed to stop\n");
> + return ret;
> + }
> +
> + stm32_adc_conv_irq_disable(adc);
> +
> + ret = stm32_adc_set_trig(indio_dev, NULL);
> + if (ret)
> + dev_warn(&indio_dev->dev, "Can't clear trigger\n");
> +
> + kfree(adc->buffer);
> + adc->buffer = NULL;
> + }
> +
> + return 0;
> +
> +err_irq_trig_disable:
> + stm32_adc_conv_irq_disable(adc);
> + stm32_adc_set_trig(indio_dev, NULL);
> +
> +err_buffer_free:
> + kfree(adc->buffer);
> + adc->buffer = NULL;
> +
> + return ret;
> +}
> +
> +static const struct iio_trigger_ops stm32_adc_trigger_ops = {
> + .owner = THIS_MODULE,
> + .validate_device = stm32_adc_validate_device,
> + .set_trigger_state = stm32_adc_set_trigger_state,
> +};
> +
> +static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct stm32_adc *adc = iio_priv(indio_dev);
> +
> + dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
> +
> + /* reset buffer index */
> + adc->bufi = 0;
> + iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
> + pf->timestamp);
> +
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + /* re-enable eoc irq */
> + stm32_adc_conv_irq_enable(adc);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void stm32_adc_trig_unregister(struct iio_dev *indio_dev)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + struct iio_trigger *trig, *_t;
> +
> + list_for_each_entry_safe(trig, _t, &adc->extrig_list, alloc_list) {
> + iio_trigger_unregister(trig);
> + list_del(&trig->alloc_list);
> + }
> +}
> +
I'd like a bit of documentation on this and a few of the other more
complex functions. Here it wasn't immediately obvious to me that it
was registering a large set of triggers. Also, silly question but
do you have any means of controlling the various timer setups from userspace?
There have been numerous discussions over the years on having a generic
timer subsystem, but if anything got written it passed me by. I have a couple
of boards where it would be handy but never had the time to do more than
talk about it ;)
> +static int stm32_adc_trig_register(struct iio_dev *indio_dev)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + struct stm32_adc_common *common = adc->common;
> + const struct stm32_adc_trig_info *ext = common->data->ext_triggers;
> + struct iio_trigger *trig;
> + int i, ret = 0;
> +
> + if (adc->injected)
> + ext = common->data->jext_triggers;
> + else
> + ext = common->data->ext_triggers;
> +
> + for (i = 0; ext && ext[i].name; i++) {
> + trig = devm_iio_trigger_alloc(common->dev, "%s_%s%d_%s",
> + indio_dev->name,
> + adc->injected ? "jext" : "ext",
> + ext[i].extsel, ext[i].name);
> + if (!trig) {
> + dev_err(common->dev, "trig %s_%s%d_%s alloc failed\n",
> + indio_dev->name,
> + adc->injected ? "jext" : "ext",
> + ext[i].extsel, ext[i].name);
> + ret = -ENOMEM;
> + goto err;
> + }
> +
> + trig->dev.parent = common->dev;
> + trig->ops = &stm32_adc_trigger_ops;
> + iio_trigger_set_drvdata(trig, indio_dev);
> +
> + ret = iio_trigger_register(trig);
> + if (ret) {
> + dev_err(common->dev,
> + "trig %s_%s%d_%s register failed\n",
> + indio_dev->name,
> + adc->injected ? "jext" : "ext",
> + ext[i].extsel, ext[i].name);
> + goto err;
> + }
> +
> + list_add_tail(&trig->alloc_list, &adc->extrig_list);
> + }
> +
> + return 0;
> +err:
> + stm32_adc_trig_unregister(indio_dev);
> +
> + return ret;
> +}
> +
> +static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
> + struct iio_chan_spec *chan,
> + const struct stm32_adc_chan_spec *channel,
> + int scan_index)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> +
> + chan->type = channel->type;
> + chan->channel = channel->channel;
> + chan->datasheet_name = channel->name;
> + chan->extend_name = channel->name;
> + chan->scan_index = scan_index;
> + chan->indexed = 1;
> + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
> + chan->scan_type.sign = 'u';
> + chan->scan_type.realbits = adc->common->data->highres;
> + chan->scan_type.storagebits = STM32_STORAGEBITS;
This is one of those cases where actually I'd argue just having the number
here and not under a define would be clearer! So just put 16 here.
> + chan->scan_type.shift = 0;
Should be unneeded. Shift of 0 is the obvious default so no info provided
to readers of the code either really.
> +}
> +
> +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
> + const struct stm32_adc_info *adc_info)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + struct device_node *node = indio_dev->dev.of_node;
> + struct property *prop;
> + const __be32 *cur;
> + struct iio_chan_spec *channels;
> + int scan_index = 0, num_channels = 0;
> + u32 val;
> +
> + of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
> + num_channels++;
> +
> + channels = devm_kcalloc(&indio_dev->dev, num_channels,
> + sizeof(struct iio_chan_spec), GFP_KERNEL);
> + if (!channels)
> + return -ENOMEM;
> +
> + of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
> + stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
> + &adc_info->channels[val],
> + scan_index);
> + scan_index++;
> + }
> +
> + adc->max_channels = adc_info->max_channels;
> + indio_dev->num_channels = scan_index;
> + indio_dev->channels = channels;
> +
> + return 0;
> +}
> +
> +static int stm32_adc_register(struct stm32_adc_common *common,
> + struct device_node *child)
> +{
> + struct iio_dev *indio_dev;
> + struct stm32_adc *adc;
> + int i, ret;
> + u32 reg;
> +
> + ret = of_property_read_u32(child, "reg", ®);
> + if (ret != 0) {
> + dev_err(common->dev, "missing reg property\n");
> + return -EINVAL;
> + }
> +
> + for (i = 0; common->data->adc_info[i].channels; i++)
> + if (common->data->adc_info[i].reg == reg)
> + break;
> +
> + if (i >= STM32_ADC_ID_MAX || !common->data->adc_info[i].channels) {
> + dev_err(common->dev, "bad adc reg offset\n");
> + return -ENOENT;
> + }
> +
> + indio_dev = devm_iio_device_alloc(common->dev, sizeof(*adc));
> + if (!indio_dev) {
> + dev_err(common->dev, "iio device allocation failed\n");
> + return -ENOMEM;
> + }
> +
> + adc = iio_priv(indio_dev);
> + adc->id = i;
> + adc->offset = reg;
> + adc->common = common;
> + INIT_LIST_HEAD(&adc->extrig_list);
> + spin_lock_init(&adc->lock);
> + init_completion(&adc->completion);
> +
> + if (child->name)
> + indio_dev->name = child->name;
> + else
> + indio_dev->name = common->data->adc_info[i].name;
> + indio_dev->dev.parent = common->dev;
> + indio_dev->dev.of_node = child;
> + indio_dev->info = &stm32_adc_iio_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + if (of_property_read_bool(child, "st,injected")) {
> + dev_dbg(common->dev, "%s Configured to use injected\n",
> + indio_dev->name);
> + adc->injected = true;
> + }
> +
> + adc->clk = of_clk_get(child, 0);
> + if (IS_ERR(adc->clk)) {
> + adc->clk = NULL;
> + dev_dbg(common->dev, "No child clk found\n");
> + } else {
> + ret = clk_prepare_enable(adc->clk);
> + if (ret < 0)
> + goto err_clk_put;
> + }
> +
> + ret = stm32_adc_chan_of_init(indio_dev, &common->data->adc_info[i]);
> + if (ret < 0) {
> + dev_err(common->dev, "iio channels init failed\n");
> + goto err_clk_disable;
> + }
> +
> + ret = stm32_adc_trig_register(indio_dev);
> + if (ret)
> + goto err_clk_disable;
> +
> + ret = iio_triggered_buffer_setup(indio_dev,
> + &iio_pollfunc_store_time,
> + &stm32_adc_trigger_handler,
> + &iio_triggered_buffer_setup_ops);
> + if (ret) {
> + dev_err(common->dev, "buffer setup failed\n");
> + goto err_trig_unregister;
> + }
> +
> + ret = iio_device_register(indio_dev);
> + if (ret) {
> + dev_err(common->dev, "iio dev register failed\n");
> + goto err_buffer_cleanup;
> + }
> +
> + list_add_tail(&adc->adc_list, &common->adc_list);
> +
> + return 0;
> +
> +err_buffer_cleanup:
> + iio_triggered_buffer_cleanup(indio_dev);
> +
> +err_trig_unregister:
> + stm32_adc_trig_unregister(indio_dev);
> +
> +err_clk_disable:
> + if (adc->clk)
> + clk_disable_unprepare(adc->clk);
> +
> +err_clk_put:
> + if (adc->clk)
> + clk_put(adc->clk);
> +
> + return ret;
> +}
> +
> +static void stm32_adc_unregister(struct stm32_adc *adc)
> +{
> + struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +
> + iio_device_unregister(indio_dev);
> + iio_triggered_buffer_cleanup(indio_dev);
> + stm32_adc_trig_unregister(indio_dev);
> + if (adc->clk) {
> + clk_disable_unprepare(adc->clk);
> + clk_put(adc->clk);
> + }
> +}
> +
> +int stm32_adc_probe(struct platform_device *pdev)
> +{
> + struct device_node *np = pdev->dev.of_node, *child;
> + struct device *dev = &pdev->dev;
> + const struct of_device_id *match;
> + struct stm32_adc_common *common;
> + struct stm32_adc *adc;
> + struct resource *res;
> + int ret;
> +
> + match = of_match_device(dev->driver->of_match_table, &pdev->dev);
> + if (!match || !match->data) {
> + dev_err(&pdev->dev, "compatible data not provided\n");
How would we have instantiated this if there was not a suitable match?
As such what does this check give us? (confused!)
> + return -EINVAL;
> + }
> +
> + common = devm_kzalloc(&pdev->dev, sizeof(*common), GFP_KERNEL);
> + if (!common)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + common->base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(common->base))
> + return PTR_ERR(common->base);
> +
> + common->data = match->data;
> + common->dev = &pdev->dev;
> + platform_set_drvdata(pdev, common);
> + mutex_init(&common->lock);
> + INIT_LIST_HEAD(&common->adc_list);
> +
> + common->vref = devm_regulator_get(&pdev->dev, "vref");
> + if (IS_ERR(common->vref)) {
> + ret = PTR_ERR(common->vref);
> + dev_err(&pdev->dev, "vref get failed, %d\n", ret);
> + return ret;
> + }
> +
> + ret = regulator_enable(common->vref);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "vref enable failed\n");
> + return ret;
> + }
> +
> + ret = regulator_get_voltage(common->vref);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
> + goto err_regulator_disable;
> + }
> + common->vref_mv = ret / 1000;
> + dev_dbg(&pdev->dev, "vref+=%dmV\n", common->vref_mv);
> +
> + common->aclk = devm_clk_get(&pdev->dev, "adc");
> + if (IS_ERR(common->aclk)) {
> + ret = PTR_ERR(common->aclk);
> + dev_err(&pdev->dev, "Can't get 'adc' clock\n");
> + goto err_regulator_disable;
> + }
> +
> + ret = clk_prepare_enable(common->aclk);
> + if (ret < 0) {
> + dev_err(common->dev, "adc clk enable failed\n");
> + goto err_regulator_disable;
> + }
> +
> + common->irq = platform_get_irq(pdev, 0);
> + if (common->irq < 0) {
> + dev_err(&pdev->dev, "failed to get irq\n");
> + ret = common->irq;
> + goto err_clk_disable;
> + }
> +
> + ret = devm_request_irq(&pdev->dev, common->irq, stm32_adc_common_isr,
> + 0, pdev->name, common);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to request irq\n");
> + goto err_clk_disable;
> + }
> +
> + /* Parse adc child nodes to retrieve master/slave instances data */
> + for_each_available_child_of_node(np, child) {
> + ret = stm32_adc_register(common, child);
> + if (ret)
> + goto err_unregister;
> + }
> +
> + dev_info(&pdev->dev, "registered\n");
No benefit in this info being provided (it's obvious, device just turned up
in sysfs :) So drop it.
> +
> + return 0;
> +
> +err_unregister:
> + list_for_each_entry(adc, &common->adc_list, adc_list)
> + stm32_adc_unregister(adc);
> +
> +err_clk_disable:
> + clk_disable_unprepare(common->aclk);
> +
> +err_regulator_disable:
> + regulator_disable(common->vref);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(stm32_adc_probe);
> +
> +int stm32_adc_remove(struct platform_device *pdev)
> +{
> + struct stm32_adc_common *common = platform_get_drvdata(pdev);
> + struct stm32_adc *adc;
> +
> + list_for_each_entry(adc, &common->adc_list, adc_list)
> + stm32_adc_unregister(adc);
> + clk_disable_unprepare(common->aclk);
> + regulator_disable(common->vref);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_adc_remove);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
> new file mode 100644
> index 0000000..0be603c
> --- /dev/null
> +++ b/drivers/iio/adc/stm32/stm32-adc.h
> @@ -0,0 +1,442 @@
> +/*
> + * This file is part of STM32 ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>.
> + *
> + * 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/>.
> + */
> +
> +#ifndef __STM32_ADC_H
> +#define __STM32_ADC_H
> +
> +/*
> + * STM32 - ADC global register map
> + * ________________________________________________________
> + * | Offset | Register |
> + * --------------------------------------------------------
> + * | 0x000 | Master ADC1 |
> + * --------------------------------------------------------
> + * | 0x100 | Slave ADC2 |
> + * --------------------------------------------------------
> + * | 0x200 | Slave ADC3 |
> + * --------------------------------------------------------
> + * | 0x300 | Master & Slave common regs |
> + * --------------------------------------------------------
> + */
> +#define STM32_ADCX_COMN_OFFSET 0x300
> +#define STM32_ADC_ID_MAX 3
> +#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
> +#define STM32_ADC_MAX_JSQ 4 /* JSQ1..JSQ4 */
> +
> +/* STM32 value masks */
> +#define STM32_RESULT_MASK GENMASK(15, 0)
> +#define STM32_STORAGEBITS 16
> +
> +/* External trigger enable for regular or injected channels (exten/jexten) */
> +enum stm32_adc_exten {
> + STM32_EXTEN_SWTRIG,
> + STM32_EXTEN_HWTRIG_RISING_EDGE,
> + STM32_EXTEN_HWTRIG_FALLING_EDGE,
> + STM32_EXTEN_HWTRIG_BOTH_EDGES,
> +};
> +
> +enum stm32_adc_extsel {
> + STM32_EXT0,
> + STM32_EXT1,
> + STM32_EXT2,
> + STM32_EXT3,
> + STM32_EXT4,
> + STM32_EXT5,
> + STM32_EXT6,
> + STM32_EXT7,
> + STM32_EXT8,
> + STM32_EXT9,
> + STM32_EXT10,
> + STM32_EXT11,
> + STM32_EXT12,
> + STM32_EXT13,
> + STM32_EXT14,
> + STM32_EXT15,
> + STM32_EXT16,
> + STM32_EXT17,
> + STM32_EXT18,
> + STM32_EXT19,
> + STM32_EXT20,
> + STM32_EXT21,
> + STM32_EXT22,
> + STM32_EXT23,
> + STM32_EXT24,
> + STM32_EXT25,
> + STM32_EXT26,
> + STM32_EXT27,
> + STM32_EXT28,
> + STM32_EXT29,
> + STM32_EXT30,
> + STM32_EXT31,
> +};
> +
> +enum stm32_adc_jextsel {
> + STM32_JEXT0,
> + STM32_JEXT1,
> + STM32_JEXT2,
> + STM32_JEXT3,
> + STM32_JEXT4,
> + STM32_JEXT5,
> + STM32_JEXT6,
> + STM32_JEXT7,
> + STM32_JEXT8,
> + STM32_JEXT9,
> + STM32_JEXT10,
> + STM32_JEXT11,
> + STM32_JEXT12,
> + STM32_JEXT13,
> + STM32_JEXT14,
> + STM32_JEXT15,
> + STM32_JEXT16,
> + STM32_JEXT17,
> + STM32_JEXT18,
> + STM32_JEXT19,
> + STM32_JEXT20,
> + STM32_JEXT21,
> + STM32_JEXT22,
> + STM32_JEXT23,
> + STM32_JEXT24,
> + STM32_JEXT25,
> + STM32_JEXT26,
> + STM32_JEXT27,
> + STM32_JEXT28,
> + STM32_JEXT29,
> + STM32_JEXT30,
> + STM32_JEXT31,
> +};
> +
> +#define STM32_ADC_TIMEOUT_US 100000
> +#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
> +
> +/**
> + * struct stm32_adc_chan_spec - specification of stm32 adc channel
> + * @type: IIO channel type
> + * @channel: channel number (single ended)
> + * @name: channel name (single ended)
> + */
> +struct stm32_adc_chan_spec {
> + enum iio_chan_type type;
> + int channel;
> + const char *name;
> +};
> +
> +/**
> + * struct stm32_adc_trig_info - ADC trigger info
> + * @extsel: trigger selection for regular or injected
> + * @name: name of the trigger, corresponding to its source
> + */
> +struct stm32_adc_trig_info {
> + u32 extsel;
> + const char *name;
> +};
> +
> +/**
> + * struct stm32_adc_info - stm32 ADC, per instance config data
> + * @name: default name for this instance (like "adc1")
> + * @reg: reg offset for this instance (e.g. 0x0 for adc1...)
> + * @channels: Reference to stm32 channels spec
> + * @max_channels: Number of single ended channels
> + */
> +struct stm32_adc_info {
> + const char *name;
> + u32 reg;
> + const struct stm32_adc_chan_spec *channels;
> + int max_channels;
> +};
> +
> +/**
> + * stm32_adc_regs - stm32 ADC misc registers & bitfield desc
> + * @reg: register offset
> + * @mask: bitfield mask
> + * @shift: left shift
> + */
> +struct stm32_adc_regs {
> + int reg;
> + int mask;
> + int shift;
> +};
> +
> +/**
> + * stm32_adc_trig_reginfo - stm32 ADC trigger control registers description
> + * @reg: trigger control register offset (exten/jexten)
> + * @exten_mask: external trigger en/polarity mask in @reg
> + * @exten_shift: external trigger en/polarity shift in @reg
> + * @extsel_mask: external trigger source mask in @reg
> + * @extsel_shift: external trigger source shift in @reg
> + */
> +struct stm32_adc_trig_reginfo {
> + u32 reg;
> + u32 exten_mask;
> + u32 exten_shift;
> + u32 extsel_mask;
> + u32 extsel_shift;
> +};
> +
> +/**
> + * struct stm32_adc_reginfo - stm32 ADC registers description
> + * @isr: interrupt status register offset
> + * @eoc: end of conversion mask in @isr
> + * @jeoc: end of injected conversion sequence mask in @isr
> + * @ier: interrupt enable register offset
> + * @eocie: end of conversion interrupt enable mask in @ier
> + * @jeocie: end of injected conversion sequence interrupt en mask
> + * @dr: data register offset
> + * @jdr: injected data registers offsets
> + * @sqr_regs: Regular sequence registers description
> + * @jsqr_reg: Injected sequence register description
> + * @trig_reginfo: regular trigger control registers description
> + * @jtrig_reginfo: injected trigger control registers description
> + */
> +struct stm32_adc_reginfo {
> + u32 isr;
> + u32 eoc;
> + u32 jeoc;
> + u32 ier;
> + u32 eocie;
> + u32 jeocie;
> + u32 dr;
> + u32 jdr[4];
> + const struct stm32_adc_regs *sqr_regs;
> + const struct stm32_adc_regs *jsqr_reg;
> + const struct stm32_adc_trig_reginfo *trig_reginfo;
> + const struct stm32_adc_trig_reginfo *jtrig_reginfo;
> +};
> +
> +struct stm32_adc;
> +
> +/**
> + * struct stm32_adc_ops - stm32 ADC, compatible dependent data
> + * - stm32 ADC may work as single ADC, or as tightly coupled master/slave ADCs.
> + *
> + * @adc_info: Array spec for stm32 adc master/slaves instances
> + * @ext_triggers: Reference to trigger info for regular channels
> + * @jext_triggers: Reference to trigger info for injected channels
> + * @adc_reginfo: stm32 ADC registers description
> + * @highres: Max resolution
> + * @max_clock_rate: Max input clock rate
> + * @clk_sel: routine to select common clock and prescaler
> + * @start_conv: routine to start conversions
> + * @stop_conv: routine to stop conversions
> + * @is_started: routine to get adc 'started' state
> + * @regular_started routine to check regular conversions status
> + * @injected_started routine to check injected conversions status
> + * @enable: optional routine to enable stm32 adc
> + * @disable: optional routine to disable stm32 adc
> + * @is_enabled reports enabled state
> + */
This is a big chunk of abstraction that seems excessive at the moment.
I'd rather see it introduced only just before it's actually used..
(I'm guessing it's intended for support of similar parts?)
Right now it just makes the driver harder to review.
> +struct stm32_adc_ops {
> + const struct stm32_adc_info *adc_info;
> + const struct stm32_adc_trig_info *ext_triggers;
> + const struct stm32_adc_trig_info *jext_triggers;
> + const struct stm32_adc_reginfo *adc_reginfo;
> + int highres;
> + unsigned long max_clock_rate;
> + int (*clk_sel)(struct stm32_adc *adc);
> + int (*start_conv)(struct stm32_adc *adc);
> + int (*stop_conv)(struct stm32_adc *adc);
> + bool (*is_started)(struct stm32_adc *adc);
> + bool (*regular_started)(struct stm32_adc *adc);
> + bool (*injected_started)(struct stm32_adc *adc);
> + int (*enable)(struct stm32_adc *adc);
> + void (*disable)(struct stm32_adc *adc);
> + bool (*is_enabled)(struct stm32_adc *adc);
> +};
> +
> +struct stm32_adc_common;
> +
> +/**
> + * struct stm32_adc - private data of each ADC IIO instance
> + * @common: reference to ADC block common data
> + * @adc_list: current ADC entry in common ADC list
> + * @id: ADC instance number (e.g. adc 1, 2 or 3)
> + * @offset: ADC instance register offset in ADC block
> + * @max_channels: Max channels number for this ADC.
> + * @extrig_list: External trigger list (for regular channel)
> + * @completion: end of single conversion completion
> + * @buffer: data buffer
> + * @bufi: data buffer index
> + * @num_conv: expected number of scan conversions
> + * @injected: use injected channels on this adc
> + * @lock: spinlock
> + * @clk: optional adc clock, for this adc instance
> + * @calib: optional calibration data
> + * @en: emulates enabled state on some stm32 adc
> + */
> +struct stm32_adc {
> + struct stm32_adc_common *common;
> + struct list_head adc_list;
> + int id;
> + int offset;
> + int max_channels;
> + struct list_head extrig_list;
> + struct completion completion;
> + u16 *buffer;
> + int bufi;
> + int num_conv;
> + bool injected;
> + spinlock_t lock; /* interrupt lock */
> + struct clk *clk;
> + void *calib;
> + bool en;
> +};
> +
> +/**
> + * struct stm32_adc_common - private data of ADC driver, common to all
> + * ADC instances (ADC block)
> + * @dev: device for this controller
> + * @base: control registers base cpu addr
> + * @irq: Common irq line for all adc instances
> + * @data: STM32 dependent data from compatible
> + * @adc_list: list of all stm32 ADC in this ADC block
> + * @aclk: common clock for the analog circuitry
> + * @vref: regulator reference
> + * @vref_mv: vref voltage (mv)
> + * @lock: mutex
> + */
> +struct stm32_adc_common {
> + struct device *dev;
> + void __iomem *base;
> + int irq;
> + const struct stm32_adc_ops *data;
> + struct list_head adc_list;
> + struct clk *aclk;
> + struct regulator *vref;
> + int vref_mv;
> + struct mutex lock; /* read_raw lock */
> +};
> +
> +/* Helper routines */
> +static inline int stm32_adc_start_conv(struct stm32_adc *adc)
> +{
> + return adc->common->data->start_conv(adc);
> +}
> +
> +static inline int stm32_adc_stop_conv(struct stm32_adc *adc)
> +{
> + return adc->common->data->stop_conv(adc);
> +}
> +
> +static inline bool stm32_adc_is_started(struct stm32_adc *adc)
> +{
> + return adc->common->data->is_started(adc);
> +}
> +
> +static inline bool stm32_adc_regular_started(struct stm32_adc *adc)
> +{
> + return adc->common->data->regular_started(adc);
> +}
> +
> +static inline bool stm32_adc_injected_started(struct stm32_adc *adc)
> +{
> + return adc->common->data->injected_started(adc);
> +}
> +
> +static inline bool stm32_adc_clk_sel(struct stm32_adc *adc)
> +{
> + return adc->common->data->clk_sel(adc);
> +}
> +
> +static inline int stm32_adc_enable(struct stm32_adc *adc)
> +{
> + if (adc->common->data->enable)
> + return adc->common->data->enable(adc);
> +
> + adc->en = true;
> +
> + return 0;
> +}
> +
> +static inline bool stm32_adc_is_enabled(struct stm32_adc *adc)
> +{
> + if (adc->common->data->is_enabled)
> + return adc->common->data->is_enabled(adc);
> + else
> + return adc->en;
> +}
> +
> +static inline void stm32_adc_disable(struct stm32_adc *adc)
> +{
> + /* Check there is no regular or injected on-going conversions */
> + if (stm32_adc_is_started(adc))
> + return;
> +
> + if (adc->common->data->disable)
> + adc->common->data->disable(adc);
> + else
> + adc->en = false;
> +}
> +
> +/* STM32 ADC registers access routines */
> +static inline u32 stm32_adc_common_readl(struct stm32_adc_common *com, u32 reg)
> +{
> + u32 val = readl_relaxed(com->base + reg);
> +
> + return val;
> +}
> +
> +static inline void stm32_adc_common_writel(struct stm32_adc_common *com,
> + u32 reg, u32 val)
> +{
> + writel_relaxed(val, com->base + reg);
> +}
> +
> +static inline u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
> +{
> + u32 val = readl_relaxed(adc->common->base + adc->offset + reg);
> +
> + return val;
> +}
> +
> +#define stm32_adc_readl_addr(addr) stm32_adc_readl(adc, addr)
> +
> +#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
> + readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
> + cond, sleep_us, timeout_us)
> +
> +static inline void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
> +{
> + writel_relaxed(val, adc->common->base + adc->offset + reg);
> +}
> +
> +static inline void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&adc->lock, flags);
> + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
> + spin_unlock_irqrestore(&adc->lock, flags);
> +}
> +
> +static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&adc->lock, flags);
> + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
> + spin_unlock_irqrestore(&adc->lock, flags);
> +}
> +
> +/* STM32 common extended attributes */
> +extern const struct iio_enum stm32_adc_trig_pol;
> +int stm32_adc_probe(struct platform_device *pdev);
> +int stm32_adc_remove(struct platform_device *pdev);
> +
> +#endif
> diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
> new file mode 100644
> index 0000000..147fe9c
> --- /dev/null
> +++ b/drivers/iio/adc/stm32/stm32f4-adc.c
> @@ -0,0 +1,574 @@
> +/*
> + * This file is part of STM32F4 ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>.
> + *
> + * 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/clk.h>
> +#include <linux/delay.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/platform_device.h>
> +#include "stm32-adc.h"
> +
> +/*
> + * STM32F4 - ADC global register map
> + * ________________________________________________________
> + * | Offset | Register |
> + * --------------------------------------------------------
> + * | 0x000 | Master ADC1 |
> + * --------------------------------------------------------
> + * | 0x100 | Slave ADC2 |
> + * --------------------------------------------------------
> + * | 0x200 | Slave ADC3 |
> + * --------------------------------------------------------
> + * | 0x300 | Master & Slave common regs |
> + * --------------------------------------------------------
> + */
> +
> +/* STM32F4 - Registers for each ADC instance */
> +#define STM32F4_ADCX_SR 0x00
> +#define STM32F4_ADCX_CR1 0x04
> +#define STM32F4_ADCX_CR2 0x08
> +#define STM32F4_ADCX_SMPR1 0x0C
> +#define STM32F4_ADCX_SMPR2 0x10
> +#define STM32F4_ADCX_HTR 0x24
> +#define STM32F4_ADCX_LTR 0x28
> +#define STM32F4_ADCX_SQR1 0x2C
> +#define STM32F4_ADCX_SQR2 0x30
> +#define STM32F4_ADCX_SQR3 0x34
> +#define STM32F4_ADCX_JSQR 0x38
> +#define STM32F4_ADCX_JDR1 0x3C
> +#define STM32F4_ADCX_JDR2 0x40
> +#define STM32F4_ADCX_JDR3 0x44
> +#define STM32F4_ADCX_JDR4 0x48
> +#define STM32F4_ADCX_DR 0x4C
> +
> +/* STM32 - Master & slave registers (common for all instances: 1, 2 & 3) */
> +#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
> +#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
> +#define STM32F4_ADC_CDR (STM32_ADCX_COMN_OFFSET + 0x08)
> +
> +/* STM32F4_ADCX_SR - bit fields */
> +#define STM32F4_OVR BIT(5)
> +#define STM32F4_STRT BIT(4)
> +#define STM32F4_JSTRT BIT(3)
> +#define STM32F4_JEOC BIT(2)
> +#define STM32F4_EOC BIT(1)
> +#define STM32F4_AWD BIT(0)
> +
> +/* STM32F4_ADCX_CR1 - bit fields */
> +#define STM32F4_OVRIE BIT(26)
> +#define STM32F4_RES_SHIFT 24
> +#define STM32F4_RES_MASK GENMASK(25, 24)
> +#define STM32F4_AWDEN BIT(23)
> +#define STM32F4_JAWDEN BIT(22)
> +#define STM32F4_DISCNUM_SHIFT 13
> +#define STM32F4_DISCNUM_MASK GENMASK(15, 13)
> +#define STM32F4_JDISCEN BIT(12)
> +#define STM32F4_DISCEN BIT(11)
> +#define STM32F4_JAUTO BIT(10)
> +#define STM32F4_AWDSGL BIT(9)
> +#define STM32F4_SCAN BIT(8)
> +#define STM32F4_JEOCIE BIT(7)
> +#define STM32F4_AWDIE BIT(6)
> +#define STM32F4_EOCIE BIT(5)
> +#define STM32F4_AWDCH_SHIFT 0
> +#define STM32F4_AWDCH_MASK GENMASK(4, 0)
> +
> +/* STM32F4_ADCX_CR2 - bit fields */
> +#define STM32F4_SWSTART BIT(30)
> +#define STM32F4_EXTEN_SHIFT 28
> +#define STM32F4_EXTEN_MASK GENMASK(29, 28)
> +#define STM32F4_EXTSEL_SHIFT 24
> +#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
> +#define STM32F4_JSWSTART BIT(22)
> +#define STM32F4_JEXTEN_SHIFT 20
> +#define STM32F4_JEXTEN_MASK GENMASK(21, 20)
> +#define STM32F4_JEXTSEL_SHIFT 16
> +#define STM32F4_JEXTSEL_MASK GENMASK(19, 16)
> +#define STM32F4_ALIGN BIT(11)
> +#define STM32F4_EOCS BIT(10)
> +#define STM32F4_DDS BIT(9)
> +#define STM32F4_DMA BIT(8)
> +#define STM32F4_CONT BIT(1)
> +#define STM32F4_ADON BIT(0)
> +
> +/* STM32F4_ADCX_SMPR1 - bit fields */
> +#define STM32F4_SMP18_SHIFT 24
> +#define STM32F4_SMP18_MASK GENMASK(26, 24)
> +#define STM32F4_SMP17_SHIFT 21
> +#define STM32F4_SMP17_MASK GENMASK(23, 21)
> +#define STM32F4_SMP16_SHIFT 18
> +#define STM32F4_SMP16_MASK GENMASK(20, 18)
> +#define STM32F4_SMP15_SHIFT 15
> +#define STM32F4_SMP15_MASK GENMASK(17, 15)
> +#define STM32F4_SMP14_SHIFT 12
> +#define STM32F4_SMP14_MASK GENMASK(14, 12)
> +#define STM32F4_SMP13_SHIFT 9
> +#define STM32F4_SMP13_MASK GENMASK(11, 9)
> +#define STM32F4_SMP12_SHIFT 6
> +#define STM32F4_SMP12_MASK GENMASK(8, 6)
> +#define STM32F4_SMP11_SHIFT 3
> +#define STM32F4_SMP11_MASK GENMASK(5, 3)
> +#define STM32F4_SMP10_SHIFT 0
> +#define STM32F4_SMP10_MASK GENMASK(2, 0)
> +
> +/* STM32F4_ADCX_SMPR2 - bit fields */
> +#define STM32F4_SMP9_SHIFT 27
> +#define STM32F4_SMP9_MASK GENMASK(29, 27)
> +#define STM32F4_SMP8_SHIFT 24
> +#define STM32F4_SMP8_MASK GENMASK(26, 24)
> +#define STM32F4_SMP7_SHIFT 21
> +#define STM32F4_SMP7_MASK GENMASK(23, 21)
> +#define STM32F4_SMP6_SHIFT 18
> +#define STM32F4_SMP6_MASK GENMASK(20, 18)
> +#define STM32F4_SMP5_SHIFT 15
> +#define STM32F4_SMP5_MASK GENMASK(17, 15)
> +#define STM32F4_SMP4_SHIFT 12
> +#define STM32F4_SMP4_MASK GENMASK(14, 12)
> +#define STM32F4_SMP3_SHIFT 9
> +#define STM32F4_SMP3_MASK GENMASK(11, 9)
> +#define STM32F4_SMP2_SHIFT 6
> +#define STM32F4_SMP2_MASK GENMASK(8, 6)
> +#define STM32F4_SMP1_SHIFT 3
> +#define STM32F4_SMP1_MASK GENMASK(5, 3)
> +#define STM32F4_SMP0_SHIFT 0
> +#define STM32F4_SMP0_MASK GENMASK(2, 0)
> +enum stm32f4_adc_smpr {
> + STM32F4_SMPR_3_CK_CYCLES,
> + STM32F4_SMPR_15_CK_CYCLES,
> + STM32F4_SMPR_28_CK_CYCLES,
> + STM32F4_SMPR_56_CK_CYCLES,
> + STM32F4_SMPR_84_CK_CYCLES,
> + STM32F4_SMPR_112_CK_CYCLES,
> + STM32F4_SMPR_144_CK_CYCLES,
> + STM32F4_SMPR_480_CK_CYCLES,
> +};
> +
> +/* STM32F4_ADCX_SQR1 - bit fields */
> +#define STM32F4_L_SHIFT 20
> +#define STM32F4_L_MASK GENMASK(23, 20)
> +#define STM32F4_SQ16_SHIFT 15
> +#define STM32F4_SQ16_MASK GENMASK(19, 15)
> +#define STM32F4_SQ15_SHIFT 10
> +#define STM32F4_SQ15_MASK GENMASK(14, 10)
> +#define STM32F4_SQ14_SHIFT 5
> +#define STM32F4_SQ14_MASK GENMASK(9, 5)
> +#define STM32F4_SQ13_SHIFT 0
> +#define STM32F4_SQ13_MASK GENMASK(4, 0)
> +
> +/* STM32F4_ADCX_SQR2 - bit fields */
> +#define STM32F4_SQ12_SHIFT 25
> +#define STM32F4_SQ12_MASK GENMASK(29, 25)
> +#define STM32F4_SQ11_SHIFT 20
> +#define STM32F4_SQ11_MASK GENMASK(24, 20)
> +#define STM32F4_SQ10_SHIFT 15
> +#define STM32F4_SQ10_MASK GENMASK(19, 15)
> +#define STM32F4_SQ9_SHIFT 10
> +#define STM32F4_SQ9_MASK GENMASK(14, 10)
> +#define STM32F4_SQ8_SHIFT 5
> +#define STM32F4_SQ8_MASK GENMASK(9, 5)
> +#define STM32F4_SQ7_SHIFT 0
> +#define STM32F4_SQ7_MASK GENMASK(4, 0)
> +
> +/* STM32F4_ADCX_SQR3 - bit fields */
> +#define STM32F4_SQ6_SHIFT 25
> +#define STM32F4_SQ6_MASK GENMASK(29, 25)
> +#define STM32F4_SQ5_SHIFT 20
> +#define STM32F4_SQ5_MASK GENMASK(24, 20)
> +#define STM32F4_SQ4_SHIFT 15
> +#define STM32F4_SQ4_MASK GENMASK(19, 15)
> +#define STM32F4_SQ3_SHIFT 10
> +#define STM32F4_SQ3_MASK GENMASK(14, 10)
> +#define STM32F4_SQ2_SHIFT 5
> +#define STM32F4_SQ2_MASK GENMASK(9, 5)
> +#define STM32F4_SQ1_SHIFT 0
> +#define STM32F4_SQ1_MASK GENMASK(4, 0)
> +
> +/* STM32F4_ADCX_JSQR - bit fields */
> +#define STM32F4_JL_SHIFT 20
> +#define STM32F4_JL_MASK GENMASK(21, 20)
> +#define STM32F4_JSQ4_SHIFT 15
> +#define STM32F4_JSQ4_MASK GENMASK(19, 15)
> +#define STM32F4_JSQ3_SHIFT 10
> +#define STM32F4_JSQ3_MASK GENMASK(14, 10)
> +#define STM32F4_JSQ2_SHIFT 5
> +#define STM32F4_JSQ2_MASK GENMASK(9, 5)
> +#define STM32F4_JSQ1_SHIFT 0
> +#define STM32F4_JSQ1_MASK GENMASK(4, 0)
> +
> +/* STM32F4_ADC_CCR - bit fields */
> +#define STM32F4_ADC_ADCPRE_SHIFT 16
> +#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
> +
> +/*
> + * stm32 ADC1, ADC2 & ADC3 are tightly coupled and may be used in multi mode
> + * Define here all inputs for all ADC instances
> + */
> +static const struct stm32_adc_chan_spec stm32f4_adc1_channels[] = {
> + /* master ADC1 */
> + { IIO_VOLTAGE, 0, "in0" },
> + { IIO_VOLTAGE, 1, "in1" },
> + { IIO_VOLTAGE, 2, "in2" },
> + { IIO_VOLTAGE, 3, "in3" },
> + { IIO_VOLTAGE, 4, "in4" },
> + { IIO_VOLTAGE, 5, "in5" },
> + { IIO_VOLTAGE, 6, "in6" },
> + { IIO_VOLTAGE, 7, "in7" },
> + { IIO_VOLTAGE, 8, "in8" },
> + { IIO_VOLTAGE, 9, "in9" },
> + { IIO_VOLTAGE, 10, "in10" },
> + { IIO_VOLTAGE, 11, "in11" },
> + { IIO_VOLTAGE, 12, "in12" },
> + { IIO_VOLTAGE, 13, "in13" },
> + { IIO_VOLTAGE, 14, "in14" },
> + { IIO_VOLTAGE, 15, "in15" },
> + /* internal analog sources available on input 16 to 18 */
> + { IIO_VOLTAGE, 16, "in16" },
> + { IIO_VOLTAGE, 17, "in17" },
> + { IIO_VOLTAGE, 18, "in18" },
> +};
> +
> +static const struct stm32_adc_chan_spec stm32f4_adc23_channels[] = {
> + /* slave ADC2 / ADC3 */
> + { IIO_VOLTAGE, 0, "in0" },
> + { IIO_VOLTAGE, 1, "in1" },
> + { IIO_VOLTAGE, 2, "in2" },
> + { IIO_VOLTAGE, 3, "in3" },
> + { IIO_VOLTAGE, 4, "in4" },
> + { IIO_VOLTAGE, 5, "in5" },
> + { IIO_VOLTAGE, 6, "in6" },
> + { IIO_VOLTAGE, 7, "in7" },
> + { IIO_VOLTAGE, 8, "in8" },
> + { IIO_VOLTAGE, 9, "in9" },
> + { IIO_VOLTAGE, 10, "in10" },
> + { IIO_VOLTAGE, 11, "in11" },
> + { IIO_VOLTAGE, 12, "in12" },
> + { IIO_VOLTAGE, 13, "in13" },
> + { IIO_VOLTAGE, 14, "in14" },
> + { IIO_VOLTAGE, 15, "in15" },
> +};
> +
> +/* Triggers for regular channels */
> +static const struct stm32_adc_trig_info stm32f4_adc_ext_triggers[] = {
> + { STM32_EXT0, "TIM1_CH1" },
> + { STM32_EXT1, "TIM1_CH2" },
> + { STM32_EXT2, "TIM1_CH3" },
> + { STM32_EXT3, "TIM2_CH2" },
> + { STM32_EXT4, "TIM2_CH3" },
> + { STM32_EXT5, "TIM2_CH4" },
> + { STM32_EXT6, "TIM2_TRGO" },
> + { STM32_EXT7, "TIM3_CH1" },
> + { STM32_EXT8, "TIM3_TRGO" },
> + { STM32_EXT9, "TIM4_CH4" },
> + { STM32_EXT10, "TIM5_CH1" },
> + { STM32_EXT11, "TIM5_CH2" },
> + { STM32_EXT12, "TIM5_CH3" },
> + { STM32_EXT13, "TIM8_CH1" },
> + { STM32_EXT14, "TIM8_TRGO" },
> + { STM32_EXT15, "EXTI_11" },
> + {},
> +};
> +
> +/* Triggers for injected channels */
> +static const struct stm32_adc_trig_info stm32f4_adc_jext_triggers[] = {
> + { STM32_JEXT0, "TIM1_CH4" },
> + { STM32_JEXT1, "TIM1_TRGO" },
> + { STM32_JEXT2, "TIM2_CH1" },
> + { STM32_JEXT3, "TIM2_TRGO" },
> + { STM32_JEXT4, "TIM3_CH2" },
> + { STM32_JEXT5, "TIM3_CH4" },
> + { STM32_JEXT6, "TIM4_CH1" },
> + { STM32_JEXT7, "TIM4_CH2" },
> + { STM32_JEXT8, "TIM4_CH3" },
> + { STM32_JEXT9, "TIM4_TRGO" },
> + { STM32_JEXT10, "TIM5_CH4" },
> + { STM32_JEXT11, "TIM5_TRGO" },
> + { STM32_JEXT12, "TIM8_CH2" },
> + { STM32_JEXT13, "TIM8_CH3" },
> + { STM32_JEXT14, "TIM8_CH4" },
> + { STM32_JEXT15, "EXTI_15" },
> + {},
> +};
> +
> +static const struct stm32_adc_info stm32f4_adc_info[] = {
> + {
> + .name = "adc1-master",
> + .reg = 0x0,
> + .channels = stm32f4_adc1_channels,
> + .max_channels = ARRAY_SIZE(stm32f4_adc1_channels),
> + },
> + {
> + .name = "adc2-slave",
> + .reg = 0x100,
> + .channels = stm32f4_adc23_channels,
> + .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
> + },
> + {
> + .name = "adc3-slave",
> + .reg = 0x200,
> + .channels = stm32f4_adc23_channels,
> + .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
> + },
> + {},
> +};
> +
> +/**
> + * stm32f4_sqr_regs - describe regular sequence registers
> + * - L: sequence len (register & bit field)
> + * - SQ1..SQ16: sequence entries (register & bit field)
> + */
> +static const struct stm32_adc_regs stm32f4_sqr_regs[STM32_ADC_MAX_SQ + 1] = {
> + /* L: len bit field description to be kept as first element */
> + { STM32F4_ADCX_SQR1, STM32F4_L_MASK, STM32F4_L_SHIFT },
> + /* SQ1..SQ16 registers & bit fields */
> + { STM32F4_ADCX_SQR3, STM32F4_SQ1_MASK, STM32F4_SQ1_SHIFT },
> + { STM32F4_ADCX_SQR3, STM32F4_SQ2_MASK, STM32F4_SQ2_SHIFT },
> + { STM32F4_ADCX_SQR3, STM32F4_SQ3_MASK, STM32F4_SQ3_SHIFT },
> + { STM32F4_ADCX_SQR3, STM32F4_SQ4_MASK, STM32F4_SQ4_SHIFT },
> + { STM32F4_ADCX_SQR3, STM32F4_SQ5_MASK, STM32F4_SQ5_SHIFT },
> + { STM32F4_ADCX_SQR3, STM32F4_SQ6_MASK, STM32F4_SQ6_SHIFT },
> + { STM32F4_ADCX_SQR2, STM32F4_SQ7_MASK, STM32F4_SQ7_SHIFT },
> + { STM32F4_ADCX_SQR2, STM32F4_SQ8_MASK, STM32F4_SQ8_SHIFT },
> + { STM32F4_ADCX_SQR2, STM32F4_SQ9_MASK, STM32F4_SQ9_SHIFT },
> + { STM32F4_ADCX_SQR2, STM32F4_SQ10_MASK, STM32F4_SQ10_SHIFT },
> + { STM32F4_ADCX_SQR2, STM32F4_SQ11_MASK, STM32F4_SQ11_SHIFT },
> + { STM32F4_ADCX_SQR2, STM32F4_SQ12_MASK, STM32F4_SQ12_SHIFT },
> + { STM32F4_ADCX_SQR1, STM32F4_SQ13_MASK, STM32F4_SQ13_SHIFT },
> + { STM32F4_ADCX_SQR1, STM32F4_SQ14_MASK, STM32F4_SQ14_SHIFT },
> + { STM32F4_ADCX_SQR1, STM32F4_SQ15_MASK, STM32F4_SQ15_SHIFT },
> + { STM32F4_ADCX_SQR1, STM32F4_SQ16_MASK, STM32F4_SQ16_SHIFT },
> +};
> +
> +/**
> + * stm32f4_jsqr_reg - describe injected sequence register:
> + * - JL: injected sequence len
> + * - JSQ4..SQ1: sequence entries
> + * When JL == 3, ADC converts JSQ1, JSQ2, JSQ3, JSQ4
> + * When JL == 2, ADC converts JSQ2, JSQ3, JSQ4
> + * When JL == 1, ADC converts JSQ3, JSQ4
> + * When JL == 0, ADC converts JSQ4
> + */
> +static const struct stm32_adc_regs stm32f4_jsqr_reg[STM32_ADC_MAX_JSQ + 1] = {
> + /* JL: len bit field description to be kept as first element */
> + {STM32F4_ADCX_JSQR, STM32F4_JL_MASK, STM32F4_JL_SHIFT},
> + /* JSQ4..JSQ1 registers & bit fields */
> + {STM32F4_ADCX_JSQR, STM32F4_JSQ4_MASK, STM32F4_JSQ4_SHIFT},
> + {STM32F4_ADCX_JSQR, STM32F4_JSQ3_MASK, STM32F4_JSQ3_SHIFT},
> + {STM32F4_ADCX_JSQR, STM32F4_JSQ2_MASK, STM32F4_JSQ2_SHIFT},
> + {STM32F4_ADCX_JSQR, STM32F4_JSQ1_MASK, STM32F4_JSQ1_SHIFT},
> +};
> +
> +static const struct stm32_adc_trig_reginfo stm32f4_adc_trig_reginfo = {
> + .reg = STM32F4_ADCX_CR2,
> + .exten_mask = STM32F4_EXTEN_MASK,
> + .exten_shift = STM32F4_EXTEN_SHIFT,
> + .extsel_mask = STM32F4_EXTSEL_MASK,
> + .extsel_shift = STM32F4_EXTSEL_SHIFT,
> +};
> +
> +static const struct stm32_adc_trig_reginfo stm32f4_adc_jtrig_reginfo = {
> + .reg = STM32F4_ADCX_CR2,
> + .exten_mask = STM32F4_JEXTEN_MASK,
> + .exten_shift = STM32F4_JEXTEN_SHIFT,
> + .extsel_mask = STM32F4_JEXTSEL_MASK,
> + .extsel_shift = STM32F4_JEXTSEL_SHIFT,
> +};
> +
> +static const struct stm32_adc_reginfo stm32f4_adc_reginfo = {
> + .isr = STM32F4_ADCX_SR,
> + .eoc = STM32F4_EOC,
> + .jeoc = STM32F4_JEOC,
> + .ier = STM32F4_ADCX_CR1,
> + .eocie = STM32F4_EOCIE,
> + .jeocie = STM32F4_JEOCIE,
> + .dr = STM32F4_ADCX_DR,
> + .jdr = {
> + STM32F4_ADCX_JDR1,
> + STM32F4_ADCX_JDR2,
> + STM32F4_ADCX_JDR3,
> + STM32F4_ADCX_JDR4,
> + },
> + .sqr_regs = stm32f4_sqr_regs,
> + .jsqr_reg = stm32f4_jsqr_reg,
> + .trig_reginfo = &stm32f4_adc_trig_reginfo,
> + .jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
> +};
> +
> +static bool stm32f4_adc_is_started(struct stm32_adc *adc)
> +{
> + u32 val = stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_ADON;
> +
> + return !!val;
> +}
> +
> +static bool stm32f4_adc_regular_started(struct stm32_adc *adc)
> +{
> + u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_STRT;
> +
> + return !!val;
> +}
> +
> +static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
> +{
> + u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_JSTRT;
> +
> + return !!val;
> +}
> +
> +/**
> + * stm32f4_adc_start_conv() - Start regular or injected conversions
> + * @adc: stm32 adc instance
> + *
> + * Start single conversions for regular or injected channels.
> + */
> +static int stm32f4_adc_start_conv(struct stm32_adc *adc)
> +{
> + u32 trig_msk, start_msk;
> +
> + dev_dbg(adc->common->dev, "%s %s\n", __func__,
> + adc->injected ? "injected" : "regular");
> +
> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
> +
> + if (!stm32f4_adc_is_started(adc)) {
> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
> + STM32F4_EOCS | STM32F4_ADON);
> +
> + /* Wait for Power-up time (tSTAB from datasheet) */
> + usleep_range(2, 3);
> + }
> +
> + if (adc->injected) {
> + trig_msk = STM32F4_JEXTEN_MASK;
> + start_msk = STM32F4_JSWSTART;
> + } else {
> + trig_msk = STM32F4_EXTEN_MASK;
> + start_msk = STM32F4_SWSTART;
> + }
> +
> + /* Software start ? (e.g. trigger detection disabled ?) */
> + if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & trig_msk))
> + stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, start_msk);
> +
> + return 0;
> +}
> +
> +static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
> +{
> + u32 val;
> +
> + dev_dbg(adc->common->dev, "%s %s\n", __func__,
> + adc->injected ? "injected" : "regular");
> +
> + /* First disable trigger for either regular or injected channels */
> + if (adc->injected) {
> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_JEXTEN_MASK);
> + stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_JSTRT);
> + } else {
> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
> + stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
> + }
> +
> + /* Disable adc when all triggered conversion have been disabled */
> + val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
> + val &= STM32F4_EXTEN_MASK | STM32F4_JEXTEN_MASK;
> + if (!val) {
> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
> + }
> +
> + return 0;
> +}
> +
> +/* ADC internal common clock prescaler division ratios */
> +static int stm32f4_pclk_div[] = {2, 4, 6, 8};
> +
> +/**
> + * stm32f4_adc_clk_sel() - Select ADC common clock prescaler
> + * @adc: stm32 adc instance
> + * Select clock prescaler used for analog conversions.
> + */
> +static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
> +{
> + struct stm32_adc_common *common = adc->common;
> + unsigned long rate;
> + u32 val;
> + int i;
> +
> + /* Common prescaler is set only once, when 1st ADC instance starts */
> + list_for_each_entry(adc, &common->adc_list, adc_list)
> + if (stm32f4_adc_is_started(adc))
> + return 0;
> +
> + rate = clk_get_rate(common->aclk);
> + for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
> + if ((rate / stm32f4_pclk_div[i]) <=
> + common->data->max_clock_rate)
> + break;
> + }
> + if (i >= ARRAY_SIZE(stm32f4_pclk_div))
> + return -EINVAL;
> +
> + val = stm32_adc_common_readl(common, STM32F4_ADC_CCR);
> + val &= ~STM32F4_ADC_ADCPRE_MASK;
> + val |= i << STM32F4_ADC_ADCPRE_SHIFT;
> + stm32_adc_common_writel(common, STM32F4_ADC_CCR, val);
> +
> + dev_dbg(common->dev, "Using analog clock source at %ld kHz\n",
> + rate / (stm32f4_pclk_div[i] * 1000));
> +
> + return 0;
> +}
> +
> +static const struct stm32_adc_ops stm32f4_adc_ops = {
> + .adc_info = stm32f4_adc_info,
> + .ext_triggers = stm32f4_adc_ext_triggers,
> + .jext_triggers = stm32f4_adc_jext_triggers,
> + .adc_reginfo = &stm32f4_adc_reginfo,
> + .highres = 12,
> + .max_clock_rate = 36000000,
> + .clk_sel = stm32f4_adc_clk_sel,
> + .start_conv = stm32f4_adc_start_conv,
> + .stop_conv = stm32f4_adc_stop_conv,
> + .is_started = stm32f4_adc_is_started,
> + .regular_started = stm32f4_adc_regular_started,
> + .injected_started = stm32f4_adc_injected_started,
> +};
> +
> +static const struct of_device_id stm32f4_adc_of_match[] = {
> + { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_ops},
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, stm32f4_adc_of_match);
> +
> +static struct platform_driver stm32f4_adc_driver = {
> + .probe = stm32_adc_probe,
> + .remove = stm32_adc_remove,
> + .driver = {
> + .name = "stm32f4-adc",
> + .of_match_table = stm32f4_adc_of_match,
> + },
> +};
> +
> +module_platform_driver(stm32f4_adc_driver);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32F4 ADC driver");
> +MODULE_LICENSE("GPL v2");
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3 6/8] iio: dpot-dac: DAC driver based on a digital potentiometer
From: Peter Meerwald-Stadler @ 2016-10-30 15:32 UTC (permalink / raw)
To: Jonathan Cameron, Peter Rosin
Cc: linux-kernel, Hartmut Knaack, Lars-Peter Clausen, Rob Herring,
Mark Rutland, linux-iio, devicetree
In-Reply-To: <301ab064-cd90-d99d-a05a-8a50ba759c62@kernel.org>
On Sun, 30 Oct 2016, Jonathan Cameron wrote:
> On 23/10/16 23:39, Peter Rosin wrote:
> > It is assumed that the dpot is used as a voltage divider between the
> > current dpot wiper setting and the maximum resistance of the dpot. The
> > divided voltage is provided by a vref regulator.
> >
> > .------.
> > .-----------. | |
> > | vref |--' .---.
> > | regulator |--. | |
> > '-----------' | | d |
> > | | p |
> > | | o | wiper
> > | | t |<---------+
> > | | |
> > | '---' dac output voltage
> > | |
> > '------+------------+
> >
> > Signed-off-by: Peter Rosin <peda@axentia.se>
> Only a trivial suggestion to drop the devinfo about max ohms now it's
> exposed (effectively) via the dpot driver. (really minor though so don't bother
> respinning for that!)
>
> Jonathan
> > ---
> > .../ABI/testing/sysfs-bus-iio-dac-dpot-dac | 8 +
> > MAINTAINERS | 2 +
> > drivers/iio/dac/Kconfig | 10 +
> > drivers/iio/dac/Makefile | 1 +
> > drivers/iio/dac/dpot-dac.c | 267 +++++++++++++++++++++
> > 5 files changed, 288 insertions(+)
> > create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac
> > create mode 100644 drivers/iio/dac/dpot-dac.c
> >
> > diff --git a/Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac b/Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac
> > new file mode 100644
> > index 000000000000..580e93f373f6
> > --- /dev/null
> > +++ b/Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac
> > @@ -0,0 +1,8 @@
> > +What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_raw_available
> > +Date: October 2016
> > +KernelVersion: 4.9
> > +Contact: Peter Rosin <peda@axentia.se>
> > +Description:
> > + The range of available values represented as the minimum value,
> > + the step and the maximum value, all enclosed in square brackets.
> > + Example: [0 1 256]
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 6218010128dc..d7375f45ff0f 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -6115,7 +6115,9 @@ IIO DIGITAL POTENTIOMETER DAC
> > M: Peter Rosin <peda@axentia.se>
> > L: linux-iio@vger.kernel.org
> > S: Maintained
> > +F: Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac
> > F: Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
> > +F: drivers/iio/dac/dpot-dac.c
> >
> > IIO SUBSYSTEM AND DRIVERS
> > M: Jonathan Cameron <jic23@kernel.org>
> > diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> > index 120b24478469..d3084028905b 100644
> > --- a/drivers/iio/dac/Kconfig
> > +++ b/drivers/iio/dac/Kconfig
> > @@ -200,6 +200,16 @@ config AD8801
> > To compile this driver as a module choose M here: the module will be called
> > ad8801.
> >
> > +config DPOT_DAC
> > + tristate "DAC emulation using a DPOT"
> > + depends on OF
> > + help
> > + Say yes here to build support for DAC emulation using a digital
> > + potentiometer.
> > +
> > + To compile this driver as a module, choose M here: the module will be
> > + called dpot-dac.
> > +
> > config LPC18XX_DAC
> > tristate "NXP LPC18xx DAC driver"
> > depends on ARCH_LPC18XX || COMPILE_TEST
> > diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
> > index 27642bbf75f2..f01bf4a99867 100644
> > --- a/drivers/iio/dac/Makefile
> > +++ b/drivers/iio/dac/Makefile
> > @@ -22,6 +22,7 @@ obj-$(CONFIG_AD5686) += ad5686.o
> > obj-$(CONFIG_AD7303) += ad7303.o
> > obj-$(CONFIG_AD8801) += ad8801.o
> > obj-$(CONFIG_CIO_DAC) += cio-dac.o
> > +obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
> > obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
> > obj-$(CONFIG_M62332) += m62332.o
> > obj-$(CONFIG_MAX517) += max517.o
> > diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c
> > new file mode 100644
> > index 000000000000..f227a211d34d
> > --- /dev/null
> > +++ b/drivers/iio/dac/dpot-dac.c
> > @@ -0,0 +1,267 @@
> > +/*
> > + * IIO DAC emulation driver using a digital potentiometer
> > + *
> > + * Copyright (C) 2016 Axentia Technologies AB
> > + *
> > + * Author: Peter Rosin <peda@axentia.se>
> > + *
> > + * 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.
> > + */
> > +
> > +/*
> > + * It is assumed that the dpot is used as a voltage divider between the
> > + * current dpot wiper setting and the maximum resistance of the dpot. The
> > + * divided voltage is provided by a vref regulator.
> > + *
> > + * .------.
> > + * .-----------. | |
> > + * | vref |--' .---.
> > + * | regulator |--. | |
> > + * '-----------' | | d |
> > + * | | p |
> > + * | | o | wiper
> > + * | | t |<---------+
> > + * | | |
> > + * | '---' dac output voltage
> > + * | |
> > + * '------+------------+
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/iio/consumer.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/regulator/consumer.h>
> > +
> > +struct dpot_dac {
> > + struct regulator *vref;
> > + struct iio_channel *dpot;
> > + u32 max_ohms;
> > +};
> > +
> > +static const struct iio_chan_spec dpot_dac_iio_channel = {
> > + .type = IIO_VOLTAGE,
> > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
> > + | BIT(IIO_CHAN_INFO_SCALE),
> > + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
> > + .output = 1,
> > + .indexed = 1,
> > +};
> > +
> > +static int dpot_dac_read_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int *val, int *val2, long mask)
> > +{
> > + struct dpot_dac *dac = iio_priv(indio_dev);
> > + int ret;
> > + unsigned long long tmp;
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_RAW:
> > + return iio_read_channel_raw(dac->dpot, val);
> > +
> > + case IIO_CHAN_INFO_SCALE:
> > + ret = iio_read_channel_scale(dac->dpot, val, val2);
> > + switch (ret) {
> > + case IIO_VAL_FRACTIONAL_LOG2:
> > + tmp = *val * 1000000000LL;
> > + do_div(tmp, dac->max_ohms);
> > + tmp *= regulator_get_voltage(dac->vref) / 1000;
> > + do_div(tmp, 1000000000LL);
> > + *val = tmp;
> > + return ret;
> > + case IIO_VAL_INT:
> > + /*
> > + * Convert integer scale to fractional scale by
> > + * setting the denominator (val2) to one...
> > + */
> > + *val2 = 1;
> > + ret = IIO_VAL_FRACTIONAL;
> > + /* ...and fall through. */
> > + case IIO_VAL_FRACTIONAL:
> > + *val *= regulator_get_voltage(dac->vref) / 1000;
> > + *val2 *= dac->max_ohms;
> > + break;
> > + }
> > +
> > + return ret;
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int dpot_dac_read_avail(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + const int **vals, int *type, int *length,
> > + long mask)
> > +{
> > + struct dpot_dac *dac = iio_priv(indio_dev);
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_RAW:
> > + return iio_read_avail_channel_raw(dac->dpot,
> > + vals, type, length);
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int dpot_dac_write_raw(struct iio_dev *indio_dev,
> > + struct iio_chan_spec const *chan,
> > + int val, int val2, long mask)
> > +{
> > + struct dpot_dac *dac = iio_priv(indio_dev);
> > +
> > + switch (mask) {
> > + case IIO_CHAN_INFO_RAW:
> > + return iio_write_channel_raw(dac->dpot, val);
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static const struct iio_info dpot_dac_info = {
> > + .read_raw = dpot_dac_read_raw,
> > + .read_avail = dpot_dac_read_avail,
> > + .write_raw = dpot_dac_write_raw,
> > + .driver_module = THIS_MODULE,
> > +};
> > +
> > +static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev)
> > +{
> > + struct device *dev = &indio_dev->dev;
> > + struct dpot_dac *dac = iio_priv(indio_dev);
> > + unsigned long long tmp;
> > + int ret;
> > + int val;
> > + int val2;
> > + int max;
> > +
> > + ret = iio_read_max_channel_raw(dac->dpot, &max);
> > + if (ret < 0) {
> > + dev_err(dev, "dpot does not indicate its raw maximum value\n");
> > + return ret;
> > + }
> > +
> > + switch (iio_read_channel_scale(dac->dpot, &val, &val2)) {
> > + case IIO_VAL_INT:
> > + return max * val;
> > + case IIO_VAL_FRACTIONAL:
> > + tmp = (unsigned long long)max * val;
> > + do_div(tmp, val2);
> > + return tmp;
> > + case IIO_VAL_FRACTIONAL_LOG2:
> > + tmp = (s64)val * 1000000000LL * max >> val2;
(s64) necessary?
or rather unsigned long long?
> > + do_div(tmp, 1000000000LL);
> > + return tmp;
> > + default:
> > + dev_err(dev, "dpot has a scale that is too weird\n");
> > + }
> > +
> > + return -EINVAL;
> > +}
> > +
> > +static int dpot_dac_probe(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct iio_dev *indio_dev;
> > + struct dpot_dac *dac;
> > + enum iio_chan_type type;
> > + int ret;
> > +
> > + indio_dev = devm_iio_device_alloc(dev, sizeof(*dac));
> > + if (!indio_dev)
> > + return -ENOMEM;
> > +
> > + platform_set_drvdata(pdev, indio_dev);
> > + dac = iio_priv(indio_dev);
> > +
> > + indio_dev->name = dev_name(dev);
> > + indio_dev->dev.parent = dev;
> > + indio_dev->info = &dpot_dac_info;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > + indio_dev->channels = &dpot_dac_iio_channel;
> > + indio_dev->num_channels = 1;
> > +
> > + dac->vref = devm_regulator_get(dev, "vref");
> > + if (IS_ERR(dac->vref)) {
> > + if (PTR_ERR(dac->dpot) != -EPROBE_DEFER)
> > + dev_err(&pdev->dev, "failed to get vref regulator\n");
> > + return PTR_ERR(dac->vref);
> > + }
> > +
> > + dac->dpot = devm_iio_channel_get(dev, "dpot");
> > + if (IS_ERR(dac->dpot)) {
> > + if (PTR_ERR(dac->dpot) != -EPROBE_DEFER)
> > + dev_err(dev, "failed to get dpot input channel\n");
> > + return PTR_ERR(dac->dpot);
> > + }
> > +
> > + ret = iio_get_channel_type(dac->dpot, &type);
> > + if (ret < 0)
> > + return ret;
> > +
> > + if (type != IIO_RESISTANCE) {
> > + dev_err(dev, "dpot is of the wrong type\n");
> > + return -EINVAL;
> > + }
> > +
> > + ret = dpot_dac_channel_max_ohms(indio_dev);
> > + if (ret < 0)
> > + return ret;
> > + dac->max_ohms = ret;
> > + dev_info(dev, "dpot max is %d\n", dac->max_ohms);
> Given we can query this (indirectly) from the dpot itself, I'd drop this now.
max_ohms is u32, so this should be %u not %d?
>
> > +
> > + ret = regulator_enable(dac->vref);
> > + if (ret) {
> > + dev_err(dev, "failed to enable the vref regulator\n");
> > + return ret;
> > + }
> > +
> > + ret = iio_device_register(indio_dev);
> > + if (ret) {
> > + dev_err(dev, "failed to register iio device\n");
> > + goto disable_reg;
> > + }
> > +
> > + return 0;
> > +
> > +disable_reg:
> > + regulator_disable(dac->vref);
> > + return ret;
> > +}
> > +
> > +static int dpot_dac_remove(struct platform_device *pdev)
> > +{
> > + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> > + struct dpot_dac *dac = iio_priv(indio_dev);
> > +
> > + iio_device_unregister(indio_dev);
> > + regulator_disable(dac->vref);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct of_device_id dpot_dac_match[] = {
> > + { .compatible = "dpot-dac" },
> > + { /* sentinel */ }
> > +};
> > +MODULE_DEVICE_TABLE(of, dpot_dac_match);
> > +
> > +static struct platform_driver dpot_dac_driver = {
> > + .probe = dpot_dac_probe,
> > + .remove = dpot_dac_remove,
> > + .driver = {
> > + .name = "iio-dpot-dac",
> > + .of_match_table = dpot_dac_match,
> > + },
> > +};
> > +module_platform_driver(dpot_dac_driver);
> > +
> > +MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer");
> > +MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
> > +MODULE_LICENSE("GPL v2");
> >
>
--
Peter Meerwald-Stadler
+43-664-2444418 (mobile)
^ permalink raw reply
* Re: [PATCH 03/10] iio: adc: stm32: add optional dma support
From: Jonathan Cameron @ 2016-10-30 15:34 UTC (permalink / raw)
To: Fabrice Gasnier, linux-iio, linux-arm-kernel, devicetree,
linux-kernel
Cc: linux, robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue,
lars, knaack.h, pmeerw
In-Reply-To: <1477412722-24061-4-git-send-email-fabrice.gasnier@st.com>
On 25/10/16 17:25, Fabrice Gasnier wrote:
> Add optional DMA support to STM32 ADC.
> Use dma cyclic mode with at least two periods.
>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Few little bits inline, but looks superficially good (my dma knowledge isn't
really up to reviewing this.) Lars can you take a look (perhaps at the next
version)?
Also, you 'could' potentially use the dma buffer stuff to give a lower overhead
route for the data to userspace if the device goes quick enough to warrant
it.
Jonathan
> ---
> drivers/iio/adc/stm32/Kconfig | 2 +
> drivers/iio/adc/stm32/stm32-adc.c | 222 ++++++++++++++++++++++++++++++++----
> drivers/iio/adc/stm32/stm32-adc.h | 15 +++
> drivers/iio/adc/stm32/stm32f4-adc.c | 20 +++-
> 4 files changed, 235 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
> index 245d037..5554ac8 100644
> --- a/drivers/iio/adc/stm32/Kconfig
> +++ b/drivers/iio/adc/stm32/Kconfig
> @@ -8,6 +8,7 @@ config STM32_ADC
> select REGULATOR_FIXED_VOLTAGE
> select IIO_BUFFER
> select IIO_TRIGGERED_BUFFER
> + select IRQ_WORK
> help
> Say yes here to build the driver for the STMicroelectronics
> STM32 analog-to-digital converter (ADC).
> @@ -18,6 +19,7 @@ config STM32_ADC
> config STM32F4_ADC
> tristate "STMicroelectronics STM32F4 adc"
> depends on ARCH_STM32 || COMPILE_TEST
> + depends on HAS_DMA
> depends on OF
> select STM32_ADC
> help
> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
> index 1e0850d..25d0307 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.c
> +++ b/drivers/iio/adc/stm32/stm32-adc.c
> @@ -21,6 +21,7 @@
>
> #include <linux/clk.h>
> #include <linux/debugfs.h>
> +#include <linux/dma-mapping.h>
> #include <linux/iio/iio.h>
> #include <linux/iio/buffer.h>
> #include <linux/iio/events.h>
> @@ -371,6 +372,34 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
> return ret;
> }
>
> +static int stm32_adc_residue(struct stm32_adc *adc)
> +{
> + struct dma_tx_state state;
> + enum dma_status status;
> +
> + if (!adc->rx_buf)
> + return 0;
> +
> + status = dmaengine_tx_status(adc->dma_chan,
> + adc->dma_chan->cookie,
> + &state);
> + if (status == DMA_IN_PROGRESS) {
> + /* Residue is size in bytes from end of buffer */
> + int i = adc->rx_buf_sz - state.residue;
> + int size;
> +
> + /* Return available bytes */
> + if (i >= adc->bufi)
> + size = i - adc->bufi;
> + else
> + size = adc->rx_buf_sz - adc->bufi + i;
> +
> + return size;
> + }
> +
> + return 0;
> +}
> +
> /**
> * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
> */
> @@ -394,8 +423,8 @@ static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
> stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
> status &= mask;
>
> - /* Regular data */
> - if (status & reginfo->eoc) {
> + /* Regular data (when dma isn't used) */
> + if ((status & reginfo->eoc) && (!adc->rx_buf)) {
> adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
> if (iio_buffer_enabled(indio_dev)) {
> adc->bufi++;
> @@ -553,29 +582,154 @@ static int stm32_adc_validate_device(struct iio_trigger *trig,
> return indio != indio_dev ? -EINVAL : 0;
> }
>
> -static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
> - bool state)
> +static void stm32_adc_dma_irq_work(struct irq_work *work)
> {
> - struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
> + struct stm32_adc *adc = container_of(work, struct stm32_adc, work);
> + struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +
> + /**
> + * iio_trigger_poll calls generic_handle_irq(). So, it requires hard
> + * irq context, and cannot be called directly from dma callback,
> + * dma cb has to schedule this work instead.
> + */
> + iio_trigger_poll(indio_dev->trig);
> +}
> +
> +static void stm32_adc_dma_buffer_done(void *data)
> +{
> + struct iio_dev *indio_dev = data;
> struct stm32_adc *adc = iio_priv(indio_dev);
> - int ret;
>
> - if (state) {
> - /* Reset adc buffer index */
> - adc->bufi = 0;
> + /* invoques iio_trigger_poll() from hard irq context */
invokes.
> + irq_work_queue(&adc->work);
> +}
> +
> +static int stm32_adc_buffer_alloc_dma_start(struct iio_dev *indio_dev)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + const struct stm32_adc_reginfo *reginfo =
> + adc->common->data->adc_reginfo;
> + struct dma_async_tx_descriptor *desc;
> + struct dma_slave_config config;
> + dma_cookie_t cookie;
> + int ret, size, watermark;
>
> + /* Reset adc buffer index */
> + adc->bufi = 0;
> +
> + if (!adc->dma_chan) {
> /* Allocate adc buffer */
> adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
> if (!adc->buffer)
> return -ENOMEM;
>
> + return 0;
> + }
> +
> + /*
> + * Allocate at least twice the buffer size for dma cyclic transfers, so
> + * we can work with at least two dma periods. There should be :
> + * - always one buffer (period) dma is working on
> + * - one buffer (period) driver can push with iio_trigger_poll().
> + */
> + size = indio_dev->buffer->bytes_per_datum * indio_dev->buffer->length;
> + size = max(indio_dev->scan_bytes * 2, size);
> +
> + adc->rx_buf = dma_alloc_coherent(adc->common->dev, PAGE_ALIGN(size),
> + &adc->rx_dma_buf,
> + GFP_KERNEL);
> + if (!adc->rx_buf)
> + return -ENOMEM;
> + adc->rx_buf_sz = size;
> + watermark = indio_dev->buffer->bytes_per_datum
> + * indio_dev->buffer->watermark;
> + watermark = max(indio_dev->scan_bytes, watermark);
> + watermark = rounddown(watermark, indio_dev->scan_bytes);
> +
> + dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, size,
> + watermark);
> +
> + /* Configure DMA channel to read data register */
> + memset(&config, 0, sizeof(config));
> + config.src_addr = (dma_addr_t)adc->common->phys_base;
> + config.src_addr += adc->offset + reginfo->dr;
> + config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
> +
> + ret = dmaengine_slave_config(adc->dma_chan, &config);
> + if (ret)
> + goto config_err;
> +
> + /* Prepare a DMA cyclic transaction */
> + desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
> + adc->rx_dma_buf,
> + size, watermark,
> + DMA_DEV_TO_MEM,
> + DMA_PREP_INTERRUPT);
> + if (!desc) {
> + ret = -ENODEV;
> + goto config_err;
> + }
> +
> + desc->callback = stm32_adc_dma_buffer_done;
> + desc->callback_param = indio_dev;
> +
> + cookie = dmaengine_submit(desc);
> + if (dma_submit_error(cookie)) {
> + ret = dma_submit_error(cookie);
> + goto config_err;
> + }
> +
> + /* Issue pending DMA requests */
> + dma_async_issue_pending(adc->dma_chan);
> +
> + return 0;
> +
> +config_err:
> + dma_free_coherent(adc->common->dev, PAGE_ALIGN(size), adc->rx_buf,
> + adc->rx_dma_buf);
> +
> + return ret;
> +}
> +
> +static void stm32_adc_dma_stop_buffer_free(struct iio_dev *indio_dev)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> +
> + if (!adc->dma_chan) {
> + kfree(adc->buffer);
> + } else {
> + dmaengine_terminate_all(adc->dma_chan);
> + irq_work_sync(&adc->work);
> + dma_free_coherent(adc->common->dev, PAGE_ALIGN(adc->rx_buf_sz),
> + adc->rx_buf, adc->rx_dma_buf);
> + adc->rx_buf = NULL;
> + }
> +
> + adc->buffer = NULL;
> +}
> +
> +static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
> + bool state)
> +{
> + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + int ret;
> +
> + if (state) {
> + ret = stm32_adc_buffer_alloc_dma_start(indio_dev);
> + if (ret) {
> + dev_err(&indio_dev->dev, "alloc failed\n");
> + return ret;
> + }
> +
> ret = stm32_adc_set_trig(indio_dev, trig);
> if (ret) {
> dev_err(&indio_dev->dev, "Can't set trigger\n");
> goto err_buffer_free;
> }
>
> - stm32_adc_conv_irq_enable(adc);
> + if (!adc->dma_chan)
> + stm32_adc_conv_irq_enable(adc);
>
> ret = stm32_adc_start_conv(adc);
> if (ret) {
> @@ -589,25 +743,25 @@ static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
> return ret;
> }
>
> - stm32_adc_conv_irq_disable(adc);
> + if (!adc->dma_chan)
> + stm32_adc_conv_irq_disable(adc);
>
> ret = stm32_adc_set_trig(indio_dev, NULL);
> if (ret)
> dev_warn(&indio_dev->dev, "Can't clear trigger\n");
>
> - kfree(adc->buffer);
> - adc->buffer = NULL;
> + stm32_adc_dma_stop_buffer_free(indio_dev);
> }
>
> return 0;
>
> err_irq_trig_disable:
> - stm32_adc_conv_irq_disable(adc);
> + if (!adc->dma_chan)
> + stm32_adc_conv_irq_disable(adc);
> stm32_adc_set_trig(indio_dev, NULL);
>
> err_buffer_free:
> - kfree(adc->buffer);
> - adc->buffer = NULL;
> + stm32_adc_dma_stop_buffer_free(indio_dev);
>
> return ret;
> }
> @@ -624,17 +778,30 @@ static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
> struct iio_dev *indio_dev = pf->indio_dev;
> struct stm32_adc *adc = iio_priv(indio_dev);
>
> - dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
> -
> - /* reset buffer index */
> - adc->bufi = 0;
> - iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
> - pf->timestamp);
> + if (!adc->dma_chan) {
> + adc->bufi = 0;
> + iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
> + pf->timestamp);
> + } else {
> + int residue = stm32_adc_residue(adc);
> +
This bit wants some explanatory comments I think.
> + while (residue >= indio_dev->scan_bytes) {
> + adc->buffer = (u16 *)&adc->rx_buf[adc->bufi];
> + iio_push_to_buffers_with_timestamp(indio_dev,
> + adc->buffer,
> + pf->timestamp);
> + residue -= indio_dev->scan_bytes;
> + adc->bufi += indio_dev->scan_bytes;
> + if (adc->bufi >= adc->rx_buf_sz)
> + adc->bufi = 0;
> + }
> + }
>
> iio_trigger_notify_done(indio_dev->trig);
>
> /* re-enable eoc irq */
> - stm32_adc_conv_irq_enable(adc);
> + if (!adc->dma_chan)
> + stm32_adc_conv_irq_enable(adc);
>
> return IRQ_HANDLED;
> }
> @@ -827,6 +994,10 @@ static int stm32_adc_register(struct stm32_adc_common *common,
> if (ret)
> goto err_clk_disable;
>
> + adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
> + if (adc->dma_chan)
> + init_irq_work(&adc->work, stm32_adc_dma_irq_work);
> +
> ret = iio_triggered_buffer_setup(indio_dev,
> &iio_pollfunc_store_time,
> &stm32_adc_trigger_handler,
> @@ -850,6 +1021,8 @@ static int stm32_adc_register(struct stm32_adc_common *common,
> iio_triggered_buffer_cleanup(indio_dev);
>
> err_trig_unregister:
> + if (adc->dma_chan)
> + dma_release_channel(adc->dma_chan);
> stm32_adc_trig_unregister(indio_dev);
>
> err_clk_disable:
> @@ -870,6 +1043,8 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
> iio_device_unregister(indio_dev);
> iio_triggered_buffer_cleanup(indio_dev);
> stm32_adc_trig_unregister(indio_dev);
> + if (adc->dma_chan)
> + dma_release_channel(adc->dma_chan);
> if (adc->clk) {
> clk_disable_unprepare(adc->clk);
> clk_put(adc->clk);
> @@ -900,6 +1075,7 @@ int stm32_adc_probe(struct platform_device *pdev)
> common->base = devm_ioremap_resource(&pdev->dev, res);
> if (IS_ERR(common->base))
> return PTR_ERR(common->base);
> + common->phys_base = res->start;
>
> common->data = match->data;
> common->dev = &pdev->dev;
> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
> index 0be603c..01a0dda 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.h
> +++ b/drivers/iio/adc/stm32/stm32-adc.h
> @@ -22,6 +22,9 @@
> #ifndef __STM32_ADC_H
> #define __STM32_ADC_H
>
> +#include <linux/dmaengine.h>
> +#include <linux/irq_work.h>
> +
> /*
> * STM32 - ADC global register map
> * ________________________________________________________
> @@ -276,6 +279,11 @@ struct stm32_adc_ops {
> * @num_conv: expected number of scan conversions
> * @injected: use injected channels on this adc
> * @lock: spinlock
> + * @work: irq work used to call trigger poll routine
> + * @dma_chan: dma channel
> + * @rx_buf: dma rx buffer cpu address
> + * @rx_dma_buf: dma rx buffer bus address
> + * @rx_buf_sz: dma rx buffer size
> * @clk: optional adc clock, for this adc instance
> * @calib: optional calibration data
> * @en: emulates enabled state on some stm32 adc
> @@ -293,6 +301,11 @@ struct stm32_adc {
> int num_conv;
> bool injected;
> spinlock_t lock; /* interrupt lock */
> + struct irq_work work;
> + struct dma_chan *dma_chan;
> + u8 *rx_buf;
> + dma_addr_t rx_dma_buf;
> + int rx_buf_sz;
> struct clk *clk;
> void *calib;
> bool en;
> @@ -302,6 +315,7 @@ struct stm32_adc {
> * struct stm32_adc_common - private data of ADC driver, common to all
> * ADC instances (ADC block)
> * @dev: device for this controller
> + * @phys_base: control registers base physical addr
> * @base: control registers base cpu addr
> * @irq: Common irq line for all adc instances
> * @data: STM32 dependent data from compatible
> @@ -313,6 +327,7 @@ struct stm32_adc {
> */
> struct stm32_adc_common {
> struct device *dev;
> + phys_addr_t phys_base;
> void __iomem *base;
> int irq;
> const struct stm32_adc_ops *data;
> diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
> index 147fe9c..4d7a2a8 100644
> --- a/drivers/iio/adc/stm32/stm32f4-adc.c
> +++ b/drivers/iio/adc/stm32/stm32f4-adc.c
> @@ -437,16 +437,30 @@ static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
> * @adc: stm32 adc instance
> *
> * Start single conversions for regular or injected channels.
> + * Also take care of normal or DMA mode. DMA is used in circular mode for
> + * regular conversions, in IIO buffer modes. Rely on rx_buf as raw
> + * read doesn't use dma, but direct DR read.
> */
> static int stm32f4_adc_start_conv(struct stm32_adc *adc)
> {
> - u32 trig_msk, start_msk;
> + u32 val, trig_msk, start_msk;
> + unsigned long flags;
>
> dev_dbg(adc->common->dev, "%s %s\n", __func__,
> adc->injected ? "injected" : "regular");
>
> stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>
> + if (!adc->injected) {
> + spin_lock_irqsave(&adc->lock, flags);
> + val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
> + val &= ~(STM32F4_DMA | STM32F4_DDS);
> + if (adc->rx_buf)
> + val |= STM32F4_DMA | STM32F4_DDS;
> + stm32_adc_writel(adc, STM32F4_ADCX_CR2, val);
> + spin_unlock_irqrestore(&adc->lock, flags);
> + }
> +
> if (!stm32f4_adc_is_started(adc)) {
> stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
> STM32F4_EOCS | STM32F4_ADON);
> @@ -494,6 +508,10 @@ static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
> stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
> }
>
> + if (!adc->injected)
> + stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2,
> + STM32F4_DMA | STM32F4_DDS);
> +
> return 0;
> }
>
>
^ permalink raw reply
* Re: [PATCH 2/2] ARM: dts: bcm283x: fix typo in mailbox address
From: Andreas Färber @ 2016-10-30 15:34 UTC (permalink / raw)
To: Stefan Wahren, Rob Herring
Cc: Mark Rutland, Stephen Warren, Eric Anholt,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1477505640-26658-2-git-send-email-stefan.wahren-eS4NqCHxEME@public.gmane.org>
Hi,
Am 26.10.2016 um 20:14 schrieb Stefan Wahren:
> The address of the mailbox node in the bcm283x.dts has also a typo.
.dts -> .dtsi
"also has" or maybe "has the same typo as in the binding example"?
> So fix it accordingly.
>
> Signed-off-by: Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org>
> Fixes: 05b682b7a3b2 ("ARM: bcm2835: dt: Add the mailbox to the device tree")
> ---
> arch/arm/boot/dts/bcm283x.dtsi | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi
> index 46d46d8..74dd21b 100644
> --- a/arch/arm/boot/dts/bcm283x.dtsi
> +++ b/arch/arm/boot/dts/bcm283x.dtsi
> @@ -104,7 +104,7 @@
> reg = <0x7e104000 0x10>;
> };
>
> - mailbox: mailbox@7e00b800 {
> + mailbox: mailbox@7e00b880 {
> compatible = "brcm,bcm2835-mbox";
> reg = <0x7e00b880 0x40>;
> interrupts = <0 1>;
Otherwise,
Reviewed-by: Andreas Färber <afaerber-l3A5Bk7waGM@public.gmane.org>
At one point in time dtc did complain about such mismatches...
Regards,
Andreas
--
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] iio: si7020: Add devicetree support and trivial bindings
From: Jonathan Cameron @ 2016-10-30 16:30 UTC (permalink / raw)
To: Paul Kocialkowski, devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-iio-u79uwXL29TY76Z2rM5mHXA
Cc: Rob Herring, Mark Rutland, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler
In-Reply-To: <20161025192010.11195-1-contact-W9ppeneeCTY@public.gmane.org>
On 25/10/16 20:20, Paul Kocialkowski wrote:
> This adds devicetree support for the si7020 iio driver. Since it works
> well without requiring any additional property, its compatible string is
> added to the trivial i2c devices bindings list.
>
> Signed-off-by: Paul Kocialkowski <contact-W9ppeneeCTY@public.gmane.org>
Applied to the togreg branch of iio.git - initially pushed out as tesitng
for the autobuilders to play with it.
Thanks,
Jonathan
> ---
> Documentation/devicetree/bindings/i2c/trivial-devices.txt | 1 +
> drivers/iio/humidity/si7020.c | 11 ++++++++++-
> 2 files changed, 11 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> index 5c70ce9..6afba26 100644
> --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> @@ -145,6 +145,7 @@ ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
> samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
> sgx,vz89x SGX Sensortech VZ89X Sensors
> sii,s35390a 2-wire CMOS real-time clock
> +silabs,si7020 Relative Humidity and Temperature Sensors
> skyworks,sky81452 Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
> st,24c256 i2c serial eeprom (24cxx)
> st,m41t00 Serial real-time clock (RTC)
> diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c
> index ffc2ccf..345a765 100644
> --- a/drivers/iio/humidity/si7020.c
> +++ b/drivers/iio/humidity/si7020.c
> @@ -154,8 +154,17 @@ static const struct i2c_device_id si7020_id[] = {
> };
> MODULE_DEVICE_TABLE(i2c, si7020_id);
>
> +static const struct of_device_id si7020_dt_ids[] = {
> + { .compatible = "silabs,si7020" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, si7020_dt_ids);
> +
> static struct i2c_driver si7020_driver = {
> - .driver.name = "si7020",
> + .driver = {
> + .name = "si7020",
> + .of_match_table = of_match_ptr(si7020_dt_ids),
> + },
> .probe = si7020_probe,
> .id_table = si7020_id,
> };
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v5 4/8] drivers:input:tsc2007: add iio interface to read external ADC input and temperature
From: Jonathan Cameron @ 2016-10-30 16:42 UTC (permalink / raw)
To: H. Nikolaus Schaller, Dmitry Torokhov, Rob Herring, Mark Rutland,
Benoît Cousson, Tony Lindgren, Russell King, Arnd Bergmann,
Michael Welling, Mika Penttilä, Javier Martinez Canillas,
Igor Grinberg, Sebastian Reichel, Andrew F. Davis, Mark Brown,
Hans de Goede, Sangwon Jee
Cc: linux-input, devicetree, linux-kernel, linux-omap, letux-kernel,
linux-iio, kernel
In-Reply-To: <a7181466063e26097a1a4b49f12a7856d68d80cd.1477423581.git.hns@goldelico.com>
On 25/10/16 20:26, H. Nikolaus Schaller wrote:
> The tsc2007 chip not only has a resistive touch screen controller but
> also an external AUX adc imput which can be used for an ambient
> light sensor, battery voltage monitoring or any general purpose.
>
> Additionally it can measure the chip temperature.
>
> This extension provides an iio interface for these adc channels.
>
> Since it is not wasting much resources and is very straightforward,
> we simply provide all other adc channels as optional iio interfaces
> as weel. This can be used for debugging or special applications.
well
>
> This patch also splits the tsc2007 driver in several source files:
> tsc2007.h -- constants, structs and stubs
> tsc2007_core.c -- functional parts of the original driver
> tsc2007_iio.c -- the optional iio stuff
>
> Makefile magic allows to conditionally link the iio
> stuff if CONFIG_IIO=y in a way that it works with
> CONFIG_TOUCHSCREEN_TSC2007=m.
>
> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
Basically sound, just a few minor bits inline. In particular the point
about including iio.h (and it applies to input.h etc) in your header.
You can define a pointer to a struct iio_dev without having to let the compiler
know what is actually in it - as long as you don't dereference it in any
place where the compiler needs to know.
Couple of examples in for example drivers/iio/adc/xilinx-xadc.h
The struct clk; stuff is just what we need here for example.
All about keeping internal structures opaque where we don't need to know
what is in them.
Anyhow, nearly there! (as far as I'm concerned anyway)
Thanks,
Jonathan
> ---
> drivers/input/touchscreen/Makefile | 2 +
> drivers/input/touchscreen/tsc2007.h | 124 ++++++++++++++++
> .../touchscreen/{tsc2007.c => tsc2007_core.c} | 119 +++++----------
> drivers/input/touchscreen/tsc2007_iio.c | 162 +++++++++++++++++++++
> 4 files changed, 323 insertions(+), 84 deletions(-)
> create mode 100644 drivers/input/touchscreen/tsc2007.h
> rename drivers/input/touchscreen/{tsc2007.c => tsc2007_core.c} (85%)
> create mode 100644 drivers/input/touchscreen/tsc2007_iio.c
>
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 81b8645..d932e2d 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -80,6 +80,8 @@ obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o
> obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o
> obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o
> obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o
> +tsc2007-y := tsc2007_core.o
> +tsc2007-$(CONFIG_IIO) += tsc2007_iio.o
> obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
> obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
> obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
> diff --git a/drivers/input/touchscreen/tsc2007.h b/drivers/input/touchscreen/tsc2007.h
> new file mode 100644
> index 0000000..7153bf3
> --- /dev/null
> +++ b/drivers/input/touchscreen/tsc2007.h
> @@ -0,0 +1,124 @@
> +/*
> + * drivers/input/touchscreen/tsc2007.h
> + *
> + * Copyright (c) 2008 MtekVision Co., Ltd.
> + * Kwangwoo Lee <kwlee@mtekvision.com>
> + *
> + * Using code from:
> + * - ads7846.c
> + * Copyright (c) 2005 David Brownell
> + * Copyright (c) 2006 Nokia Corporation
> + * - corgi_ts.c
> + * Copyright (C) 2004-2005 Richard Purdie
> + * - omap_ts.[hc], ads7846.h, ts_osk.c
> + * Copyright (C) 2002 MontaVista Software
> + * Copyright (C) 2004 Texas Instruments
> + * Copyright (C) 2005 Dirk Behme
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/input.h>
You shouldn't need to include input.h in here...
(see below comment on the struct iio_dev *
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c/tsc2007.h>
> +#include <linux/of_device.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/input/touchscreen.h>
Not sure why this is included either here.
> +
> +#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
> +#define TSC2007_MEASURE_AUX (0x2 << 4)
> +#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
> +#define TSC2007_ACTIVATE_XN (0x8 << 4)
> +#define TSC2007_ACTIVATE_YN (0x9 << 4)
> +#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
> +#define TSC2007_SETUP (0xb << 4)
> +#define TSC2007_MEASURE_X (0xc << 4)
> +#define TSC2007_MEASURE_Y (0xd << 4)
> +#define TSC2007_MEASURE_Z1 (0xe << 4)
> +#define TSC2007_MEASURE_Z2 (0xf << 4)
> +
> +#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
> +#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
> +#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
> +#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
> +
> +#define TSC2007_12BIT (0x0 << 1)
> +#define TSC2007_8BIT (0x1 << 1)
> +
> +#define MAX_12BIT ((1 << 12) - 1)
> +
> +#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
> +
> +#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
> +#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
> +#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
> +#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
> +#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
> +
> +struct ts_event {
> + u16 x;
> + u16 y;
> + u16 z1, z2;
> +};
> +
> +struct tsc2007 {
> + struct input_dev *input;
> + char phys[32];
> +
> + struct i2c_client *client;
> +
> + u16 model;
> + u16 x_plate_ohms;
> +
> + struct touchscreen_properties prop;
> +
> + bool report_resistance;
> + u16 min_x;
> + u16 min_y;
> + u16 max_x;
> + u16 max_y;
> + u16 max_rt;
> + unsigned long poll_period; /* in jiffies */
> + int fuzzx;
> + int fuzzy;
> + int fuzzz;
> +
> + unsigned int gpio;
> + int irq;
> +
> + wait_queue_head_t wait;
> + bool stopped;
> + bool pendown;
> +
> + int (*get_pendown_state)(struct device *);
> + void (*clear_penirq)(void);
> +
> + struct mutex mlock;
> +#ifdef CONFIG_IIO
I don't like this. Nothing stops you using a struct iio_dev * pointer
without including iio.h. It's a commmon thing to do. Just put
struct *iio_dev above and don't ever dereference it without having include
iio.h in the file.
> + void *private;
> +#endif
> +};
> +
> +int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd);
> +u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
> + struct ts_event *tc);
> +bool tsc2007_is_pen_down(struct tsc2007 *ts);
> +
> +#ifdef CONFIG_IIO
> +
> +/* defined in tsc2007_iio.c */
> +int tsc2007_iio_configure(struct tsc2007 *ts);
> +void tsc2007_iio_unconfigure(struct tsc2007 *ts);
> +
> +#else /* CONFIG_IIO */
> +
> +static inline int tsc2007_iio_configure(struct tsc2007 *ts) { }
should return 0.
> +static inline void tsc2007_iio_unconfigure(struct tsc2007 *ts) { }
> +
> +#endif /* CONFIG_IIO */
> diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007_core.c
> similarity index 85%
> rename from drivers/input/touchscreen/tsc2007.c
> rename to drivers/input/touchscreen/tsc2007_core.c
> index 5e3c4bf..19dfc9b 100644
> --- a/drivers/input/touchscreen/tsc2007.c
> +++ b/drivers/input/touchscreen/tsc2007_core.c
> @@ -20,87 +20,9 @@
> * published by the Free Software Foundation.
> */
>
> -#include <linux/module.h>
> -#include <linux/slab.h>
> -#include <linux/input.h>
> -#include <linux/interrupt.h>
> -#include <linux/i2c.h>
> -#include <linux/i2c/tsc2007.h>
> -#include <linux/of_device.h>
> -#include <linux/of.h>
> -#include <linux/of_gpio.h>
> -#include <linux/input/touchscreen.h>
> -
> -#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
> -#define TSC2007_MEASURE_AUX (0x2 << 4)
> -#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
> -#define TSC2007_ACTIVATE_XN (0x8 << 4)
> -#define TSC2007_ACTIVATE_YN (0x9 << 4)
> -#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
> -#define TSC2007_SETUP (0xb << 4)
> -#define TSC2007_MEASURE_X (0xc << 4)
> -#define TSC2007_MEASURE_Y (0xd << 4)
> -#define TSC2007_MEASURE_Z1 (0xe << 4)
> -#define TSC2007_MEASURE_Z2 (0xf << 4)
> -
> -#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
> -#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
> -#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
> -#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
> -
> -#define TSC2007_12BIT (0x0 << 1)
> -#define TSC2007_8BIT (0x1 << 1)
> -
> -#define MAX_12BIT ((1 << 12) - 1)
> -
> -#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
> -
> -#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
> -#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
> -#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
> -#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
> -#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
> -
> -struct ts_event {
> - u16 x;
> - u16 y;
> - u16 z1, z2;
> -};
> -
> -struct tsc2007 {
> - struct input_dev *input;
> - char phys[32];
> -
> - struct i2c_client *client;
> -
> - u16 model;
> - u16 x_plate_ohms;
> -
> - struct touchscreen_properties prop;
> -
> - bool report_resistance;
> - u16 min_x;
> - u16 min_y;
> - u16 max_x;
> - u16 max_y;
> - u16 max_rt;
> - unsigned long poll_period; /* in jiffies */
> - int fuzzx;
> - int fuzzy;
> - int fuzzz;
> +#include "tsc2007.h"
>
> - unsigned gpio;
> - int irq;
> -
> - wait_queue_head_t wait;
> - bool stopped;
> - bool pendown;
> -
> - int (*get_pendown_state)(struct device *);
> - void (*clear_penirq)(void);
> -};
> -
> -static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
> +int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
> {
> s32 data;
> u16 val;
> @@ -121,6 +43,7 @@ static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
>
> return val;
> }
> +EXPORT_SYMBOL(tsc2007_xfer);
>
> static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
> {
> @@ -138,7 +61,7 @@ static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
> tsc2007_xfer(tsc, PWRDOWN);
> }
>
> -static u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
> +u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
> struct ts_event *tc)
> {
> u32 rt = 0;
> @@ -158,8 +81,9 @@ static u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
>
> return rt;
> }
> +EXPORT_SYMBOL(tsc2007_calculate_resistance);
>
> -static bool tsc2007_is_pen_down(struct tsc2007 *ts)
> +bool tsc2007_is_pen_down(struct tsc2007 *ts)
> {
> /*
> * NOTE: We can't rely on the pressure to determine the pen down
> @@ -180,6 +104,7 @@ static bool tsc2007_is_pen_down(struct tsc2007 *ts)
>
> return ts->get_pendown_state(&ts->client->dev);
> }
> +EXPORT_SYMBOL(tsc2007_is_pen_down);
>
> static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
> {
> @@ -192,7 +117,10 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
> while (!ts->stopped && tsc2007_is_pen_down(ts)) {
>
> /* pen is down, continue with the measurement */
> +
> + mutex_lock(&ts->mlock);
> tsc2007_read_values(ts, &tc);
> + mutex_unlock(&ts->mlock);
>
> rt = tsc2007_calculate_resistance(ts, &tc);
>
> @@ -450,7 +378,8 @@ static void tsc2007_call_exit_platform_hw(void *data)
> static int tsc2007_probe(struct i2c_client *client,
> const struct i2c_device_id *id)
> {
> - const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
> + const struct tsc2007_platform_data *pdata =
> + dev_get_platdata(&client->dev);
> struct tsc2007 *ts;
> struct input_dev *input_dev;
> int err;
> @@ -472,7 +401,13 @@ static int tsc2007_probe(struct i2c_client *client,
> ts->client = client;
> ts->irq = client->irq;
> ts->input = input_dev;
> +
> + err = tsc2007_iio_configure(ts);
> + if (err < 0)
> + return err;
> +
> init_waitqueue_head(&ts->wait);
> + mutex_init(&ts->mlock);
>
> snprintf(ts->phys, sizeof(ts->phys),
> "%s/input0", dev_name(&client->dev));
> @@ -503,8 +438,10 @@ static int tsc2007_probe(struct i2c_client *client,
> ts->fuzzz, 0);
> } else {
> err = tsc2007_probe_dt(client, ts);
> - if (err)
> + if (err) {
> + tsc2007_iio_unconfigure(ts);
> return err;
> + }
> }
>
> if (pdata) {
> @@ -516,6 +453,7 @@ static int tsc2007_probe(struct i2c_client *client,
> dev_err(&client->dev,
> "Failed to register exit_platform_hw action, %d\n",
> err);
> + tsc2007_iio_unconfigure(ts);
> return err;
> }
> }
> @@ -533,6 +471,7 @@ static int tsc2007_probe(struct i2c_client *client,
> if (err) {
> dev_err(&client->dev, "Failed to request irq %d: %d\n",
> ts->irq, err);
> + tsc2007_iio_unconfigure(ts);
> return err;
> }
>
> @@ -543,6 +482,7 @@ static int tsc2007_probe(struct i2c_client *client,
> if (err < 0) {
> dev_err(&client->dev,
> "Failed to setup chip: %d\n", err);
> + tsc2007_iio_unconfigure(ts);
> return err; /* usually, chip does not respond */
> }
>
> @@ -550,12 +490,22 @@ static int tsc2007_probe(struct i2c_client *client,
> if (err) {
> dev_err(&client->dev,
> "Failed to register input device: %d\n", err);
> + tsc2007_iio_unconfigure(ts);
> return err;
> }
>
> return 0;
> }
>
> +static int tsc2007_remove(struct i2c_client *client)
> +{
> + struct tsc2007 *ts = i2c_get_clientdata(client);
> +
> + tsc2007_iio_unconfigure(ts);
> + input_unregister_device(ts->input);
> + return 0;
> +}
> +
> static const struct i2c_device_id tsc2007_idtable[] = {
> { "tsc2007", 0 },
> { }
> @@ -578,6 +528,7 @@ static struct i2c_driver tsc2007_driver = {
> },
> .id_table = tsc2007_idtable,
> .probe = tsc2007_probe,
> + .remove = tsc2007_remove,
> };
>
> module_i2c_driver(tsc2007_driver);
> diff --git a/drivers/input/touchscreen/tsc2007_iio.c b/drivers/input/touchscreen/tsc2007_iio.c
> new file mode 100644
> index 0000000..608db45
> --- /dev/null
> +++ b/drivers/input/touchscreen/tsc2007_iio.c
> @@ -0,0 +1,162 @@
> +/*
> + * drivers/input/touchscreen/tsc2007_iio.c
> + *
> + * Copyright (c) 2008 MtekVision Co., Ltd.
> + * Kwangwoo Lee <kwlee@mtekvision.com>
> + *
> + * Using code from:
> + * - ads7846.c
> + * Copyright (c) 2005 David Brownell
> + * Copyright (c) 2006 Nokia Corporation
> + * - corgi_ts.c
> + * Copyright (C) 2004-2005 Richard Purdie
> + * - omap_ts.[hc], ads7846.h, ts_osk.c
> + * Copyright (C) 2002 MontaVista Software
> + * Copyright (C) 2004 Texas Instruments
> + * Copyright (C) 2005 Dirk Behme
> + *
> + * 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.
> + */
> +
> +#include "tsc2007.h"
> +#include <linux/iio/iio.h>
> +
> +struct tsc2007_iio {
> + struct tsc2007 *ts;
> +};
> +
> +#define TSC2007_CHAN_IIO(_chan, _name, _type, _chan_info) \
> +{ \
> + .datasheet_name = _name, \
> + .type = _type, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(_chan_info), \
> + .indexed = 1, \
> + .channel = _chan, \
> +}
> +
> +static const struct iio_chan_spec tsc2007_iio_channel[] = {
> + TSC2007_CHAN_IIO(0, "x", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(1, "y", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(2, "z1", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(3, "z2", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(4, "adc", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(5, "rt", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), /* Ohms? */
> + TSC2007_CHAN_IIO(6, "pen", IIO_PRESSURE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(7, "temp0", IIO_TEMP, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(8, "temp1", IIO_TEMP, IIO_CHAN_INFO_RAW),
> +};
> +
> +static int tsc2007_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val, int *val2, long mask)
> +{
> + struct tsc2007_iio *iio = iio_priv(indio_dev);
> + struct tsc2007 *tsc = iio->ts;
> + int adc_chan = chan->channel;
> + int ret = 0;
> +
> + if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel))
> + return -EINVAL;
> +
> + if (mask != IIO_CHAN_INFO_RAW)
> + return -EINVAL;
> +
> + mutex_lock(&tsc->mlock);
> +
> + switch (chan->channel) {
> + case 0:
> + *val = tsc2007_xfer(tsc, READ_X);
> + break;
> + case 1:
> + *val = tsc2007_xfer(tsc, READ_Y);
> + break;
> + case 2:
> + *val = tsc2007_xfer(tsc, READ_Z1);
> + break;
> + case 3:
> + *val = tsc2007_xfer(tsc, READ_Z2);
> + break;
> + case 4:
> + *val = tsc2007_xfer(tsc, (ADC_ON_12BIT | TSC2007_MEASURE_AUX));
> + break;
> + case 5: {
> + struct ts_event tc;
> +
> + tc.x = tsc2007_xfer(tsc, READ_X);
> + tc.z1 = tsc2007_xfer(tsc, READ_Z1);
> + tc.z2 = tsc2007_xfer(tsc, READ_Z2);
> + *val = tsc2007_calculate_resistance(tsc, &tc);
> + break;
> + }
> + case 6:
> + *val = tsc2007_is_pen_down(tsc);
> + break;
> + case 7:
> + *val = tsc2007_xfer(tsc,
> + (ADC_ON_12BIT | TSC2007_MEASURE_TEMP0));
> + break;
> + case 8:
> + *val = tsc2007_xfer(tsc,
> + (ADC_ON_12BIT | TSC2007_MEASURE_TEMP1));
> + break;
> + }
> +
> + /* Prepare for next touch reading - power down ADC, enable PENIRQ */
> + tsc2007_xfer(tsc, PWRDOWN);
> +
> + mutex_unlock(&tsc->mlock);
> +
> + ret = IIO_VAL_INT;
> +
> + return ret;
return IIO_VAL_INT;
Though feels like tsc2007_xfer might return an error and should
be handled...
> +}
> +
> +static const struct iio_info tsc2007_iio_info = {
> + .read_raw = tsc2007_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +int tsc2007_iio_configure(struct tsc2007 *ts)
> +{
> + int err;
> + struct iio_dev *indio_dev;
> + struct tsc2007_iio *iio;
> +
> + indio_dev = devm_iio_device_alloc(&ts->client->dev,
> + sizeof(struct tsc2007_iio));
> + if (!indio_dev) {
> + dev_err(&ts->client->dev, "iio_device_alloc failed\n");
> + return -ENOMEM;
> + }
> +
> + iio = iio_priv(indio_dev);
> + iio->ts = ts;
> + ts->private = (void *) indio_dev;
> +
> + indio_dev->name = "tsc2007";
> + indio_dev->dev.parent = &ts->client->dev;
> + indio_dev->info = &tsc2007_iio_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->channels = tsc2007_iio_channel;
> + indio_dev->num_channels = ARRAY_SIZE(tsc2007_iio_channel);
> +
> + err = iio_device_register(indio_dev);
> + if (err < 0) {
> + dev_err(&ts->client->dev, "iio_device_register() failed: %d\n",
> + err);
> + return err;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(tsc2007_iio_configure);
> +
> +void tsc2007_iio_unconfigure(struct tsc2007 *ts)
> +{
> + struct iio_dev *indio_dev = ts->private;
> +
> + iio_device_unregister(indio_dev);
> +}
> +EXPORT_SYMBOL(tsc2007_iio_unconfigure);
>
^ permalink raw reply
* Re: [PATCH 1/2] iio: accel: st_accel: add support to lng2dm
From: Jonathan Cameron @ 2016-10-30 16:52 UTC (permalink / raw)
To: Lorenzo Bianconi
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.bianconi-qxv4g6HH51o,
Denis CIOCCA
In-Reply-To: <1477429744-27713-2-git-send-email-lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
On 25/10/16 22:09, Lorenzo Bianconi wrote:
> add support to STMicroelectronics LNG2DM accelerometer to
> st_accel framework
>
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
Looks fine to me. If anyone want to add anything it'll be in
a tree I can rebase for at least a few days.
Thanks,
Jonathan
> ---
> drivers/iio/accel/Kconfig | 3 +-
> drivers/iio/accel/st_accel.h | 1 +
> drivers/iio/accel/st_accel_core.c | 73 +++++++++++++++++++++++++++++++++++++++
> drivers/iio/accel/st_accel_i2c.c | 5 +++
> drivers/iio/accel/st_accel_spi.c | 1 +
> 5 files changed, 82 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> index c6cc2c0..c68bdb6 100644
> --- a/drivers/iio/accel/Kconfig
> +++ b/drivers/iio/accel/Kconfig
> @@ -127,7 +127,8 @@ config IIO_ST_ACCEL_3AXIS
> help
> Say yes here to build support for STMicroelectronics accelerometers:
> LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
> - LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL.
> + LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
> + LNG2DM
>
> This driver can also be built as a module. If so, these modules
> will be created:
> diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h
> index f8dfdb6..7c23168 100644
> --- a/drivers/iio/accel/st_accel.h
> +++ b/drivers/iio/accel/st_accel.h
> @@ -30,6 +30,7 @@
> #define LSM303AGR_ACCEL_DEV_NAME "lsm303agr_accel"
> #define LIS2DH12_ACCEL_DEV_NAME "lis2dh12_accel"
> #define LIS3L02DQ_ACCEL_DEV_NAME "lis3l02dq"
> +#define LNG2DM_ACCEL_DEV_NAME "lng2dm"
>
> /**
> * struct st_sensors_platform_data - default accel platform data
> diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
> index ce69048..bdb619a 100644
> --- a/drivers/iio/accel/st_accel_core.c
> +++ b/drivers/iio/accel/st_accel_core.c
> @@ -231,6 +231,12 @@
> #define ST_ACCEL_7_DRDY_IRQ_INT1_MASK 0x04
> #define ST_ACCEL_7_MULTIREAD_BIT false
>
> +/* CUSTOM VALUES FOR SENSOR 8 */
> +#define ST_ACCEL_8_FS_AVL_2_GAIN IIO_G_TO_M_S_2(15600)
> +#define ST_ACCEL_8_FS_AVL_4_GAIN IIO_G_TO_M_S_2(31200)
> +#define ST_ACCEL_8_FS_AVL_8_GAIN IIO_G_TO_M_S_2(62500)
> +#define ST_ACCEL_8_FS_AVL_16_GAIN IIO_G_TO_M_S_2(187500)
> +
> static const struct iio_chan_spec st_accel_8bit_channels[] = {
> ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
> BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
> @@ -726,6 +732,73 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
> .multi_read_bit = ST_ACCEL_7_MULTIREAD_BIT,
> .bootime = 2,
> },
> + {
> + .wai = ST_ACCEL_1_WAI_EXP,
> + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
> + .sensors_supported = {
> + [0] = LNG2DM_ACCEL_DEV_NAME,
> + },
> + .ch = (struct iio_chan_spec *)st_accel_8bit_channels,
> + .odr = {
> + .addr = ST_ACCEL_1_ODR_ADDR,
> + .mask = ST_ACCEL_1_ODR_MASK,
> + .odr_avl = {
> + { 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
> + { 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
> + { 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
> + { 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
> + { 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
> + { 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
> + { 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
> + { 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
> + },
> + },
> + .pw = {
> + .addr = ST_ACCEL_1_ODR_ADDR,
> + .mask = ST_ACCEL_1_ODR_MASK,
> + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
> + },
> + .enable_axis = {
> + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
> + .mask = ST_SENSORS_DEFAULT_AXIS_MASK,
> + },
> + .fs = {
> + .addr = ST_ACCEL_1_FS_ADDR,
> + .mask = ST_ACCEL_1_FS_MASK,
> + .fs_avl = {
> + [0] = {
> + .num = ST_ACCEL_FS_AVL_2G,
> + .value = ST_ACCEL_1_FS_AVL_2_VAL,
> + .gain = ST_ACCEL_8_FS_AVL_2_GAIN,
> + },
> + [1] = {
> + .num = ST_ACCEL_FS_AVL_4G,
> + .value = ST_ACCEL_1_FS_AVL_4_VAL,
> + .gain = ST_ACCEL_8_FS_AVL_4_GAIN,
> + },
> + [2] = {
> + .num = ST_ACCEL_FS_AVL_8G,
> + .value = ST_ACCEL_1_FS_AVL_8_VAL,
> + .gain = ST_ACCEL_8_FS_AVL_8_GAIN,
> + },
> + [3] = {
> + .num = ST_ACCEL_FS_AVL_16G,
> + .value = ST_ACCEL_1_FS_AVL_16_VAL,
> + .gain = ST_ACCEL_8_FS_AVL_16_GAIN,
> + },
> + },
> + },
> + .drdy_irq = {
> + .addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
> + .mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK,
> + .mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK,
> + .addr_ihl = ST_ACCEL_1_IHL_IRQ_ADDR,
> + .mask_ihl = ST_ACCEL_1_IHL_IRQ_MASK,
> + .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
> + },
> + .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
> + .bootime = 2,
> + },
> };
>
> static int st_accel_read_raw(struct iio_dev *indio_dev,
> diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
> index e9d427a..c0f8867 100644
> --- a/drivers/iio/accel/st_accel_i2c.c
> +++ b/drivers/iio/accel/st_accel_i2c.c
> @@ -84,6 +84,10 @@ static const struct of_device_id st_accel_of_match[] = {
> .compatible = "st,lis3l02dq",
> .data = LIS3L02DQ_ACCEL_DEV_NAME,
> },
> + {
> + .compatible = "st,lng2dm-accel",
> + .data = LNG2DM_ACCEL_DEV_NAME,
> + },
> {},
> };
> MODULE_DEVICE_TABLE(of, st_accel_of_match);
> @@ -135,6 +139,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
> { LSM303AGR_ACCEL_DEV_NAME },
> { LIS2DH12_ACCEL_DEV_NAME },
> { LIS3L02DQ_ACCEL_DEV_NAME },
> + { LNG2DM_ACCEL_DEV_NAME },
> {},
> };
> MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
> diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
> index efd4394..c25ac50 100644
> --- a/drivers/iio/accel/st_accel_spi.c
> +++ b/drivers/iio/accel/st_accel_spi.c
> @@ -60,6 +60,7 @@ static const struct spi_device_id st_accel_id_table[] = {
> { LSM303AGR_ACCEL_DEV_NAME },
> { LIS2DH12_ACCEL_DEV_NAME },
> { LIS3L02DQ_ACCEL_DEV_NAME },
> + { LNG2DM_ACCEL_DEV_NAME },
> {},
> };
> MODULE_DEVICE_TABLE(spi, st_accel_id_table);
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 2/2] Documentation: dt: iio: accel: add lng2dm sensor device binding
From: Jonathan Cameron @ 2016-10-30 16:53 UTC (permalink / raw)
To: Lorenzo Bianconi
Cc: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, lorenzo.bianconi-qxv4g6HH51o
In-Reply-To: <1477429744-27713-3-git-send-email-lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
On 25/10/16 22:09, Lorenzo Bianconi wrote:
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi-qxv4g6HH51o@public.gmane.org>
Applied.
> ---
> Documentation/devicetree/bindings/iio/st-sensors.txt | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt
> index e41fe34..c040c9a 100644
> --- a/Documentation/devicetree/bindings/iio/st-sensors.txt
> +++ b/Documentation/devicetree/bindings/iio/st-sensors.txt
> @@ -42,6 +42,7 @@ Accelerometers:
> - st,lsm303agr-accel
> - st,lis2dh12-accel
> - st,h3lis331dl-accel
> +- st,lng2dm-accel
>
> Gyroscopes:
> - st,l3g4200d-gyro
>
^ permalink raw reply
* Re: [PATCH 1/4] mfd: ti_am335x_tscadc: store physical address
From: Jonathan Cameron @ 2016-10-30 17:05 UTC (permalink / raw)
To: Lee Jones, Mugunthan V N
Cc: linux-iio, Tony Lindgren, Rob Herring, Mark Rutland, Russell King,
Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
Vignesh R, Andrew F . Davis, linux-omap, devicetree,
linux-arm-kernel, linux-kernel, Sekhar Nori, Peter Ujfalusi
In-Reply-To: <20161026121730.GS8574@dell>
On 26/10/16 13:17, Lee Jones wrote:
> On Fri, 30 Sep 2016, Mugunthan V N wrote:
>
>> On Wednesday 28 September 2016 01:10 AM, Lee Jones wrote:
>>> On Wed, 21 Sep 2016, Mugunthan V N wrote:
>>>
>>>> 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 <mugunthanvnm@ti.com>
>>>> ---
>>>> 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;
>>>
>>> This is unusual. Can't you use a virt_to_phys() variant instead?
>>>
>>
>> I tried using virt_to_phys(), but its not working for me.
>> Also saw many drivers uses like this to get physical address
>> ("git grep -n " res->start;" drivers/*").
>
> Very well:
>
> For my own reference:
> Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
>
> Let me know how you wish this set to be handled.
I'm happy to pick up the whole series. There are some more mfd
header changes in patch 2 but as they only add defines, I
don't mind that much if I don't an Ack from you on those
(btw this got to V3 but as patch 1 didn't change I'll carry
your ack forwards).
Do you want an immutable branch? Seems unlikely to cause
much trouble even if there is a merge issue on all 10ish
lines of mfd code in the next merge window.
Jonathan
>
^ permalink raw reply
* Re: [PATCH v6 4/8] drivers:input:tsc2007: add iio interface to read external ADC input and temperature
From: Jonathan Cameron @ 2016-10-30 17:11 UTC (permalink / raw)
To: H. Nikolaus Schaller, Dmitry Torokhov, Rob Herring, Mark Rutland,
Benoît Cousson, Tony Lindgren, Russell King, Arnd Bergmann,
Michael Welling, Mika Penttilä, Javier Martinez Canillas,
Igor Grinberg, Sebastian Reichel, Andrew F. Davis, Mark Brown,
Hans de Goede, Sangwon Jee
Cc: linux-input, devicetree, linux-kernel, linux-omap, letux-kernel,
linux-iio, kernel
In-Reply-To: <810571cc83159c674116f6f55624ca6227510f96.1477557860.git.hns@goldelico.com>
On 27/10/16 09:44, H. Nikolaus Schaller wrote:
> The tsc2007 chip not only has a resistive touch screen controller but
> also an external AUX adc imput which can be used for an ambient
> light sensor, battery voltage monitoring or any general purpose.
>
> Additionally it can measure the chip temperature.
>
> This extension provides an iio interface for these adc channels.
>
> Since it is not wasting much resources and is very straightforward,
> we simply provide all other adc channels as optional iio interfaces
> as weel. This can be used for debugging or special applications.
>
> This patch also splits the tsc2007 driver in several source files:
> tsc2007.h -- constants, structs and stubs
> tsc2007_core.c -- functional parts of the original driver
> tsc2007_iio.c -- the optional iio stuff
>
> Makefile magic allows to conditionally link the iio
> stuff if CONFIG_IIO=y in a way that it works with
> CONFIG_TOUCHSCREEN_TSC2007=m.
>
> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
oops. Missed this newer version. Replied to V5. Most of the comments are still
relevant I think.
J
> ---
> drivers/input/touchscreen/Makefile | 2 +
> drivers/input/touchscreen/tsc2007.h | 129 ++++++++++++++++++
> .../touchscreen/{tsc2007.c => tsc2007_core.c} | 127 ++++++-----------
> drivers/input/touchscreen/tsc2007_iio.c | 151 +++++++++++++++++++++
> 4 files changed, 320 insertions(+), 89 deletions(-)
> create mode 100644 drivers/input/touchscreen/tsc2007.h
> rename drivers/input/touchscreen/{tsc2007.c => tsc2007_core.c} (84%)
> create mode 100644 drivers/input/touchscreen/tsc2007_iio.c
>
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index 81b8645..d932e2d 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -80,6 +80,8 @@ obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o
> obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o
> obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o
> obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o
> +tsc2007-y := tsc2007_core.o
> +tsc2007-$(CONFIG_IIO) += tsc2007_iio.o
> obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
> obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
> obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
> diff --git a/drivers/input/touchscreen/tsc2007.h b/drivers/input/touchscreen/tsc2007.h
> new file mode 100644
> index 0000000..87d5ce5
> --- /dev/null
> +++ b/drivers/input/touchscreen/tsc2007.h
> @@ -0,0 +1,129 @@
> +/*
> + * drivers/input/touchscreen/tsc2007.h
> + *
> + * Copyright (c) 2008 MtekVision Co., Ltd.
> + * Kwangwoo Lee <kwlee@mtekvision.com>
> + *
> + * Using code from:
> + * - ads7846.c
> + * Copyright (c) 2005 David Brownell
> + * Copyright (c) 2006 Nokia Corporation
> + * - corgi_ts.c
> + * Copyright (C) 2004-2005 Richard Purdie
> + * - omap_ts.[hc], ads7846.h, ts_osk.c
> + * Copyright (C) 2002 MontaVista Software
> + * Copyright (C) 2004 Texas Instruments
> + * Copyright (C) 2005 Dirk Behme
> + *
> + * 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.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c/tsc2007.h>
> +#include <linux/of_device.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/input/touchscreen.h>
> +
> +#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
> +#define TSC2007_MEASURE_AUX (0x2 << 4)
> +#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
> +#define TSC2007_ACTIVATE_XN (0x8 << 4)
> +#define TSC2007_ACTIVATE_YN (0x9 << 4)
> +#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
> +#define TSC2007_SETUP (0xb << 4)
> +#define TSC2007_MEASURE_X (0xc << 4)
> +#define TSC2007_MEASURE_Y (0xd << 4)
> +#define TSC2007_MEASURE_Z1 (0xe << 4)
> +#define TSC2007_MEASURE_Z2 (0xf << 4)
> +
> +#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
> +#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
> +#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
> +#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
> +
> +#define TSC2007_12BIT (0x0 << 1)
> +#define TSC2007_8BIT (0x1 << 1)
> +
> +#define MAX_12BIT ((1 << 12) - 1)
> +
> +#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
> +
> +#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
> +#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
> +#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
> +#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
> +#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
> +
> +struct ts_event {
> + u16 x;
> + u16 y;
> + u16 z1, z2;
> +};
> +
> +struct tsc2007 {
> + struct input_dev *input;
> + char phys[32];
> +
> + struct i2c_client *client;
> +
> + u16 model;
> + u16 x_plate_ohms;
> +
> + struct touchscreen_properties prop;
> +
> + bool report_resistance;
> + u16 min_x;
> + u16 min_y;
> + u16 max_x;
> + u16 max_y;
> + u16 max_rt;
> + unsigned long poll_period; /* in jiffies */
> + int fuzzx;
> + int fuzzy;
> + int fuzzz;
> +
> + unsigned int gpio;
> + int irq;
> +
> + wait_queue_head_t wait;
> + bool stopped;
> + bool pendown;
> +
> + int (*get_pendown_state)(struct device *);
> + void (*clear_penirq)(void);
> +
> + struct mutex mlock;
> +#ifdef CONFIG_IIO
> + void *private;
> +#endif
> +};
> +
> +int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd);
> +u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
> + struct ts_event *tc);
> +bool tsc2007_is_pen_down(struct tsc2007 *ts);
> +
> +#ifdef CONFIG_IIO
> +
> +/* defined in tsc2007_iio.c */
> +int tsc2007_iio_configure(struct tsc2007 *ts);
> +void tsc2007_iio_unconfigure(struct tsc2007 *ts);
> +
> +#else /* CONFIG_IIO */
> +
> +static inline int tsc2007_iio_configure(struct tsc2007 *ts)
> +{
> + return 0;
> +}
> +static inline void tsc2007_iio_unconfigure(struct tsc2007 *ts)
> +{
> +}
> +
> +#endif /* CONFIG_IIO */
> diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007_core.c
> similarity index 84%
> rename from drivers/input/touchscreen/tsc2007.c
> rename to drivers/input/touchscreen/tsc2007_core.c
> index 5e3c4bf..56c9a52 100644
> --- a/drivers/input/touchscreen/tsc2007.c
> +++ b/drivers/input/touchscreen/tsc2007_core.c
> @@ -20,87 +20,9 @@
> * published by the Free Software Foundation.
> */
>
> -#include <linux/module.h>
> -#include <linux/slab.h>
> -#include <linux/input.h>
> -#include <linux/interrupt.h>
> -#include <linux/i2c.h>
> -#include <linux/i2c/tsc2007.h>
> -#include <linux/of_device.h>
> -#include <linux/of.h>
> -#include <linux/of_gpio.h>
> -#include <linux/input/touchscreen.h>
> -
> -#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
> -#define TSC2007_MEASURE_AUX (0x2 << 4)
> -#define TSC2007_MEASURE_TEMP1 (0x4 << 4)
> -#define TSC2007_ACTIVATE_XN (0x8 << 4)
> -#define TSC2007_ACTIVATE_YN (0x9 << 4)
> -#define TSC2007_ACTIVATE_YP_XN (0xa << 4)
> -#define TSC2007_SETUP (0xb << 4)
> -#define TSC2007_MEASURE_X (0xc << 4)
> -#define TSC2007_MEASURE_Y (0xd << 4)
> -#define TSC2007_MEASURE_Z1 (0xe << 4)
> -#define TSC2007_MEASURE_Z2 (0xf << 4)
> -
> -#define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2)
> -#define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2)
> -#define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2)
> -#define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2)
> -
> -#define TSC2007_12BIT (0x0 << 1)
> -#define TSC2007_8BIT (0x1 << 1)
> -
> -#define MAX_12BIT ((1 << 12) - 1)
> -
> -#define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
> -
> -#define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y)
> -#define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
> -#define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
> -#define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X)
> -#define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
> -
> -struct ts_event {
> - u16 x;
> - u16 y;
> - u16 z1, z2;
> -};
> -
> -struct tsc2007 {
> - struct input_dev *input;
> - char phys[32];
> -
> - struct i2c_client *client;
> -
> - u16 model;
> - u16 x_plate_ohms;
> -
> - struct touchscreen_properties prop;
> -
> - bool report_resistance;
> - u16 min_x;
> - u16 min_y;
> - u16 max_x;
> - u16 max_y;
> - u16 max_rt;
> - unsigned long poll_period; /* in jiffies */
> - int fuzzx;
> - int fuzzy;
> - int fuzzz;
> +#include "tsc2007.h"
>
> - unsigned gpio;
> - int irq;
> -
> - wait_queue_head_t wait;
> - bool stopped;
> - bool pendown;
> -
> - int (*get_pendown_state)(struct device *);
> - void (*clear_penirq)(void);
> -};
> -
> -static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
> +int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
> {
> s32 data;
> u16 val;
> @@ -121,6 +43,7 @@ static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
>
> return val;
> }
> +EXPORT_SYMBOL(tsc2007_xfer);
>
> static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
> {
> @@ -138,7 +61,7 @@ static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
> tsc2007_xfer(tsc, PWRDOWN);
> }
>
> -static u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
> +u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
> struct ts_event *tc)
> {
> u32 rt = 0;
> @@ -158,8 +81,9 @@ static u32 tsc2007_calculate_resistance(struct tsc2007 *tsc,
>
> return rt;
> }
> +EXPORT_SYMBOL(tsc2007_calculate_resistance);
>
> -static bool tsc2007_is_pen_down(struct tsc2007 *ts)
> +bool tsc2007_is_pen_down(struct tsc2007 *ts)
> {
> /*
> * NOTE: We can't rely on the pressure to determine the pen down
> @@ -180,6 +104,7 @@ static bool tsc2007_is_pen_down(struct tsc2007 *ts)
>
> return ts->get_pendown_state(&ts->client->dev);
> }
> +EXPORT_SYMBOL(tsc2007_is_pen_down);
>
> static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
> {
> @@ -192,7 +117,10 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
> while (!ts->stopped && tsc2007_is_pen_down(ts)) {
>
> /* pen is down, continue with the measurement */
> +
> + mutex_lock(&ts->mlock);
> tsc2007_read_values(ts, &tc);
> + mutex_unlock(&ts->mlock);
>
> rt = tsc2007_calculate_resistance(ts, &tc);
>
> @@ -450,7 +378,8 @@ static void tsc2007_call_exit_platform_hw(void *data)
> static int tsc2007_probe(struct i2c_client *client,
> const struct i2c_device_id *id)
> {
> - const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
> + const struct tsc2007_platform_data *pdata =
> + dev_get_platdata(&client->dev);
> struct tsc2007 *ts;
> struct input_dev *input_dev;
> int err;
> @@ -472,7 +401,13 @@ static int tsc2007_probe(struct i2c_client *client,
> ts->client = client;
> ts->irq = client->irq;
> ts->input = input_dev;
> +
> + err = tsc2007_iio_configure(ts);
> + if (err < 0)
> + return err;
> +
> init_waitqueue_head(&ts->wait);
> + mutex_init(&ts->mlock);
>
> snprintf(ts->phys, sizeof(ts->phys),
> "%s/input0", dev_name(&client->dev));
> @@ -494,7 +429,7 @@ static int tsc2007_probe(struct i2c_client *client,
> if (pdata) {
> err = tsc2007_probe_pdev(client, ts, pdata, id);
> if (err)
> - return err;
> + goto probe_err;
> input_set_abs_params(input_dev, ABS_X, 0, ts->max_x-ts->min_x,
> ts->fuzzx, 0);
> input_set_abs_params(input_dev, ABS_Y, 0, ts->max_y-ts->min_y,
> @@ -504,7 +439,7 @@ static int tsc2007_probe(struct i2c_client *client,
> } else {
> err = tsc2007_probe_dt(client, ts);
> if (err)
> - return err;
> + goto probe_err;
> }
>
> if (pdata) {
> @@ -516,7 +451,7 @@ static int tsc2007_probe(struct i2c_client *client,
> dev_err(&client->dev,
> "Failed to register exit_platform_hw action, %d\n",
> err);
> - return err;
> + goto probe_err;
> }
> }
>
> @@ -533,7 +468,7 @@ static int tsc2007_probe(struct i2c_client *client,
> if (err) {
> dev_err(&client->dev, "Failed to request irq %d: %d\n",
> ts->irq, err);
> - return err;
> + goto probe_err;
> }
>
> tsc2007_stop(ts);
> @@ -543,17 +478,30 @@ static int tsc2007_probe(struct i2c_client *client,
> if (err < 0) {
> dev_err(&client->dev,
> "Failed to setup chip: %d\n", err);
> - return err; /* usually, chip does not respond */
> + goto probe_err; /* chip does not respond */
> }
>
> err = input_register_device(input_dev);
> if (err) {
> dev_err(&client->dev,
> "Failed to register input device: %d\n", err);
> - return err;
> + goto probe_err;
> }
>
> return 0;
> +
> +probe_err:
> + tsc2007_iio_unconfigure(ts);
> + return err;
> +}
> +
> +static int tsc2007_remove(struct i2c_client *client)
> +{
> + struct tsc2007 *ts = i2c_get_clientdata(client);
> +
> + tsc2007_iio_unconfigure(ts);
> + input_unregister_device(ts->input);
> + return 0;
> }
>
> static const struct i2c_device_id tsc2007_idtable[] = {
> @@ -578,6 +526,7 @@ static struct i2c_driver tsc2007_driver = {
> },
> .id_table = tsc2007_idtable,
> .probe = tsc2007_probe,
> + .remove = tsc2007_remove,
> };
>
> module_i2c_driver(tsc2007_driver);
> diff --git a/drivers/input/touchscreen/tsc2007_iio.c b/drivers/input/touchscreen/tsc2007_iio.c
> new file mode 100644
> index 0000000..0375d8b
> --- /dev/null
> +++ b/drivers/input/touchscreen/tsc2007_iio.c
> @@ -0,0 +1,151 @@
> +/*
> + * drivers/input/touchscreen/tsc2007_iio.c
> + *
> + * Copyright (c) 2016 Golden Delicious Comp. GmbH&Co. KG
> + * Nikolaus Schaller <hns@goldelico.com>
> + *
> + * 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.
> + */
> +
> +#include "tsc2007.h"
> +#include <linux/iio/iio.h>
> +
> +struct tsc2007_iio {
> + struct tsc2007 *ts;
> +};
> +
> +#define TSC2007_CHAN_IIO(_chan, _name, _type, _chan_info) \
> +{ \
> + .datasheet_name = _name, \
> + .type = _type, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(_chan_info), \
> + .indexed = 1, \
> + .channel = _chan, \
> +}
> +
> +static const struct iio_chan_spec tsc2007_iio_channel[] = {
> + TSC2007_CHAN_IIO(0, "x", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(1, "y", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(2, "z1", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(3, "z2", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(4, "adc", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(5, "rt", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), /* Ohms? */
> + TSC2007_CHAN_IIO(6, "pen", IIO_PRESSURE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(7, "temp0", IIO_TEMP, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(8, "temp1", IIO_TEMP, IIO_CHAN_INFO_RAW),
> +};
> +
> +static int tsc2007_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val, int *val2, long mask)
> +{
> + struct tsc2007_iio *iio = iio_priv(indio_dev);
> + struct tsc2007 *tsc = iio->ts;
> + int adc_chan = chan->channel;
> + int ret = 0;
> +
> + if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel))
> + return -EINVAL;
> +
> + if (mask != IIO_CHAN_INFO_RAW)
> + return -EINVAL;
> +
> + mutex_lock(&tsc->mlock);
> +
> + switch (chan->channel) {
> + case 0:
> + *val = tsc2007_xfer(tsc, READ_X);
> + break;
> + case 1:
> + *val = tsc2007_xfer(tsc, READ_Y);
> + break;
> + case 2:
> + *val = tsc2007_xfer(tsc, READ_Z1);
> + break;
> + case 3:
> + *val = tsc2007_xfer(tsc, READ_Z2);
> + break;
> + case 4:
> + *val = tsc2007_xfer(tsc, (ADC_ON_12BIT | TSC2007_MEASURE_AUX));
> + break;
> + case 5: {
> + struct ts_event tc;
> +
> + tc.x = tsc2007_xfer(tsc, READ_X);
> + tc.z1 = tsc2007_xfer(tsc, READ_Z1);
> + tc.z2 = tsc2007_xfer(tsc, READ_Z2);
> + *val = tsc2007_calculate_resistance(tsc, &tc);
> + break;
> + }
> + case 6:
> + *val = tsc2007_is_pen_down(tsc);
> + break;
> + case 7:
> + *val = tsc2007_xfer(tsc,
> + (ADC_ON_12BIT | TSC2007_MEASURE_TEMP0));
> + break;
> + case 8:
> + *val = tsc2007_xfer(tsc,
> + (ADC_ON_12BIT | TSC2007_MEASURE_TEMP1));
> + break;
> + }
> +
> + /* Prepare for next touch reading - power down ADC, enable PENIRQ */
> + tsc2007_xfer(tsc, PWRDOWN);
> +
> + mutex_unlock(&tsc->mlock);
> +
> + ret = IIO_VAL_INT;
> +
> + return ret;
> +}
> +
> +static const struct iio_info tsc2007_iio_info = {
> + .read_raw = tsc2007_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +int tsc2007_iio_configure(struct tsc2007 *ts)
> +{
> + int err;
> + struct iio_dev *indio_dev;
> + struct tsc2007_iio *iio;
> +
> + indio_dev = devm_iio_device_alloc(&ts->client->dev,
> + sizeof(struct tsc2007_iio));
> + if (!indio_dev) {
> + dev_err(&ts->client->dev, "iio_device_alloc failed\n");
> + return -ENOMEM;
> + }
> +
> + iio = iio_priv(indio_dev);
> + iio->ts = ts;
> + ts->private = (void *) indio_dev;
> +
> + indio_dev->name = "tsc2007";
> + indio_dev->dev.parent = &ts->client->dev;
> + indio_dev->info = &tsc2007_iio_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->channels = tsc2007_iio_channel;
> + indio_dev->num_channels = ARRAY_SIZE(tsc2007_iio_channel);
> +
> + err = iio_device_register(indio_dev);
> + if (err < 0) {
> + dev_err(&ts->client->dev, "iio_device_register() failed: %d\n",
> + err);
> + return err;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(tsc2007_iio_configure);
> +
> +void tsc2007_iio_unconfigure(struct tsc2007 *ts)
> +{
> + struct iio_dev *indio_dev = ts->private;
> +
> + iio_device_unregister(indio_dev);
> +}
> +EXPORT_SYMBOL(tsc2007_iio_unconfigure);
>
^ permalink raw reply
* Re: [PATCH V3 2/2] iio: adc: spmi-vadc: Changes to support different scaling
From: Jonathan Cameron @ 2016-10-30 17:13 UTC (permalink / raw)
To: Phani A, Rama Krishna, Stanimir Varbanov, linux-iio, devicetree
Cc: robh, linux-arm-msm, smohanad, mgautam, sivaa, knaack.h, lars,
pmeerw, Julia.Lawall
In-Reply-To: <be7d758f-1698-1b6d-e6a1-a2910a914737@codeaurora.org>
On 27/10/16 18:37, Phani A, Rama Krishna wrote:
> Hi Stan,
>
> On 27-Oct-16 4:48 PM, Stanimir Varbanov wrote:
>> Hi Rama,
>>
>> On 10/26/2016 05:41 PM, Rama Krishna Phani A wrote:
>>> Polling can also be used for End of conversion completion. Implement logic
>>> to choose either polling or interrupt for End of conversion completion.
>>> Scaling can be done on the voltage to report adc code in physical units.
>>> Add changes to support different scale functions to convert adc code to
>>> physical units.
>>>
>>> Signed-off-by: Rama Krishna Phani A <rphani@codeaurora.org>
>>> ---
>>> .../devicetree/bindings/iio/adc/qcom,spmi-vadc.txt | 14 ++
>>> drivers/iio/adc/qcom-spmi-vadc.c | 263 +++++++++++++++++----
>>> 2 files changed, 236 insertions(+), 41 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
>>> index 0fb4613..39e31c0e 100644
>>> --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
>>> +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
>>> @@ -37,6 +37,12 @@ VADC node:
>>> Value type: <prop-encoded-array>
>>> Definition: End of conversion interrupt.
>>>
>>> +- qcom,vadc-poll-eoc:
>>> + Usage: optional
>>> + Value type: <bool>
>>> + Definition: Use polling instead of interrupts for End of Conversion
>>> + completion.
>>
>> Why you need to add such a flag in DT?
>>
>> The DT should describe hardware details not how the driver will choose
>> pooling vs interrupt.
>>
>> On which use-case you would prefer pooling?
>>
>
> Few PMIC's support interrupt functionality for ADC where as few
> PMIC's dont support. Based on the functionality that is supported in
> hardware we choose whether to go for polling or for interrupt.
Can't use the usual trick of an optional interrupt in DT?
If it's there we try to use it, if not then fall back to polling?
>>> +
>>> Channel node properties:
>>>
>>> - reg:
>>> @@ -92,6 +98,14 @@ Channel node properties:
>>> Valid values are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512
>>> If property is not found, 1 sample will be used.
>>>
>>> +- qcom,scale-function:
>>> + Usage: optional
>>> + Value type: <u32>
>>> + Definition: Scaling function used to convert raw ADC code to
>>> + units specific to a given channel. Scaled units can be
>>> + microvolts, millidegC.Valid values are: 0, 1, 2, 3, 4.
>>> + If property is not found, 0 scaling will be used.
>>
>> This shouldn't be in DT binding. Just select the scale function for each
>> channel in the driver based on compatible property.
>>
>>
> Ok ., Will remove this binding from DT, implement logic in driver and will post next patch.
>
> Thanks,
> Ramakrishna
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH V2 2/2] ARM: dts: bcm283x: fix typo in mailbox address
From: Stefan Wahren @ 2016-10-30 17:22 UTC (permalink / raw)
To: Mark Rutland, Rob Herring, Stephen Warren, Eric Anholt
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
afaerber-l3A5Bk7waGM, Stefan Wahren
In-Reply-To: <6b13e96d-78d1-fcab-4273-803310ae4da6-l3A5Bk7waGM@public.gmane.org>
The address of the mailbox node in the bcm283x.dtsi also has a typo.
So fix it accordingly.
Signed-off-by: Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org>
Reviewed-by: Andreas Färber <afaerber-l3A5Bk7waGM@public.gmane.org>
Fixes: 05b682b7a3b2 ("ARM: bcm2835: dt: Add the mailbox to the device tree")
---
Changes in V2:
* fixed commit message as reported by Andreas
arch/arm/boot/dts/bcm283x.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi
index 46d46d8..74dd21b 100644
--- a/arch/arm/boot/dts/bcm283x.dtsi
+++ b/arch/arm/boot/dts/bcm283x.dtsi
@@ -104,7 +104,7 @@
reg = <0x7e104000 0x10>;
};
- mailbox: mailbox@7e00b800 {
+ mailbox: mailbox@7e00b880 {
compatible = "brcm,bcm2835-mbox";
reg = <0x7e00b880 0x40>;
interrupts = <0 1>;
--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH] clk: cdce925: add support for CDCE913, CDCE937, and CDCE949
From: Akinobu Mita @ 2016-10-30 17:30 UTC (permalink / raw)
To: linux-clk-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Akinobu Mita, Mike Looijmans, Michael Turquette, Stephen Boyd
The CDCE925 is a member of the CDCE(L)9xx programmable clock generator
family. There are also CDCE913, CDCE937, CDCE949 which have different
number of PLLs and outputs.
The clk-cdce925 driver supports only CDCE925 in the family. This adds
support for the CDCE913, CDCE937, CDCE949, too.
Signed-off-by: Akinobu Mita <akinobu.mita-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Cc: Mike Looijmans <mike.looijmans-Oq418RWZeHk@public.gmane.org>
Cc: Michael Turquette <mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: Stephen Boyd <sboyd-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
---
.../devicetree/bindings/clock/ti,cdce925.txt | 15 ++-
drivers/clk/Kconfig | 11 ++-
drivers/clk/clk-cdce925.c | 106 ++++++++++++++++-----
3 files changed, 99 insertions(+), 33 deletions(-)
diff --git a/Documentation/devicetree/bindings/clock/ti,cdce925.txt b/Documentation/devicetree/bindings/clock/ti,cdce925.txt
index 4c7669a..0d01f2d 100644
--- a/Documentation/devicetree/bindings/clock/ti,cdce925.txt
+++ b/Documentation/devicetree/bindings/clock/ti,cdce925.txt
@@ -1,15 +1,22 @@
-Binding for TO CDCE925 programmable I2C clock synthesizers.
+Binding for TI CDCE913/925/937/949 programmable I2C clock synthesizers.
Reference
This binding uses the common clock binding[1].
[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
-[2] http://www.ti.com/product/cdce925
+[2] http://www.ti.com/product/cdce913
+[3] http://www.ti.com/product/cdce925
+[4] http://www.ti.com/product/cdce937
+[5] http://www.ti.com/product/cdce949
The driver provides clock sources for each output Y1 through Y5.
Required properties:
- - compatible: Shall be "ti,cdce925"
+ - compatible: Shall be one of the following:
+ - "ti,cdce913": 1-PLL, 3 Outputs
+ - "ti,cdce925": 2-PLL, 5 Outputs
+ - "ti,cdce937": 3-PLL, 7 Outputs
+ - "ti,cdce949": 4-PLL, 9 Outputs
- reg: I2C device address.
- clocks: Points to a fixed parent clock that provides the input frequency.
- #clock-cells: From common clock bindings: Shall be 1.
@@ -18,7 +25,7 @@ Optional properties:
- xtal-load-pf: Crystal load-capacitor value to fine-tune performance on a
board, or to compensate for external influences.
-For both PLL1 and PLL2 an optional child node can be used to specify spread
+For all PLL1, PLL2, ... an optional child node can be used to specify spread
spectrum clocking parameters for a board.
- spread-spectrum: SSC mode as defined in the data sheet.
- spread-spectrum-center: Use "centered" mode instead of "max" mode. When
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index e99a42f..bc0a6e6 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -104,16 +104,17 @@ config COMMON_CLK_CDCE706
This driver supports TI CDCE706 programmable 3-PLL clock synthesizer.
config COMMON_CLK_CDCE925
- tristate "Clock driver for TI CDCE925 devices"
+ tristate "Clock driver for TI CDCE913/925/937/949 devices"
depends on I2C
depends on OF
select REGMAP_I2C
help
---help---
- This driver supports the TI CDCE925 programmable clock synthesizer.
- The chip contains two PLLs with spread-spectrum clocking support and
- five output dividers. The driver only supports the following setup,
- and uses a fixed setting for the output muxes.
+ This driver supports the TI CDCE913/925/937/949 programmable clock
+ synthesizer. Each chip has different number of PLLs and outputs.
+ For example, the CDCE925 contains two PLLs with spread-spectrum
+ clocking support and five output dividers. The driver only supports
+ the following setup, and uses a fixed setting for the output muxes.
Y1 is derived from the input clock
Y2 and Y3 derive from PLL1
Y4 and Y5 derive from PLL2
diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c
index b8459c1..fbac08d 100644
--- a/drivers/clk/clk-cdce925.c
+++ b/drivers/clk/clk-cdce925.c
@@ -1,8 +1,8 @@
/*
- * Driver for TI Dual PLL CDCE925 clock synthesizer
+ * Driver for TI Multi PLL CDCE913/925/937/949 clock synthesizer
*
- * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1
- * and Y4/Y5 to PLL2. PLL frequency is set on a first-come-first-serve
+ * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1,
+ * Y4/Y5 to PLL2, and so on. PLL frequency is set on a first-come-first-serve
* basis. Clients can directly request any frequency that the chip can
* deliver using the standard clk framework. In addition, the device can
* be configured and activated via the devicetree.
@@ -19,11 +19,32 @@
#include <linux/slab.h>
#include <linux/gcd.h>
-/* The chip has 2 PLLs which can be routed through dividers to 5 outputs.
+/* Each chip has different number of PLLs and outputs, for example:
+ * The CECE925 has 2 PLLs which can be routed through dividers to 5 outputs.
* Model this as 2 PLL clocks which are parents to the outputs.
*/
-#define NUMBER_OF_PLLS 2
-#define NUMBER_OF_OUTPUTS 5
+
+enum {
+ CDCE913,
+ CDCE925,
+ CDCE937,
+ CDCE949,
+};
+
+struct clk_cdce925_chip_info {
+ int num_plls;
+ int num_outputs;
+};
+
+static const struct clk_cdce925_chip_info clk_cdce925_chip_info_tbl[] = {
+ [CDCE913] = { .num_plls = 1, .num_outputs = 3 },
+ [CDCE925] = { .num_plls = 2, .num_outputs = 5 },
+ [CDCE937] = { .num_plls = 3, .num_outputs = 7 },
+ [CDCE949] = { .num_plls = 4, .num_outputs = 9 },
+};
+
+#define MAX_NUMBER_OF_PLLS 4
+#define MAX_NUMBER_OF_OUTPUTS 9
#define CDCE925_REG_GLOBAL1 0x01
#define CDCE925_REG_Y1SPIPDIVH 0x02
@@ -43,7 +64,7 @@ struct clk_cdce925_output {
struct clk_hw hw;
struct clk_cdce925_chip *chip;
u8 index;
- u16 pdiv; /* 1..127 for Y2-Y5; 1..1023 for Y1 */
+ u16 pdiv; /* 1..127 for Y2-Y9; 1..1023 for Y1 */
};
#define to_clk_cdce925_output(_hw) \
container_of(_hw, struct clk_cdce925_output, hw)
@@ -60,8 +81,9 @@ struct clk_cdce925_pll {
struct clk_cdce925_chip {
struct regmap *regmap;
struct i2c_client *i2c_client;
- struct clk_cdce925_pll pll[NUMBER_OF_PLLS];
- struct clk_cdce925_output clk[NUMBER_OF_OUTPUTS];
+ const struct clk_cdce925_chip_info *chip_info;
+ struct clk_cdce925_pll pll[MAX_NUMBER_OF_PLLS];
+ struct clk_cdce925_output clk[MAX_NUMBER_OF_OUTPUTS];
};
/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */
@@ -284,6 +306,18 @@ static void cdce925_clk_set_pdiv(struct clk_cdce925_output *data, u16 pdiv)
case 4:
regmap_update_bits(data->chip->regmap, 0x27, 0x7F, pdiv);
break;
+ case 5:
+ regmap_update_bits(data->chip->regmap, 0x36, 0x7F, pdiv);
+ break;
+ case 6:
+ regmap_update_bits(data->chip->regmap, 0x37, 0x7F, pdiv);
+ break;
+ case 7:
+ regmap_update_bits(data->chip->regmap, 0x46, 0x7F, pdiv);
+ break;
+ case 8:
+ regmap_update_bits(data->chip->regmap, 0x47, 0x7F, pdiv);
+ break;
}
}
@@ -302,6 +336,14 @@ static void cdce925_clk_activate(struct clk_cdce925_output *data)
case 4:
regmap_update_bits(data->chip->regmap, 0x24, 0x03, 0x03);
break;
+ case 5:
+ case 6:
+ regmap_update_bits(data->chip->regmap, 0x34, 0x03, 0x03);
+ break;
+ case 7:
+ case 8:
+ regmap_update_bits(data->chip->regmap, 0x44, 0x03, 0x03);
+ break;
}
}
@@ -474,15 +516,6 @@ static const struct clk_ops cdce925_clk_y1_ops = {
.set_rate = cdce925_clk_y1_set_rate,
};
-
-static struct regmap_config cdce925_regmap_config = {
- .name = "configuration0",
- .reg_bits = 8,
- .val_bits = 8,
- .cache_type = REGCACHE_RBTREE,
- .max_register = 0x2F,
-};
-
#define CDCE925_I2C_COMMAND_BLOCK_TRANSFER 0x00
#define CDCE925_I2C_COMMAND_BYTE_TRANSFER 0x80
@@ -582,13 +615,19 @@ static int cdce925_probe(struct i2c_client *client,
struct clk_cdce925_chip *data;
struct device_node *node = client->dev.of_node;
const char *parent_name;
- const char *pll_clk_name[NUMBER_OF_PLLS] = {NULL,};
+ const char *pll_clk_name[MAX_NUMBER_OF_PLLS] = {NULL,};
struct clk_init_data init;
u32 value;
int i;
int err;
struct device_node *np_output;
char child_name[6];
+ struct regmap_config cdce925_regmap_config = {
+ .name = "configuration0",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ };
dev_dbg(&client->dev, "%s\n", __func__);
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
@@ -596,6 +635,9 @@ static int cdce925_probe(struct i2c_client *client,
return -ENOMEM;
data->i2c_client = client;
+ data->chip_info = &clk_cdce925_chip_info_tbl[id->driver_data];
+ cdce925_regmap_config.max_register = CDCE925_OFFSET_PLL +
+ data->chip_info->num_plls * 0x10 - 1;
data->regmap = devm_regmap_init(&client->dev, ®map_cdce925_bus,
&client->dev, &cdce925_regmap_config);
if (IS_ERR(data->regmap)) {
@@ -626,7 +668,7 @@ static int cdce925_probe(struct i2c_client *client,
init.num_parents = parent_name ? 1 : 0;
/* Register PLL clocks */
- for (i = 0; i < NUMBER_OF_PLLS; ++i) {
+ for (i = 0; i < data->chip_info->num_plls; ++i) {
pll_clk_name[i] = kasprintf(GFP_KERNEL, "%s.pll%d",
client->dev.of_node->name, i);
init.name = pll_clk_name[i];
@@ -684,7 +726,7 @@ static int cdce925_probe(struct i2c_client *client,
init.ops = &cdce925_clk_ops;
init.flags = CLK_SET_RATE_PARENT;
init.num_parents = 1;
- for (i = 1; i < NUMBER_OF_OUTPUTS; ++i) {
+ for (i = 1; i < data->chip_info->num_outputs; ++i) {
init.name = kasprintf(GFP_KERNEL, "%s.Y%d",
client->dev.of_node->name, i+1);
data->clk[i].chip = data;
@@ -702,6 +744,16 @@ static int cdce925_probe(struct i2c_client *client,
/* Mux Y4/5 to PLL2 */
init.parent_names = &pll_clk_name[1];
break;
+ case 5:
+ case 6:
+ /* Mux Y6/7 to PLL3 */
+ init.parent_names = &pll_clk_name[2];
+ break;
+ case 7:
+ case 8:
+ /* Mux Y8/9 to PLL4 */
+ init.parent_names = &pll_clk_name[3];
+ break;
}
err = devm_clk_hw_register(&client->dev, &data->clk[i].hw);
kfree(init.name); /* clock framework made a copy of the name */
@@ -720,7 +772,7 @@ static int cdce925_probe(struct i2c_client *client,
err = 0;
error:
- for (i = 0; i < NUMBER_OF_PLLS; ++i)
+ for (i = 0; i < data->chip_info->num_plls; ++i)
/* clock framework made a copy of the name */
kfree(pll_clk_name[i]);
@@ -728,13 +780,19 @@ static int cdce925_probe(struct i2c_client *client,
}
static const struct i2c_device_id cdce925_id[] = {
- { "cdce925", 0 },
+ { "cdce913", CDCE913 },
+ { "cdce925", CDCE925 },
+ { "cdce937", CDCE937 },
+ { "cdce949", CDCE949 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cdce925_id);
static const struct of_device_id clk_cdce925_of_match[] = {
+ { .compatible = "ti,cdce913" },
{ .compatible = "ti,cdce925" },
+ { .compatible = "ti,cdce937" },
+ { .compatible = "ti,cdce949" },
{ },
};
MODULE_DEVICE_TABLE(of, clk_cdce925_of_match);
@@ -750,5 +808,5 @@ static struct i2c_driver cdce925_driver = {
module_i2c_driver(cdce925_driver);
MODULE_AUTHOR("Mike Looijmans <mike.looijmans-Oq418RWZeHk@public.gmane.org>");
-MODULE_DESCRIPTION("cdce925 driver");
+MODULE_DESCRIPTION("TI CDCE913/925/937/949 driver");
MODULE_LICENSE("GPL");
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH V3 1/2] iio: adc: spmi-vadc: Update changes to support reporting of Raw adc code.
From: Jonathan Cameron @ 2016-10-30 17:35 UTC (permalink / raw)
To: Rama Krishna Phani A, linux-iio, devicetree
Cc: robh, linux-arm-msm, smohanad, mgautam, sivaa, knaack.h, lars,
pmeerw, Julia.Lawall
In-Reply-To: <1477492887-1663-2-git-send-email-rphani@codeaurora.org>
On 26/10/16 15:41, Rama Krishna Phani A wrote:
> Logic to convert adc code to voltage is generic across all channels.
> Different scaling can be done on the obtained voltage to report in physical
> units. Implement separate function for generic conversion logic.
> Scaling functionality can be changed per channel. Update changes to support
> reporting of Raw adc code.
Pleas rewrite this description. Perhaps give examples of the changes
it makes to what is read from the various attributes?
I haven't immediately followed what this change is actually doing.
I 'think' the point here is to not apply the calibration to
the raw adc counts when a true raw read is requested?
There are several unconnected looking changes in here...
>
> Signed-off-by: Rama Krishna Phani A <rphani@codeaurora.org>
> ---
> drivers/iio/adc/qcom-spmi-vadc.c | 54 +++++++++++++++++++++-------------------
> 1 file changed, 28 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
> index c2babe5..ff4d549 100644
> --- a/drivers/iio/adc/qcom-spmi-vadc.c
> +++ b/drivers/iio/adc/qcom-spmi-vadc.c
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
> *
> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License version 2 and
> @@ -84,7 +84,7 @@
> #define VADC_MAX_ADC_CODE 0xa800
>
> #define VADC_ABSOLUTE_RANGE_UV 625000
> -#define VADC_RATIOMETRIC_RANGE_UV 1800000
> +#define VADC_RATIOMETRIC_RANGE 1800
>
> #define VADC_DEF_PRESCALING 0 /* 1:1 */
> #define VADC_DEF_DECIMATION 0 /* 512 */
> @@ -418,7 +418,7 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc)
> u16 read_1, read_2;
> int ret;
>
> - vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE_UV;
> + vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE;
> vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV;
>
> prop = vadc_get_channel(vadc, VADC_REF_1250MV);
> @@ -468,21 +468,30 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc)
> return ret;
> }
>
> -static s32 vadc_calibrate(struct vadc_priv *vadc,
> - const struct vadc_channel_prop *prop, u16 adc_code)
> +static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code,
> + const struct vadc_channel_prop *prop,
> + s64 *scale_voltage)
> {
> - const struct vadc_prescale_ratio *prescale;
> - s64 voltage;
> + *scale_voltage = (adc_code -
> + vadc->graph[prop->calibration].gnd);
> + *scale_voltage *= vadc->graph[prop->calibration].dx;
> + *scale_voltage = div64_s64(*scale_voltage,
> + vadc->graph[prop->calibration].dy);
> + if (prop->calibration == VADC_CALIB_ABSOLUTE)
> + *scale_voltage +=
> + vadc->graph[prop->calibration].dx;
>
> - voltage = adc_code - vadc->graph[prop->calibration].gnd;
> - voltage *= vadc->graph[prop->calibration].dx;
> - voltage = div64_s64(voltage, vadc->graph[prop->calibration].dy);
> + if (*scale_voltage < 0)
> + *scale_voltage = 0;
> +}
>
> - if (prop->calibration == VADC_CALIB_ABSOLUTE)
> - voltage += vadc->graph[prop->calibration].dx;
> +static s64 vadc_scale_fn(struct vadc_priv *vadc,
> + const struct vadc_channel_prop *prop, u16 adc_code)
> +{
> + const struct vadc_prescale_ratio *prescale;
> + s64 voltage = 0;
>
> - if (voltage < 0)
> - voltage = 0;
> + vadc_scale_calib(vadc, adc_code, prop, &voltage);
>
> prescale = &vadc_prescale_ratios[prop->prescale];
>
> @@ -552,11 +561,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
> if (ret)
> break;
>
> - *val = vadc_calibrate(vadc, prop, adc_code);
> + *val = vadc_scale_fn(vadc, prop, adc_code);
>
> - /* 2mV/K, return milli Celsius */
> - *val /= 2;
> - *val -= KELVINMIL_CELSIUSMIL;
> return IIO_VAL_INT;
> case IIO_CHAN_INFO_RAW:
> prop = &vadc->chan_props[chan->address];
> @@ -564,12 +570,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
> if (ret)
> break;
>
> - *val = vadc_calibrate(vadc, prop, adc_code);
> + *val = (int)adc_code;
> return IIO_VAL_INT;
So this is 'more raw'.
> - case IIO_CHAN_INFO_SCALE:
> - *val = 0;
> - *val2 = 1000;
> - return IIO_VAL_INT_PLUS_MICRO;
> default:
> ret = -EINVAL;
> break;
> @@ -616,8 +618,8 @@ struct vadc_channels {
> VADC_CHAN(_dname, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre) \
>
> #define VADC_CHAN_VOLT(_dname, _pre) \
> - VADC_CHAN(_dname, IIO_VOLTAGE, \
> - BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
> + VADC_CHAN(_dname, IIO_VOLTAGE, \
> + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
> _pre) \
It very unusual to report both raw and processed values. Please explain
why that is needed here? It may be valid to maintain backwards compatibility
of ABI. Which would be fine. However if I read the above correctly you are
changing what comes out of reading the raw value so the ABI just changed...
>
> /*
> @@ -850,9 +852,9 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
>
> iio_chan->channel = prop.channel;
> iio_chan->datasheet_name = vadc_chan->datasheet_name;
> + iio_chan->extend_name = child->name;
What's this change?
> iio_chan->info_mask_separate = vadc_chan->info_mask;
> iio_chan->type = vadc_chan->type;
> - iio_chan->indexed = 1;
Or for that matter this one...
> iio_chan->address = index++;
>
> iio_chan++;
>
^ permalink raw reply
* Re: [PATCH V3 2/2] iio: adc: spmi-vadc: Changes to support different scaling
From: Jonathan Cameron @ 2016-10-30 17:39 UTC (permalink / raw)
To: Rama Krishna Phani A, linux-iio, devicetree
Cc: robh, linux-arm-msm, smohanad, mgautam, sivaa, knaack.h, lars,
pmeerw, Julia.Lawall
In-Reply-To: <1477492887-1663-3-git-send-email-rphani@codeaurora.org>
On 26/10/16 15:41, Rama Krishna Phani A wrote:
> Polling can also be used for End of conversion completion. Implement logic
> to choose either polling or interrupt for End of conversion completion.
Why is this change in a patch with the title above? Should be a separate patch.
> Scaling can be done on the voltage to report adc code in physical units.
> Add changes to support different scale functions to convert adc code to
> physical units.
>
> Signed-off-by: Rama Krishna Phani A <rphani@codeaurora.org>
> ---
> .../devicetree/bindings/iio/adc/qcom,spmi-vadc.txt | 14 ++
> drivers/iio/adc/qcom-spmi-vadc.c | 263 +++++++++++++++++----
> 2 files changed, 236 insertions(+), 41 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
> index 0fb4613..39e31c0e 100644
> --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
> +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt
> @@ -37,6 +37,12 @@ VADC node:
> Value type: <prop-encoded-array>
> Definition: End of conversion interrupt.
>
> +- qcom,vadc-poll-eoc:
> + Usage: optional
> + Value type: <bool>
> + Definition: Use polling instead of interrupts for End of Conversion
> + completion.
> +
> Channel node properties:
>
> - reg:
> @@ -92,6 +98,14 @@ Channel node properties:
> Valid values are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512
> If property is not found, 1 sample will be used.
>
> +- qcom,scale-function:
> + Usage: optional
> + Value type: <u32>
> + Definition: Scaling function used to convert raw ADC code to
> + units specific to a given channel. Scaled units can be
> + microvolts, millidegC.Valid values are: 0, 1, 2, 3, 4.
> + If property is not found, 0 scaling will be used.
> +
> NOTE:
>
> Following channels, also known as reference point channels, are used for
> diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
> index ff4d549..6e521a9 100644
> --- a/drivers/iio/adc/qcom-spmi-vadc.c
> +++ b/drivers/iio/adc/qcom-spmi-vadc.c
> @@ -92,6 +92,8 @@
> #define VADC_DEF_AVG_SAMPLES 0 /* 1 sample */
> #define VADC_DEF_CALIB_TYPE VADC_CALIB_ABSOLUTE
>
> +#define VADC_DEF_SCALE_FN SCALE_DEFAULT
> +
> #define VADC_DECIMATION_MIN 512
> #define VADC_DECIMATION_MAX 4096
>
> @@ -100,9 +102,43 @@
>
> #define KELVINMIL_CELSIUSMIL 273150
>
> +#define PMI_CHG_SCALE_1 -138890
> +#define PMI_CHG_SCALE_2 391750000000
> +
> #define VADC_CHAN_MIN VADC_USBIN
> #define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
>
> +/**
> + * enum vadc_scale_fn_type - Scaling function to convert ADC code to
> + * physical scaled units for the channel.
> + * %SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
> + * %SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
> + * Uses a mapping table with 100K pullup.
> + * %SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
> + * %SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
> + * %SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
> + * %SCALE_NONE: Do not use this scaling type.
> + */
> +enum vadc_scale_fn_type {
> + SCALE_DEFAULT = 0,
> + SCALE_THERM_100K_PULLUP,
> + SCALE_PMIC_THERM,
> + SCALE_XOTHERM,
> + SCALE_PMI_CHG_TEMP,
> + SCALE_NONE,
> +};
> +
> +/**
> + * struct vadc_map_pt - Map the graph representation for ADC channel
> + * @x: Represent the ADC digitized code.
> + * @y: Represent the physical data which can be temperature, voltage,
> + * resistance.
> + */
> +struct vadc_map_pt {
> + s32 x;
> + s32 y;
> +};
> +
> /*
> * VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
> * VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
> @@ -148,6 +184,9 @@ struct vadc_prescale_ratio {
> * start of conversion.
> * @avg_samples: ability to provide single result from the ADC
> * that is an average of multiple measurements.
Make sure your indentation matches the rest of the comment..
> + *@scale_function: Represents the scaling function to convert voltage
> + * physical units desired by the client for the channel.
> + * Referenced from enum vadc_scale_fn_type.
> */
> struct vadc_channel_prop {
> unsigned int channel;
> @@ -156,6 +195,7 @@ struct vadc_channel_prop {
> unsigned int prescale;
> unsigned int hw_settle_time;
> unsigned int avg_samples;
> + unsigned int scale_function;
> };
>
> /**
> @@ -197,6 +237,44 @@ struct vadc_priv {
> {.num = 1, .den = 10}
> };
>
> +/* Voltage to temperature */
> +static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
> + {1758, -40},
> + {1742, -35},
> + {1719, -30},
> + {1691, -25},
> + {1654, -20},
> + {1608, -15},
> + {1551, -10},
> + {1483, -5},
> + {1404, 0},
> + {1315, 5},
> + {1218, 10},
> + {1114, 15},
> + {1007, 20},
> + {900, 25},
> + {795, 30},
> + {696, 35},
> + {605, 40},
> + {522, 45},
> + {448, 50},
> + {383, 55},
> + {327, 60},
> + {278, 65},
> + {237, 70},
> + {202, 75},
> + {172, 80},
> + {146, 85},
> + {125, 90},
> + {107, 95},
> + {92, 100},
> + {79, 105},
> + {68, 110},
> + {59, 115},
> + {51, 120},
> + {44, 125}
> +};
> +
> static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
> {
> return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
> @@ -468,6 +546,51 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc)
> return ret;
> }
>
> +static int vadc_map_voltage_temp(const struct vadc_map_pt *pts,
> + u32 tablesize, s32 input, s64 *output)
> +{
> + bool descending = 1;
> + u32 i = 0;
> +
> + if (!pts)
> + return -EINVAL;
> +
> + /* Check if table is descending or ascending */
> + if (tablesize > 1) {
> + if (pts[0].x < pts[1].x)
> + descending = 0;
> + }
> +
> + while (i < tablesize) {
> + if ((descending) && (pts[i].x < input)) {
> + /* table entry is less than measured*/
> + /* value and table is descending, stop */
> + break;
> + } else if ((!descending) &&
> + (pts[i].x > input)) {
> + /* table entry is greater than measured*/
> + /*value and table is ascending, stop */
> + break;
> + }
> + i++;
> + }
> +
> + if (i == 0) {
> + *output = pts[0].y;
> + } else if (i == tablesize) {
> + *output = pts[tablesize - 1].y;
> + } else {
> + /* result is between search_index and search_index-1 */
> + /* interpolate linearly */
> + *output = (((s32)((pts[i].y - pts[i - 1].y) *
> + (input - pts[i - 1].x)) /
> + (pts[i].x - pts[i - 1].x)) +
> + pts[i - 1].y);
> + }
> +
> + return 0;
> +}
> +
> static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code,
> const struct vadc_channel_prop *prop,
> s64 *scale_voltage)
> @@ -489,15 +612,61 @@ static s64 vadc_scale_fn(struct vadc_priv *vadc,
> const struct vadc_channel_prop *prop, u16 adc_code)
> {
> const struct vadc_prescale_ratio *prescale;
> - s64 voltage = 0;
> + s64 voltage = 0, result = 0;
> + int ret;
> +
> + switch (prop->scale_function) {
> + case SCALE_DEFAULT:
> + vadc_scale_calib(vadc, adc_code, prop, &voltage);
> +
> + prescale = &vadc_prescale_ratios[prop->prescale];
> + voltage = voltage * prescale->den;
> + return div64_s64(voltage, prescale->num);
> +
> + case SCALE_THERM_100K_PULLUP:
> + case SCALE_XOTHERM:
> + vadc_scale_calib(vadc, adc_code, prop, &voltage);
> +
> + if (prop->calibration == VADC_CALIB_ABSOLUTE)
> + voltage /= 1000;
> +
> + vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
> + ARRAY_SIZE(adcmap_100k_104ef_104fb),
> + voltage, &result);
> + result *= 1000;
> + return result;
> +
> + case SCALE_PMIC_THERM:
> + vadc_scale_calib(vadc, adc_code, prop, &voltage);
> +
> + if (voltage > 0) {
> + prescale = &vadc_prescale_ratios[prop->prescale];
> + voltage = voltage * prescale->den;
> + voltage /= (prescale->num * 2);
> + } else {
> + voltage = 0;
> + }
> +
> + voltage -= KELVINMIL_CELSIUSMIL;
>
> - vadc_scale_calib(vadc, adc_code, prop, &voltage);
> + return voltage;
>
> - prescale = &vadc_prescale_ratios[prop->prescale];
> + case SCALE_PMI_CHG_TEMP:
> + vadc_scale_calib(vadc, adc_code, prop, &voltage);
> + prescale = &vadc_prescale_ratios[prop->prescale];
> + voltage = voltage * prescale->den;
>
> - voltage = voltage * prescale->den;
> + voltage = div64_s64(voltage, prescale->num);
> + voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
> + voltage = (voltage + PMI_CHG_SCALE_2);
> + return div64_s64(voltage, 1000000);
>
> - return div64_s64(voltage, prescale->num);
> + default:
> + ret = -EINVAL;
> + break;
> + }
> +
> + return ret;
> }
>
> static int vadc_decimation_from_dt(u32 value)
> @@ -615,7 +784,9 @@ struct vadc_channels {
> }, \
>
> #define VADC_CHAN_TEMP(_dname, _pre) \
> - VADC_CHAN(_dname, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre) \
> + VADC_CHAN(_dname, IIO_TEMP, \
> + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \
> + _pre) \
>
> #define VADC_CHAN_VOLT(_dname, _pre) \
> VADC_CHAN(_dname, IIO_VOLTAGE, \
> @@ -639,7 +810,7 @@ struct vadc_channels {
> VADC_CHAN_TEMP(DIE_TEMP, 0)
> VADC_CHAN_VOLT(REF_625MV, 0)
> VADC_CHAN_VOLT(REF_1250MV, 0)
> - VADC_CHAN_VOLT(CHG_TEMP, 0)
> + VADC_CHAN_TEMP(CHG_TEMP, 0)
> VADC_CHAN_VOLT(SPARE1, 0)
> VADC_CHAN_VOLT(SPARE2, 0)
> VADC_CHAN_VOLT(GND_REF, 0)
> @@ -693,41 +864,41 @@ struct vadc_channels {
> VADC_CHAN_VOLT(AMUX_PU2, 0)
> VADC_CHAN_VOLT(LR_MUX3_BUF_XO_THERM, 0)
>
> - VADC_CHAN_VOLT(LR_MUX1_PU1_BAT_THERM, 0)
> + VADC_CHAN_TEMP(LR_MUX1_PU1_BAT_THERM, 0)
> VADC_CHAN_VOLT(LR_MUX2_PU1_BAT_ID, 0)
> - VADC_CHAN_VOLT(LR_MUX3_PU1_XO_THERM, 0)
> - VADC_CHAN_VOLT(LR_MUX4_PU1_AMUX_THM1, 0)
> - VADC_CHAN_VOLT(LR_MUX5_PU1_AMUX_THM2, 0)
> - VADC_CHAN_VOLT(LR_MUX6_PU1_AMUX_THM3, 0)
> + VADC_CHAN_TEMP(LR_MUX3_PU1_XO_THERM, 0)
> + VADC_CHAN_TEMP(LR_MUX4_PU1_AMUX_THM1, 0)
> + VADC_CHAN_TEMP(LR_MUX5_PU1_AMUX_THM2, 0)
> + VADC_CHAN_TEMP(LR_MUX6_PU1_AMUX_THM3, 0)
> VADC_CHAN_VOLT(LR_MUX7_PU1_AMUX_HW_ID, 0)
> - VADC_CHAN_VOLT(LR_MUX8_PU1_AMUX_THM4, 0)
> - VADC_CHAN_VOLT(LR_MUX9_PU1_AMUX_THM5, 0)
> + VADC_CHAN_TEMP(LR_MUX8_PU1_AMUX_THM4, 0)
> + VADC_CHAN_TEMP(LR_MUX9_PU1_AMUX_THM5, 0)
> VADC_CHAN_VOLT(LR_MUX10_PU1_AMUX_USB_ID, 0)
> - VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_XO_THERM, 0)
> + VADC_CHAN_TEMP(LR_MUX3_BUF_PU1_XO_THERM, 0)
>
> - VADC_CHAN_VOLT(LR_MUX1_PU2_BAT_THERM, 0)
> + VADC_CHAN_TEMP(LR_MUX1_PU2_BAT_THERM, 0)
> VADC_CHAN_VOLT(LR_MUX2_PU2_BAT_ID, 0)
> - VADC_CHAN_VOLT(LR_MUX3_PU2_XO_THERM, 0)
> - VADC_CHAN_VOLT(LR_MUX4_PU2_AMUX_THM1, 0)
> - VADC_CHAN_VOLT(LR_MUX5_PU2_AMUX_THM2, 0)
> - VADC_CHAN_VOLT(LR_MUX6_PU2_AMUX_THM3, 0)
> + VADC_CHAN_TEMP(LR_MUX3_PU2_XO_THERM, 0)
> + VADC_CHAN_TEMP(LR_MUX4_PU2_AMUX_THM1, 0)
> + VADC_CHAN_TEMP(LR_MUX5_PU2_AMUX_THM2, 0)
> + VADC_CHAN_TEMP(LR_MUX6_PU2_AMUX_THM3, 0)
> VADC_CHAN_VOLT(LR_MUX7_PU2_AMUX_HW_ID, 0)
> - VADC_CHAN_VOLT(LR_MUX8_PU2_AMUX_THM4, 0)
> - VADC_CHAN_VOLT(LR_MUX9_PU2_AMUX_THM5, 0)
> + VADC_CHAN_TEMP(LR_MUX8_PU2_AMUX_THM4, 0)
> + VADC_CHAN_TEMP(LR_MUX9_PU2_AMUX_THM5, 0)
> VADC_CHAN_VOLT(LR_MUX10_PU2_AMUX_USB_ID, 0)
> - VADC_CHAN_VOLT(LR_MUX3_BUF_PU2_XO_THERM, 0)
> + VADC_CHAN_TEMP(LR_MUX3_BUF_PU2_XO_THERM, 0)
>
> - VADC_CHAN_VOLT(LR_MUX1_PU1_PU2_BAT_THERM, 0)
> + VADC_CHAN_TEMP(LR_MUX1_PU1_PU2_BAT_THERM, 0)
> VADC_CHAN_VOLT(LR_MUX2_PU1_PU2_BAT_ID, 0)
> - VADC_CHAN_VOLT(LR_MUX3_PU1_PU2_XO_THERM, 0)
> - VADC_CHAN_VOLT(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
> - VADC_CHAN_VOLT(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
> - VADC_CHAN_VOLT(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
> + VADC_CHAN_TEMP(LR_MUX3_PU1_PU2_XO_THERM, 0)
> + VADC_CHAN_TEMP(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
> + VADC_CHAN_TEMP(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
> + VADC_CHAN_TEMP(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
> VADC_CHAN_VOLT(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0)
> - VADC_CHAN_VOLT(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
> - VADC_CHAN_VOLT(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
> + VADC_CHAN_TEMP(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
> + VADC_CHAN_TEMP(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
> VADC_CHAN_VOLT(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0)
> - VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
> + VADC_CHAN_TEMP(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
> };
>
> static int vadc_get_dt_channel_data(struct device *dev,
> @@ -804,6 +975,11 @@ static int vadc_get_dt_channel_data(struct device *dev,
> prop->avg_samples = VADC_DEF_AVG_SAMPLES;
> }
>
> + ret = of_property_read_u32(node, "qcom,scale-function",
> + &prop->scale_function);
> + if (ret)
> + prop->scale_function = SCALE_DEFAULT;
> +
> if (of_property_read_bool(node, "qcom,ratiometric"))
> prop->calibration = VADC_CALIB_RATIOMETRIC;
> else
> @@ -966,16 +1142,21 @@ static int vadc_probe(struct platform_device *pdev)
> if (ret)
> return ret;
>
> - irq_eoc = platform_get_irq(pdev, 0);
> - if (irq_eoc < 0) {
> - if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
> - return irq_eoc;
> - vadc->poll_eoc = true;
> - } else {
> - ret = devm_request_irq(dev, irq_eoc, vadc_isr, 0,
> - "spmi-vadc", vadc);
> - if (ret)
> - return ret;
> + vadc->poll_eoc = of_property_read_bool(node,
> + "qcom,vadc-poll-eoc");
Should definitely be done on availability of the IRQ rather than a separate
device tree element.
> +
> + if (!vadc->poll_eoc) {
> + irq_eoc = platform_get_irq(pdev, 0);
> + if (irq_eoc < 0) {
> + if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
> + return irq_eoc;
> + vadc->poll_eoc = true;
> + } else {
> + ret = devm_request_irq(dev, irq_eoc, vadc_isr, 0,
> + "spmi-vadc", vadc);
> + if (ret)
> + return ret;
> + }
> }
>
> ret = vadc_reset(vadc);
>
^ permalink raw reply
* Re: [PATCH 01/10] staging: iio: tsl2583: add of_match table for device tree support
From: Jonathan Cameron @ 2016-10-30 17:43 UTC (permalink / raw)
To: Brian Masney, linux-iio
Cc: devel, devicetree, lars, gregkh, linux-kernel, robh+dt, pmeerw,
knaack.h, Mark.Rutland
In-Reply-To: <1477648821-3786-2-git-send-email-masneyb@onstation.org>
On 28/10/16 11:00, Brian Masney wrote:
> Add device tree support for the tsl2583 IIO driver with no custom
> properties.
>
> Signed-off-by: Brian Masney <masneyb@onstation.org>
Trivial enough that I feel I can take this without an explicit devicetree
ack. Of course one is always welcome if anyone gets a chance to look at it.
I won't have it exposed in a non rebasing tree until perhaps the second half
of the week.
> ---
> .../devicetree/bindings/iio/light/tsl2583.txt | 26 ++++++++++++++++++++++
> drivers/staging/iio/light/tsl2583.c | 13 +++++++++++
> 2 files changed, 39 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/light/tsl2583.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/light/tsl2583.txt b/Documentation/devicetree/bindings/iio/light/tsl2583.txt
> new file mode 100644
> index 0000000..8e2066c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/light/tsl2583.txt
> @@ -0,0 +1,26 @@
> +* TAOS TSL 2580/2581/2583 ALS sensor
> +
> +Required properties:
> +
> + - compatible: Should be one of
> + "amstaos,tsl2580"
> + "amstaos,tsl2581"
> + "amstaos,tsl2583"
> + - reg: the I2C address of the device
> +
> +Optional properties:
> +
> + - interrupt-parent: should be the phandle for the interrupt controller
> + - interrupts: the sole interrupt generated by the device
> +
> + Refer to interrupt-controller/interrupts.txt for generic interrupt client
> + node bindings.
> +
> + - vcc-supply: phandle to the regulator that provides power to the sensor.
> +
> +Example:
> +
> +tsl2581@29 {
> + compatible = "amstaos,tsl2581";
> + reg = <0x29>;
> +};
> diff --git a/drivers/staging/iio/light/tsl2583.c b/drivers/staging/iio/light/tsl2583.c
> index 08f1583..fd4b6ef 100644
> --- a/drivers/staging/iio/light/tsl2583.c
> +++ b/drivers/staging/iio/light/tsl2583.c
> @@ -947,11 +947,24 @@ static struct i2c_device_id taos_idtable[] = {
> };
> MODULE_DEVICE_TABLE(i2c, taos_idtable);
>
> +#ifdef CONFIG_OF
> +static const struct of_device_id taos2583_of_match[] = {
> + { .compatible = "amstaos,tsl2580", },
> + { .compatible = "amstaos,tsl2581", },
> + { .compatible = "amstaos,tsl2583", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, taos2583_of_match);
> +#else
> +#define taos2583_of_match NULL
> +#endif
> +
> /* Driver definition */
> static struct i2c_driver taos_driver = {
> .driver = {
> .name = "tsl2583",
> .pm = TAOS_PM_OPS,
> + .of_match_table = taos2583_of_match,
> },
> .id_table = taos_idtable,
> .probe = taos_probe,
>
^ permalink raw reply
* Re: [PATCH 01/10] staging: iio: tsl2583: add of_match table for device tree support
From: Jonathan Cameron @ 2016-10-30 17:44 UTC (permalink / raw)
To: Brian Masney, linux-iio-u79uwXL29TY76Z2rM5mHXA
Cc: devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
knaack.h-Mmb7MZpHnFY, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Mark.Rutland-5wv7dgnIgG8
In-Reply-To: <246d8513-3505-31cd-4cfa-4f2c2e9266d8-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On 30/10/16 17:43, Jonathan Cameron wrote:
> On 28/10/16 11:00, Brian Masney wrote:
>> Add device tree support for the tsl2583 IIO driver with no custom
>> properties.
>>
>> Signed-off-by: Brian Masney <masneyb-1iNe0GrtECGEi8DpZVb4nw@public.gmane.org>
> Trivial enough that I feel I can take this without an explicit devicetree
> ack. Of course one is always welcome if anyone gets a chance to look at it.
> I won't have it exposed in a non rebasing tree until perhaps the second half
> of the week.
oops. Forgot to say - applied to the togreg branch of iio.git - initially pushed
out as testing for the autobuilders to play with it.
Thanks,
Jonathan
>> ---
>> .../devicetree/bindings/iio/light/tsl2583.txt | 26 ++++++++++++++++++++++
>> drivers/staging/iio/light/tsl2583.c | 13 +++++++++++
>> 2 files changed, 39 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/iio/light/tsl2583.txt
>>
>> diff --git a/Documentation/devicetree/bindings/iio/light/tsl2583.txt b/Documentation/devicetree/bindings/iio/light/tsl2583.txt
>> new file mode 100644
>> index 0000000..8e2066c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/light/tsl2583.txt
>> @@ -0,0 +1,26 @@
>> +* TAOS TSL 2580/2581/2583 ALS sensor
>> +
>> +Required properties:
>> +
>> + - compatible: Should be one of
>> + "amstaos,tsl2580"
>> + "amstaos,tsl2581"
>> + "amstaos,tsl2583"
>> + - reg: the I2C address of the device
>> +
>> +Optional properties:
>> +
>> + - interrupt-parent: should be the phandle for the interrupt controller
>> + - interrupts: the sole interrupt generated by the device
>> +
>> + Refer to interrupt-controller/interrupts.txt for generic interrupt client
>> + node bindings.
>> +
>> + - vcc-supply: phandle to the regulator that provides power to the sensor.
>> +
>> +Example:
>> +
>> +tsl2581@29 {
>> + compatible = "amstaos,tsl2581";
>> + reg = <0x29>;
>> +};
>> diff --git a/drivers/staging/iio/light/tsl2583.c b/drivers/staging/iio/light/tsl2583.c
>> index 08f1583..fd4b6ef 100644
>> --- a/drivers/staging/iio/light/tsl2583.c
>> +++ b/drivers/staging/iio/light/tsl2583.c
>> @@ -947,11 +947,24 @@ static struct i2c_device_id taos_idtable[] = {
>> };
>> MODULE_DEVICE_TABLE(i2c, taos_idtable);
>>
>> +#ifdef CONFIG_OF
>> +static const struct of_device_id taos2583_of_match[] = {
>> + { .compatible = "amstaos,tsl2580", },
>> + { .compatible = "amstaos,tsl2581", },
>> + { .compatible = "amstaos,tsl2583", },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, taos2583_of_match);
>> +#else
>> +#define taos2583_of_match NULL
>> +#endif
>> +
>> /* Driver definition */
>> static struct i2c_driver taos_driver = {
>> .driver = {
>> .name = "tsl2583",
>> .pm = TAOS_PM_OPS,
>> + .of_match_table = taos2583_of_match,
>> },
>> .id_table = taos_idtable,
>> .probe = taos_probe,
>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 02/10] staging: iio: tsl2583: check for error code from i2c_smbus_read_byte()
From: Jonathan Cameron @ 2016-10-30 17:49 UTC (permalink / raw)
To: Brian Masney, linux-iio
Cc: devel, devicetree, lars, gregkh, linux-kernel, robh+dt,
Jon Brenner, pmeerw, knaack.h, Mark.Rutland
In-Reply-To: <1477648821-3786-3-git-send-email-masneyb@onstation.org>
On 28/10/16 11:00, Brian Masney wrote:
> taos_i2c_read() and taos_als_calibrate() does not check to see if the
> value returned by i2c_smbus_read_byte() was an error code. This patch
> adds the appropriate error handling.
>
> Signed-off-by: Brian Masney <masneyb@onstation.org>
Applied to the togreg branch of iio.git and pushed out as testing for the autobuilders
to play with it.
Also Cc'd Jon on basis he might want to run his eye of these.
> ---
> drivers/staging/iio/light/tsl2583.c | 22 +++++++++++++++++++++-
> 1 file changed, 21 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/staging/iio/light/tsl2583.c b/drivers/staging/iio/light/tsl2583.c
> index fd4b6ef..35c1696 100644
> --- a/drivers/staging/iio/light/tsl2583.c
> +++ b/drivers/staging/iio/light/tsl2583.c
> @@ -171,7 +171,14 @@ taos_i2c_read(struct i2c_client *client, u8 reg, u8 *val, unsigned int len)
> return ret;
> }
> /* read the data */
> - *val = i2c_smbus_read_byte(client);
> + ret = i2c_smbus_read_byte(client);
> + if (ret < 0) {
> + dev_err(&client->dev,
> + "%s failed to read byte after writing to register %x\n",
> + __func__, reg);
> + return ret;
> + }
> + *val = ret;
> val++;
> reg++;
> }
> @@ -355,6 +362,13 @@ static int taos_als_calibrate(struct iio_dev *indio_dev)
> }
>
> reg_val = i2c_smbus_read_byte(chip->client);
> + if (reg_val < 0) {
> + dev_err(&chip->client->dev,
> + "%s failed to read after writing to the CNTRL register\n",
> + __func__);
> + return ret;
> + }
> +
> if ((reg_val & (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON))
> != (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON)) {
> dev_err(&chip->client->dev,
> @@ -371,6 +385,12 @@ static int taos_als_calibrate(struct iio_dev *indio_dev)
> return ret;
> }
> reg_val = i2c_smbus_read_byte(chip->client);
> + if (reg_val < 0) {
> + dev_err(&chip->client->dev,
> + "%s failed to read after writing to the STATUS register\n",
> + __func__);
> + return ret;
> + }
>
> if ((reg_val & TSL258X_STA_ADC_VALID) != TSL258X_STA_ADC_VALID) {
> dev_err(&chip->client->dev,
>
^ permalink raw reply
* Re: [PATCH 03/10] staging: iio: tsl2583: return proper error code instead of -1
From: Jonathan Cameron @ 2016-10-30 17:50 UTC (permalink / raw)
To: Brian Masney, linux-iio-u79uwXL29TY76Z2rM5mHXA
Cc: devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
knaack.h-Mmb7MZpHnFY, linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Mark.Rutland-5wv7dgnIgG8
In-Reply-To: <1477648821-3786-4-git-send-email-masneyb-1iNe0GrtECGEi8DpZVb4nw@public.gmane.org>
On 28/10/16 11:00, Brian Masney wrote:
> taos_als_calibrate() has a code path where -1 is returned. This patch
> changes the code so that a proper error code is returned.
>
> Signed-off-by: Brian Masney <masneyb-1iNe0GrtECGEi8DpZVb4nw@public.gmane.org>
Applied to the togreg branch of iio.git and pushed out as
testing for the autobuilders to play with it.
(I'm going to begin to get lazy on this message ;)
> ---
> drivers/staging/iio/light/tsl2583.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/staging/iio/light/tsl2583.c b/drivers/staging/iio/light/tsl2583.c
> index 35c1696..47fd373 100644
> --- a/drivers/staging/iio/light/tsl2583.c
> +++ b/drivers/staging/iio/light/tsl2583.c
> @@ -373,7 +373,7 @@ static int taos_als_calibrate(struct iio_dev *indio_dev)
> != (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON)) {
> dev_err(&chip->client->dev,
> "taos_als_calibrate failed: device not powered on with ADC enabled\n");
> - return -1;
> + return -EINVAL;
> }
>
> ret = i2c_smbus_write_byte(chip->client,
>
^ permalink raw reply
* Re: [PATCH 04/10] staging: iio: tsl2583: remove redundant power_state sysfs attribute
From: Jonathan Cameron @ 2016-10-30 17:51 UTC (permalink / raw)
To: Brian Masney, linux-iio
Cc: devel, devicetree, lars, gregkh, linux-kernel, robh+dt, pmeerw,
knaack.h, Mark.Rutland
In-Reply-To: <1477648821-3786-5-git-send-email-masneyb@onstation.org>
On 28/10/16 11:00, Brian Masney wrote:
> IIO devices have a /sys/bus/iio/devices/iio:deviceX/power/ directory
> that allows viewing and controling various power parameters. The tsl2583
> driver also has an additional custom sysfs attribute named power_state
> that is not needed. This patch removes the redundant power_state sysfs
> attribute.
>
> Signed-off-by: Brian Masney <masneyb@onstation.org>
Applied. etc.
> ---
> drivers/staging/iio/light/tsl2583.c | 31 -------------------------------
> 1 file changed, 31 deletions(-)
>
> diff --git a/drivers/staging/iio/light/tsl2583.c b/drivers/staging/iio/light/tsl2583.c
> index 47fd373..f8ccb4d 100644
> --- a/drivers/staging/iio/light/tsl2583.c
> +++ b/drivers/staging/iio/light/tsl2583.c
> @@ -511,33 +511,6 @@ static int taos_chip_off(struct iio_dev *indio_dev)
>
> /* Sysfs Interface Functions */
>
> -static ssize_t taos_power_state_show(struct device *dev,
> - struct device_attribute *attr, char *buf)
> -{
> - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct tsl2583_chip *chip = iio_priv(indio_dev);
> -
> - return sprintf(buf, "%d\n", chip->taos_chip_status);
> -}
> -
> -static ssize_t taos_power_state_store(struct device *dev,
> - struct device_attribute *attr,
> - const char *buf, size_t len)
> -{
> - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - int value;
> -
> - if (kstrtoint(buf, 0, &value))
> - return -EINVAL;
> -
> - if (!value)
> - taos_chip_off(indio_dev);
> - else
> - taos_chip_on(indio_dev);
> -
> - return len;
> -}
> -
> static ssize_t taos_gain_show(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> @@ -785,9 +758,6 @@ static ssize_t taos_luxtable_store(struct device *dev,
> return len;
> }
>
> -static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
> - taos_power_state_show, taos_power_state_store);
> -
> static DEVICE_ATTR(illuminance0_calibscale, S_IRUGO | S_IWUSR,
> taos_gain_show, taos_gain_store);
> static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
> @@ -810,7 +780,6 @@ static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
> taos_luxtable_show, taos_luxtable_store);
>
> static struct attribute *sysfs_attrs_ctrl[] = {
> - &dev_attr_power_state.attr,
> &dev_attr_illuminance0_calibscale.attr, /* Gain */
> &dev_attr_illuminance0_calibscale_available.attr,
> &dev_attr_illuminance0_integration_time.attr, /* I time*/
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox