* 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 v3 6/8] iio: dpot-dac: DAC driver based on a digital potentiometer
From: Peter Rosin @ 2016-10-30 14:24 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
In-Reply-To: <301ab064-cd90-d99d-a05a-8a50ba759c62-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
On 2016-10-30 14:33, 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-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> 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!)
*snip*
Right, I'm making that change locally so that it's not forgotten, and
feel free to squash that dropped line before pushing it out if you like,
in case there's no respin...
Cheers,
Peter
--
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 v7 REPOST 0/9] CPUs capacity information for heterogeneous systems
From: Catalin Marinas @ 2016-10-30 14:22 UTC (permalink / raw)
To: Juri Lelli
Cc: mark.rutland, devicetree, lorenzo.pieralisi, vincent.guittot,
linux-pm, peterz, will.deacon, linux-kernel, robh+dt,
morten.rasmussen, broonie, sudeep.holla, linux, dietmar.eggemann,
linux-arm-kernel
In-Reply-To: <20161017154650.18779-1-juri.lelli@arm.com>
On Mon, Oct 17, 2016 at 04:46:41PM +0100, Juri Lelli wrote:
> I'm thus now assuming that everybody is OK with the patches and that they can
> be queued for 4.10 (we certainly need this plumbing at this point). Please
> speak if my assumption is wrong (and provide feedback! :).
> Otherwise I'm going to:
>
> - use Russell's patching system for patches 2 and 8
> - ask Sudeep to pull patches 3,5,6 and 7
> - ask Catalin/Will to pull patches 1,4 and 9
I'm happy to queue patches 1, 4 and 9 for 4.10 (though it might have
been easier for the whole series to go through arm-soc).
> Do you think we might get into trouble splitting the merge process this way?
Probably not. The only minor downside is that I have to grab a new DT
for Juno from Sudeep to test the patches. Not an issue, though.
--
Catalin
^ permalink raw reply
* Re: [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time
From: Jonathan Cameron @ 2016-10-30 14:21 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-7-git-send-email-fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
On 25/10/16 17:25, Fabrice Gasnier wrote:
> Add per channel "smpr" IIO extended attribute, to allow sampling
> time configuration.
>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
First thing is that any attributes need to be documented in
Documentation/ABI/testing/sysfs-bus-iio-*
Secondly this feels rather like it should be possible to use standard ABI for
this. The documentation manages to successfully skip passed the use of
any standard ADC terminology. I'm not actually sure what its
doing when it talks about sampling time... Does this device even have an
inbuilt track and hold? Presumably but who knows... Ah, the document
'How to get the best ADC accuracy in STM32FX ... ' gives some details in the
section on how to deal with high impedance sources. Worth adding a reference
to that for this.
Arguably the 'right' value for these is dependent on the local electrical
properties, so should be described in the device tree rather than tweaked
from userspace.
If you do want to do still want to provide userspace control then it
needs to fit nicely within the IIO ABI. Now arguably it's 'sort of'
integration time but only via a nasty non linear relationship so lets
not abuse that but rather work up something ADC specific.
As such we would be looking at something like.
in_voltageX_track_time and the units should be seconds (it'll be a pain, but
we need to end up with a generic ABI so it has to be in standard units).
It might make sense to add this to the core info_mask element list and
handle it that way. I'm still dubious that this isn't really a hardware
question that should never be exposed to userspace in the first place...
You will need to convince me ;)
A few more specific comments inline.
> ---
> drivers/iio/adc/stm32/stm32-adc.c | 75 +++++++++++++++++++++++++++++++++++++
> drivers/iio/adc/stm32/stm32-adc.h | 25 +++++++++++++
> drivers/iio/adc/stm32/stm32f4-adc.c | 48 ++++++++++++++++++++++++
> 3 files changed, 148 insertions(+)
>
> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
> index 9b4b459..1681f75 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.c
> +++ b/drivers/iio/adc/stm32/stm32-adc.c
> @@ -38,6 +38,61 @@
> #include "stm32-adc.h"
>
> /**
> + * stm32_adc_conf_smp() - Configure sampling time for each channel
> + * @indio_dev: IIO device
> + * @scan_mask: channels to be converted
> + */
> +static int stm32_adc_conf_smp(struct iio_dev *indio_dev,
> + const unsigned long *scan_mask)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + const struct stm32_adc_regs *smp =
> + adc->common->data->adc_reginfo->smpr_regs;
> + struct stm32_adc_chan *stm32_chan;
> + const struct iio_chan_spec *chan;
> + u32 bit, val;
> + int i;
> +
> + for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
> + chan = indio_dev->channels + bit;
> + stm32_chan = to_stm32_chan(adc, chan);
> + i = chan->channel;
> +
> + if (i >= adc->max_channels)
> + return -EINVAL;
> +
> + val = stm32_adc_readl(adc, smp[i].reg);
> + val &= ~smp[i].mask;
> + val |= (stm32_chan->smpr << smp[i].shift) & smp[i].mask;
> + stm32_adc_writel(adc, smp[i].reg, val);
> + }
> +
> + return 0;
> +}
> +
> +int stm32_adc_set_smpr(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan, unsigned int smpr)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
> +
> + stm32_chan->smpr = smpr;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_adc_set_smpr);
> +
> +int stm32_adc_get_smpr(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
> +
> + return stm32_chan->smpr;
> +}
> +EXPORT_SYMBOL_GPL(stm32_adc_get_smpr);
> +
> +/**
> * stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
> * @indio_dev: IIO device
> * @scan_mask: channels to be converted
> @@ -126,6 +181,12 @@ static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
> return ret;
> }
>
> + ret = stm32_adc_conf_smp(indio_dev, scan_mask);
> + if (ret) {
> + dev_err(&indio_dev->dev, "Failed to configure samp time\n");
> + goto err_dis;
> + }
> +
> ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
> if (ret) {
> dev_err(&indio_dev->dev, "Failed to configure sequence\n");
> @@ -938,6 +999,7 @@ 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 stm32_adc_common *common = adc->common;
> struct device_node *node = indio_dev->dev.of_node;
> struct property *prop;
> const __be32 *cur;
> @@ -945,6 +1007,19 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
> int scan_index = 0, num_channels = 0;
> u32 val;
>
> + if (!common->stm32_chans[adc->id]) {
> + /* Allocate extended attributes structure for an instance */
> + struct stm32_adc_chan *stm32_chans;
> +
> + stm32_chans = devm_kcalloc(&indio_dev->dev,
> + adc_info->max_channels,
> + sizeof(*stm32_chans), GFP_KERNEL);
> + if (!stm32_chans)
> + return -ENOMEM;
> +
> + common->stm32_chans[adc->id] = stm32_chans;
> + }
> +
> of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
> num_channels++;
>
> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
> index 6c9b70d..8cf1d5c 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.h
> +++ b/drivers/iio/adc/stm32/stm32-adc.h
> @@ -143,6 +143,14 @@ struct stm32_adc_chan_spec {
> };
>
> /**
> + * struct stm32_adc_chan - Extended specifications of stm32 adc channels
> + * @smpr: per channel sampling time selection
> + */
> +struct stm32_adc_chan {
> + unsigned smpr:3;
> +};
> +
> +/**
> * struct stm32_adc_trig_info - ADC trigger info
> * @extsel: trigger selection for regular or injected
> * @name: name of the trigger, corresponding to its source
> @@ -206,6 +214,7 @@ struct stm32_adc_trig_reginfo {
> * @jdr: injected data registers offsets
> * @sqr_regs: Regular sequence registers description
> * @jsqr_reg: Injected sequence register description
> + * @smpr_regs: Sampling time registers description
> * @trig_reginfo: regular trigger control registers description
> * @jtrig_reginfo: injected trigger control registers description
> */
> @@ -220,6 +229,7 @@ struct stm32_adc_reginfo {
> u32 jdr[4];
> const struct stm32_adc_regs *sqr_regs;
> const struct stm32_adc_regs *jsqr_reg;
> + const struct stm32_adc_regs *smpr_regs;
> const struct stm32_adc_trig_reginfo *trig_reginfo;
> const struct stm32_adc_trig_reginfo *jtrig_reginfo;
> };
> @@ -328,6 +338,7 @@ struct stm32_adc {
> * @aclk: common clock for the analog circuitry
> * @vref: regulator reference
> * @vref_mv: vref voltage (mv)
> + * @stm32_chans: stm32 channels extended specification data
> * @gpio_descs: gpio descriptor used to configure EXTi triggers
> * @lock: mutex
> */
> @@ -341,11 +352,21 @@ struct stm32_adc_common {
> struct clk *aclk;
> struct regulator *vref;
> int vref_mv;
> + struct stm32_adc_chan *stm32_chans[STM32_ADC_ID_MAX];
> struct gpio_descs *gpios;
> struct mutex lock; /* read_raw lock */
> };
>
> /* Helper routines */
> +static inline struct stm32_adc_chan *to_stm32_chan(struct stm32_adc *adc,
> + const struct iio_chan_spec
> + *chan)
> +{
> + struct stm32_adc_chan *stm32_chans = adc->common->stm32_chans[adc->id];
> +
> + return &stm32_chans[chan->channel];
> +}
> +
> static inline int stm32_adc_start_conv(struct stm32_adc *adc)
> {
> return adc->common->data->start_conv(adc);
> @@ -458,6 +479,10 @@ static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>
> /* STM32 common extended attributes */
> extern const struct iio_enum stm32_adc_trig_pol;
> +int stm32_adc_set_smpr(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan, unsigned int smpr);
> +int stm32_adc_get_smpr(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan);
> int stm32_adc_probe(struct platform_device *pdev);
> int stm32_adc_remove(struct platform_device *pdev);
>
> diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
> index e033a68..e0e6211 100644
> --- a/drivers/iio/adc/stm32/stm32f4-adc.c
> +++ b/drivers/iio/adc/stm32/stm32f4-adc.c
> @@ -330,6 +330,32 @@ enum stm32f4_adc_smpr {
> };
>
> /**
> + * stm32f4_smpr_regs[] - describe sampling time registers & bit fields
> + * Sorted so it can be indexed by channel number.
> + */
> +static const struct stm32_adc_regs stm32f4_smpr_regs[] = {
> + { STM32F4_ADCX_SMPR2, STM32F4_SMP0_MASK, STM32F4_SMP0_SHIFT },
> + { STM32F4_ADCX_SMPR2, STM32F4_SMP1_MASK, STM32F4_SMP1_SHIFT },
> + { STM32F4_ADCX_SMPR2, STM32F4_SMP2_MASK, STM32F4_SMP2_SHIFT },
> + { STM32F4_ADCX_SMPR2, STM32F4_SMP3_MASK, STM32F4_SMP3_SHIFT },
> + { STM32F4_ADCX_SMPR2, STM32F4_SMP4_MASK, STM32F4_SMP4_SHIFT },
> + { STM32F4_ADCX_SMPR2, STM32F4_SMP5_MASK, STM32F4_SMP5_SHIFT },
> + { STM32F4_ADCX_SMPR2, STM32F4_SMP6_MASK, STM32F4_SMP6_SHIFT },
> + { STM32F4_ADCX_SMPR2, STM32F4_SMP7_MASK, STM32F4_SMP7_SHIFT },
> + { STM32F4_ADCX_SMPR2, STM32F4_SMP8_MASK, STM32F4_SMP8_SHIFT },
> + { STM32F4_ADCX_SMPR2, STM32F4_SMP9_MASK, STM32F4_SMP9_SHIFT },
> + { STM32F4_ADCX_SMPR1, STM32F4_SMP10_MASK, STM32F4_SMP10_SHIFT },
> + { STM32F4_ADCX_SMPR1, STM32F4_SMP11_MASK, STM32F4_SMP11_SHIFT },
> + { STM32F4_ADCX_SMPR1, STM32F4_SMP12_MASK, STM32F4_SMP12_SHIFT },
> + { STM32F4_ADCX_SMPR1, STM32F4_SMP13_MASK, STM32F4_SMP13_SHIFT },
> + { STM32F4_ADCX_SMPR1, STM32F4_SMP14_MASK, STM32F4_SMP14_SHIFT },
> + { STM32F4_ADCX_SMPR1, STM32F4_SMP15_MASK, STM32F4_SMP15_SHIFT },
> + { STM32F4_ADCX_SMPR1, STM32F4_SMP16_MASK, STM32F4_SMP16_SHIFT },
> + { STM32F4_ADCX_SMPR1, STM32F4_SMP17_MASK, STM32F4_SMP17_SHIFT },
> + { STM32F4_ADCX_SMPR1, STM32F4_SMP18_MASK, STM32F4_SMP18_SHIFT },
> +};
> +
> +/**
> * stm32f4_sqr_regs - describe regular sequence registers
> * - L: sequence len (register & bit field)
> * - SQ1..SQ16: sequence entries (register & bit field)
> @@ -407,6 +433,7 @@ enum stm32f4_adc_smpr {
> },
> .sqr_regs = stm32f4_sqr_regs,
> .jsqr_reg = stm32f4_jsqr_reg,
> + .smpr_regs = stm32f4_smpr_regs,
> .trig_reginfo = &stm32f4_adc_trig_reginfo,
> .jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
> };
> @@ -555,7 +582,28 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
> return 0;
> }
>
> +/* stm32f4_smpr_items : Channel-wise programmable sampling time */
> +static const char * const stm32f4_smpr_items[] = {
> + [STM32F4_SMPR_3_CK_CYCLES] = "3_cycles",
> + [STM32F4_SMPR_15_CK_CYCLES] = "15_cycles",
> + [STM32F4_SMPR_28_CK_CYCLES] = "28_cycles",
> + [STM32F4_SMPR_56_CK_CYCLES] = "56_cycles",
> + [STM32F4_SMPR_84_CK_CYCLES] = "84_cycles",
> + [STM32F4_SMPR_112_CK_CYCLES] = "112_cycles",
> + [STM32F4_SMPR_144_CK_CYCLES] = "144_cycles",
> + [STM32F4_SMPR_480_CK_CYCLES] = "480_cycles",
This is a whole level of nasty magic numbers. Always take real numeric
values and either provide an _available attribute listing the options,
or if appropriate round to the next best (probably up here).
Units shouldn't be in cycles as that has no meaning without a deep and
dirty knowledge of what clocks are being fed to the device. Stuff that
is at least tricky for userspace to work out. THis is a time, so it
should be in seconds.
> +};
> +
> +static const struct iio_enum stm32f4_smpr = {
> + .items = stm32f4_smpr_items,
> + .num_items = ARRAY_SIZE(stm32f4_smpr_items),
> + .get = stm32_adc_get_smpr,
> + .set = stm32_adc_set_smpr,
> +};
> +
> static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
> + IIO_ENUM("smpr", IIO_SEPARATE, &stm32f4_smpr),
> + IIO_ENUM_AVAILABLE("smpr", &stm32f4_smpr),
> IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
> {
> .name = "trigger_pol_available",
>
--
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 8/8] iio: envelope-detector: ADC driver based on a DAC and a comparator
From: Jonathan Cameron @ 2016-10-30 13:44 UTC (permalink / raw)
To: Peter Rosin, 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: <1477262381-7800-9-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
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.
> +
> 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");
>
^ permalink raw reply
* Re: [PATCH v3 2/4] usb: musb: core: added helper function for parsing DT
From: Kevin Hilman @ 2016-10-30 13:35 UTC (permalink / raw)
To: Alexandre Bailon
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, b-liu-l0cyMroinI0,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Petr Kulhavy
In-Reply-To: <1477560847-8929-3-git-send-email-abailon-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
Alexandre Bailon <abailon-rdvid1DuHRBWk0Htik3J/w@public.gmane.org> writes:
> From: Petr Kulhavy <petr-Qh/3xLP0EvwAvxtiuMwx3w@public.gmane.org>
>
> This adds the function musb_get_mode() to get the DT property "dr_mode"
>
> Signed-off-by: Petr Kulhavy <petr-Qh/3xLP0EvwAvxtiuMwx3w@public.gmane.org>
> Acked-by: Sergei Shtylyov <sshtylyov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
> Signed-off-by: Alexandre Bailon <abailon-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> Tested-by: David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
> ---
> drivers/usb/musb/musb_core.c | 19 +++++++++++++++++++
> drivers/usb/musb/musb_core.h | 5 +++++
> 2 files changed, 24 insertions(+)
>
> diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
> index 27dadc0..bba07e7 100644
> --- a/drivers/usb/musb/musb_core.c
> +++ b/drivers/usb/musb/musb_core.c
> @@ -100,6 +100,7 @@
> #include <linux/io.h>
> #include <linux/dma-mapping.h>
> #include <linux/usb.h>
> +#include <linux/usb/of.h>
>
> #include "musb_core.h"
> #include "musb_trace.h"
> @@ -130,6 +131,24 @@ static inline struct musb *dev_to_musb(struct device *dev)
> return dev_get_drvdata(dev);
> }
>
> +enum musb_mode musb_get_mode(struct device *dev)
> +{
> + enum usb_dr_mode mode;
> +
> + mode = usb_get_dr_mode(dev);
> + switch (mode) {
> + case USB_DR_MODE_HOST:
> + return MUSB_HOST;
> + case USB_DR_MODE_PERIPHERAL:
> + return MUSB_PERIPHERAL;
> + case USB_DR_MODE_OTG:
> + case USB_DR_MODE_UNKNOWN:
> + default:
> + return MUSB_OTG;
> + }
> +}
> +EXPORT_SYMBOL_GPL(musb_get_mode);
> +
> /*-------------------------------------------------------------------------*/
>
> #ifndef CONFIG_BLACKFIN
> diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
> index 2cb88a49..a406468 100644
> --- a/drivers/usb/musb/musb_core.h
> +++ b/drivers/usb/musb/musb_core.h
> @@ -617,4 +617,9 @@ static inline void musb_platform_post_root_reset_end(struct musb *musb)
> musb->ops->post_root_reset_end(musb);
> }
>
> +/* gets the "dr_mode" property from DT and converts it into musb_mode
> + * if the property is not found or not recognized returns MUSB_OTG
> + */
nit: multi-line comment style
> +extern enum musb_mode musb_get_mode(struct device *dev);
> +
> #endif /* __MUSB_CORE_H__ */
Otherwise,
Reviewed-by: Kevin Hilman <khilman-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
Kevin
--
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 7/8] dt-bindings: iio: document envelope-detector bindings
From: Jonathan Cameron @ 2016-10-30 13:35 UTC (permalink / raw)
To: Peter Rosin, linux-kernel
Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
Rob Herring, Mark Rutland, linux-iio, devicetree
In-Reply-To: <1477262381-7800-8-git-send-email-peda@axentia.se>
On 23/10/16 23:39, Peter Rosin wrote:
> Signed-off-by: Peter Rosin <peda@axentia.se>
I'm happy with this, but again it's odd enough I'd like some input from
a device tree bindings maintainer.
Thanks,
Jonathan
> ---
> .../bindings/iio/adc/envelope-detector.txt | 54 ++++++++++++++++++++++
> MAINTAINERS | 6 +++
> 2 files changed, 60 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/envelope-detector.txt b/Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
> new file mode 100644
> index 000000000000..27544bdd4478
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
> @@ -0,0 +1,54 @@
> +Bindings for ADC envelope detector using a DAC and a comparator
> +
> +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|------<-------'
> + | |
> + '-------'
> +
> +Required properties:
> +- compatible: Should be "axentia,tse850-envelope-detector"
> +- io-channels: Channel node of the dac to be used for comparator input.
> +- io-channel-names: Should be "dac".
> +- interrupt specification for one client interrupt,
> + see ../../interrupt-controller/interrupts.txt for details.
> +- interrupt-names: Should be "comp".
> +
> +Example:
> +
> + &i2c {
> + dpot: mcp4651-104@28 {
> + compatible = "microchip,mcp4651-104";
> + reg = <0x28>;
> + #io-channel-cells = <1>;
> + };
> + };
> +
> + dac: dac {
> + compatible = "dpot-dac";
> + vref-supply = <®_3v3>;
> + io-channels = <&dpot 0>;
> + io-channel-names = "dpot";
> + #io-channel-cells = <1>;
> + };
> +
> + envelope-detector {
> + compatible = "axentia,tse850-envelope-detector";
> + io-channels = <&dac 0>;
> + io-channel-names = "dac";
> +
> + interrupt-parent = <&gpio>;
> + interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
> + interrupt-names = "comp";
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d7375f45ff0f..fca35d16037d 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6119,6 +6119,12 @@ 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 ENVELOPE DETECTOR
> +M: Peter Rosin <peda@axentia.se>
> +L: linux-iio@vger.kernel.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
> +
> IIO SUBSYSTEM AND DRIVERS
> M: Jonathan Cameron <jic23@kernel.org>
> R: Hartmut Knaack <knaack.h@gmx.de>
>
^ permalink raw reply
* Re: [PATCH v3 6/8] iio: dpot-dac: DAC driver based on a digital potentiometer
From: Jonathan Cameron @ 2016-10-30 13:33 UTC (permalink / raw)
To: Peter Rosin, linux-kernel
Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
Rob Herring, Mark Rutland, linux-iio, devicetree
In-Reply-To: <1477262381-7800-7-git-send-email-peda@axentia.se>
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;
> + 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.
> +
> + 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");
>
^ permalink raw reply
* Re: [PATCH v3 5/8] dt-bindings: iio: document dpot-dac bindings
From: Jonathan Cameron @ 2016-10-30 13:28 UTC (permalink / raw)
To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
Rob Herring, Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1477262381-7800-6-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
On 23/10/16 23:39, Peter Rosin wrote:
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
I'm happy with this, but it's odd enough I'd like some devicetree maintainer
feedback ideally.
Thanks,
Jonathan
> ---
> .../devicetree/bindings/iio/dac/dpot-dac.txt | 41 ++++++++++++++++++++++
> MAINTAINERS | 6 ++++
> 2 files changed, 47 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt b/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
> new file mode 100644
> index 000000000000..fdf47a01bfef
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
> @@ -0,0 +1,41 @@
> +Bindings for DAC emulation using a digital potentiometer
> +
> +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
> + | |
> + '------+------------+
> +
> +Required properties:
> +- compatible: Should be "dpot-dac"
> +- vref-supply: The regulator supplying the voltage divider.
> +- io-channels: Channel node of the dpot to be used for the voltage division.
> +- io-channel-names: Should be "dpot".
> +
> +Example:
> +
> + &i2c {
> + dpot: mcp4651-503@28 {
> + compatible = "microchip,mcp4651-503";
> + reg = <0x28>;
> + #io-channel-cells = <1>;
> + };
> + };
> +
> + dac {
> + compatible = "dpot-dac";
> + vref-supply = <®_3v3>;
> + io-channels = <&dpot 0>;
> + io-channel-names = "dpot";
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 7c65585e1230..6218010128dc 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6111,6 +6111,12 @@ L: linux-media-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> S: Maintained
> F: drivers/media/rc/iguanair.c
>
> +IIO DIGITAL POTENTIOMETER DAC
> +M: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> +L: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
> +
> IIO SUBSYSTEM AND DRIVERS
> M: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> R: Hartmut Knaack <knaack.h-Mmb7MZpHnFY@public.gmane.org>
>
--
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 3/8] iio: mcp4531: provide range of available raw values
From: Jonathan Cameron @ 2016-10-30 13:26 UTC (permalink / raw)
To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
Rob Herring, Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1477262381-7800-4-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
On 23/10/16 23:39, Peter Rosin wrote:
> Example:
>
> $ cat '/sys/bus/iio/devices/iio:device0/out_resistance_raw_available'
> [0 1 256]
>
> Meaning: min 0, step 1 and max 256.
>
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Looks good. On comment inline, but nothing to change.
This series is likely to just be waiting on some additional review of that first patch!
J
> ---
> .../testing/sysfs-bus-iio-potentiometer-mcp4531 | 8 ++
> MAINTAINERS | 1 +
> drivers/iio/potentiometer/mcp4531.c | 104 ++++++++++++---------
> 3 files changed, 71 insertions(+), 42 deletions(-)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531 b/Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
> new file mode 100644
> index 000000000000..2a91fbe394fc
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
> @@ -0,0 +1,8 @@
> +What: /sys/bus/iio/devices/iio:deviceX/out_resistance_raw_available
> +Date: October 2016
> +KernelVersion: 4.9
> +Contact: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> +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]
I suspect we'll want to move this out into the main file pretty soon but guess it
can go here for now.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1cd38a7e0064..7c65585e1230 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -7694,6 +7694,7 @@ MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVER
> 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-potentiometer-mcp4531
> F: drivers/iio/potentiometer/mcp4531.c
>
> MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
> diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c
> index 13b6ae2fcf7b..0d1bcf89ae17 100644
> --- a/drivers/iio/potentiometer/mcp4531.c
> +++ b/drivers/iio/potentiometer/mcp4531.c
> @@ -38,7 +38,7 @@
>
> struct mcp4531_cfg {
> int wipers;
> - int max_pos;
> + int avail[3];
> int kohms;
> };
>
> @@ -78,38 +78,38 @@ enum mcp4531_type {
> };
>
> static const struct mcp4531_cfg mcp4531_cfg[] = {
> - [MCP453x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, },
> - [MCP453x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },
> - [MCP453x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, },
> - [MCP453x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
> - [MCP454x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, },
> - [MCP454x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },
> - [MCP454x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, },
> - [MCP454x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
> - [MCP455x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, },
> - [MCP455x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, },
> - [MCP455x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, },
> - [MCP455x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
> - [MCP456x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, },
> - [MCP456x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, },
> - [MCP456x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, },
> - [MCP456x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
> - [MCP463x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, },
> - [MCP463x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, },
> - [MCP463x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, },
> - [MCP463x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
> - [MCP464x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, },
> - [MCP464x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, },
> - [MCP464x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, },
> - [MCP464x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
> - [MCP465x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, },
> - [MCP465x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, },
> - [MCP465x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, },
> - [MCP465x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
> - [MCP466x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, },
> - [MCP466x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, },
> - [MCP466x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, },
> - [MCP466x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
> + [MCP453x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 5, },
> + [MCP453x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 10, },
> + [MCP453x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 50, },
> + [MCP453x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, },
> + [MCP454x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 5, },
> + [MCP454x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 10, },
> + [MCP454x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 50, },
> + [MCP454x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, },
> + [MCP455x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 5, },
> + [MCP455x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 10, },
> + [MCP455x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 50, },
> + [MCP455x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, },
> + [MCP456x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 5, },
> + [MCP456x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 10, },
> + [MCP456x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 50, },
> + [MCP456x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, },
> + [MCP463x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 5, },
> + [MCP463x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 10, },
> + [MCP463x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 50, },
> + [MCP463x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, },
> + [MCP464x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 5, },
> + [MCP464x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 10, },
> + [MCP464x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 50, },
> + [MCP464x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, },
> + [MCP465x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 5, },
> + [MCP465x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 10, },
> + [MCP465x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 50, },
> + [MCP465x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, },
> + [MCP466x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 5, },
> + [MCP466x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 10, },
> + [MCP466x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 50, },
> + [MCP466x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, },
> };
>
> #define MCP4531_WRITE (0 << 2)
> @@ -124,13 +124,14 @@ struct mcp4531_data {
> const struct mcp4531_cfg *cfg;
> };
>
> -#define MCP4531_CHANNEL(ch) { \
> - .type = IIO_RESISTANCE, \
> - .indexed = 1, \
> - .output = 1, \
> - .channel = (ch), \
> - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
> +#define MCP4531_CHANNEL(ch) { \
> + .type = IIO_RESISTANCE, \
> + .indexed = 1, \
> + .output = 1, \
> + .channel = (ch), \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
> + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_RAW), \
> }
>
> static const struct iio_chan_spec mcp4531_channels[] = {
> @@ -156,13 +157,31 @@ static int mcp4531_read_raw(struct iio_dev *indio_dev,
> return IIO_VAL_INT;
> case IIO_CHAN_INFO_SCALE:
> *val = 1000 * data->cfg->kohms;
> - *val2 = data->cfg->max_pos;
> + *val2 = data->cfg->avail[2];
> return IIO_VAL_FRACTIONAL;
> }
>
> return -EINVAL;
> }
>
> +static int mcp4531_read_avail(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + const int **vals, int *type, int *length,
> + long mask)
> +{
> + struct mcp4531_data *data = iio_priv(indio_dev);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + *length = ARRAY_SIZE(data->cfg->avail);
> + *vals = data->cfg->avail;
> + *type = IIO_VAL_INT;
> + return IIO_AVAIL_RANGE;
> + }
> +
> + return -EINVAL;
> +}
> +
> static int mcp4531_write_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan,
> int val, int val2, long mask)
> @@ -172,7 +191,7 @@ static int mcp4531_write_raw(struct iio_dev *indio_dev,
>
> switch (mask) {
> case IIO_CHAN_INFO_RAW:
> - if (val > data->cfg->max_pos || val < 0)
> + if (val > data->cfg->avail[2] || val < 0)
> return -EINVAL;
> break;
> default:
> @@ -186,6 +205,7 @@ static int mcp4531_write_raw(struct iio_dev *indio_dev,
>
> static const struct iio_info mcp4531_info = {
> .read_raw = mcp4531_read_raw,
> + .read_avail = mcp4531_read_avail,
> .write_raw = mcp4531_write_raw,
> .driver_module = THIS_MODULE,
> };
>
--
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 2/8] iio: inkern: add helpers to query available values from channels
From: Jonathan Cameron @ 2016-10-30 13:21 UTC (permalink / raw)
To: Peter Rosin, linux-kernel
Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
Rob Herring, Mark Rutland, linux-iio, devicetree
In-Reply-To: <1477262381-7800-3-git-send-email-peda@axentia.se>
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.
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
> + * 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.
> + * @type: Type of available values in vals.
> + * @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
> + * 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, \
>
^ permalink raw reply
* Re: [PATCH v3 1/8] iio:core: add a callback to allow drivers to provide _available attributes
From: Jonathan Cameron @ 2016-10-30 13:11 UTC (permalink / raw)
To: Peter Rosin, linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
Rob Herring, Mark Rutland, linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1477262381-7800-2-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
On 23/10/16 23:39, Peter Rosin wrote:
> From: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>
> A large number of attributes can only take a limited range of values.
> Currently in IIO this is handled by directly registering additional
> *_available attributes thus providing this information to userspace.
>
> It is desirable to provide this information via the core for much the same
> reason this was done for the actual channel information attributes in the
> first place. If it isn't there, then it can only really be accessed from
> userspace. Other in kernel IIO consumers have no access to what valid
> parameters are.
>
> Two forms are currently supported:
> * list of values in one particular IIO_VAL_* format.
> e.g. 1.300000 1.500000 1.730000
> * range specification with a step size:
> e.g. [1.000000 0.500000 2.500000]
> equivalent to 1.000000 1.5000000 2.000000 2.500000
>
> An addition set of masks are used to allow different sharing rules for the
> *_available attributes generated.
>
> This allows for example:
>
> in_accel_x_offset
> in_accel_y_offset
> in_accel_offset_available.
>
> We could have gone with having a specification for each and every
> info_mask element but that would have meant changing the existing userspace
> ABI. This approach does not.
>
> Signed-off-by: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> [forward ported, added some docs and fixed buffer overflows /peda]
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Looks nicely cleaned up to me, but I'm really not going to be the best
person to review this. So would appreciate anyone else who has the time
taking a look at this.
Thanks,
Jonathan
> ---
> drivers/iio/industrialio-core.c | 259 +++++++++++++++++++++++++++++++++++-----
> include/linux/iio/iio.h | 29 +++++
> include/linux/iio/types.h | 5 +
> 3 files changed, 260 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index fc340ed3dca1..b35c61a31fa6 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -575,66 +575,82 @@ int of_iio_read_mount_matrix(const struct device *dev,
> #endif
> EXPORT_SYMBOL(of_iio_read_mount_matrix);
>
> -/**
> - * iio_format_value() - Formats a IIO value into its string representation
> - * @buf: The buffer to which the formatted value gets written
> - * @type: One of the IIO_VAL_... constants. This decides how the val
> - * and val2 parameters are formatted.
> - * @size: Number of IIO value entries contained in vals
> - * @vals: Pointer to the values, exact meaning depends on the
> - * type parameter.
> - *
> - * Return: 0 by default, a negative number on failure or the
> - * total number of characters written for a type that belongs
> - * to the IIO_VAL_... constant.
> - */
> -ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
> +static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type,
> + int size, const int *vals)
> {
> unsigned long long tmp;
> + int tmp0, tmp1;
> bool scale_db = false;
>
> switch (type) {
> case IIO_VAL_INT:
> - return sprintf(buf, "%d\n", vals[0]);
> + return snprintf(buf, len, "%d", vals[0]);
> case IIO_VAL_INT_PLUS_MICRO_DB:
> scale_db = true;
> case IIO_VAL_INT_PLUS_MICRO:
> if (vals[1] < 0)
> - return sprintf(buf, "-%d.%06u%s\n", abs(vals[0]),
> - -vals[1], scale_db ? " dB" : "");
> + return snprintf(buf, len, "-%d.%06u%s", abs(vals[0]),
> + -vals[1], scale_db ? " dB" : "");
> else
> - return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1],
> - scale_db ? " dB" : "");
> + return snprintf(buf, len, "%d.%06u%s", vals[0], vals[1],
> + scale_db ? " dB" : "");
> case IIO_VAL_INT_PLUS_NANO:
> if (vals[1] < 0)
> - return sprintf(buf, "-%d.%09u\n", abs(vals[0]),
> - -vals[1]);
> + return snprintf(buf, len, "-%d.%09u", abs(vals[0]),
> + -vals[1]);
> else
> - return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
> + return snprintf(buf, len, "%d.%09u", vals[0], vals[1]);
> case IIO_VAL_FRACTIONAL:
> tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]);
> - vals[0] = (int)div_s64_rem(tmp, 1000000000, &vals[1]);
> - return sprintf(buf, "%d.%09u\n", vals[0], abs(vals[1]));
> + tmp1 = vals[1];
> + tmp0 = (int)div_s64_rem(tmp, 1000000000, &tmp1);
> + return snprintf(buf, len, "%d.%09u", tmp0, abs(tmp1));
> case IIO_VAL_FRACTIONAL_LOG2:
> tmp = (s64)vals[0] * 1000000000LL >> vals[1];
> - vals[1] = do_div(tmp, 1000000000LL);
> - vals[0] = tmp;
> - return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
> + tmp1 = do_div(tmp, 1000000000LL);
> + tmp0 = tmp;
> + return snprintf(buf, len, "%d.%09u", tmp0, tmp1);
> case IIO_VAL_INT_MULTIPLE:
> {
> int i;
> - int len = 0;
> + int l = 0;
>
> - for (i = 0; i < size; ++i)
> - len += snprintf(&buf[len], PAGE_SIZE - len, "%d ",
> - vals[i]);
> - len += snprintf(&buf[len], PAGE_SIZE - len, "\n");
> - return len;
> + for (i = 0; i < size; ++i) {
> + l += snprintf(&buf[l], len - l, "%d ", vals[i]);
> + if (l >= len)
> + break;
> + }
> + return l;
> }
> default:
> return 0;
> }
> }
> +
> +/**
> + * iio_format_value() - Formats a IIO value into its string representation
> + * @buf: The buffer to which the formatted value gets written
> + * which is assumed to be big enough (i.e. PAGE_SIZE).
> + * @type: One of the IIO_VAL_... constants. This decides how the val
> + * and val2 parameters are formatted.
> + * @size: Number of IIO value entries contained in vals
> + * @vals: Pointer to the values, exact meaning depends on the
> + * type parameter.
> + *
> + * Return: 0 by default, a negative number on failure or the
> + * total number of characters written for a type that belongs
> + * to the IIO_VAL_... constant.
> + */
> +ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
> +{
> + ssize_t len;
> +
> + len = __iio_format_value(buf, PAGE_SIZE, type, size, vals);
> + if (len >= PAGE_SIZE - 1)
> + return -EFBIG;
> +
> + return len + sprintf(buf + len, "\n");
> +}
> EXPORT_SYMBOL_GPL(iio_format_value);
>
> static ssize_t iio_read_channel_info(struct device *dev,
> @@ -662,6 +678,119 @@ static ssize_t iio_read_channel_info(struct device *dev,
> return iio_format_value(buf, ret, val_len, vals);
> }
>
> +static ssize_t iio_format_avail_list(char *buf, const int *vals,
> + int type, int length)
> +{
> + int i;
> + ssize_t len = 0;
> +
> + switch (type) {
> + case IIO_VAL_INT:
> + for (i = 0; i < length; i++) {
> + len += __iio_format_value(buf + len, PAGE_SIZE - len,
> + type, 1, &vals[i]);
> + if (len >= PAGE_SIZE)
> + return -EFBIG;
> + if (i < length - 1)
> + len += snprintf(buf + len, PAGE_SIZE - len,
> + " ");
> + else
> + len += snprintf(buf + len, PAGE_SIZE - len,
> + "\n");
> + if (len >= PAGE_SIZE)
> + return -EFBIG;
> + }
> + break;
> + default:
> + for (i = 0; i < length / 2; i++) {
> + len += __iio_format_value(buf + len, PAGE_SIZE - len,
> + type, 2, &vals[i * 2]);
> + if (len >= PAGE_SIZE)
> + return -EFBIG;
> + if (i < length / 2 - 1)
> + len += snprintf(buf + len, PAGE_SIZE - len,
> + " ");
> + else
> + len += snprintf(buf + len, PAGE_SIZE - len,
> + "\n");
> + if (len >= PAGE_SIZE)
> + return -EFBIG;
> + }
> + };
> +
> + return len;
> +}
> +
> +static ssize_t iio_format_avail_range(char *buf, const int *vals, int type)
> +{
> + int i;
> + ssize_t len;
> +
> + len = snprintf(buf, PAGE_SIZE, "[");
> + switch (type) {
> + case IIO_VAL_INT:
> + for (i = 0; i < 3; i++) {
> + len += __iio_format_value(buf + len, PAGE_SIZE - len,
> + type, 1, &vals[i]);
> + if (len >= PAGE_SIZE)
> + return -EFBIG;
> + if (i < 2)
> + len += snprintf(buf + len, PAGE_SIZE - len,
> + " ");
> + else
> + len += snprintf(buf + len, PAGE_SIZE - len,
> + "]\n");
> + if (len >= PAGE_SIZE)
> + return -EFBIG;
> + }
> + break;
> + default:
> + for (i = 0; i < 3; i++) {
> + len += __iio_format_value(buf + len, PAGE_SIZE - len,
> + type, 2, &vals[i * 2]);
> + if (len >= PAGE_SIZE)
> + return -EFBIG;
> + if (i < 2)
> + len += snprintf(buf + len, PAGE_SIZE - len,
> + " ");
> + else
> + len += snprintf(buf + len, PAGE_SIZE - len,
> + "]\n");
> + if (len >= PAGE_SIZE)
> + return -EFBIG;
> + }
> + };
> +
> + return len;
> +}
> +
> +static ssize_t iio_read_channel_info_avail(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> + const int *vals;
> + int ret;
> + int length;
> + int type;
> +
> + ret = indio_dev->info->read_avail(indio_dev, this_attr->c,
> + &vals, &type, &length,
> + this_attr->address);
> +
> + if (ret < 0)
> + return ret;
> + switch (ret) {
> + case IIO_AVAIL_LIST:
> + return iio_format_avail_list(buf, vals, type, length);
> + case IIO_AVAIL_RANGE:
> + return iio_format_avail_range(buf, vals, type);
> + default:
> + return -EINVAL;
> + }
> +}
> +
> /**
> * iio_str_to_fixpoint() - Parse a fixed-point number from a string
> * @str: The string to parse
> @@ -978,6 +1107,40 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev,
> return attrcount;
> }
>
> +static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + enum iio_shared_by shared_by,
> + const long *infomask)
> +{
> + int i, ret, attrcount = 0;
> + char *avail_postfix;
> +
> + for_each_set_bit(i, infomask, sizeof(infomask) * 8) {
> + avail_postfix = kasprintf(GFP_KERNEL,
> + "%s_available",
> + iio_chan_info_postfix[i]);
> + if (!avail_postfix)
> + return -ENOMEM;
> +
> + ret = __iio_add_chan_devattr(avail_postfix,
> + chan,
> + &iio_read_channel_info_avail,
> + NULL,
> + i,
> + shared_by,
> + &indio_dev->dev,
> + &indio_dev->channel_attr_list);
> + kfree(avail_postfix);
> + if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
> + continue;
> + else if (ret < 0)
> + return ret;
> + attrcount++;
> + }
> +
> + return attrcount;
> +}
> +
> static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan)
> {
> @@ -993,6 +1156,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
> return ret;
> attrcount += ret;
>
> + ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
> + IIO_SEPARATE,
> + &chan->
> + info_mask_separate_available);
> + if (ret < 0)
> + return ret;
> + attrcount += ret;
> +
> ret = iio_device_add_info_mask_type(indio_dev, chan,
> IIO_SHARED_BY_TYPE,
> &chan->info_mask_shared_by_type);
> @@ -1000,6 +1171,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
> return ret;
> attrcount += ret;
>
> + ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
> + IIO_SHARED_BY_TYPE,
> + &chan->
> + info_mask_shared_by_type_available);
> + if (ret < 0)
> + return ret;
> + attrcount += ret;
> +
> ret = iio_device_add_info_mask_type(indio_dev, chan,
> IIO_SHARED_BY_DIR,
> &chan->info_mask_shared_by_dir);
> @@ -1007,6 +1186,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
> return ret;
> attrcount += ret;
>
> + ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
> + IIO_SHARED_BY_DIR,
> + &chan->info_mask_shared_by_dir_available);
> + if (ret < 0)
> + return ret;
> + attrcount += ret;
> +
> ret = iio_device_add_info_mask_type(indio_dev, chan,
> IIO_SHARED_BY_ALL,
> &chan->info_mask_shared_by_all);
> @@ -1014,6 +1200,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
> return ret;
> attrcount += ret;
>
> + ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
> + IIO_SHARED_BY_ALL,
> + &chan->info_mask_shared_by_all_available);
> + if (ret < 0)
> + return ret;
> + attrcount += ret;
> +
> if (chan->ext_info) {
> unsigned int i = 0;
> for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index b4a0679e4a49..45b781084a4b 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -225,12 +225,22 @@ struct iio_event_spec {
> * endianness: little or big endian
> * @info_mask_separate: What information is to be exported that is specific to
> * this channel.
> + * @info_mask_separate_available: What availability information is to be
> + * exported that is specific to this channel.
> * @info_mask_shared_by_type: What information is to be exported that is shared
> * by all channels of the same type.
> + * @info_mask_shared_by_type_available: What availability information is to be
> + * exported that is shared by all channels of the same
> + * type.
> * @info_mask_shared_by_dir: What information is to be exported that is shared
> * by all channels of the same direction.
> + * @info_mask_shared_by_dir_available: What availability information is to be
> + * exported that is shared by all channels of the same
> + * direction.
> * @info_mask_shared_by_all: What information is to be exported that is shared
> * by all channels.
> + * @info_mask_shared_by_all_available: What availability information is to be
> + * exported that is shared by all channels.
> * @event_spec: Array of events which should be registered for this
> * channel.
> * @num_event_specs: Size of the event_spec array.
> @@ -269,9 +279,13 @@ struct iio_chan_spec {
> enum iio_endian endianness;
> } scan_type;
> long info_mask_separate;
> + long info_mask_separate_available;
> long info_mask_shared_by_type;
> + long info_mask_shared_by_type_available;
> long info_mask_shared_by_dir;
> + long info_mask_shared_by_dir_available;
> long info_mask_shared_by_all;
> + long info_mask_shared_by_all_available;
> const struct iio_event_spec *event_spec;
> unsigned int num_event_specs;
> const struct iio_chan_spec_ext_info *ext_info;
> @@ -349,6 +363,14 @@ struct iio_dev;
> * max_len specifies maximum number of elements
> * vals pointer can contain. val_len is used to return
> * length of valid elements in vals.
> + * @read_avail: function to return the available values from the device.
> + * mask specifies which value. Note 0 means the available
> + * values for the channel in question. Return value
> + * specifies if a IIO_AVAIL_LIST or a IIO_AVAIL_RANGE is
> + * returned in vals. The type of the vals are returned in
> + * type and the number of vals is returned in length. For
> + * ranges, there are always three vals returned; min, step
> + * and max. For lists, all possible values are enumerated.
> * @write_raw: function to write a value to the device.
> * Parameters are the same as for read_raw.
> * @write_raw_get_fmt: callback function to query the expected
> @@ -397,6 +419,13 @@ struct iio_info {
> int *val_len,
> long mask);
>
> + int (*read_avail)(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan,
> + const int **vals,
> + int *type,
> + int *length,
> + long mask);
> +
> int (*write_raw)(struct iio_dev *indio_dev,
> struct iio_chan_spec const *chan,
> int val,
> diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
> index 32b579525004..2aa7b6384d64 100644
> --- a/include/linux/iio/types.h
> +++ b/include/linux/iio/types.h
> @@ -29,4 +29,9 @@ enum iio_event_info {
> #define IIO_VAL_FRACTIONAL 10
> #define IIO_VAL_FRACTIONAL_LOG2 11
>
> +enum iio_available_type {
> + IIO_AVAIL_LIST,
> + IIO_AVAIL_RANGE,
> +};
> +
> #endif /* _IIO_TYPES_H_ */
>
--
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 01/14] dma: sun6i-dma: Add burst case of 4
From: Jean-Francois Moine @ 2016-10-30 6:16 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Maxime Ripard, Mark Rutland, Linux-ALSA, Liam Girdwood,
Mike Turquette, Takashi Iwai, linux-sunxi, Alexandre Belloni,
Lee Jones, linux-clk, Vinod Koul, Mylène Josserand,
devicetree, Rob Herring, Jaroslav Kysela, linux-arm-kernel,
Thomas Petazzoni, Stephen Boyd, linux-kernel, Mark Brown,
dmaengine-u79uwXL29TasMV2rI37PzA
In-Reply-To: <CAGb2v67SSZF6XL-HXv83nuwTKpJc53h8gsrb2r2V98LNZBzqEA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Sun, 30 Oct 2016 10:06:20 +0800
Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org> wrote:
> >> Yes, I know that the burst size is always a power of 2.
> >> The best way to check it would be to change the {src,dts}_maxburst to a
> >> bitmap of the possible bursts as 0x0d for 1,4 and 8 bytes. But this
> >> asks for a lot of changes...
> >
> > To be honest, I'm not really a big fan of those tree wide changes
> > without any way to maintain compatibility. It ends up in a single,
> > huge patch if we want to maintain bisectability that just isn't
> > reviewable.
>
> Looking at the dmaengine API, I believe we got it wrong.
>
> max_burst in dma_slave_config denotes the largest amount of data
> a single transfer should be, as described in dmaengine.h:
>
> * @src_maxburst: the maximum number of words (note: words, as in
> * units of the src_addr_width member, not bytes) that can be sent
> * in one burst to the device. Typically something like half the
> * FIFO depth on I/O peripherals so you don't overflow it. This
> * may or may not be applicable on memory sources.
> * @dst_maxburst: same as src_maxburst but for destination target
> * mutatis mutandis.
>
> The DMA engine driver should be free to select whatever burst size
> that doesn't exceed this. So for max_burst = 4, the driver can select
> burst = 4 for controllers that do support it, or burst = 1 for those
> that don't, and do more bursts.
>
> This also means we can increase max_burst for the audio codec, as
> the FIFO is 64 samples deep for stereo, or 128 samples for mono.
>
>
> If we agree on the above I can send some patches for this.
That's fine to me, but this does not solve the main problem which is
how/where are defined the possible bursts of a SoC.
--
Ken ar c'hentañ | ** Breizh ha Linux atav! **
Jef | http://moinejf.free.fr/
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [PATCH v4 2/2] fpga: Add support for Lattice iCE40 FPGAs
From: kbuild test robot @ 2016-10-30 6:12 UTC (permalink / raw)
Cc: kbuild-all, atull, moritz.fischer, devicetree, linux-kernel,
linux-spi, Joel Holdsworth
In-Reply-To: <1477776746-3678-2-git-send-email-joel@airwebreathe.org.uk>
[-- Attachment #1: Type: text/plain, Size: 4064 bytes --]
Hi Joel,
[auto build test ERROR on linus/master]
[also build test ERROR on v4.9-rc3 next-20161028]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]
url: https://github.com/0day-ci/linux/commits/Joel-Holdsworth/of-Add-vendor-prefix-for-Lattice-Semiconductor/20161030-053525
config: openrisc-allmodconfig (attached as .config)
compiler: or32-linux-gcc (GCC) 4.5.1-or32-1.0rc1
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=openrisc
All errors (new ones prefixed by >>):
drivers/fpga/ice40-spi.c:203:1: warning: data definition has no type or storage class
drivers/fpga/ice40-spi.c:203:1: error: type defaults to 'int' in declaration of 'MODULE_DEVICE_TABLE'
drivers/fpga/ice40-spi.c:203:1: warning: parameter names (without types) in function declaration
drivers/fpga/ice40-spi.c:215:1: warning: data definition has no type or storage class
drivers/fpga/ice40-spi.c:215:1: error: type defaults to 'int' in declaration of 'module_init'
drivers/fpga/ice40-spi.c:215:1: warning: parameter names (without types) in function declaration
drivers/fpga/ice40-spi.c:215:1: warning: data definition has no type or storage class
drivers/fpga/ice40-spi.c:215:1: error: type defaults to 'int' in declaration of 'module_exit'
drivers/fpga/ice40-spi.c:215:1: warning: parameter names (without types) in function declaration
drivers/fpga/ice40-spi.c:217:15: error: expected declaration specifiers or '...' before string constant
drivers/fpga/ice40-spi.c:217:1: warning: data definition has no type or storage class
>> drivers/fpga/ice40-spi.c:217:1: error: type defaults to 'int' in declaration of 'MODULE_AUTHOR'
>> drivers/fpga/ice40-spi.c:217:15: error: function declaration isn't a prototype
drivers/fpga/ice40-spi.c:218:20: error: expected declaration specifiers or '...' before string constant
drivers/fpga/ice40-spi.c:218:1: warning: data definition has no type or storage class
>> drivers/fpga/ice40-spi.c:218:1: error: type defaults to 'int' in declaration of 'MODULE_DESCRIPTION'
drivers/fpga/ice40-spi.c:218:20: error: function declaration isn't a prototype
drivers/fpga/ice40-spi.c:219:16: error: expected declaration specifiers or '...' before string constant
drivers/fpga/ice40-spi.c:219:1: warning: data definition has no type or storage class
>> drivers/fpga/ice40-spi.c:219:1: error: type defaults to 'int' in declaration of 'MODULE_LICENSE'
drivers/fpga/ice40-spi.c:219:16: error: function declaration isn't a prototype
drivers/fpga/ice40-spi.c:215:1: warning: 'ice40_fpga_driver_init' defined but not used
drivers/fpga/ice40-spi.c:215:1: warning: 'ice40_fpga_driver_exit' defined but not used
vim +217 drivers/fpga/ice40-spi.c
197
198 #ifdef CONFIG_OF
199 static const struct of_device_id ice40_fpga_of_match[] = {
200 { .compatible = "lattice,ice40-fpga-mgr", },
201 {},
202 };
> 203 MODULE_DEVICE_TABLE(of, ice40_fpga_of_match);
204 #endif
205
206 static struct spi_driver ice40_fpga_driver = {
207 .probe = ice40_fpga_probe,
208 .remove = ice40_fpga_remove,
209 .driver = {
210 .name = "ice40spi",
211 .of_match_table = of_match_ptr(ice40_fpga_of_match),
212 },
213 };
214
> 215 module_spi_driver(ice40_fpga_driver);
216
> 217 MODULE_AUTHOR("Joel Holdsworth <joel@airwebreathe.org.uk>");
> 218 MODULE_DESCRIPTION("Lattice iCE40 FPGA Manager");
> 219 MODULE_LICENSE("GPL v2");
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 39284 bytes --]
^ permalink raw reply
* [PATCH] arm64: dts: rockchip: add cpu-id nvmem cell node for rk3399
From: Ziyuan Xu @ 2016-10-30 2:57 UTC (permalink / raw)
To: heiko-4mtYJXux2i+zQB+pC5nmwQ
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
zhengsq-TNX95d0MmH7DzftRWevZcw, zyw-TNX95d0MmH7DzftRWevZcw,
finley.xiao-TNX95d0MmH7DzftRWevZcw,
dianders-F7+t8E8rja9g9hUCZPvPmw, Ziyuan Xu
There is a 'cpu-id' field in efuse, export it for other drivers
reference.
Signed-off-by: Ziyuan Xu <xzy.xu-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 3 +++
1 file changed, 3 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 76b2864..71c3ce7 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1023,6 +1023,9 @@
clock-names = "pclk_efuse";
/* Data cells */
+ cpu_id: cpu-id {
+ reg = <0x07 0x10>;
+ };
cpub_leakage: cpu-leakage@17 {
reg = <0x17 0x1>;
};
--
2.9.3
--
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 v4 2/2] fpga: Add support for Lattice iCE40 FPGAs
From: kbuild test robot @ 2016-10-30 2:50 UTC (permalink / raw)
Cc: kbuild-all, atull, moritz.fischer, devicetree, linux-kernel,
linux-spi, Joel Holdsworth
In-Reply-To: <1477776746-3678-2-git-send-email-joel@airwebreathe.org.uk>
[-- Attachment #1: Type: text/plain, Size: 7035 bytes --]
Hi Joel,
[auto build test ERROR on linus/master]
[also build test ERROR on v4.9-rc3 next-20161028]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]
url: https://github.com/0day-ci/linux/commits/Joel-Holdsworth/of-Add-vendor-prefix-for-Lattice-Semiconductor/20161030-053525
config: powerpc-allmodconfig (attached as .config)
compiler: powerpc64-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=powerpc
All errors (new ones prefixed by >>):
drivers/fpga/ice40-spi.c:203:1: warning: data definition has no type or storage class
MODULE_DEVICE_TABLE(of, ice40_fpga_of_match);
^~~~~~~~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:203:1: error: type defaults to 'int' in declaration of 'MODULE_DEVICE_TABLE' [-Werror=implicit-int]
drivers/fpga/ice40-spi.c:203:1: warning: parameter names (without types) in function declaration
In file included from include/linux/platform_device.h:14:0,
from include/linux/fpga/fpga-mgr.h:19,
from drivers/fpga/ice40-spi.c:14:
include/linux/device.h:1353:1: warning: data definition has no type or storage class
module_init(__driver##_init); \
^
include/linux/spi/spi.h:290:2: note: in expansion of macro 'module_driver'
module_driver(__spi_driver, spi_register_driver, \
^~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:215:1: note: in expansion of macro 'module_spi_driver'
module_spi_driver(ice40_fpga_driver);
^~~~~~~~~~~~~~~~~
>> include/linux/device.h:1353:1: error: type defaults to 'int' in declaration of 'module_init' [-Werror=implicit-int]
module_init(__driver##_init); \
^
include/linux/spi/spi.h:290:2: note: in expansion of macro 'module_driver'
module_driver(__spi_driver, spi_register_driver, \
^~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:215:1: note: in expansion of macro 'module_spi_driver'
module_spi_driver(ice40_fpga_driver);
^~~~~~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:215:1: warning: parameter names (without types) in function declaration
In file included from include/linux/platform_device.h:14:0,
from include/linux/fpga/fpga-mgr.h:19,
from drivers/fpga/ice40-spi.c:14:
include/linux/device.h:1358:1: warning: data definition has no type or storage class
module_exit(__driver##_exit);
^
include/linux/spi/spi.h:290:2: note: in expansion of macro 'module_driver'
module_driver(__spi_driver, spi_register_driver, \
^~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:215:1: note: in expansion of macro 'module_spi_driver'
module_spi_driver(ice40_fpga_driver);
^~~~~~~~~~~~~~~~~
>> include/linux/device.h:1358:1: error: type defaults to 'int' in declaration of 'module_exit' [-Werror=implicit-int]
module_exit(__driver##_exit);
^
include/linux/spi/spi.h:290:2: note: in expansion of macro 'module_driver'
module_driver(__spi_driver, spi_register_driver, \
^~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:215:1: note: in expansion of macro 'module_spi_driver'
module_spi_driver(ice40_fpga_driver);
^~~~~~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:215:1: warning: parameter names (without types) in function declaration
drivers/fpga/ice40-spi.c:217:15: error: expected declaration specifiers or '...' before string constant
MODULE_AUTHOR("Joel Holdsworth <joel@airwebreathe.org.uk>");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:218:20: error: expected declaration specifiers or '...' before string constant
MODULE_DESCRIPTION("Lattice iCE40 FPGA Manager");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:219:16: error: expected declaration specifiers or '...' before string constant
MODULE_LICENSE("GPL v2");
^~~~~~~~
In file included from include/linux/platform_device.h:14:0,
from include/linux/fpga/fpga-mgr.h:19,
from drivers/fpga/ice40-spi.c:14:
drivers/fpga/ice40-spi.c:215:19: warning: 'ice40_fpga_driver_exit' defined but not used [-Wunused-function]
module_spi_driver(ice40_fpga_driver);
^
include/linux/device.h:1354:20: note: in definition of macro 'module_driver'
static void __exit __driver##_exit(void) \
^~~~~~~~
drivers/fpga/ice40-spi.c:215:1: note: in expansion of macro 'module_spi_driver'
module_spi_driver(ice40_fpga_driver);
^~~~~~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:215:19: warning: 'ice40_fpga_driver_init' defined but not used [-Wunused-function]
module_spi_driver(ice40_fpga_driver);
^
include/linux/device.h:1349:19: note: in definition of macro 'module_driver'
static int __init __driver##_init(void) \
^~~~~~~~
drivers/fpga/ice40-spi.c:215:1: note: in expansion of macro 'module_spi_driver'
module_spi_driver(ice40_fpga_driver);
^~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors
vim +1353 include/linux/device.h
907d0ed1 Lars-Peter Clausen 2011-11-16 1347 */
cd494618 Lars-Peter Clausen 2012-02-25 1348 #define module_driver(__driver, __register, __unregister, ...) \
907d0ed1 Lars-Peter Clausen 2011-11-16 1349 static int __init __driver##_init(void) \
907d0ed1 Lars-Peter Clausen 2011-11-16 1350 { \
cd494618 Lars-Peter Clausen 2012-02-25 1351 return __register(&(__driver) , ##__VA_ARGS__); \
907d0ed1 Lars-Peter Clausen 2011-11-16 1352 } \
907d0ed1 Lars-Peter Clausen 2011-11-16 @1353 module_init(__driver##_init); \
907d0ed1 Lars-Peter Clausen 2011-11-16 1354 static void __exit __driver##_exit(void) \
907d0ed1 Lars-Peter Clausen 2011-11-16 1355 { \
cd494618 Lars-Peter Clausen 2012-02-25 1356 __unregister(&(__driver) , ##__VA_ARGS__); \
907d0ed1 Lars-Peter Clausen 2011-11-16 1357 } \
907d0ed1 Lars-Peter Clausen 2011-11-16 @1358 module_exit(__driver##_exit);
907d0ed1 Lars-Peter Clausen 2011-11-16 1359
f309d444 Paul Gortmaker 2015-05-01 1360 /**
f309d444 Paul Gortmaker 2015-05-01 1361 * builtin_driver() - Helper macro for drivers that don't do anything
:::::: The code at line 1353 was first introduced by commit
:::::: 907d0ed1c84114d4e8dafd66af982515d3739c90 drivercore: Generalize module_platform_driver
:::::: TO: Lars-Peter Clausen <lars@metafoo.de>
:::::: CC: Greg Kroah-Hartman <gregkh@suse.de>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 50796 bytes --]
^ permalink raw reply
* Re: [PATCH v4 2/2] fpga: Add support for Lattice iCE40 FPGAs
From: kbuild test robot @ 2016-10-30 2:27 UTC (permalink / raw)
Cc: kbuild-all-JC7UmRfGjtg, atull-yzvPICuk2ABMcg4IHK0kFoH6Mc4MB0Vx,
moritz.fischer-+aYTwkv1SeIAvxtiuMwx3w,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-spi-u79uwXL29TY76Z2rM5mHXA, Joel Holdsworth
In-Reply-To: <1477776746-3678-2-git-send-email-joel-IJEoVVyKhCJXvIrf17iDB/XRex20P6io@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 5188 bytes --]
Hi Joel,
[auto build test ERROR on linus/master]
[also build test ERROR on v4.9-rc3 next-20161028]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
[Suggest to use git(>=2.9.0) format-patch --base=<commit> (or --base=auto for convenience) to record what (public, well-known) commit your patch series was built on]
[Check https://git-scm.com/docs/git-format-patch for more information]
url: https://github.com/0day-ci/linux/commits/Joel-Holdsworth/of-Add-vendor-prefix-for-Lattice-Semiconductor/20161030-053525
config: i386-allyesconfig (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All errors (new ones prefixed by >>):
drivers/fpga/ice40-spi.c:203:1: warning: data definition has no type or storage class
MODULE_DEVICE_TABLE(of, ice40_fpga_of_match);
^~~~~~~~~~~~~~~~~~~
>> drivers/fpga/ice40-spi.c:203:1: error: type defaults to 'int' in declaration of 'MODULE_DEVICE_TABLE' [-Werror=implicit-int]
drivers/fpga/ice40-spi.c:203:1: warning: parameter names (without types) in function declaration
drivers/fpga/ice40-spi.c:215:229: warning: data definition has no type or storage class
module_spi_driver(ice40_fpga_driver);
^
drivers/fpga/ice40-spi.c:215:229: error: type defaults to 'int' in declaration of 'module_init' [-Werror=implicit-int]
drivers/fpga/ice40-spi.c:215:191: warning: parameter names (without types) in function declaration
module_spi_driver(ice40_fpga_driver);
^
drivers/fpga/ice40-spi.c:215:492: warning: data definition has no type or storage class
module_spi_driver(ice40_fpga_driver);
^
drivers/fpga/ice40-spi.c:215:492: error: type defaults to 'int' in declaration of 'module_exit' [-Werror=implicit-int]
drivers/fpga/ice40-spi.c:215:191: warning: parameter names (without types) in function declaration
module_spi_driver(ice40_fpga_driver);
^
>> drivers/fpga/ice40-spi.c:217:15: error: expected declaration specifiers or '...' before string constant
MODULE_AUTHOR("Joel Holdsworth <joel-IJEoVVyKhCJXvIrf17iDB/XRex20P6io@public.gmane.org>");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:218:20: error: expected declaration specifiers or '...' before string constant
MODULE_DESCRIPTION("Lattice iCE40 FPGA Manager");
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/fpga/ice40-spi.c:219:16: error: expected declaration specifiers or '...' before string constant
MODULE_LICENSE("GPL v2");
^~~~~~~~
drivers/fpga/ice40-spi.c:215:122: warning: 'ice40_fpga_driver_init' defined but not used [-Wunused-function]
module_spi_driver(ice40_fpga_driver);
^
cc1: some warnings being treated as errors
vim +203 drivers/fpga/ice40-spi.c
197
198 #ifdef CONFIG_OF
199 static const struct of_device_id ice40_fpga_of_match[] = {
200 { .compatible = "lattice,ice40-fpga-mgr", },
201 {},
202 };
> 203 MODULE_DEVICE_TABLE(of, ice40_fpga_of_match);
204 #endif
205
206 static struct spi_driver ice40_fpga_driver = {
207 .probe = ice40_fpga_probe,
208 .remove = ice40_fpga_remove,
209 .driver = {
210 .name = "ice40spi",
211 .of_match_table = of_match_ptr(ice40_fpga_of_match),
212 },
213 };
214
> 215 module_spi_driver(ice40_fpga_driver);
216
> 217 MODULE_AUTHOR("Joel Holdsworth <joel-IJEoVVyKhCJXvIrf17iDB/XRex20P6io@public.gmane.org>");
218 MODULE_DESCRIPTION("Lattice iCE40 FPGA Manager");
219 MODULE_LICENSE("GPL v2");
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 56218 bytes --]
^ permalink raw reply
* Re: [PATCH 01/14] dma: sun6i-dma: Add burst case of 4
From: Chen-Yu Tsai @ 2016-10-30 2:06 UTC (permalink / raw)
To: Maxime Ripard
Cc: Jean-Francois Moine, Mylène Josserand, Vinod Koul,
Chen-Yu Tsai, Mike Turquette, Stephen Boyd, Liam Girdwood,
Mark Brown, Jaroslav Kysela, Takashi Iwai, Lee Jones,
Mark Rutland, Rob Herring, Thomas Petazzoni, devicetree,
Linux-ALSA, linux-kernel, linux-sunxi, Alexandre Belloni,
dmaengine-u79uwXL29TY76Z2rM5mHXA, linux-clk
In-Reply-To: <20161027175154.v4kuhyvm3r5n6tdo@lukather>
Hi,
On Fri, Oct 28, 2016 at 1:51 AM, Maxime Ripard
<maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:
> On Sun, Oct 23, 2016 at 06:31:07PM +0200, Jean-Francois Moine wrote:
>> On Tue, 4 Oct 2016 18:55:54 +0200
>> Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:
>>
>> > On Tue, Oct 04, 2016 at 12:40:11PM +0200, Jean-Francois Moine wrote:
>> > > On Tue, 4 Oct 2016 11:46:14 +0200
>> > > Mylène Josserand <mylene.josserand-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:
>> > >
>> > > > Add the case of a burst of 4 which is handled by the SoC.
>> > > >
>> > > > Signed-off-by: Mylène Josserand <mylene.josserand@free-electrons.com>
>> > > > ---
>> > > > drivers/dma/sun6i-dma.c | 2 ++
>> > > > 1 file changed, 2 insertions(+)
>> > > >
>> > > > diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
>> > > > index 8346199..0485204 100644
>> > > > --- a/drivers/dma/sun6i-dma.c
>> > > > +++ b/drivers/dma/sun6i-dma.c
>> > > > @@ -240,6 +240,8 @@ static inline s8 convert_burst(u32 maxburst)
>> > > > switch (maxburst) {
>> > > > case 1:
>> > > > return 0;
>> > > > + case 4:
>> > > > + return 1;
>> > > > case 8:
>> > > > return 2;
>> > > > default:
>> > > > --
>> > > > 2.9.3
>> > >
>> > > This patch has already been rejected by Maxime in the threads
>> > > http://www.spinics.net/lists/dmaengine/msg08610.html
>> > > and
>> > > http://www.spinics.net/lists/dmaengine/msg08719.html
>> > >
>> > > I hope you will find the way he wants for this maxburst to be added.
>> >
>> > I was talking about something along these lines (not tested):
>>
>> I wonder why you don't submit this yourself.
>
> I thought you were the one who cared. You asked for what I had in
> mind, here it is.
>
>> > -------8<---------
>> > diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
>> > index 83461994e418..573ac4608293 100644
>> > --- a/drivers/dma/sun6i-dma.c
>> > +++ b/drivers/dma/sun6i-dma.c
>> > @@ -240,6 +240,8 @@ static inline s8 convert_burst(u32 maxburst)
>> > switch (maxburst) {
>> > case 1:
>> > return 0;
>> > + case 4:
>> > + return 1;
>> > case 8:
>> > return 2;
>> > default:
>> > @@ -1110,11 +1112,19 @@ static int sun6i_dma_probe(struct platform_device *pdev)
>> > sdc->slave.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
>> > BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
>> > BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
>> > + sdc->slave.dst_bursts = BIT(1) | BIT(8);
>> > + sdc->slave.src_bursts = BIT(1) | BIT(8);
>> > sdc->slave.directions = BIT(DMA_DEV_TO_MEM) |
>> > BIT(DMA_MEM_TO_DEV);
>> > sdc->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
>> > sdc->slave.dev = &pdev->dev;
>> >
>> > + if (of_device_is_compatible(pdev->dev.of_node,
>> > + "allwinner,sun8i-h3-dma")) {
>> > + sdc->slave.dst_bursts |= BIT(4);
>> > + sdc->slave.src_bursts |= BIT(4);
>> > + }
>> > +
>> > sdc->pchans = devm_kcalloc(&pdev->dev, sdc->cfg->nr_max_channels,
>> > sizeof(struct sun6i_pchan), GFP_KERNEL);
>> > if (!sdc->pchans)
>> > diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
>> > index cc535a478bae..f7bbec24bb58 100644
>> > --- a/include/linux/dmaengine.h
>> > +++ b/include/linux/dmaengine.h
>> > @@ -673,6 +673,8 @@ struct dma_filter {
>> > * each type of direction, the dma controller should fill (1 <<
>> > * <TYPE>) and same should be checked by controller as well
>> > * @max_burst: max burst capability per-transfer
>> > + * @dst_bursts: bitfield of the available burst sizes for the destination
>> > + * @src_bursts: bitfield of the available burst sizes for the source
>>
>> You did not define dst_bursts nor src_bursts.
>>
>> > * @residue_granularity: granularity of the transfer residue reported
>> > * by tx_status
>> > * @device_alloc_chan_resources: allocate resources and return the
>> > @@ -800,6 +802,14 @@ struct dma_device {
>> > static inline int dmaengine_slave_config(struct dma_chan *chan,
>> > struct dma_slave_config *config)
>> > {
>> > + if (config->src_maxburst && config->device->src_bursts &&
>> > + !(BIT(config->src_maxburst) & config->device->src_bursts))
>>
>> The maxburst may be as big as 4Kibytes, then, I am not sure that this
>> code will work!
>>
>> > + return -EINVAL;
>> > +
>> > + if (config->dst_maxburst && config->device->dst_bursts &&
>> > + !(BIT(config->dst_maxburst) & config->device->dst_bursts))
>> > + return -EINVAL;
>> > +
>> > if (chan->device->device_config)
>> > return chan->device->device_config(chan, config);
>> > -------8<------------
>>
>> Yes, I know that the burst size is always a power of 2.
>> The best way to check it would be to change the {src,dts}_maxburst to a
>> bitmap of the possible bursts as 0x0d for 1,4 and 8 bytes. But this
>> asks for a lot of changes...
>
> To be honest, I'm not really a big fan of those tree wide changes
> without any way to maintain compatibility. It ends up in a single,
> huge patch if we want to maintain bisectability that just isn't
> reviewable.
Looking at the dmaengine API, I believe we got it wrong.
max_burst in dma_slave_config denotes the largest amount of data
a single transfer should be, as described in dmaengine.h:
* @src_maxburst: the maximum number of words (note: words, as in
* units of the src_addr_width member, not bytes) that can be sent
* in one burst to the device. Typically something like half the
* FIFO depth on I/O peripherals so you don't overflow it. This
* may or may not be applicable on memory sources.
* @dst_maxburst: same as src_maxburst but for destination target
* mutatis mutandis.
The DMA engine driver should be free to select whatever burst size
that doesn't exceed this. So for max_burst = 4, the driver can select
burst = 4 for controllers that do support it, or burst = 1 for those
that don't, and do more bursts.
This also means we can increase max_burst for the audio codec, as
the FIFO is 64 samples deep for stereo, or 128 samples for mono.
If we agree on the above I can send some patches for this.
Regards
ChenYu
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
^ permalink raw reply
* Re: [v2 2/2] fpga: Add support for Lattice iCE40 FPGAs
From: Joel Holdsworth @ 2016-10-29 21:38 UTC (permalink / raw)
To: Moritz Fischer
Cc: atull, Ian Campbell, Kumar Gala, Mark Rutland,
pawel.moll-5wv7dgnIgG8@public.gmane.org, Rob Herring,
Devicetree List, Linux Kernel Mailing List
In-Reply-To: <CAAtXAHfcfveozw_3kpXz9pA0ZSTeuo9CNs9vRq1Y+BhrNuim7A-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
I just submitted a version of the driver without copy-pasted chip-select
code. The PATCH v4 version uses a pair of zero-byte SPI transfers to
control the CS line.
I didn't get a response from the linux-spi mailing list, but in my
opinion they're probably not going to want spi_set_cs to become a
public-internal API, and zero-byte transfers have the desired effect.
Thanks
Joel
On 10/25/2016 10:48 AM, Moritz Fischer wrote:
> Hi Joel,
>
> On Mon, Oct 24, 2016 at 9:51 PM, Joel Holdsworth
> <joel-IJEoVVyKhCJXvIrf17iDB/XRex20P6io@public.gmane.org> wrote:
>
>> I think my set_cs() function is ok-ish. It's copied from spi_set_cs() in
>> drivers/spi/spi.c . This function is a static internal helper, so I
>> copy/pasted the function into the ice40 driver. Given that it's only 4-lines
>> of code, it didn't seem too bad - though I'm not exactly sure why
>> spi_set_cs() isn't a public API. It seems like quite a common-place thing to
>> need to do with certain devices.
>>
>> However, perhaps the function is internal because the authors of the SPI
>> framework foresaw how easy it would be to screw up a shared bus with that
>> function. I had to take care to make sure the SPI bus was locked throughout.
>>
>> Do you agree that it's the right thing to copy the function in? Or do you
>> think it would be better to ask for spi_set_cs to be exposed publicly?
>
> I'd poke the SPI maintainers about what their reasoning was to make it
> non-public,
> and how they'd go about doing what you're trying to do.
> I can imagine there might be some SPI controllers where the above
> doesn't work well,
> because the controller automatically handles the CS line and you don't
> get control over it.
>
> Cheers,
>
> Moritz
>
>
--
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
* [PATCH v4 2/2] fpga: Add support for Lattice iCE40 FPGAs
From: Joel Holdsworth @ 2016-10-29 21:32 UTC (permalink / raw)
To: atull, moritz.fischer, devicetree, linux-kernel, linux-spi
Cc: Joel Holdsworth
In-Reply-To: <1477776746-3678-1-git-send-email-joel@airwebreathe.org.uk>
The Lattice iCE40 is a family of FPGAs with a minimalistic architecture
and very regular structure, designed for low-cost, high-volume consumer
and system applications.
This patch adds support to the FPGA manager for configuring the SRAM of
iCE40LM, iCE40LP, iCE40HX, iCE40 Ultra, iCE40 UltraLite and iCE40
UltraPlus devices, through slave SPI.
The iCE40 family is notable because it is the first FPGA family to have
complete reverse engineered bit-stream documentation for the iCE40LP and
iCE40HX devices. Furthermore, there is now a Free Software Verilog
synthesis tool-chain: the "IceStorm" tool-chain.
This project is the work of Clifford Wolf, who is the maintainer of
Yosys Verilog RTL synthesis framework, and Mathias Lasser, with notable
contributions from "Cotton Seed", the main author of "arachne-pnr"; a
place-and-route tool for iCE40 FPGAs.
Having a Free Software synthesis tool-chain offers interesting
opportunities for embedded devices that are able reconfigure themselves
with open firmware that is generated on the device itself. For example
a mobile device might have an application processor with an iCE40 FPGA
attached, which implements slave devices, or through which the processor
communicates with other devices through the FPGA fabric.
A kernel driver for the iCE40 is useful, because in some cases, the FPGA
may need to be configured before other devices can be accessed.
An example of such a device is the icoBoard; a RaspberryPI HAT which
features an iCE40HX8K with a 1 or 8 MBit SRAM and ports for
Digilent-compatible PMOD modules. A PMOD module may contain a device
with which the kernel communicates, via the FPGA.
---
.../bindings/fpga/lattice-ice40-fpga-mgr.txt | 23 +++
drivers/fpga/Kconfig | 6 +
drivers/fpga/Makefile | 1 +
drivers/fpga/ice40-spi.c | 219 +++++++++++++++++++++
4 files changed, 249 insertions(+)
create mode 100644 Documentation/devicetree/bindings/fpga/lattice-ice40-fpga-mgr.txt
create mode 100644 drivers/fpga/ice40-spi.c
diff --git a/Documentation/devicetree/bindings/fpga/lattice-ice40-fpga-mgr.txt b/Documentation/devicetree/bindings/fpga/lattice-ice40-fpga-mgr.txt
new file mode 100644
index 0000000..3d2917c
--- /dev/null
+++ b/Documentation/devicetree/bindings/fpga/lattice-ice40-fpga-mgr.txt
@@ -0,0 +1,23 @@
+Lattice iCE40 FPGA Manager
+
+Required properties:
+- compatible: should contain "lattice,ice40-fpga-mgr"
+- reg: SPI chip select
+- spi-max-frequency: Maximum SPI frequency (>=1000000, <=25000000)
+- cdone-gpio: GPIO connected to CDONE pin
+- creset_b-gpio: GPIO connected to CRESET_B pin. Note that CRESET_B is
+ treated as an active-low output because the signal is
+ treated as an enable signal, rather than a reset. This
+ is necessary because the FPGA will enter Master SPI
+ mode and drive SCK with a clock signal, potentially
+ jamming other devices on the bus, unless CRESET_B is
+ held high until the firmware is loaded.
+
+Example:
+ ice40: ice40@0 {
+ compatible = "lattice,ice40-fpga-mgr";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ cdone-gpio = <&gpio 24 GPIO_ACTIVE_HIGH>;
+ creset_b-gpio = <&gpio 22 GPIO_ACTIVE_LOW>;
+ };
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
index d614102..85ff429 100644
--- a/drivers/fpga/Kconfig
+++ b/drivers/fpga/Kconfig
@@ -13,6 +13,12 @@ config FPGA
if FPGA
+config FPGA_MGR_ICE40_SPI
+ tristate "Lattice iCE40 SPI"
+ depends on SPI
+ help
+ FPGA manager driver support for Lattice iCE40 FPGAs over SPI.
+
config FPGA_MGR_SOCFPGA
tristate "Altera SOCFPGA FPGA Manager"
depends on ARCH_SOCFPGA
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
index 8d83fc6..adb5811 100644
--- a/drivers/fpga/Makefile
+++ b/drivers/fpga/Makefile
@@ -6,5 +6,6 @@
obj-$(CONFIG_FPGA) += fpga-mgr.o
# FPGA Manager Drivers
+obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o
obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c
new file mode 100644
index 0000000..77ca482
--- /dev/null
+++ b/drivers/fpga/ice40-spi.c
@@ -0,0 +1,219 @@
+/*
+ * FPGA Manager Driver for Lattice iCE40.
+ *
+ * Copyright (c) 2016 Joel Holdsworth
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This driver adds support to the FPGA manager for configuring the SRAM of
+ * Lattice iCE40 FPGAs through slave SPI.
+ */
+
+#include <linux/fpga/fpga-mgr.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+
+#define ICE40_SPI_FPGAMGR_RESET_DELAY 1 /* us (>200ns) */
+#define ICE40_SPI_FPGAMGR_HOUSEKEEPING_DELAY 1200 /* us */
+
+#define ICE40_SPI_FPGAMGR_NUM_ACTIVATION_BITS 49 /* bits */
+
+struct ice40_fpga_priv {
+ struct spi_device *dev;
+ struct gpio_desc *creset_b;
+ struct gpio_desc *cdone;
+};
+
+static enum fpga_mgr_states ice40_fpga_ops_state(struct fpga_manager *mgr)
+{
+ struct ice40_fpga_priv *priv = mgr->priv;
+ return gpiod_get_value(priv->cdone) ? FPGA_MGR_STATE_OPERATING :
+ FPGA_MGR_STATE_UNKNOWN;
+}
+
+static int ice40_fpga_ops_write_init(struct fpga_manager *mgr, u32 flags,
+ const char *buf, size_t count)
+{
+ struct ice40_fpga_priv *priv = mgr->priv;
+ struct spi_device *dev = priv->dev;
+ struct spi_message message;
+ int ret;
+
+ if ((flags & FPGA_MGR_PARTIAL_RECONFIG)) {
+ dev_err(&dev->dev,
+ "Partial reconfiguration is not supported\n");
+ return -ENOTSUPP;
+ }
+
+ /* Lock the bus, assert CRESET_B and SS_B and delay >200ns */
+ ret = spi_bus_lock(dev->master);
+ if (ret) {
+ dev_err(&dev->dev, "Failed to lock SPI bus, ret: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value(priv->creset_b, 1);
+
+ spi_message_init(&message);
+ spi_message_add_tail(&(struct spi_transfer){.cs_change = 1,
+ .delay_usecs = ICE40_SPI_FPGAMGR_RESET_DELAY}, &message);
+ ret = spi_sync_locked(dev, &message);
+ if (ret) {
+ dev_err(&dev->dev, "Failed to set chip-select, ret: %d\n", ret);
+ spi_bus_unlock(dev->master);
+ return ret;
+ }
+
+ /* Come out of reset */
+ gpiod_set_value(priv->creset_b, 0);
+
+ /* Check CDONE is de-asserted i.e. the FPGA is reset */
+ if (gpiod_get_value(priv->cdone)) {
+ dev_err(&dev->dev, "Device reset failed, CDONE is asserted\n");
+ ret = -EIO;
+ }
+
+ /* Wait for the housekeeping to complete, and release SS_B */
+ spi_message_init(&message);
+ spi_message_add_tail(&(struct spi_transfer){
+ .delay_usecs = ICE40_SPI_FPGAMGR_HOUSEKEEPING_DELAY}, &message);
+ ret = spi_sync_locked(dev, &message);
+ if (ret)
+ dev_err(&dev->dev, "Failed while releasing chip-select, ret: %d\n", ret);
+
+ spi_bus_unlock(dev->master);
+
+ return ret;
+}
+
+static int ice40_fpga_ops_write(struct fpga_manager *mgr,
+ const char *buf, size_t count)
+{
+ struct ice40_fpga_priv *priv = mgr->priv;
+ struct spi_device *dev = priv->dev;
+ int ret;
+
+ ret = spi_write(dev, buf, count);
+ if (ret)
+ dev_err(&dev->dev, "Error sending SPI data, ret: %d\n", ret);
+
+ return ret;
+}
+
+static int ice40_fpga_ops_write_complete(struct fpga_manager *mgr, u32 flags)
+{
+ struct ice40_fpga_priv *priv = mgr->priv;
+ struct spi_device *dev = priv->dev;
+ int ret = 0;
+
+ /* Check CDONE is asserted */
+ if (!gpiod_get_value(priv->cdone)) {
+ dev_err(&dev->dev,
+ "CDONE was not asserted after firmware transfer\n");
+ return -EIO;
+ }
+
+ /* Send of zero-padding to activate the firmware */
+ ret = spi_write(dev, NULL, (ICE40_SPI_FPGAMGR_NUM_ACTIVATION_BITS +
+ dev->bits_per_word - 1) / dev->bits_per_word);
+ if (ret) {
+ dev_err(&dev->dev, "Error sending zero padding, ret: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* Success */
+ return 0;
+}
+
+static const struct fpga_manager_ops ice40_fpga_ops = {
+ .state = ice40_fpga_ops_state,
+ .write_init = ice40_fpga_ops_write_init,
+ .write = ice40_fpga_ops_write,
+ .write_complete = ice40_fpga_ops_write_complete,
+};
+
+static int ice40_fpga_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct device_node *np = spi->dev.of_node;
+ struct ice40_fpga_priv *priv;
+ int ret;
+
+ if (!np) {
+ dev_err(dev, "No Device Tree entry\n");
+ return -EINVAL;
+ }
+
+ priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = spi;
+
+ /* Check board setup data. */
+ if (spi->max_speed_hz > 25000000) {
+ dev_err(dev, "speed is too high\n");
+ return -EINVAL;
+ } else if (spi->mode & SPI_CPHA) {
+ dev_err(dev, "bad mode\n");
+ return -EINVAL;
+ }
+
+ /* Set up the GPIOs */
+ priv->cdone = devm_gpiod_get(dev, "cdone", GPIOD_IN);
+ if (IS_ERR(priv->cdone)) {
+ dev_err(dev, "Failed to get CDONE GPIO: %ld\n",
+ PTR_ERR(priv->cdone));
+ return ret;
+ }
+
+ priv->creset_b = devm_gpiod_get(dev, "creset_b", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->creset_b)) {
+ dev_err(dev, "Failed to get CRESET_B GPIO: %ld\n",
+ PTR_ERR(priv->creset_b));
+ return ret;
+ }
+
+ /* Register with the FPGA manager */
+ ret = fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager",
+ &ice40_fpga_ops, priv);
+ if (ret) {
+ dev_err(dev, "unable to register FPGA manager");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ice40_fpga_remove(struct spi_device *spi)
+{
+ fpga_mgr_unregister(&spi->dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ice40_fpga_of_match[] = {
+ { .compatible = "lattice,ice40-fpga-mgr", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ice40_fpga_of_match);
+#endif
+
+static struct spi_driver ice40_fpga_driver = {
+ .probe = ice40_fpga_probe,
+ .remove = ice40_fpga_remove,
+ .driver = {
+ .name = "ice40spi",
+ .of_match_table = of_match_ptr(ice40_fpga_of_match),
+ },
+};
+
+module_spi_driver(ice40_fpga_driver);
+
+MODULE_AUTHOR("Joel Holdsworth <joel@airwebreathe.org.uk>");
+MODULE_DESCRIPTION("Lattice iCE40 FPGA Manager");
+MODULE_LICENSE("GPL v2");
--
2.7.4
^ permalink raw reply related
* [PATCH v4 1/2] of: Add vendor prefix for Lattice Semiconductor
From: Joel Holdsworth @ 2016-10-29 21:32 UTC (permalink / raw)
To: atull, moritz.fischer, devicetree, linux-kernel, linux-spi
Cc: Joel Holdsworth
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 1992aa9..d64a835 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -146,6 +146,7 @@ kosagi Sutajio Ko-Usagi PTE Ltd.
kyo Kyocera Corporation
lacie LaCie
lantiq Lantiq Semiconductor
+lattice Lattice Semiconductor
lenovo Lenovo Group Ltd.
lg LG Corporation
linux Linux-specific binding
--
2.7.4
^ permalink raw reply related
* [PATCH] ARM: dts: r8a7794: remove Z clock
From: Sergei Shtylyov @ 2016-10-29 21:31 UTC (permalink / raw)
To: horms-/R6kz+dDXgpPR4JQBCEnsQ,
linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: magnus.damm-Re5JQEeQqe8AvxtiuMwx3w, linux-lFZ/pmaqli7XmaaqVzeoHQ,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <1529351.Fac5N2tKoF-gHKXc3Y1Z8zGSmamagVegGFoWSdPRAKMAL8bYrjMMd8@public.gmane.org>
R8A7794 doesn't have Cortex-A15 CPUs, thus there's no Z clock...
Fixes: 0dce5454d5c2 ("ARM: shmobile: Initial r8a7794 SoC device tree")
Signed-off-by: Sergei Shtylyov <sergei.shtylyov-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>
---
The patch is against the 'master' branch of Simon Horman's 'renesas.git' repo.
arch/arm/boot/dts/r8a7794.dtsi | 3 +--
include/dt-bindings/clock/r8a7794-clock.h | 3 +--
2 files changed, 2 insertions(+), 4 deletions(-)
Index: renesas/arch/arm/boot/dts/r8a7794.dtsi
===================================================================
--- renesas.orig/arch/arm/boot/dts/r8a7794.dtsi
+++ renesas/arch/arm/boot/dts/r8a7794.dtsi
@@ -1025,8 +1025,7 @@
clocks = <&extal_clk &usb_extal_clk>;
#clock-cells = <1>;
clock-output-names = "main", "pll0", "pll1", "pll3",
- "lb", "qspi", "sdh", "sd0", "z",
- "rcan";
+ "lb", "qspi", "sdh", "sd0", "rcan";
#power-domain-cells = <0>;
};
/* Variable factor clocks */
Index: renesas/include/dt-bindings/clock/r8a7794-clock.h
===================================================================
--- renesas.orig/include/dt-bindings/clock/r8a7794-clock.h
+++ renesas/include/dt-bindings/clock/r8a7794-clock.h
@@ -20,8 +20,7 @@
#define R8A7794_CLK_QSPI 5
#define R8A7794_CLK_SDH 6
#define R8A7794_CLK_SD0 7
-#define R8A7794_CLK_Z 8
-#define R8A7794_CLK_RCAN 9
+#define R8A7794_CLK_RCAN 8
/* MSTP0 */
#define R8A7794_CLK_MSIOF0 0
--
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] net: mv643xx_eth: Fetch the phy connection type from DT
From: David Miller @ 2016-10-29 19:11 UTC (permalink / raw)
To: jgunthorpe; +Cc: sebastian.hesselbarth, devicetree, netdev
In-Reply-To: <20161026174702.GB24717@obsidianresearch.com>
From: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
Date: Wed, 26 Oct 2016 11:47:02 -0600
> The MAC is capable of RGMII mode and that is probably a more typical
> connection type than GMII today (eg it is used by Marvell Reference
> designs for several SOCs). Let DT users specify the standard
>
> phy-connection-type = "rgmii-id";
>
> On a phy node.
>
> Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
Applied.
^ permalink raw reply
* Re: [PATCH v4 6/8] dt-bindings: Add support for Amlogic GXBB SCPI Interface
From: Olof Johansson @ 2016-10-29 18:39 UTC (permalink / raw)
To: Neil Armstrong
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Sudeep Holla, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Kevin Hilman,
Heiko Stübner, wxt-TNX95d0MmH7DzftRWevZcw,
frank.wang-TNX95d0MmH7DzftRWevZcw
In-Reply-To: <1475652814-30619-7-git-send-email-narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
Hi,
On Wed, Oct 5, 2016 at 12:33 AM, Neil Armstrong <narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org> wrote:
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Signed-off-by: Neil Armstrong <narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> ---
> Documentation/devicetree/bindings/arm/arm,scpi.txt | 8 +++++---
> 1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/arm/arm,scpi.txt b/Documentation/devicetree/bindings/arm/arm,scpi.txt
> index faa4b44..04bc171 100644
> --- a/Documentation/devicetree/bindings/arm/arm,scpi.txt
> +++ b/Documentation/devicetree/bindings/arm/arm,scpi.txt
> @@ -7,7 +7,7 @@ by Linux to initiate various system control and power operations.
>
> Required properties:
>
> -- compatible : should be "arm,scpi"
> +- compatible : should be "arm,scpi" or "amlogic,meson-gxbb-scpi"
This doesn't seem right to document here. If anything you might want
to have a table of more-specific-compatibles for specific
implementations, but "arm,scpi" should still be the compatible of the
node (just not the most specific one).
Also, documenting it here indiciates that non-amlogic implementations
can/should use that compatible, which is misleading.
> - mboxes: List of phandle and mailbox channel specifiers
> All the channels reserved by remote SCP firmware for use by
> SCPI message protocol should be specified in any order
> @@ -60,7 +60,8 @@ A small area of SRAM is reserved for SCPI communication between application
> processors and SCP.
>
> Required properties:
> -- compatible : should be "arm,juno-sram-ns" for Non-secure SRAM on Juno
> +- compatible : should be "arm,juno-sram-ns" for Non-secure SRAM on Juno,
> + or "amlogic,meson-gxbb-sram" for Amlogic GXBB SoC.
Maybe you'd be better of with a meson-specific document that refers to
these but with different compatible values.
Come to think of it, the Juno-specific one maybe shouldn't be in
arm,scpi at all, since that adds confusion here.
It's somewhat confusing that ARM is both a platform, architecture and
in some cases implementer of specific systems. :)
> The rest of the properties should follow the generic mmio-sram description
> found in ../../sram/sram.txt
> @@ -70,7 +71,8 @@ Each sub-node represents the reserved area for SCPI.
> Required sub-node properties:
> - reg : The base offset and size of the reserved area with the SRAM
> - compatible : should be "arm,juno-scp-shmem" for Non-secure SRAM based
> - shared memory on Juno platforms
> + shared memory on Juno platforms or
> + "amlogic,meson-gxbb-scp-shmem" for Amlogic GXBB SoC.
Same here. It won't scale if all vendors are expected to add an entry here.
-Olof
--
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 V2] pinctrl: qcom: Add msm8994 pinctrl driver
From: Bjorn Andersson @ 2016-10-29 18:06 UTC (permalink / raw)
To: Michael Scott
Cc: linux-gpio, linux-arm-msm, devicetree, linux-kernel,
Linus Walleij, Rob Herring, Mark Rutland, Andy Gross, David Brown,
Joonwoo Park, Jeremy McNicoll
In-Reply-To: <20161026233228.15219-1-michael.scott@linaro.org>
On Wed 26 Oct 16:32 PDT 2016, Michael Scott wrote:
> Initial pinctrl driver for QCOM msm8994 platforms.
>
> In order to continue the initial board support for QCOM msm8994/msm8992
> presented in patches from Jeremy McNicoll <jeremymc@redhat.com>, let's put
> a proper pinctrl driver in place.
>
> Currently, the DT for these platforms uses the msm8x74 pinctrl driver to enable
> basic UART. Beyond the first few pins the rest are different enough to justify
> it's own driver.
>
> Note: This driver is also be used by QCOM's msm8992 platform as it's TLM block
> is the same.
>
I'm not up to speed on the 8992/8994 story, but please add a
"qcom,msm8992-pinctrl" compatible to the list as well.
> - Initial formatting and style was taken from the msm8x74 pinctrl driver added
> by Björn Andersson <bjorn.andersson@linaro.org>
> - Data was then adjusted per QCOM MSM8994 documentation for Top Level Multiplexing
I think this is good enough. It looks reasonable and you based it on the
data sheet. Any fixes to the tables can be added incrementally.
> - Bindings documentation was based on qcom,msm8996-pinctrl.txt by
> Joonwoo Park <joonwoop@codeaurora.org> and then modified for msm8994 content
>
> Signed-off-by: Michael Scott <michael.scott@linaro.org>
Please add the compatible and you have my
Acked-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Regards,
Bjorn
^ 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