* Re: [PATCH v2 5/7] iio: dpot-dac: DAC driver based on a digital potentiometer
From: Jonathan Cameron @ 2016-10-23 9:42 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: <1477176226-10566-6-git-send-email-peda@axentia.se>
On 22/10/16 23:43, Peter Rosin wrote:
> It is assumed the 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 suggstions is that some of the stuff for reading max value from
the channel wan't to get wrapped up in a utility function in iio/consumer.h
like the various channel value read and write functions.
As much of possible of internal IIO stuff should be opaque to consumers
(even when they happen to know about it as they are also IIO drivers ;)
Jonathan
> ---
> MAINTAINERS | 1 +
> drivers/iio/dac/Kconfig | 10 ++
> drivers/iio/dac/Makefile | 1 +
> drivers/iio/dac/dpot-dac.c | 298 +++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 310 insertions(+)
> create mode 100644 drivers/iio/dac/dpot-dac.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c68b72088945..8c8aae24b96b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6116,6 +6116,7 @@ M: Peter Rosin <peda@axentia.se>
> L: linux-iio@vger.kernel.org
> S: Maintained
> 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..5613eae32347
> --- /dev/null
> +++ b/drivers/iio/dac/dpot-dac.c
> @@ -0,0 +1,298 @@
> +/*
> + * 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);
> + struct iio_dev *dpot_dev = dac->dpot->indio_dev;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW:
> + return dpot_dev->info->read_avail(dpot_dev, dac->dpot->channel,
> + vals, type, length, mask);
> + }
> +
> + 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);
> + struct iio_channel *ch = dac->dpot;
> + struct iio_dev *iio_ch_dev = ch->indio_dev;
> + const int *vals;
> + unsigned long long tmp;
> + int type;
> + int len;
> + int ret;
> + int val1;
> + int val2;
> + int max;
> +
Wants a wrapper in iio/consumer.h like we do for the various reads.
Otherwise, we'll end up with this as cut and paste boiler plate all over
the place.
> + if (!iio_ch_dev->info->read_avail) {
> + dev_err(dev, "dpot does not provide available raw values\n");
> + return -EINVAL;
> + }
> +
> + ret = iio_ch_dev->info->read_avail(iio_ch_dev, ch->channel,
> + &vals, &type, &len,
> + IIO_CHAN_INFO_RAW);
> +
> + if (ret < 0) {
> + dev_err(dev, "dpot failed to provide available raw values\n");
> + return ret;
> + }
(wrapper down to here..)
Worth noting that for the consumers they shouldn't need to know anything
about internal IIO structures (obviously you do here for other reasons
but try not to use that knowledge - should all be opaque outside the
subsystem.)
> + if (type != IIO_VAL_INT) {
> + dev_err(dev, "dpot provides strange raw values\n");
> + return -EINVAL;
> + }
> +
> + switch (ret) {
> + case IIO_AVAIL_RANGE:
> + if (len != 3) {
> + dev_err(dev, "need a range of available values\n");
> + return -EINVAL;
> + }
> + max = vals[2];
> + break;
> + default:
> + dev_err(dev, "dpot doesn't provide range of available values\n");
You could handle the set of values but it would be more complex so fair
enough. Any non linear dpots out there (with few enough values that we
can describe them in under page size)?
> + return -EINVAL;
> + }
> +
> + switch (iio_read_channel_scale(dac->dpot, &val1, &val2)) {
> + case IIO_VAL_INT:
> + return max * val1;
> + case IIO_VAL_FRACTIONAL:
> + tmp = (unsigned long long)max * val1;
> + do_div(tmp, val2);
> + return tmp;
> + case IIO_VAL_FRACTIONAL_LOG2:
> + tmp = (s64)vals[0] * 1000000000LL * max >> vals[1];
> + 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);
> +
> + 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 v2 2/7] iio: mcp4531: provide range of available raw values
From: Jonathan Cameron @ 2016-10-23 9:36 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: <1477176226-10566-3-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
On 22/10/16 23:43, Peter Rosin wrote:
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Looks great to me.
Perhaps it's worth doing a cat *_available in the directory and pasting
the result here to show what the resulting ABI looks like.
Also new ABI so needs documenting! (you didn't think it would be
this easy did you? :)
J
> ---
> drivers/iio/potentiometer/mcp4531.c | 104 +++++++++++++++++++++---------------
> 1 file changed, 62 insertions(+), 42 deletions(-)
>
> 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 v2 1/7] iio:core: add a callback to allow drivers to provide _available attributes
From: Jonathan Cameron @ 2016-10-23 9: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: <1477176226-10566-2-git-send-email-peda@axentia.se>
On 22/10/16 23:43, Peter Rosin wrote:
> From: Jonathan Cameron <jic23@kernel.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.
I'm wondering what I meant by this... where does it cause use ABI issues?
Do you have any idea?
I'd forgotten I'd even done it like this rather than just insisting on
available for everything (which is more logical to me as we should know
the range of everything).
It's pretty low cost though and gives a way of adding this to drivers
piecemeal which is probably a good idea!
>
> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
> [rather mindlessly forward ported from approx 3 years ago /peda]
More importantly shepherding it through review!
Naturally it's perfect code so not comments inline ;)
Thanks again for picking this up!
So... I want to see lots of comments on this. If people are happy with
it (unlikely ;) then please say so. It's at least a bit controversial
and adds a 'lot' of new ABI.
Jonathan
> Signed-off-by: Peter Rosin <peda@axentia.se>
> ---
> drivers/iio/industrialio-core.c | 232 +++++++++++++++++++++++++++++++++++-----
> include/linux/iio/iio.h | 11 ++
> include/linux/iio/types.h | 5 +
> 3 files changed, 223 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index fc340ed3dca1..93c69ebeda1e 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -575,51 +575,41 @@ 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, 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 sprintf(buf, "%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]),
> + return sprintf(buf, "-%d.%06u%s", abs(vals[0]),
> -vals[1], scale_db ? " dB" : "");
> else
> - return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1],
> + return sprintf(buf, "%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]),
> + return sprintf(buf, "-%d.%09u", abs(vals[0]),
> -vals[1]);
> else
> - return sprintf(buf, "%d.%09u\n", vals[0], vals[1]);
> + return sprintf(buf, "%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 sprintf(buf, "%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 sprintf(buf, "%d.%09u", tmp0, tmp1);
> case IIO_VAL_INT_MULTIPLE:
> {
> int i;
> @@ -628,13 +618,34 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
> 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;
> }
> default:
> return 0;
> }
> }
> +
> +/**
> + * 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)
> +{
> + ssize_t len;
> +
> + len = __iio_format_value(buf, type, size, vals);
> +
> + return len + sprintf(buf + len, "\n");
> +}
> EXPORT_SYMBOL_GPL(iio_format_value);
>
> static ssize_t iio_read_channel_info(struct device *dev,
> @@ -662,6 +673,113 @@ 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, type, 1, &vals[i]);
> + if (i < length - 1)
> + len += snprintf(buf + len,
> + PAGE_SIZE - len,
> + " ");
> + else
> + len += snprintf(buf + len,
> + PAGE_SIZE - len,
> + "\n");
> + }
> + break;
> + default:
> + for (i = 0; i < length / 2; i++) {
> + len += __iio_format_value(buf + len,
> + type,
> + 2,
> + &vals[i * 2]);
> + if (i < length / 2 - 1)
> + len += snprintf(buf + len,
> + PAGE_SIZE - len,
> + " ");
> + else
> + len += snprintf(buf + len,
> + PAGE_SIZE - len,
> + "\n");
> + }
> + };
> +
> + 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, type, 1, &vals[i]);
> + if (i < 2)
> + len += snprintf(buf + len,
> + PAGE_SIZE - len,
> + " ");
> + else
> + len += snprintf(buf + len,
> + PAGE_SIZE - len,
> + "]\n");
> + }
> + break;
> + default:
> + for (i = 0; i < 3; i++) {
> + len += __iio_format_value(buf + len,
> + type,
> + 2,
> + &vals[i * 2]);
> + if (i < 2)
> + len += snprintf(buf + len,
> + PAGE_SIZE - len,
> + " ");
> + else
> + len += snprintf(buf + len,
> + PAGE_SIZE - len,
> + "]\n");
> + }
> + };
> +
> + 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 +1096,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 +1145,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 +1160,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 +1175,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 +1189,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..c0f897084620 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -269,9 +269,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;
> @@ -397,6 +401,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_el);
> +
> 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_ */
>
^ permalink raw reply
* Re: [PATCH v9 1/2] usb: dwc2: assert phy reset when waking up in rk3288 platform
From: ayaka @ 2016-10-23 9:33 UTC (permalink / raw)
To: John Youn, linux-usb-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: kishon-l0cyMroinI0@public.gmane.org,
felipe.balbi-VuQAYsv1563Yd54FQh9/CA@public.gmane.org,
mark.rutland-5wv7dgnIgG8@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
randy.li-TNX95d0MmH7DzftRWevZcw@public.gmane.org
In-Reply-To: <4ee5817d-0076-9974-8b0e-182a0241e1f5-HKixBCOQz3hWk0Htik3J/w@public.gmane.org>
On 10/22/2016 03:27 AM, John Youn wrote:
> On 10/20/2016 11:38 AM, Randy Li wrote:
>> On the rk3288 USB host-only port (the one that's not the OTG-enabled
>> port) the PHY can get into a bad state when a wakeup is asserted (not
>> just a wakeup from full system suspend but also a wakeup from
>> autosuspend).
>>
>> We can get the PHY out of its bad state by asserting its "port reset",
>> but unfortunately that seems to assert a reset onto the USB bus so it
>> could confuse things if we don't actually deenumerate / reenumerate the
>> device.
>>
>> We can also get the PHY out of its bad state by fully resetting it using
>> the reset from the CRU (clock reset unit) in chip, which does a more full
>> reset. The CRU-based reset appears to actually cause devices on the bus
>> to be removed and reinserted, which fixes the problem (albeit in a hacky
>> way).
>>
>> It's unfortunate that we need to do a full re-enumeration of devices at
>> wakeup time, but this is better than alternative of letting the bus get
>> wedged.
>>
>> Signed-off-by: Randy Li <ayaka-xPW3/0Ywev/iB9QmIjCX8w@public.gmane.org>
>> ---
>> drivers/usb/dwc2/core.h | 1 +
>> drivers/usb/dwc2/core_intr.c | 11 +++++++++++
>> drivers/usb/dwc2/platform.c | 9 +++++++++
>> 3 files changed, 21 insertions(+)
>>
>> diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
>> index 2a21a04..e91ddbc 100644
>> --- a/drivers/usb/dwc2/core.h
>> +++ b/drivers/usb/dwc2/core.h
>> @@ -859,6 +859,7 @@ struct dwc2_hsotg {
>> unsigned int ll_hw_enabled:1;
>>
>> struct phy *phy;
>> + struct work_struct phy_rst_work;
>> struct usb_phy *uphy;
>> struct dwc2_hsotg_plat *plat;
>> struct regulator_bulk_data supplies[ARRAY_SIZE(dwc2_hsotg_supply_names)];
>> diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
>> index d85c5c9..c3d2168 100644
>> --- a/drivers/usb/dwc2/core_intr.c
>> +++ b/drivers/usb/dwc2/core_intr.c
>> @@ -345,6 +345,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
>> static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
>> {
>> int ret;
>> + struct device_node *np = hsotg->dev->of_node;
>>
>> /* Clear interrupt */
>> dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
>> @@ -379,6 +380,16 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
>> /* Restart the Phy Clock */
>> pcgcctl &= ~PCGCTL_STOPPCLK;
>> dwc2_writel(pcgcctl, hsotg->regs + PCGCTL);
>> +
>> + /*
>> + * It is a quirk in Rockchip RK3288, causing by
>> + * a hardware bug. This will propagate out and
>> + * eventually we'll re-enumerate the device.
>> + * Not great but the best we can do
>> + */
>> + if (of_device_is_compatible(np, "rockchip,rk3288-usb"))
>> + schedule_work(&hsotg->phy_rst_work);
>> +
>> mod_timer(&hsotg->wkp_timer,
>> jiffies + msecs_to_jiffies(71));
>> } else {
>> diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
>> index 8e1728b..65953cf 100644
>> --- a/drivers/usb/dwc2/platform.c
>> +++ b/drivers/usb/dwc2/platform.c
>> @@ -366,6 +366,14 @@ int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
>> return ret;
>> }
>>
>> +/* Only used to reset usb phy at interrupter runtime */
>> +static void dwc2_reset_phy_work(struct work_struct *data)
>> +{
>> + struct dwc2_hsotg *hsotg = container_of(data, struct dwc2_hsotg,
>> + phy_rst_work);
>> + phy_reset(hsotg->phy);
>> +}
>> +
>> static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
>> {
>> int i, ret;
>> @@ -410,6 +418,7 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
>> return ret;
>> }
>> }
>> + INIT_WORK(&hsotg->phy_rst_work, dwc2_reset_phy_work);
>>
>> if (!hsotg->phy) {
>> hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2);
>>
> Hi Randy,
>
> This fails compile if CONFIG_GENERIC_PHY is disabled. I think you need
> to make a fix to your phy_reset patch first.
In the last time, cac18ecb6f44b11bc303d7afbae3887b27938fa4 have not been
merged, I though the
[PATCH v8 1/3] phy: Add reset callback for not generic phy have been
merged before that. when the rebase abandon it.
Should re-send that patch? As the mainline have not been affected, could
you arrange a squash for it?
>
> Regards,
> John
--
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 v4 4/8] drivers:input:tsc2007: add iio interface to read external ADC input and temperature
From: Jonathan Cameron @ 2016-10-23 9:24 UTC (permalink / raw)
To: H. Nikolaus Schaller
Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Benoît Cousson,
Tony Lindgren, Russell King, Arnd Bergmann, Michael Welling,
Mika Penttilä, Javier Martinez Canillas, Igor Grinberg,
Sebastian Reichel, Andrew F. Davis, Mark Brown, linux-input,
devicetree, linux-kernel, linux-omap, letux-kernel, linux-iio,
kernel
In-Reply-To: <7A0E332C-4930-4F1A-8320-F87F864B53AE@goldelico.com>
On 22/10/16 21:46, H. Nikolaus Schaller wrote:
> Hi Jonathan,
>
>> Am 22.10.2016 um 20:33 schrieb Jonathan Cameron <jic23@kernel.org>:
>>
>> On 17/10/16 14:57, H. Nikolaus Schaller wrote:
>>> The tsc2007 chip not only has a resistive touch screen controller but
>>> also an external AUX adc imput which can be used for an ambient
>>> light sensor, battery voltage monitoring or any general purpose.
>>>
>>> Additionally it can measure the chip temperature.
>>>
>>> This extension provides an iio interface for these adc channels.
>>>
>>> Since it is not wasting much resources and is very straightforward,
>>> we simply provide all other adc channels as optional iio interfaces
>>> as weel. This can be used for debugging or special applications.
>> well
>>>
>>> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
>> This could be cleaner done perhaps by factoring out the IIO stuff into a separate
>> file and using a header with stubs to deal with the no available case.
>>
>> There will only be a handful of stubs and it'll give you a lot cleaner code
>> in here.
>>
>> If def fun in .c files is always harder to deal with than in a header
>> where stubs are really obvious.
>
> Yes, it became a lot of #ifdefs spread over the source file.
>
> The easiest thing would be to require IIO to be enabled :)
>
> With your proposal to consider refactoring, I think the crucial part
> is the conditional allocation either through devm_iio_device_alloc()
> or devm_kzalloc(). This can be refactored into some conditional
> tsc2007_alloc().
>
> I have tried some draft (not tested and not tidied up) to check if the
> direction is good.
>
> This reduces the number of #ifdef CONFIG_IIO from 7 to 2 without introducing
> new files or includes. There are also 2 other #ifdef CONFIG_OF so it doesn't
> seem to be very complex now in comparison. And the patch itself has only a
> handful of hunks (8).
>
> Moving tsc2007_alloc into a separate file tsc2007_iio.c would only move around
> one #ifdef CONFIG_OF from tsc2007.c but IMHO makes it more difficult to understand
> because it is not really iio specific and one has to switch between two source
> files. And I would have to touch the Makefile as well.
>
> What do you think?
I'd still split it. The only bit of the IIO block that isn't specific is
a tiny chunk of the allocation code (as you've highlighted).
Even that can be avoided by adding a tiny bit more indirection than would
otherwise be needed (it's not pretty but it would give a clean separation).
It's pretty much the way this sort of optional functionality should always
be done - means that if you don't care (i.e. it's not enabled) you don't
even have to see the code.
Jonathan
>
> If generally ok, I can include that in [PATCH v5].
>
> BR and thanks,
> Nikolaus
>
>
> diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
> index 5e3c4bf..691e79f 100644
> --- a/drivers/input/touchscreen/tsc2007.c
> +++ b/drivers/input/touchscreen/tsc2007.c
> @@ -30,6 +30,7 @@
> #include <linux/of.h>
> #include <linux/of_gpio.h>
> #include <linux/input/touchscreen.h>
> +#include <linux/iio/iio.h>
>
> #define TSC2007_MEASURE_TEMP0 (0x0 << 4)
> #define TSC2007_MEASURE_AUX (0x2 << 4)
> @@ -69,9 +70,13 @@ struct ts_event {
>
> struct tsc2007 {
> struct input_dev *input;
> +#ifdef CONFIG_IIO
> + struct iio_dev *indio;
> +#endif
I wouldn't bother with this one. Just have
struct iio_dev; before this and it'll waste a whole
one pointer (+ you shouldn't need to have iio.h included
in here once you have spit the files).
> char phys[32];
>
> struct i2c_client *client;
> + struct mutex mlock;
>
> u16 model;
> u16 x_plate_ohms;
> @@ -192,7 +197,10 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
> while (!ts->stopped && tsc2007_is_pen_down(ts)) {
>
> /* pen is down, continue with the measurement */
> +
> + mutex_lock(&ts->mlock);
> tsc2007_read_values(ts, &tc);
> + mutex_unlock(&ts->mlock);
>
> rt = tsc2007_calculate_resistance(ts, &tc);
>
> @@ -319,6 +327,162 @@ static void tsc2007_close(struct input_dev *input_dev)
> tsc2007_stop(ts);
> }
>
> +#ifdef CONFIG_IIO
> +
> +#define TSC2007_CHAN_IIO(_chan, _name, _type, _chan_info) \
> +{ \
> + .datasheet_name = _name, \
> + .type = _type, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(_chan_info), \
> + .indexed = 1, \
> + .channel = _chan, \
> +}
> +
> +static const struct iio_chan_spec tsc2007_iio_channel[] = {
> + TSC2007_CHAN_IIO(0, "x", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(1, "y", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(2, "z1", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(3, "z2", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(4, "adc", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(5, "rt", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), /* Ohms? */
> + TSC2007_CHAN_IIO(6, "pen", IIO_PRESSURE, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(7, "temp0", IIO_TEMP, IIO_CHAN_INFO_RAW),
> + TSC2007_CHAN_IIO(8, "temp1", IIO_TEMP, IIO_CHAN_INFO_RAW),
> +};
> +
> +static int tsc2007_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val, int *val2, long mask)
> +{
> + struct tsc2007 *tsc = iio_priv(indio_dev);
> + int adc_chan = chan->channel;
> + int ret = 0;
> +
> + if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel))
> + return -EINVAL;
> +
> + if (mask != IIO_CHAN_INFO_RAW)
> + return -EINVAL;
> +
> + mutex_lock(&tsc->mlock);
> +
> + switch (chan->channel) {
> + case 0:
> + *val = tsc2007_xfer(tsc, READ_X);
> + break;
> + case 1:
> + *val = tsc2007_xfer(tsc, READ_Y);
> + break;
> + case 2:
> + *val = tsc2007_xfer(tsc, READ_Z1);
> + break;
> + case 3:
> + *val = tsc2007_xfer(tsc, READ_Z2);
> + break;
> + case 4:
> + *val = tsc2007_xfer(tsc, (ADC_ON_12BIT | TSC2007_MEASURE_AUX));
> + break;
> + case 5: {
> + struct ts_event tc;
> +
> + tc.x = tsc2007_xfer(tsc, READ_X);
> + tc.z1 = tsc2007_xfer(tsc, READ_Z1);
> + tc.z2 = tsc2007_xfer(tsc, READ_Z2);
> + *val = tsc2007_calculate_resistance(tsc, &tc);
> + break;
> + }
> + case 6:
> + *val = tsc2007_is_pen_down(tsc);
> + break;
> + case 7:
> + *val = tsc2007_xfer(tsc,
> + (ADC_ON_12BIT | TSC2007_MEASURE_TEMP0));
> + break;
> + case 8:
> + *val = tsc2007_xfer(tsc,
> + (ADC_ON_12BIT | TSC2007_MEASURE_TEMP1));
> + break;
> + }
> +
> + /* Prepare for next touch reading - power down ADC, enable PENIRQ */
> + tsc2007_xfer(tsc, PWRDOWN);
> +
> + mutex_unlock(&tsc->mlock);
> +
> + ret = IIO_VAL_INT;
> +
> + return ret;
> +}
> +
> +static const struct iio_info tsc2007_iio_info = {
> + .read_raw = tsc2007_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static int tsc2007_alloc(struct i2c_client *client, struct tsc2007 **ts,
> + struct input_dev **input_dev)
> +{
> + int err;
> + struct iio_dev *indio_dev;
> +
> + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*ts));
Instead of doing this to reduce the delta between versions make
iio_priv a struct tsc2007 **
That is have a single pointer in there and do your allocation of struct
tsc2007 separately.
Having doing that, you can have this CONFIG_IIO block as just
doing the iio stuff with the input elements pulled back into the main
probe function.
Then define something like
iio_configure (stubbed to nothing if no IIO)
and
iio_unconfigure (also stubbed to nothing if no IIO).
A couple of additions in the header to make it all work
(the struct tsc2007 and tsc2007_xfer() + a few of the
register defines..
Nothing big and gets all the CONFIG_IIO into some really
obvious stubbing out in the header.
> + if (!indio_dev) {
> + dev_err(&client->dev, "iio_device_alloc failed\n");
> + return -ENOMEM;
> + }
> +
> + *ts = iio_priv(indio_dev);
> +
> + *input_dev = devm_input_allocate_device(&client->dev);
> + if (!*input_dev)
> + return -ENOMEM;
> +
> + i2c_set_clientdata(client, *ts);
> + (*ts)->indio = indio_dev;
> +
> + indio_dev->name = "tsc2007";
> + indio_dev->dev.parent = &client->dev;
> + indio_dev->info = &tsc2007_iio_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> + indio_dev->channels = tsc2007_iio_channel;
> + indio_dev->num_channels = ARRAY_SIZE(tsc2007_iio_channel);
> +
> + err = iio_device_register(indio_dev);
> + if (err < 0) {
> + dev_err(&client->dev, "iio_device_register() failed: %d\n",
> + err);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +#define tsc2007_iio_device_unregister(ts) iio_device_unregister(ts->indio)
> +
> +#else /* CONFIG_IIO */
> +
> +static int tsc2007_alloc(struct i2c_client *client, struct tsc2007 **ts,
> + struct input_dev **input_dev)
> +{
> + int err;
> +
> + *ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
> + if (!*ts)
> + return -ENOMEM;
> +
> + *input_dev = devm_input_allocate_device(&client->dev);
> + if (!*input_dev)
> + return -ENOMEM;
> +
> + i2c_set_clientdata(client, *ts);
> +
> + return 0;
> +}
> +
> +#define tsc2007_iio_device_unregister(ts) /* not needed */
That's rather ugly and fragile. I'd stub it out as an actual function
with no content and let the compiler drop it.
> +
> +#endif /* CONFIG_IIO */
> +
> #ifdef CONFIG_OF
> static int tsc2007_get_pendown_state_gpio(struct device *dev)
> {
> @@ -459,20 +623,15 @@ static int tsc2007_probe(struct i2c_client *client,
> I2C_FUNC_SMBUS_READ_WORD_DATA))
> return -EIO;
>
> - ts = devm_kzalloc(&client->dev, sizeof(struct tsc2007), GFP_KERNEL);
> - if (!ts)
> - return -ENOMEM;
> -
> - input_dev = devm_input_allocate_device(&client->dev);
> - if (!input_dev)
> - return -ENOMEM;
> -
> - i2c_set_clientdata(client, ts);
> + err = tsc2007_alloc(client, &ts, &input_dev);
> + if (err < 0)
> + return err;
>
> ts->client = client;
> ts->irq = client->irq;
> ts->input = input_dev;
> init_waitqueue_head(&ts->wait);
> + mutex_init(&ts->mlock);
>
> snprintf(ts->phys, sizeof(ts->phys),
> "%s/input0", dev_name(&client->dev));
> @@ -543,6 +702,7 @@ static int tsc2007_probe(struct i2c_client *client,
> if (err < 0) {
> dev_err(&client->dev,
> "Failed to setup chip: %d\n", err);
> + tsc2007_iio_device_unregister(ts);
> return err; /* usually, chip does not respond */
> }
>
> @@ -556,6 +716,14 @@ static int tsc2007_probe(struct i2c_client *client,
> return 0;
> }
>
> +static int tsc2007_remove(struct i2c_client *client)
> +{
> + struct tsc2007 *ts = i2c_get_clientdata(client);
> + input_unregister_device(ts->input);
> + tsc2007_iio_device_unregister(ts);
> + return 0;
> +}
> +
> static const struct i2c_device_id tsc2007_idtable[] = {
> { "tsc2007", 0 },
> { }
> @@ -578,6 +746,7 @@ static struct i2c_driver tsc2007_driver = {
> },
> .id_table = tsc2007_idtable,
> .probe = tsc2007_probe,
> + .remove = tsc2007_remove,
> };
>
> module_i2c_driver(tsc2007_driver);
>
^ permalink raw reply
* Re: [PATCH v2 6/7] dt-bindings: iio: document envelope-detector bindings
From: Peter Rosin @ 2016-10-23 9:01 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Rob Herring, Mark Rutland,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1477176226-10566-7-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
On 2016-10-23 00:43, Peter Rosin wrote:
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> ---
> .../bindings/iio/adc/envelope-detector.txt | 55 ++++++++++++++++++++++
> MAINTAINERS | 6 +++
> 2 files changed, 61 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..9099531cffff
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
> @@ -0,0 +1,55 @@
> +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";
> + dpot-dac,max-ohms = <100000>;
Just noticed that this line isn't needed anymore. I'll kill it for v3.
Cheers,
Peter
> + #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 8c8aae24b96b..4b6f6ec1b703 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6118,6 +6118,12 @@ S: Maintained
> F: Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
> F: drivers/iio/dac/dpot-dac.c
>
> +IIO ENVELOPE DETECTOR
> +M: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> +L: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/iio/adc/envelope-detector.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 v4 01/10] ethernet: add sun8i-emac driver
From: LABBE Corentin @ 2016-10-23 8:55 UTC (permalink / raw)
To: Joe Perches
Cc: robh+dt, mark.rutland, maxime.ripard, wens, linux, davem,
f.fainelli, andrew, netdev, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <1475852559.1945.2.camel@perches.com>
On Fri, Oct 07, 2016 at 08:02:39AM -0700, Joe Perches wrote:
> On Fri, 2016-10-07 at 10:25 +0200, Corentin Labbe wrote:
> > This patch add support for sun8i-emac ethernet MAC hardware.
> > It could be found in Allwinner H3/A83T/A64 SoCs.
>
> trivial notes:
>
> > diff --git a/drivers/net/ethernet/allwinner/sun8i-emac.c b/drivers/net/ethernet/allwinner/sun8i-emac.c
> []
> > +static const char const estats_str[][ETH_GSTRING_LEN] = {
>
> one too many const
>
> > +/* MAGIC value for knowing if a descriptor is available or not */
> > +#define DCLEAN cpu_to_le32(BIT(16) | BIT(14) | BIT(12) | BIT(10) | BIT(9))
>
> Aren't there #defines for these bits?
>
> > +static void sun8i_emac_flow_ctrl(struct sun8i_emac_priv *priv, int duplex,
> > + int fc)
> > +{
> > + u32 flow = 0;
> > +
> > + flow = readl(priv->base + EMAC_RX_CTL0);
> > + if (fc & EMAC_FLOW_RX)
> > + flow |= BIT(16);
> > + else
> > + flow &= ~BIT(16);
> > + writel(flow, priv->base + EMAC_RX_CTL0);
> > +
> > + flow = readl(priv->base + EMAC_TX_FLOW_CTL);
> > + if (fc & EMAC_FLOW_TX)
> > + flow |= BIT(0);
> > + else
> > + flow &= ~BIT(0);
>
> more magic bits that could be #defines
>
> > +static int sun8i_emac_rx_from_ddesc(struct net_device *ndev, int i)
> > +{
> > []
> > + /* the checksum or length of received frame's payload is wrong*/
> > + if (dstatus & BIT(0)) {
> []
> > + if (dstatus & BIT(1)) {
> []
> > + if ((dstatus & BIT(3))) {
>
> etc...
Thanks for the review, I will fix all thoses issue.
Regards
Corentin Labbe
^ permalink raw reply
* Re: [PATCH] ARM: dt: sun8i-h3: Add sunxi-sid to dts for sun8i-h3
From: LABBE Corentin @ 2016-10-23 8:26 UTC (permalink / raw)
To: Maxime Ripard
Cc: Jean-Francois Moine, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8, wens-jdAy2FN1RRM,
devicetree-u79uwXL29TY76Z2rM5mHXA, linux-I+IVW8TIWO2tmTQ+vhA3Yw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20161020203654.2erph2mr73yzksla@lukather>
On Thu, Oct 20, 2016 at 10:36:54PM +0200, Maxime Ripard wrote:
> On Wed, Oct 19, 2016 at 09:40:16AM +0200, LABBE Corentin wrote:
> > On Wed, Oct 05, 2016 at 12:21:30PM +0200, Jean-Francois Moine wrote:
> > > On Wed, 5 Oct 2016 11:48:24 +0200
> > > Corentin Labbe <clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> > >
> > > > This patch add support for the sunxi-sid driver to the device tree for sun8i-h3.
> > > >
> > > > Signed-off-by: Corentin Labbe <clabbe.montjoie-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> > > > ---
> > > > arch/arm/boot/dts/sun8i-h3.dtsi | 5 +++++
> > > > 1 file changed, 5 insertions(+)
> > > >
> > > > diff --git a/arch/arm/boot/dts/sun8i-h3.dtsi b/arch/arm/boot/dts/sun8i-h3.dtsi
> > > > index 9f58bb4..abfd29c 100644
> > > > --- a/arch/arm/boot/dts/sun8i-h3.dtsi
> > > > +++ b/arch/arm/boot/dts/sun8i-h3.dtsi
> > > > @@ -211,6 +211,11 @@
> > > > #size-cells = <0>;
> > > > };
> > > >
> > > > + sid: eeprom@01c14200 {
> > > > + compatible = "allwinner,sun7i-a20-sid";
> > > > + reg = <0x01c14200 0x200>;
> > >
> > > The datasheet says 1Kb starting at 0x01c14000.
> > > Is there any reason to reduce the area and to shift the offset?
> > >
> >
> > According to http://linux-sunxi.org/SID_Register_Guide "For
> > Allwinner A83T and H3 the SID address space starts at 0x01c14000,
> > and the e-fuses are at offset 0x200".
> >
> > So I use this offset, since the sunxi_sid driver need the base
> > address of e-fuses.
> >
> > The easiest solution is to use 0x01c14200 since the other part of
> > sid is not used and not known (A83T/H3 user manual doesnt give any
> > information on all sid space, worse for A64 which reference SID only
> > in memory map).
> >
> > So probably for H3/A64/A83T, there will never any usage of the rest
> > of the SID address space.
>
> And since we can't know that, and we have to maintain the DT ABI,
> using the whole address map and an offset, with a new compatible, is
> definetely the safest thing to do.
>
I have two way of doing it, which one do you prefer ?
- Adding two optionnal properties: efuses-offset and efuses-size defaulting to 0 and resourcesize.
- Adding a subnode called efuses with its own reg=<>
The first one is easy and didnt need any work on previous DT entries.
Regards
Corentin Labbe
--
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 4/7] dt-bindings: iio: document dpot-dac bindings
From: Peter Meerwald-Stadler @ 2016-10-23 8:02 UTC (permalink / raw)
To: Peter Rosin
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Jonathan Cameron,
Hartmut Knaack, Lars-Peter Clausen, Rob Herring, Mark Rutland,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1477176226-10566-5-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
nitpicking below
> Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
> ---
> .../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..4fd5d63cc2d6
> --- /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 the that the dpot is used as a voltage divider between the
delete 'the' before 'that'
> +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 1cd38a7e0064..c68b72088945 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>
>
--
Peter Meerwald-Stadler
+43-664-2444418 (mobile)
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v5 4/7] ASoC: sunxi: Add sun8i I2S driver
From: Jean-Francois Moine @ 2016-10-23 7:45 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Dave Airlie, Liam Girdwood, Mark Brown, Maxime Ripard,
Rob Herring, Linux-ALSA, devicetree, dri-devel, linux-arm-kernel,
linux-sunxi
In-Reply-To: <CAGb2v67gDd650TJk_-oHOehnzdH2qor=36HXdPt339Ji=ToAMg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Sun, 23 Oct 2016 09:33:16 +0800
Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org> wrote:
> > Note: This driver is closed to the sun4i-i2s except that:
> > - it handles the H3
>
> If it's close to sun4i-i2s, you should probably rework that one to support
> the newer SoCs.
>
> > - it creates the sound card (with sun4i-i2s, the sound card is created
> > by the CODECs)
>
> I think this is wrong. I2S is only the DAI. You typically have a separate
> platform driver for the whole card, or just use simple-card.
An other device is not needed. The layout is simple:
I2S_controller (CPU DAI) <-> HDMI_CODEC (CODEC DAI)
The HDMI CODEC is supported by the HDMI video driver (only one device),
so, it cannot be the card device.
ASoC does not use the CPU DAI device (I2S_controller), so, it is
natural to use it to handle the card.
Otherwise, the simple-card asks for a node definition in the DT and
this node is a pure Linux software entity. On the other side, the
simple-graph-card from Kuninori is not useful for this simple case.
--
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: [linux-sunxi] [PATCH v5 0/7] ARM: ASoC: drm: sun8i: Add DE2 HDMI audio and video
From: Jean-Francois Moine @ 2016-10-23 7:35 UTC (permalink / raw)
To: Chen-Yu Tsai
Cc: Dave Airlie, Liam Girdwood, Mark Brown, Maxime Ripard,
Rob Herring, Linux-ALSA, devicetree, dri-devel, linux-arm-kernel,
linux-sunxi
In-Reply-To: <CAGb2v64fgJShfyEHnjm6ryg00WhHkmnPj+FdjHcXBa6HQbyTuA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Sun, 23 Oct 2016 09:38:04 +0800
Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org> wrote:
> > Recently, an announce about Tina OS for the R series
> > https://www.youtube.com/watch?v=h7KD-6HblAU
> > was followed by the upload of a new linux-3.4 source tree
> > https://github.com/tinalinux/linux-3.4
> > with files containing GPL headers.
> >
> > Well, I don't know if these sources are really from Allwinner, but
> > anyway, this is the opportunity to propose a new version of my DRM
> > HDMI driver.
>
> Could you clarify about this bit? Did you just clean up Allwinner's
> existing drivers? Or just use them as reference? Either way I think
> this deserves some mention in all your copyright headers.
>
> Otherwise what difference does the new release make?
- Allwinner's video driver is not DRM.
- their driver has no hardware cursor nor video overlay.
- I wrote the video DRM driver from the document
Allwinner_H3_Datasheet_V1.2.pdf
and the structures defined in
linux-3.4/drivers/video/sunxi/disp2/disp/de/lowlevel_sun8iw7/de_rtmx.h
Reading Allwinner's code helped me to understand how the DE2
is working.
- my lowlevel HDMI is just a cleanup of theirs with explanations
about the registers. Magic constants remain due to the lack of
knowledge about the PHYs.
- the mention of Allwinner in the copyright headers is needed to
indicate the source of the structures (DE2) and code (HDMI).
The main difference is the DRM interface and the use of the EDID,
permitting dynamic video resolution change with xrandr.
--
Ken ar c'hentañ | ** Breizh ha Linux atav! **
Jef | http://moinejf.free.fr/
--
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 0/2] media: add et8ek8 camera sensor driver and documentation
From: Pavel Machek @ 2016-10-23 7:33 UTC (permalink / raw)
To: Ivaylo Dimitrov
Cc: sakari.ailus-X3B1VOXEql0, sre-DgEjT+Ai2ygdnm+yROfE0A,
pali.rohar-Re5JQEeQqe8AvxtiuMwx3w,
linux-media-u79uwXL29TY76Z2rM5mHXA,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
galak-sgV2jX0FEOL9JmXXK+q4OQ, mchehab-JPH+aEBZ4P+UEJcrhfAQsw,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1465659593-16858-1-git-send-email-ivo.g.dimitrov.75-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 676 bytes --]
Hi!
> This series adds driver for Toshiba et8ek8 camera sensor found in Nokia N900
>
> Changes from v2:
>
> - fix build when CONFIG_PM is not defined
>
> Changes from v1:
>
> - driver and documentation split into separate patches
> - removed custom controls
> - code changed according to the comments on v1
> Ivaylo Dimitrov (2):
> media: Driver for Toshiba et8ek8 5MP sensor
> media: et8ek8: Add documentation
Is there any progress here? Is there any way I could help?
Best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]
^ permalink raw reply
* [PATCH 3/3] arm64: arch timer: Add timer erratum property for Hip05-d02 and Hip06-d03
From: Ding Tianhong @ 2016-10-23 3:21 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Marc Zyngier, Mark Rutland,
Scott Wood, devicetree-u79uwXL29TY76Z2rM5mHXA, Shawn Guo,
stuart.yoder-3arQi8VN3Tc,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Signed-off-by: Ding Tianhong <dingtianhong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
---
arch/arm64/boot/dts/hisilicon/hip05.dtsi | 1 +
arch/arm64/boot/dts/hisilicon/hip06.dtsi | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/arm64/boot/dts/hisilicon/hip05.dtsi b/arch/arm64/boot/dts/hisilicon/hip05.dtsi
index bf322ed..9d18875 100644
--- a/arch/arm64/boot/dts/hisilicon/hip05.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip05.dtsi
@@ -281,6 +281,7 @@
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ hisilicon,erratum-161x01;
};
pmu {
diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index 5927bc4..c38c658 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -260,6 +260,7 @@
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
<GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ hisilicon,erratum-161x01;
};
pmu {
--
1.9.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 related
* [PATCH 2/3] arm64: arch_timer: Work around QorIQ Erratum Hisilicon-161x01
From: Ding Tianhong @ 2016-10-23 3:21 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Marc Zyngier, Mark Rutland
Cc: Scott Wood, devicetree, Shawn Guo, stuart.yoder, linux-arm-kernel
Erratum Hisilicon-161x01 says that the ARM generic timer counter "has the
potential to contain an erroneous value when the timer value changes".
Accesses to TVAL (both read and write) are also affected due to the implicit counter
read. Accesses to CVAL are not affected.
The workaround is to reread TVAL and count registers until successive
reads return the limited range value (32) by back-to-back reads. Writes to TVAL are
replaced with an equivalent write to CVAL.
The workaround is enabled if the hisilicon,erratum-161x01 property is found in
the timer node in the device tree. This can be overridden with the
clocksource.arm_arch_timer.hisilicon-161x01 boot parameter, which allows KVM
users to enable the workaround until a mechanism is implemented to
automatically communicate this information.
Signed-off-by: Ding Tianhong <dingtianhong@huawei.com>
---
Documentation/arm64/silicon-errata.txt | 1 +
Documentation/kernel-parameters.txt | 9 ++
arch/arm64/include/asm/arch_timer.h | 41 +++++++--
drivers/clocksource/Kconfig | 14 ++-
drivers/clocksource/arm_arch_timer.c | 153 +++++++++++++++++++++++++++------
5 files changed, 182 insertions(+), 36 deletions(-)
diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt
index 405da11..3a79803 100644
--- a/Documentation/arm64/silicon-errata.txt
+++ b/Documentation/arm64/silicon-errata.txt
@@ -63,3 +63,4 @@ stable kernels.
| Cavium | ThunderX SMMUv2 | #27704 | N/A |
| | | | |
| Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
+| Hisilicon | Hip05/Hip06/Hip07 | #161x01 | HISILICON_ERRATUM_161x01|
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 6fa1d8a..175f349 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -707,6 +707,15 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
erratum. If unspecified, the workaround is
enabled based on the device tree.
+ clocksource.arm_arch_timer.hisilicon-161x01=
+ [ARM64]
+ Format: <bool>
+ Enable/disable the workaround of Hisilicon
+ erratum 161x01. This can be useful for KVM
+ guests, if the guest device tree doesn't show the
+ erratum. If unspecified, the workaround is
+ enabled based on the device tree.
+
clearcpuid=BITNUM [X86]
Disable CPUID feature X for the kernel. See
arch/x86/include/asm/cpufeatures.h for the valid bit
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index eaa5bbe..6b510db 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -29,17 +29,24 @@
#include <clocksource/arm_arch_timer.h>
-#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585)
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161X01)
extern struct static_key_false arch_timer_read_ool_enabled;
-#define needs_fsl_a008585_workaround() \
+extern struct arch_timer_erratum_workaround *erratum_workaround;
+#define needs_timer_erratum_workaround() \
static_branch_unlikely(&arch_timer_read_ool_enabled)
#else
-#define needs_fsl_a008585_workaround() false
+#define needs_timer_erratum_workaround() false
#endif
-u32 __fsl_a008585_read_cntp_tval_el0(void);
-u32 __fsl_a008585_read_cntv_tval_el0(void);
-u64 __fsl_a008585_read_cntvct_el0(void);
+struct clock_event_device;
+
+struct arch_timer_erratum_workaround {
+ int erratum;
+ u32 (*read_cntp_tval_el0)(void);
+ u32 (*read_cntv_tval_el0)(void);
+ u64 (*read_cntvct_el0)(void);
+};
+extern struct arch_timer_erratum_workaround *erratum_workaround;
/*
* The number of retries is an arbitrary value well beyond the highest number
@@ -59,16 +66,34 @@ u64 __fsl_a008585_read_cntvct_el0(void);
_new; \
})
+#define __hisi_161x01_read_reg(reg) ({ \
+ u64 _old, _new; \
+ int _retries = 200; \
+ \
+ do { \
+ _old = read_sysreg(reg); \
+ _new = read_sysreg(reg); \
+ _retries--; \
+ } while (unlikely((_new - _old) >> 5) && _retries); \
+ \
+ WARN_ON_ONCE(!_retries); \
+ _new; \
+})
+
+
+
#define arch_timer_reg_read_stable(reg) \
({ \
u64 _val; \
- if (needs_fsl_a008585_workaround()) \
- _val = __fsl_a008585_read_##reg(); \
+ if (needs_timer_erratum_workaround()) \
+ _val = erratum_workaround->read_##reg(); \
else \
_val = read_sysreg(reg); \
_val; \
})
+
+
/*
* These register accessors are marked inline so the compiler can
* nicely work out which register we want, and chuck away the rest of
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 8a753fd..fcfcdc7 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -312,8 +312,20 @@ config FSL_ERRATUM_A008585
help
This option enables a workaround for Freescale/NXP Erratum
A-008585 ("ARM generic timer may contain an erroneous
- value"). The workaround will only be active if the
+ value"). The workaround will be active if the
fsl,erratum-a008585 property is found in the timer node.
+ This can be overridden with the clocksource.arm_arch_timer.fsl-a008585
+ boot parameter.
+
+config HISILICON_ERRATUM_161X01
+ bool "Workaround for Hisilicon Erratum 161201"
+ default y
+ depends on ARM_ARCH_TIMER && ARM64
+ help
+ This option enables a workaround for Hisilicon Erratum
+ 161201. The workaround will be active if the hisi,erratum-161201
+ property is found in the timer node. This can be overridden with
+ the clocksource.arm_arch_timer.hisi-161201 boot parameter.
config ARM_GLOBAL_TIMER
bool "Support for the ARM global timer" if COMPILE_TEST
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 73c487d..e1cf0ad 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -90,16 +90,23 @@ static int __init early_evtstrm_cfg(char *buf)
}
early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
-/*
- * Architected system timer support.
- */
+#define FSL_A008585 1
+#define HISILICON_161X01 2
+
+struct arch_timer_erratum_workaround *erratum_workaround;
+
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161X01)
+static int arch_timer_uses_erratum = 0;
-#ifdef CONFIG_FSL_ERRATUM_A008585
DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
+#endif
-static int fsl_a008585_enable = -1;
+/*
+ * Architected system timer support.
+ */
+#ifdef CONFIG_FSL_ERRATUM_A008585
static int __init early_fsl_a008585_cfg(char *buf)
{
int ret;
@@ -109,28 +116,96 @@ static int __init early_fsl_a008585_cfg(char *buf)
if (ret)
return ret;
- fsl_a008585_enable = val;
+ if (val)
+ arch_timer_uses_erratum = FSL_A008585;
+
return 0;
}
early_param("clocksource.arm_arch_timer.fsl-a008585", early_fsl_a008585_cfg);
-u32 __fsl_a008585_read_cntp_tval_el0(void)
+u32 fsl_a008585_read_cntp_tval_el0(void)
{
return __fsl_a008585_read_reg(cntp_tval_el0);
}
-u32 __fsl_a008585_read_cntv_tval_el0(void)
+u32 fsl_a008585_read_cntv_tval_el0(void)
{
return __fsl_a008585_read_reg(cntv_tval_el0);
}
-u64 __fsl_a008585_read_cntvct_el0(void)
+u64 fsl_a008585_read_cntvct_el0(void)
{
return __fsl_a008585_read_reg(cntvct_el0);
}
-EXPORT_SYMBOL(__fsl_a008585_read_cntvct_el0);
+EXPORT_SYMBOL(fsl_a008585_read_cntvct_el0);
+#else
+u32 fsl_a008585_read_cntp_tval_el0(void)
+{
+ return 0;
+}
+
+u32 fsl_a008585_read_cntv_tval_el0(void)
+{
+ return 0;
+}
+
+u64 fsl_a008585_read_cntvct_el0(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(fsl_a008585_read_cntvct_el0);
#endif /* CONFIG_FSL_ERRATUM_A008585 */
+#ifdef CONFIG_HISILICON_ERRATUM_161X01
+static int __init early_hisi_161x01_cfg(char *buf)
+{
+ int ret;
+ bool val;
+
+ ret = strtobool(buf, &val);
+ if (ret)
+ return ret;
+
+ if (val)
+ arch_timer_uses_erratum = HISILICON_161X01;
+
+ return 0;
+}
+early_param("clocksource.arm_arch_timer.hisilicon-161x01", early_hisi_161x01_cfg);
+
+u32 hisi_161x01_read_cntp_tval_el0(void)
+{
+ return __hisi_161x01_read_reg(cntp_tval_el0);
+}
+
+u32 hisi_161x01_read_cntv_tval_el0(void)
+{
+ return __hisi_161x01_read_reg(cntv_tval_el0);
+}
+
+u64 hisi_161x01_read_cntvct_el0(void)
+{
+ return __hisi_161x01_read_reg(cntvct_el0);
+}
+EXPORT_SYMBOL(hisi_161x01_read_cntvct_el0);
+#else
+u32 hisi_161x01_read_cntp_tval_el0(void)
+{
+ return 0;
+}
+
+u32 hisi_161x01_read_cntv_tval_el0(void)
+{
+ return 0;
+}
+
+u64 hisi_161x01_read_cntvct_el0(void)
+{
+ return 0;
+}
+EXPORT_SYMBOL(hisi_161x01_read_cntvct_el0);
+#endif
+
static __always_inline
void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val,
struct clock_event_device *clk)
@@ -280,8 +355,8 @@ static __always_inline void set_next_event(const int access, unsigned long evt,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-#ifdef CONFIG_FSL_ERRATUM_A008585
-static __always_inline void fsl_a008585_set_next_event(const int access,
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161X01)
+static __always_inline void erratum_set_next_event(const int access,
unsigned long evt, struct clock_event_device *clk)
{
unsigned long ctrl;
@@ -299,20 +374,35 @@ static __always_inline void fsl_a008585_set_next_event(const int access,
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
}
-static int fsl_a008585_set_next_event_virt(unsigned long evt,
+static int erratum_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
+ erratum_set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
return 0;
}
-static int fsl_a008585_set_next_event_phys(unsigned long evt,
+static int erratum_set_next_event_phys(unsigned long evt,
struct clock_event_device *clk)
{
- fsl_a008585_set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
+ erratum_set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
return 0;
}
-#endif /* CONFIG_FSL_ERRATUM_A008585 */
+#endif /* CONFIG_FSL_ERRATUM_A008585 || CONFIG_HISILICON_ERRATUM_161X01 */
+
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161X01)
+static struct arch_timer_erratum_workaround arch_timer_erratum[] = {
+{
+ .erratum = FSL_A008585,
+ .read_cntp_tval_el0 = fsl_a008585_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = fsl_a008585_read_cntv_tval_el0,
+ .read_cntvct_el0 = fsl_a008585_read_cntvct_el0,
+},{
+ .erratum = HISILICON_161X01,
+ .read_cntp_tval_el0 = hisi_161x01_read_cntp_tval_el0,
+ .read_cntv_tval_el0 = hisi_161x01_read_cntv_tval_el0,
+ .read_cntvct_el0 = hisi_161x01_read_cntvct_el0,
+} };
+#endif
static int arch_timer_set_next_event_virt(unsigned long evt,
struct clock_event_device *clk)
@@ -342,16 +432,16 @@ static int arch_timer_set_next_event_phys_mem(unsigned long evt,
return 0;
}
-static void fsl_a008585_set_sne(struct clock_event_device *clk)
+static void erratum_set_sne(struct clock_event_device *clk)
{
-#ifdef CONFIG_FSL_ERRATUM_A008585
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161X01)
if (!static_branch_unlikely(&arch_timer_read_ool_enabled))
return;
if (arch_timer_uses_ppi == VIRT_PPI)
- clk->set_next_event = fsl_a008585_set_next_event_virt;
+ clk->set_next_event = erratum_set_next_event_virt;
else
- clk->set_next_event = fsl_a008585_set_next_event_phys;
+ clk->set_next_event = erratum_set_next_event_phys;
#endif
}
@@ -384,7 +474,7 @@ static void __arch_timer_setup(unsigned type,
BUG();
}
- fsl_a008585_set_sne(clk);
+ erratum_set_sne(clk);
} else {
clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
clk->name = "arch_mem_timer";
@@ -890,12 +980,21 @@ static int __init arch_timer_of_init(struct device_node *np)
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
-#ifdef CONFIG_FSL_ERRATUM_A008585
- if (fsl_a008585_enable < 0)
- fsl_a008585_enable = of_property_read_bool(np, "fsl,erratum-a008585");
- if (fsl_a008585_enable) {
+#if IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) || IS_ENABLED(CONFIG_HISILICON_ERRATUM_161X01)
+ if (!arch_timer_uses_erratum) {
+ if (IS_ENABLED(CONFIG_FSL_ERRATUM_A008585) &&
+ of_property_read_bool(np, "fsl,erratum-a008585"))
+ arch_timer_uses_erratum = FSL_A008585;
+ else if (IS_ENABLED(CONFIG_HISI_ERRATUM_161X01) &&
+ of_property_read_bool(np, "hisilicon,erratum-161x01"))
+ arch_timer_uses_erratum = HISILICON_161X01;
+ }
+
+ if (arch_timer_uses_erratum) {
+ erratum_workaround = &arch_timer_erratum[arch_timer_uses_erratum - 1];
+ pr_info("Enabling workaround for %s\n", arch_timer_uses_erratum == FSL_A008585 ?
+ "FSL erratum A-008585" : "HISILICON ERRATUM 161x01");
static_branch_enable(&arch_timer_read_ool_enabled);
- pr_info("Enabling workaround for FSL erratum A-008585\n");
}
#endif
--
1.9.0
^ permalink raw reply related
* [PATCH 1/3] arm64: arch_timer: Add device tree binding for hisilicon-161x01 erratum
From: Ding Tianhong @ 2016-10-23 3:21 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Marc Zyngier, Mark Rutland
Cc: Scott Wood, devicetree-u79uwXL29TY76Z2rM5mHXA, Shawn Guo,
stuart.yoder-3arQi8VN3Tc,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
This erratum describes a bug in logic outside the core, so MIDR can't be
used to identify its presence, and reading an SoC-specific revision
register from common arch timer code would be awkward. So, describe it
in the device tree.
Signed-off-by: Ding Tianhong <dingtianhong-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
---
Documentation/devicetree/bindings/arm/arch_timer.txt | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/arm/arch_timer.txt b/Documentation/devicetree/bindings/arm/arch_timer.txt
index ef5fbe9..26bc837 100644
--- a/Documentation/devicetree/bindings/arm/arch_timer.txt
+++ b/Documentation/devicetree/bindings/arm/arch_timer.txt
@@ -31,6 +31,12 @@ to deliver its interrupts via SPIs.
This also affects writes to the tval register, due to the implicit
counter read.
+- hisilicon,erratum-161x01 : A boolean property. Indicates the presence of
+ QorIQ erratum 161201, which says that reading the counter is
+ unreliable unless the small range of value is returned by back-to-back reads.
+ This also affects writes to the tval register, due to the implicit
+ counter read.
+
** Optional properties:
- arm,cpu-registers-not-fw-configured : Firmware does not initialize
--
1.9.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 related
* Re: [PATCH v3] pinctrl: Add SX150X GPIO Extender Pinctrl Driver
From: Andrey Smirnov @ 2016-10-23 2:50 UTC (permalink / raw)
To: Neil Armstrong
Cc: Linus Walleij, peda-koto5C5qi+TLoDKTGw+V6w,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-gpio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, stigge-uj/7R2tJ6VmzQB+pC5nmwQ,
vladimir_zapolskiy-nmGgyN9QBj3QT0dZR+AlfA
In-Reply-To: <1477040998-2016-1-git-send-email-narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
On Fri, Oct 21, 2016 at 2:09 AM, Neil Armstrong <narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org> wrote:
> Since the I2C sx150x GPIO expander driver uses platform_data to manage
> the pins configurations, rewrite the driver as a pinctrl driver using
> pinconf to get/set pin configurations from DT or debugfs.
>
> The pinctrl driver is functionnally equivalent as the gpio-only driver
> and can use DT for pinconf. The platform_data confirmation is dropped.
>
> This patchset removed the gpio-only driver and selects the Pinctrl driver
> config instead. This patchset also migrates the gpio dt-bindings to pinctrl
> and add the pinctrl optional properties.
>
> The driver was tested with a SX1509 device on a BeagleBone black with
> interrupt support and on an X86_64 machine over an I2C to USB converter.
>
> Signed-off-by: Neil Armstrong <narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> ---
> This is a fixed version that builds and runs on non-OF platforms and on
> arm based OF. The GPIO version is removed and the bindings are also moved
> to the pinctrl bindings.
>
> Changes since v2 at http://lkml.kernel.org/r/1474991325-1754-1-git-send-email-narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org:
> - rebased on v4.9-rc1
> - removed MODULE_DEVICE_TABLE as in upstream bb411e771b0e [1]
>
> Changes since v1 at http://lkml.kernel.org/r/1473166599-29266-1-git-send-email-narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org:
> - Fix Kconfig descriptions on pinctrl and gpio
> - Fix Kconfig dependency
> - Remove oscio support for non-789 devices
> - correct typo in dt bindings
> - remove probe reset for non-789 devices
>
> Changes since RFC at http://lkml.kernel.org/r/1472130692-14404-1-git-send-email-narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org:
> - Put #ifdef CONFIG_OF/CONFIG_OF_GPIO to remove OF code for non-of platforms
> - No more rely on OF_GPIO config
> - Moved and enhanced bindings to pinctrl bindings
> - Removed gpio-sx150x.c
> - Temporary select PINCTRL_SX150X when GPIO_SX150X
> - Temporary mark GPIO_SX150X as deprecated
>
> [1] gpio: sx150x: fix implicit assumption module.h is present
>
> .../gpio-sx150x.txt => pinctrl/pinctrl-sx150x.txt} | 46 +-
> drivers/gpio/Kconfig | 13 +-
> drivers/gpio/Makefile | 1 -
> drivers/pinctrl/Kconfig | 14 +
> drivers/pinctrl/Makefile | 1 +
> .../gpio-sx150x.c => pinctrl/pinctrl-sx150x.c} | 1174 ++++++++++++--------
> 6 files changed, 779 insertions(+), 470 deletions(-)
> rename Documentation/devicetree/bindings/{gpio/gpio-sx150x.txt => pinctrl/pinctrl-sx150x.txt} (40%)
> rename drivers/{gpio/gpio-sx150x.c => pinctrl/pinctrl-sx150x.c} (23%)
>
> diff --git a/Documentation/devicetree/bindings/gpio/gpio-sx150x.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
> similarity index 40%
> rename from Documentation/devicetree/bindings/gpio/gpio-sx150x.txt
> rename to Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
> index c809acb..c293c8a 100644
> --- a/Documentation/devicetree/bindings/gpio/gpio-sx150x.txt
> +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-sx150x.txt
> @@ -1,34 +1,54 @@
> SEMTECH SX150x GPIO expander bindings
>
> +Please refer to pinctrl-bindings.txt, ../gpio/gpio.txt, and
> +../interrupt-controller/interrupts.txt for generic information regarding
> +pin controller, GPIO, and interrupt bindings.
>
> Required properties:
> -
> -- compatible: should be "semtech,sx1506q",
> +- compatible: should be one of :
> + "semtech,sx1506q",
> "semtech,sx1508q",
> "semtech,sx1509q",
> "semtech,sx1502q".
>
> - reg: The I2C slave address for this device.
>
> -- interrupt-parent: phandle of the parent interrupt controller.
> -
> -- interrupts: Interrupt specifier for the controllers interrupt.
> -
> - #gpio-cells: Should be 2. The first cell is the GPIO number and the
> second cell is used to specify optional parameters:
> bit 0: polarity (0: normal, 1: inverted)
>
> - gpio-controller: Marks the device as a GPIO controller.
>
> +Optional properties :
> +- interrupt-parent: phandle of the parent interrupt controller.
> +
> +- interrupts: Interrupt specifier for the controllers interrupt.
> +
> - interrupt-controller: Marks the device as a interrupt controller.
>
> +- semtech,probe-reset: Will trigger a reset of the GPIO expander on probe,
> + only for sx1508q and sx1509q
> +
> The GPIO expander can optionally be used as an interrupt controller, in
> -which case it uses the default two cell specifier as described in
> -Documentation/devicetree/bindings/interrupt-controller/interrupts.txt.
> +which case it uses the default two cell specifier.
> +
> +Required properties for pin configuration sub-nodes:
> + - pins: List of pins to which the configuration applies.
> +
> +Optional properties for pin configuration sub-nodes:
> +----------------------------------------------------
> + - bias-disable: disable any pin bias, except the OSCIO pin
> + - bias-pull-up: pull up the pin, except the OSCIO pin
> + - bias-pull-down: pull down the pin, except the OSCIO pin
> + - bias-pull-pin-default: use pin-default pull state, except the OSCIO pin
> + - drive-push-pull: drive actively high and low
> + - drive-open-drain: drive with open drain only for sx1508q and sx1509q and except the OSCIO pin
> + - output-low: set the pin to output mode with low level
> + - output-high: set the pin to output mode with high level
>
> Example:
>
> - i2c_gpio_expander@20{
> + i2c0gpio-expander@20{
> #gpio-cells = <2>;
> #interrupt-cells = <2>;
> compatible = "semtech,sx1506q";
> @@ -38,4 +58,12 @@ Example:
>
> gpio-controller;
> interrupt-controller;
> +
> + pinctrl-names = "default";
> + pinctrl-0 = <&gpio1_cfg_pins>;
> +
> + gpio1_cfg_pins: gpio1-cfg {
> + pins = "gpio1";
> + bias-pull-up;
> + };
> };
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 26ee00f..db3f7aa 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -781,16 +781,13 @@ config GPIO_PCF857X
> platform-neutral GPIO calls.
>
> config GPIO_SX150X
> - bool "Semtech SX150x I2C GPIO expander"
> - depends on I2C=y
> - select GPIOLIB_IRQCHIP
> + bool "Semtech SX150x I2C GPIO expander (deprecated)"
> + depends on PINCTRL && I2C=y
> + select PINCTRL_SX150X
> default n
> help
> - Say yes here to provide support for Semtech SX150-series I2C
> - GPIO expanders. Compatible models include:
> -
> - 8 bits: sx1508q
> - 16 bits: sx1509q
> + Say yes here to provide support for Semtech SX150x-series I2C
> + GPIO expanders. The GPIO driver was replaced by a Pinctrl version.
>
> config GPIO_TPIC2810
> tristate "TPIC2810 8-Bit I2C GPO expander"
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index ab28a2d..76c7af6 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -102,7 +102,6 @@ obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
> obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
> obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
> obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
> -obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
> obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o
> obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
> obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 0e75d94..801fa8b 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -164,6 +164,20 @@ config PINCTRL_SIRF
> select GENERIC_PINCONF
> select GPIOLIB_IRQCHIP
>
> +config PINCTRL_SX150X
> + bool "Semtech SX150x I2C GPIO expander pinctrl driver"
> + depends on GPIOLIB && I2C=y
> + select PINMUX
> + select PINCONF
> + select GENERIC_PINCONF
> + select GPIOLIB_IRQCHIP
> + help
> + Say yes here to provide support for Semtech SX150x-series I2C
> + GPIO expanders as pinctrl module.
> + Compatible models include:
> + - 8 bits: sx1508q, sx1502q
> + - 16 bits: sx1509q, sx1506q
> +
> config PINCTRL_PISTACHIO
> def_bool y if MACH_PISTACHIO
> depends on GPIOLIB
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index 11bad37..3b8e6f7 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
> obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
> obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
> obj-$(CONFIG_PINCTRL_SIRF) += sirf/
> +obj-$(CONFIG_PINCTRL_SX150X) += pinctrl-sx150x.o
> obj-$(CONFIG_ARCH_TEGRA) += tegra/
> obj-$(CONFIG_PINCTRL_TZ1090) += pinctrl-tz1090.o
> obj-$(CONFIG_PINCTRL_TZ1090_PDC) += pinctrl-tz1090-pdc.o
> diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/pinctrl/pinctrl-sx150x.c
> similarity index 23%
> rename from drivers/gpio/gpio-sx150x.c
> rename to drivers/pinctrl/pinctrl-sx150x.c
> index af95de8..e985471 100644
> --- a/drivers/gpio/gpio-sx150x.c
> +++ b/drivers/pinctrl/pinctrl-sx150x.c
> @@ -1,4 +1,8 @@
> -/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
> +/*
> + * Copyright (c) 2016, BayLibre, SAS. All rights reserved.
> + * Author: Neil Armstrong <narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> + *
> + * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
> *
> * Driver for Semtech SX150X I2C GPIO Expanders
> *
> @@ -12,13 +16,8 @@
> * but WITHOUT ANY WARRANTY; without even the implied warranty of
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> - * 02110-1301, USA.
> */
> -#include <linux/gpio.h>
> +
> #include <linux/i2c.h>
> #include <linux/init.h>
> #include <linux/interrupt.h>
> @@ -26,17 +25,23 @@
> #include <linux/mutex.h>
> #include <linux/slab.h>
> #include <linux/of.h>
> -#include <linux/of_address.h>
> -#include <linux/of_irq.h>
> -#include <linux/of_gpio.h>
> -#include <linux/of_device.h>
> +#include <linux/gpio.h>
> +#include <linux/pinctrl/machine.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf-generic.h>
>
> -#define NO_UPDATE_PENDING -1
> +#include "core.h"
> +#include "pinconf.h"
> +#include "pinctrl-utils.h"
>
> /* The chip models of sx150x */
> -#define SX150X_123 0
> -#define SX150X_456 1
> -#define SX150X_789 2
> +enum {
> + SX150X_123 = 0,
> + SX150X_456,
> + SX150X_789,
> +};
>
> struct sx150x_123_pri {
> u8 reg_pld_mode;
> @@ -82,167 +87,146 @@ struct sx150x_device_data {
> struct sx150x_456_pri x456;
> struct sx150x_789_pri x789;
> } pri;
> + const struct pinctrl_pin_desc *pins;
> + unsigned int npins;
> };
>
> -/**
> - * struct sx150x_platform_data - config data for SX150x driver
> - * @gpio_base: The index number of the first GPIO assigned to this
> - * GPIO expander. The expander will create a block of
> - * consecutively numbered gpios beginning at the given base,
> - * with the size of the block depending on the model of the
> - * expander chip.
> - * @oscio_is_gpo: If set to true, the driver will configure OSCIO as a GPO
> - * instead of as an oscillator, increasing the size of the
> - * GP(I)O pool created by this expander by one. The
> - * output-only GPO pin will be added at the end of the block.
> - * @io_pullup_ena: A bit-mask which enables or disables the pull-up resistor
> - * for each IO line in the expander. Setting the bit at
> - * position n will enable the pull-up for the IO at
> - * the corresponding offset. For chips with fewer than
> - * 16 IO pins, high-end bits are ignored.
> - * @io_pulldn_ena: A bit-mask which enables-or disables the pull-down
> - * resistor for each IO line in the expander. Setting the
> - * bit at position n will enable the pull-down for the IO at
> - * the corresponding offset. For chips with fewer than
> - * 16 IO pins, high-end bits are ignored.
> - * @io_polarity: A bit-mask which enables polarity inversion for each IO line
> - * in the expander. Setting the bit at position n inverts
> - * the polarity of that IO line, while clearing it results
> - * in normal polarity. For chips with fewer than 16 IO pins,
> - * high-end bits are ignored.
> - * @irq_summary: The 'summary IRQ' line to which the GPIO expander's INT line
> - * is connected, via which it reports interrupt events
> - * across all GPIO lines. This must be a real,
> - * pre-existing IRQ line.
> - * Setting this value < 0 disables the irq_chip functionality
> - * of the driver.
> - * @irq_base: The first 'virtual IRQ' line at which our block of GPIO-based
> - * IRQ lines will appear. Similarly to gpio_base, the expander
> - * will create a block of irqs beginning at this number.
> - * This value is ignored if irq_summary is < 0.
> - * @reset_during_probe: If set to true, the driver will trigger a full
> - * reset of the chip at the beginning of the probe
> - * in order to place it in a known state.
> - */
> -struct sx150x_platform_data {
> - unsigned gpio_base;
> - bool oscio_is_gpo;
> - u16 io_pullup_ena;
> - u16 io_pulldn_ena;
> - u16 io_polarity;
> - int irq_summary;
> - unsigned irq_base;
> - bool reset_during_probe;
> +struct sx150x_pinctrl {
> + struct device *dev;
> + struct i2c_client *client;
> + struct pinctrl_dev *pctldev;
> + struct pinctrl_desc pinctrl_desc;
> + struct gpio_chip gpio;
> + struct irq_chip irq_chip;
> + struct {
> + int update;
> + u32 sense;
> + u32 masked;
> + u32 dev_sense;
> + u32 dev_masked;
> + } irq;
> + struct mutex lock;
> + const struct sx150x_device_data *data;
> };
>
> -struct sx150x_chip {
> - struct gpio_chip gpio_chip;
> - struct i2c_client *client;
> - const struct sx150x_device_data *dev_cfg;
> - int irq_summary;
> - int irq_base;
> - int irq_update;
> - u32 irq_sense;
> - u32 irq_masked;
> - u32 dev_sense;
> - u32 dev_masked;
> - struct irq_chip irq_chip;
> - struct mutex lock;
> +static const struct pinctrl_pin_desc sx150x_8_pins[] = {
> + PINCTRL_PIN(0, "gpio0"),
> + PINCTRL_PIN(1, "gpio1"),
> + PINCTRL_PIN(2, "gpio2"),
> + PINCTRL_PIN(3, "gpio3"),
> + PINCTRL_PIN(4, "gpio4"),
> + PINCTRL_PIN(5, "gpio5"),
> + PINCTRL_PIN(6, "gpio6"),
> + PINCTRL_PIN(7, "gpio7"),
> + PINCTRL_PIN(8, "oscio"),
> };
>
> -static const struct sx150x_device_data sx150x_devices[] = {
> - [0] = { /* sx1508q */
> - .model = SX150X_789,
> - .reg_pullup = 0x03,
> - .reg_pulldn = 0x04,
> - .reg_dir = 0x07,
> - .reg_data = 0x08,
> - .reg_irq_mask = 0x09,
> - .reg_irq_src = 0x0c,
> - .reg_sense = 0x0b,
> - .pri.x789 = {
> - .reg_drain = 0x05,
> - .reg_polarity = 0x06,
> - .reg_clock = 0x0f,
> - .reg_misc = 0x10,
> - .reg_reset = 0x7d,
> - },
> - .ngpios = 8,
> - },
> - [1] = { /* sx1509q */
> - .model = SX150X_789,
> - .reg_pullup = 0x07,
> - .reg_pulldn = 0x09,
> - .reg_dir = 0x0f,
> - .reg_data = 0x11,
> - .reg_irq_mask = 0x13,
> - .reg_irq_src = 0x19,
> - .reg_sense = 0x17,
> - .pri.x789 = {
> - .reg_drain = 0x0b,
> - .reg_polarity = 0x0d,
> - .reg_clock = 0x1e,
> - .reg_misc = 0x1f,
> - .reg_reset = 0x7d,
> - },
> - .ngpios = 16
> - },
> - [2] = { /* sx1506q */
> - .model = SX150X_456,
> - .reg_pullup = 0x05,
> - .reg_pulldn = 0x07,
> - .reg_dir = 0x03,
> - .reg_data = 0x01,
> - .reg_irq_mask = 0x09,
> - .reg_irq_src = 0x0f,
> - .reg_sense = 0x0d,
> - .pri.x456 = {
> - .reg_pld_mode = 0x21,
> - .reg_pld_table0 = 0x23,
> - .reg_pld_table1 = 0x25,
> - .reg_pld_table2 = 0x27,
> - .reg_pld_table3 = 0x29,
> - .reg_pld_table4 = 0x2b,
> - .reg_advance = 0xad,
> - },
> - .ngpios = 16
> +static const struct pinctrl_pin_desc sx150x_16_pins[] = {
> + PINCTRL_PIN(0, "gpio0"),
> + PINCTRL_PIN(1, "gpio1"),
> + PINCTRL_PIN(2, "gpio2"),
> + PINCTRL_PIN(3, "gpio3"),
> + PINCTRL_PIN(4, "gpio4"),
> + PINCTRL_PIN(5, "gpio5"),
> + PINCTRL_PIN(6, "gpio6"),
> + PINCTRL_PIN(7, "gpio7"),
> + PINCTRL_PIN(8, "gpio8"),
> + PINCTRL_PIN(9, "gpio9"),
> + PINCTRL_PIN(10, "gpio10"),
> + PINCTRL_PIN(11, "gpio11"),
> + PINCTRL_PIN(12, "gpio12"),
> + PINCTRL_PIN(13, "gpio13"),
> + PINCTRL_PIN(14, "gpio14"),
> + PINCTRL_PIN(15, "gpio15"),
> + PINCTRL_PIN(16, "oscio"),
> +};
> +
> +static const struct sx150x_device_data sx1508q_device_data = {
> + .model = SX150X_789,
> + .reg_pullup = 0x03,
> + .reg_pulldn = 0x04,
> + .reg_dir = 0x07,
> + .reg_data = 0x08,
> + .reg_irq_mask = 0x09,
> + .reg_irq_src = 0x0c,
> + .reg_sense = 0x0b,
> + .pri.x789 = {
> + .reg_drain = 0x05,
> + .reg_polarity = 0x06,
> + .reg_clock = 0x0f,
> + .reg_misc = 0x10,
> + .reg_reset = 0x7d,
> },
> - [3] = { /* sx1502q */
> - .model = SX150X_123,
> - .reg_pullup = 0x02,
> - .reg_pulldn = 0x03,
> - .reg_dir = 0x01,
> - .reg_data = 0x00,
> - .reg_irq_mask = 0x05,
> - .reg_irq_src = 0x08,
> - .reg_sense = 0x07,
> - .pri.x123 = {
> - .reg_pld_mode = 0x10,
> - .reg_pld_table0 = 0x11,
> - .reg_pld_table1 = 0x12,
> - .reg_pld_table2 = 0x13,
> - .reg_pld_table3 = 0x14,
> - .reg_pld_table4 = 0x15,
> - .reg_advance = 0xad,
> - },
> - .ngpios = 8,
> + .ngpios = 8,
> + .pins = sx150x_8_pins,
> + .npins = ARRAY_SIZE(sx150x_8_pins),
> +};
> +
> +static const struct sx150x_device_data sx1509q_device_data = {
> + .model = SX150X_789,
> + .reg_pullup = 0x07,
> + .reg_pulldn = 0x09,
> + .reg_dir = 0x0f,
> + .reg_data = 0x11,
> + .reg_irq_mask = 0x13,
> + .reg_irq_src = 0x19,
> + .reg_sense = 0x17,
> + .pri.x789 = {
> + .reg_drain = 0x0b,
> + .reg_polarity = 0x0d,
> + .reg_clock = 0x1e,
> + .reg_misc = 0x1f,
> + .reg_reset = 0x7d,
> },
> + .ngpios = 16,
> + .pins = sx150x_16_pins,
> + .npins = ARRAY_SIZE(sx150x_16_pins),
> };
>
> -static const struct i2c_device_id sx150x_id[] = {
> - {"sx1508q", 0},
> - {"sx1509q", 1},
> - {"sx1506q", 2},
> - {"sx1502q", 3},
> - {}
> +static const struct sx150x_device_data sx1506q_device_data = {
> + .model = SX150X_456,
> + .reg_pullup = 0x05,
> + .reg_pulldn = 0x07,
> + .reg_dir = 0x03,
> + .reg_data = 0x01,
> + .reg_irq_mask = 0x09,
> + .reg_irq_src = 0x0f,
> + .reg_sense = 0x0d,
> + .pri.x456 = {
> + .reg_pld_mode = 0x21,
> + .reg_pld_table0 = 0x23,
> + .reg_pld_table1 = 0x25,
> + .reg_pld_table2 = 0x27,
> + .reg_pld_table3 = 0x29,
> + .reg_pld_table4 = 0x2b,
> + .reg_advance = 0xad,
> + },
> + .ngpios = 16,
> + .pins = sx150x_16_pins,
> + .npins = 16, /* oscio not available */
> };
>
> -static const struct of_device_id sx150x_of_match[] = {
> - { .compatible = "semtech,sx1508q" },
> - { .compatible = "semtech,sx1509q" },
> - { .compatible = "semtech,sx1506q" },
> - { .compatible = "semtech,sx1502q" },
> - {},
> +static const struct sx150x_device_data sx1502q_device_data = {
> + .model = SX150X_123,
> + .reg_pullup = 0x02,
> + .reg_pulldn = 0x03,
> + .reg_dir = 0x01,
> + .reg_data = 0x00,
> + .reg_irq_mask = 0x05,
> + .reg_irq_src = 0x08,
> + .reg_sense = 0x07,
> + .pri.x123 = {
> + .reg_pld_mode = 0x10,
> + .reg_pld_table0 = 0x11,
> + .reg_pld_table1 = 0x12,
> + .reg_pld_table2 = 0x13,
> + .reg_pld_table3 = 0x14,
> + .reg_pld_table4 = 0x15,
> + .reg_advance = 0xad,
> + },
> + .ngpios = 8,
> + .pins = sx150x_8_pins,
> + .npins = 8, /* oscio not available */
> };
>
> static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val)
> @@ -269,11 +253,6 @@ static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
> return err;
> }
>
> -static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
> -{
> - return (chip->dev_cfg->ngpios == offset);
> -}
> -
> /*
> * These utility functions solve the common problem of locating and setting
> * configuration bits. Configuration bits are grouped into registers
> @@ -294,7 +273,7 @@ static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset)
> * register and mask the correct bits.
> */
> static inline void sx150x_find_cfg(u8 offset, u8 width,
> - u8 *reg, u8 *mask, u8 *shift)
> + u8 *reg, u8 *mask, u8 *shift)
> {
> *reg -= offset * width / 8;
> *mask = (1 << width) - 1;
> @@ -302,185 +281,241 @@ static inline void sx150x_find_cfg(u8 offset, u8 width,
> *mask <<= *shift;
> }
>
> -static s32 sx150x_write_cfg(struct sx150x_chip *chip,
> - u8 offset, u8 width, u8 reg, u8 val)
> +static int sx150x_write_cfg(struct i2c_client *client,
> + u8 offset, u8 width, u8 reg, u8 val)
> {
> u8 mask;
> u8 data;
> u8 shift;
> - s32 err;
> + int err;
>
> sx150x_find_cfg(offset, width, ®, &mask, &shift);
> - err = sx150x_i2c_read(chip->client, reg, &data);
> + err = sx150x_i2c_read(client, reg, &data);
> if (err < 0)
> return err;
>
> data &= ~mask;
> data |= (val << shift) & mask;
> - return sx150x_i2c_write(chip->client, reg, data);
> + return sx150x_i2c_write(client, reg, data);
> }
>
> -static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset)
> +static int sx150x_read_cfg(struct i2c_client *client,
> + u8 offset, u8 width, u8 reg)
> {
> - u8 reg = chip->dev_cfg->reg_data;
> u8 mask;
> u8 data;
> u8 shift;
> - s32 err;
> + int err;
>
> - sx150x_find_cfg(offset, 1, ®, &mask, &shift);
> - err = sx150x_i2c_read(chip->client, reg, &data);
> - if (err >= 0)
> - err = (data & mask) != 0 ? 1 : 0;
> + sx150x_find_cfg(offset, width, ®, &mask, &shift);
> + err = sx150x_i2c_read(client, reg, &data);
> + if (err < 0)
> + return err;
>
> - return err;
> + return (data & mask);
> }
>
> -static void sx150x_set_oscio(struct sx150x_chip *chip, int val)
> +static int sx150x_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
> {
> - sx150x_i2c_write(chip->client,
> - chip->dev_cfg->pri.x789.reg_clock,
> - (val ? 0x1f : 0x10));
> + return 0;
> }
>
> -static void sx150x_set_io(struct sx150x_chip *chip, unsigned offset, int val)
> +static const char *sx150x_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
> + unsigned int group)
> {
> - sx150x_write_cfg(chip,
> - offset,
> - 1,
> - chip->dev_cfg->reg_data,
> - (val ? 1 : 0));
> + return NULL;
> }
>
> -static int sx150x_io_input(struct sx150x_chip *chip, unsigned offset)
> +static int sx150x_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
> + unsigned int group,
> + const unsigned int **pins,
> + unsigned int *num_pins)
> {
> - return sx150x_write_cfg(chip,
> - offset,
> - 1,
> - chip->dev_cfg->reg_dir,
> - 1);
> + return -ENOTSUPP;
> }
>
> -static int sx150x_io_output(struct sx150x_chip *chip, unsigned offset, int val)
> +static const struct pinctrl_ops sx150x_pinctrl_ops = {
> + .get_groups_count = sx150x_pinctrl_get_groups_count,
> + .get_group_name = sx150x_pinctrl_get_group_name,
> + .get_group_pins = sx150x_pinctrl_get_group_pins,
> +#ifdef CONFIG_OF
> + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
> + .dt_free_map = pinctrl_utils_free_map,
> +#endif
> +};
> +
> +static bool sx150x_pin_is_oscio(struct sx150x_pinctrl *pctl, unsigned int pin)
> {
> - int err;
> + if (pin >= pctl->data->npins)
> + return false;
>
> - err = sx150x_write_cfg(chip,
> - offset,
> - 1,
> - chip->dev_cfg->reg_data,
> - (val ? 1 : 0));
> - if (err >= 0)
> - err = sx150x_write_cfg(chip,
> - offset,
> - 1,
> - chip->dev_cfg->reg_dir,
> - 0);
> - return err;
> + /* OSCIO pin is only present in 789 devices */
> + if (pctl->data->model != SX150X_789)
> + return false;
> +
> + return !strcmp(pctl->data->pins[pin].name, "oscio");
> }
>
> -static int sx150x_gpio_get(struct gpio_chip *gc, unsigned offset)
> +static int sx150x_gpio_get_direction(struct gpio_chip *chip,
> + unsigned int offset)
> {
> - struct sx150x_chip *chip = gpiochip_get_data(gc);
> - int status = -EINVAL;
> + struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> + int status;
>
> - if (!offset_is_oscio(chip, offset)) {
> - mutex_lock(&chip->lock);
> - status = sx150x_get_io(chip, offset);
> - mutex_unlock(&chip->lock);
> - }
> + if (sx150x_pin_is_oscio(pctl, offset))
> + return false;
> +
> + status = sx150x_read_cfg(pctl->client, offset, 1, pctl->data->reg_dir);
> + if (status >= 0)
> + status = !!status;
>
> - return (status < 0) ? status : !!status;
> + return status;
> }
>
> -static void sx150x_gpio_set(struct gpio_chip *gc, unsigned offset, int val)
> +static int sx150x_gpio_get(struct gpio_chip *chip, unsigned int offset)
> {
> - struct sx150x_chip *chip = gpiochip_get_data(gc);
> + struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> + int status;
>
> - mutex_lock(&chip->lock);
> - if (offset_is_oscio(chip, offset))
> - sx150x_set_oscio(chip, val);
> - else
> - sx150x_set_io(chip, offset, val);
> - mutex_unlock(&chip->lock);
> + if (sx150x_pin_is_oscio(pctl, offset))
> + return -EINVAL;
> +
> + status = sx150x_read_cfg(pctl->client, offset, 1, pctl->data->reg_data);
> + if (status >= 0)
> + status = !!status;
> +
> + return status;
> }
>
> -static int sx150x_gpio_set_single_ended(struct gpio_chip *gc,
> - unsigned offset,
> - enum single_ended_mode mode)
> +static int sx150x_gpio_set_single_ended(struct gpio_chip *chip,
> + unsigned int offset,
> + enum single_ended_mode mode)
> {
> - struct sx150x_chip *chip = gpiochip_get_data(gc);
> -
> - /* On the SX160X 789 we can set open drain */
> - if (chip->dev_cfg->model != SX150X_789)
> + struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> + int ret;
> +
> + switch (mode) {
> + case LINE_MODE_PUSH_PULL:
> + if (pctl->data->model != SX150X_789 ||
> + sx150x_pin_is_oscio(pctl, offset))
> + return 0;
> +
> + mutex_lock(&pctl->lock);
> + ret = sx150x_write_cfg(pctl->client, offset, 1,
> + pctl->data->pri.x789.reg_drain,
> + 0);
> + mutex_unlock(&pctl->lock);
> + if (ret < 0)
> + return ret;
> + break;
> +
> + case LINE_MODE_OPEN_DRAIN:
> + if (pctl->data->model != SX150X_789 ||
> + sx150x_pin_is_oscio(pctl, offset))
> + return -ENOTSUPP;
> +
> + mutex_lock(&pctl->lock);
> + ret = sx150x_write_cfg(pctl->client, offset, 1,
> + pctl->data->pri.x789.reg_drain,
> + 1);
> + mutex_unlock(&pctl->lock);
> + if (ret < 0)
> + return ret;
> + break;
> +
> + default:
> return -ENOTSUPP;
> + }
>
> - if (mode == LINE_MODE_PUSH_PULL)
> - return sx150x_write_cfg(chip,
> - offset,
> - 1,
> - chip->dev_cfg->pri.x789.reg_drain,
> - 0);
> -
> - if (mode == LINE_MODE_OPEN_DRAIN)
> - return sx150x_write_cfg(chip,
> - offset,
> - 1,
> - chip->dev_cfg->pri.x789.reg_drain,
> - 1);
> - return -ENOTSUPP;
> + return 0;
> }
>
> -static int sx150x_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
> +static void sx150x_gpio_set(struct gpio_chip *chip, unsigned int offset,
> + int value)
> {
> - struct sx150x_chip *chip = gpiochip_get_data(gc);
> - int status = -EINVAL;
> + struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> +
> + if (sx150x_pin_is_oscio(pctl, offset)) {
>
> - if (!offset_is_oscio(chip, offset)) {
> - mutex_lock(&chip->lock);
> - status = sx150x_io_input(chip, offset);
> - mutex_unlock(&chip->lock);
> + mutex_lock(&pctl->lock);
> + sx150x_i2c_write(pctl->client,
> + pctl->data->pri.x789.reg_clock,
> + (value ? 0x1f : 0x10));
> + mutex_unlock(&pctl->lock);
> + } else {
> + mutex_lock(&pctl->lock);
> + sx150x_write_cfg(pctl->client, offset, 1,
> + pctl->data->reg_data,
> + (value ? 1 : 0));
> + mutex_unlock(&pctl->lock);
> }
> - return status;
> }
>
> -static int sx150x_gpio_direction_output(struct gpio_chip *gc,
> - unsigned offset,
> - int val)
> +static int sx150x_gpio_direction_input(struct gpio_chip *chip,
> + unsigned int offset)
> {
> - struct sx150x_chip *chip = gpiochip_get_data(gc);
> - int status = 0;
> + struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> + int ret;
>
> - if (!offset_is_oscio(chip, offset)) {
> - mutex_lock(&chip->lock);
> - status = sx150x_io_output(chip, offset, val);
> - mutex_unlock(&chip->lock);
> + if (sx150x_pin_is_oscio(pctl, offset))
> + return -EINVAL;
> +
> + mutex_lock(&pctl->lock);
> + ret = sx150x_write_cfg(pctl->client, offset, 1,
> + pctl->data->reg_dir, 1);
> + mutex_unlock(&pctl->lock);
> +
> + return ret;
> +}
> +
> +static int sx150x_gpio_direction_output(struct gpio_chip *chip,
> + unsigned int offset, int value)
> +{
> + struct sx150x_pinctrl *pctl = gpiochip_get_data(chip);
> + int status;
> +
> + if (sx150x_pin_is_oscio(pctl, offset)) {
> + sx150x_gpio_set(chip, offset, value);
> + return 0;
> }
> +
> + mutex_lock(&pctl->lock);
> + status = sx150x_write_cfg(pctl->client, offset, 1,
> + pctl->data->reg_data,
> + (value ? 1 : 0));
> + if (status >= 0)
> + status = sx150x_write_cfg(pctl->client, offset, 1,
> + pctl->data->reg_dir, 0);
> + mutex_unlock(&pctl->lock);
> +
> return status;
> }
>
> static void sx150x_irq_mask(struct irq_data *d)
> {
> - struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> - unsigned n = d->hwirq;
> + struct sx150x_pinctrl *pctl =
> + gpiochip_get_data(irq_data_get_irq_chip_data(d));
> + unsigned int n = d->hwirq;
>
> - chip->irq_masked |= (1 << n);
> - chip->irq_update = n;
> + pctl->irq.masked |= (1 << n);
> + pctl->irq.update = n;
> }
>
> static void sx150x_irq_unmask(struct irq_data *d)
> {
> - struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> - unsigned n = d->hwirq;
> + struct sx150x_pinctrl *pctl =
> + gpiochip_get_data(irq_data_get_irq_chip_data(d));
> + unsigned int n = d->hwirq;
>
> - chip->irq_masked &= ~(1 << n);
> - chip->irq_update = n;
> + pctl->irq.masked &= ~(1 << n);
> + pctl->irq.update = n;
> }
>
> static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
> {
> - struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> - unsigned n, val = 0;
> + struct sx150x_pinctrl *pctl =
> + gpiochip_get_data(irq_data_get_irq_chip_data(d));
> + unsigned int n, val = 0;
>
> if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
> return -EINVAL;
> @@ -492,37 +527,40 @@ static int sx150x_irq_set_type(struct irq_data *d, unsigned int flow_type)
> if (flow_type & IRQ_TYPE_EDGE_FALLING)
> val |= 0x2;
>
> - chip->irq_sense &= ~(3UL << (n * 2));
> - chip->irq_sense |= val << (n * 2);
> - chip->irq_update = n;
> + pctl->irq.sense &= ~(3UL << (n * 2));
> + pctl->irq.sense |= val << (n * 2);
> + pctl->irq.update = n;
> return 0;
> }
>
> static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
> {
> - struct sx150x_chip *chip = (struct sx150x_chip *)dev_id;
> - unsigned nhandled = 0;
> - unsigned sub_irq;
> - unsigned n;
> + struct sx150x_pinctrl *pctl = (struct sx150x_pinctrl *)dev_id;
> + unsigned int nhandled = 0;
> + unsigned int sub_irq;
> + unsigned int n;
> s32 err;
> u8 val;
> int i;
>
> - for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) {
> - err = sx150x_i2c_read(chip->client,
> - chip->dev_cfg->reg_irq_src - i,
> + for (i = (pctl->data->ngpios / 8) - 1; i >= 0; --i) {
> + err = sx150x_i2c_read(pctl->client,
> + pctl->data->reg_irq_src - i,
> &val);
> if (err < 0)
> continue;
>
> - sx150x_i2c_write(chip->client,
> - chip->dev_cfg->reg_irq_src - i,
> - val);
> + err = sx150x_i2c_write(pctl->client,
> + pctl->data->reg_irq_src - i,
> + val);
> + if (err < 0)
> + continue;
> +
> for (n = 0; n < 8; ++n) {
> if (val & (1 << n)) {
> sub_irq = irq_find_mapping(
> - chip->gpio_chip.irqdomain,
> - (i * 8) + n);
> + pctl->gpio.irqdomain,
> + (i * 8) + n);
> handle_nested_irq(sub_irq);
> ++nhandled;
> }
> @@ -534,251 +572,483 @@ static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id)
>
> static void sx150x_irq_bus_lock(struct irq_data *d)
> {
> - struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> + struct sx150x_pinctrl *pctl =
> + gpiochip_get_data(irq_data_get_irq_chip_data(d));
>
> - mutex_lock(&chip->lock);
> + mutex_lock(&pctl->lock);
> }
>
> static void sx150x_irq_bus_sync_unlock(struct irq_data *d)
> {
> - struct sx150x_chip *chip = gpiochip_get_data(irq_data_get_irq_chip_data(d));
> - unsigned n;
> + struct sx150x_pinctrl *pctl =
> + gpiochip_get_data(irq_data_get_irq_chip_data(d));
> + unsigned int n;
>
> - if (chip->irq_update == NO_UPDATE_PENDING)
> + if (pctl->irq.update < 0)
> goto out;
>
> - n = chip->irq_update;
> - chip->irq_update = NO_UPDATE_PENDING;
> + n = pctl->irq.update;
> + pctl->irq.update = -1;
>
> /* Avoid updates if nothing changed */
> - if (chip->dev_sense == chip->irq_sense &&
> - chip->dev_masked == chip->irq_masked)
> + if (pctl->irq.dev_sense == pctl->irq.sense &&
> + pctl->irq.dev_masked == pctl->irq.masked)
> goto out;
>
> - chip->dev_sense = chip->irq_sense;
> - chip->dev_masked = chip->irq_masked;
> + pctl->irq.dev_sense = pctl->irq.sense;
> + pctl->irq.dev_masked = pctl->irq.masked;
>
> - if (chip->irq_masked & (1 << n)) {
> - sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 1);
> - sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense, 0);
> + if (pctl->irq.masked & (1 << n)) {
> + sx150x_write_cfg(pctl->client, n, 1,
> + pctl->data->reg_irq_mask, 1);
> + sx150x_write_cfg(pctl->client, n, 2,
> + pctl->data->reg_sense, 0);
> } else {
> - sx150x_write_cfg(chip, n, 1, chip->dev_cfg->reg_irq_mask, 0);
> - sx150x_write_cfg(chip, n, 2, chip->dev_cfg->reg_sense,
> - chip->irq_sense >> (n * 2));
> + sx150x_write_cfg(pctl->client, n, 1,
> + pctl->data->reg_irq_mask, 0);
> + sx150x_write_cfg(pctl->client, n, 2,
> + pctl->data->reg_sense,
> + pctl->irq.sense >> (n * 2));
> }
> out:
> - mutex_unlock(&chip->lock);
> + mutex_unlock(&pctl->lock);
> }
>
> -static void sx150x_init_chip(struct sx150x_chip *chip,
> - struct i2c_client *client,
> - kernel_ulong_t driver_data,
> - struct sx150x_platform_data *pdata)
> +static int sx150x_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
> + unsigned long *config)
> {
> - mutex_init(&chip->lock);
> -
> - chip->client = client;
> - chip->dev_cfg = &sx150x_devices[driver_data];
> - chip->gpio_chip.parent = &client->dev;
> - chip->gpio_chip.label = client->name;
> - chip->gpio_chip.direction_input = sx150x_gpio_direction_input;
> - chip->gpio_chip.direction_output = sx150x_gpio_direction_output;
> - chip->gpio_chip.get = sx150x_gpio_get;
> - chip->gpio_chip.set = sx150x_gpio_set;
> - chip->gpio_chip.set_single_ended = sx150x_gpio_set_single_ended;
> - chip->gpio_chip.base = pdata->gpio_base;
> - chip->gpio_chip.can_sleep = true;
> - chip->gpio_chip.ngpio = chip->dev_cfg->ngpios;
> -#ifdef CONFIG_OF_GPIO
> - chip->gpio_chip.of_node = client->dev.of_node;
> - chip->gpio_chip.of_gpio_n_cells = 2;
> -#endif
> - if (pdata->oscio_is_gpo)
> - ++chip->gpio_chip.ngpio;
> -
> - chip->irq_chip.name = client->name;
> - chip->irq_chip.irq_mask = sx150x_irq_mask;
> - chip->irq_chip.irq_unmask = sx150x_irq_unmask;
> - chip->irq_chip.irq_set_type = sx150x_irq_set_type;
> - chip->irq_chip.irq_bus_lock = sx150x_irq_bus_lock;
> - chip->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
> - chip->irq_summary = -1;
> - chip->irq_base = -1;
> - chip->irq_masked = ~0;
> - chip->irq_sense = 0;
> - chip->dev_masked = ~0;
> - chip->dev_sense = 0;
> - chip->irq_update = NO_UPDATE_PENDING;
> + struct sx150x_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
> + unsigned int param = pinconf_to_config_param(*config);
> + int ret;
> + u32 arg;
> +
> + if (sx150x_pin_is_oscio(pctl, pin)) {
> + u8 data;
> +
> + switch (param) {
> + case PIN_CONFIG_DRIVE_PUSH_PULL:
> + case PIN_CONFIG_OUTPUT:
> + mutex_lock(&pctl->lock);
> + ret = sx150x_i2c_read(pctl->client,
> + pctl->data->pri.x789.reg_clock,
> + &data);
> + mutex_unlock(&pctl->lock);
> +
> + if (ret < 0)
> + return ret;
> +
> + if (param == PIN_CONFIG_DRIVE_PUSH_PULL)
> + arg = (data & 0x1f) ? 1 : 0;
> + else {
> + if ((data & 0x1f) == 0x1f)
> + arg = 1;
> + else if ((data & 0x1f) == 0x10)
> + arg = 0;
> + else
> + return -EINVAL;
> + }
> +
> + break;
> + default:
> + return -ENOTSUPP;
> + }
> +
> + goto out;
> + }
> +
> + switch (param) {
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + mutex_lock(&pctl->lock);
> + ret = sx150x_read_cfg(pctl->client, pin, 1,
> + pctl->data->reg_pulldn);
> + mutex_unlock(&pctl->lock);
> +
> + if (ret < 0)
> + return ret;
> +
> + if (!ret)
> + return -EINVAL;
> +
> + arg = 1;
> + break;
> +
> + case PIN_CONFIG_BIAS_PULL_UP:
> + mutex_lock(&pctl->lock);
> + ret = sx150x_read_cfg(pctl->client, pin, 1,
> + pctl->data->reg_pullup);
> + mutex_unlock(&pctl->lock);
> +
> + if (ret < 0)
> + return ret;
> +
> + if (!ret)
> + return -EINVAL;
> +
> + arg = 1;
> + break;
> +
> + case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> + if (pctl->data->model != SX150X_789)
> + return -ENOTSUPP;
> +
> + mutex_lock(&pctl->lock);
> + ret = sx150x_read_cfg(pctl->client, pin, 1,
> + pctl->data->pri.x789.reg_drain);
> + mutex_unlock(&pctl->lock);
> +
> + if (ret < 0)
> + return ret;
> +
> + if (!ret)
> + return -EINVAL;
> +
> + arg = 1;
> + break;
> +
> + case PIN_CONFIG_DRIVE_PUSH_PULL:
> + if (pctl->data->model != SX150X_789)
> + arg = true;
> + else {
> + mutex_lock(&pctl->lock);
> + ret = sx150x_read_cfg(pctl->client, pin, 1,
> + pctl->data->pri.x789.reg_drain);
> + mutex_unlock(&pctl->lock);
> +
> + if (ret < 0)
> + return ret;
> +
> + if (ret)
> + return -EINVAL;
> +
> + arg = 1;
> + }
> + break;
> +
> + case PIN_CONFIG_OUTPUT:
> + ret = sx150x_gpio_get_direction(&pctl->gpio, pin);
> + if (ret < 0)
> + return ret;
> +
> + if (ret)
> + return -EINVAL;
> +
> + ret = sx150x_gpio_get(&pctl->gpio, pin);
> + if (ret < 0)
> + return ret;
> +
> + arg = ret;
> + break;
> +
> + default:
> + return -ENOTSUPP;
> + }
> +
> +out:
> + *config = pinconf_to_config_packed(param, arg);
> +
> + return 0;
> +}
> +
> +static int sx150x_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
> + unsigned long *configs, unsigned int num_configs)
> +{
> + struct sx150x_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
> + enum pin_config_param param;
> + u32 arg;
> + int i;
> + int ret;
> +
> + for (i = 0; i < num_configs; i++) {
> + param = pinconf_to_config_param(configs[i]);
> + arg = pinconf_to_config_argument(configs[i]);
> +
> + if (sx150x_pin_is_oscio(pctl, pin)) {
> + if (param == PIN_CONFIG_OUTPUT) {
> + ret = sx150x_gpio_direction_output(&pctl->gpio,
> + pin, arg);
> + if (ret < 0)
> + return ret;
> +
> + continue;
> + } else
> + return -ENOTSUPP;
> + }
> +
> + switch (param) {
> + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
> + case PIN_CONFIG_BIAS_DISABLE:
> + mutex_lock(&pctl->lock);
> + ret = sx150x_write_cfg(pctl->client, pin, 1,
> + pctl->data->reg_pulldn, 0);
> + mutex_unlock(&pctl->lock);
> + if (ret < 0)
> + return ret;
> +
> + mutex_lock(&pctl->lock);
> + ret = sx150x_write_cfg(pctl->client, pin, 1,
> + pctl->data->reg_pullup, 0);
> + mutex_unlock(&pctl->lock);
> + if (ret < 0)
> + return ret;
> +
> + break;
> +
> + case PIN_CONFIG_BIAS_PULL_UP:
> + mutex_lock(&pctl->lock);
> + ret = sx150x_write_cfg(pctl->client, pin, 1,
> + pctl->data->reg_pullup,
> + 1);
> + mutex_unlock(&pctl->lock);
> + if (ret < 0)
> + return ret;
> +
> + break;
> +
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + mutex_lock(&pctl->lock);
> + ret = sx150x_write_cfg(pctl->client, pin, 1,
> + pctl->data->reg_pulldn,
> + 1);
> + mutex_unlock(&pctl->lock);
> + if (ret < 0)
> + return ret;
> +
> + break;
> +
> + case PIN_CONFIG_DRIVE_OPEN_DRAIN:
> + ret = sx150x_gpio_set_single_ended(&pctl->gpio,
> + pin, LINE_MODE_OPEN_DRAIN);
> + if (ret < 0)
> + return ret;
> +
> + break;
> +
> + case PIN_CONFIG_DRIVE_PUSH_PULL:
> + ret = sx150x_gpio_set_single_ended(&pctl->gpio,
> + pin, LINE_MODE_PUSH_PULL);
> + if (ret < 0)
> + return ret;
> +
> + break;
> +
> + case PIN_CONFIG_OUTPUT:
> + ret = sx150x_gpio_direction_output(&pctl->gpio,
> + pin, arg);
> + if (ret < 0)
> + return ret;
> +
> + break;
> +
> + default:
> + return -ENOTSUPP;
> + }
> + } /* for each config */
> +
> + return 0;
> }
>
> -static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
> +static const struct pinconf_ops sx150x_pinconf_ops = {
> + .pin_config_get = sx150x_pinconf_get,
> + .pin_config_set = sx150x_pinconf_set,
> + .is_generic = true,
> +};
> +
> +static const struct i2c_device_id sx150x_id[] = {
> + {"sx1508q", (kernel_ulong_t) &sx1508q_device_data },
> + {"sx1509q", (kernel_ulong_t) &sx1509q_device_data },
> + {"sx1506q", (kernel_ulong_t) &sx1506q_device_data },
> + {"sx1502q", (kernel_ulong_t) &sx1502q_device_data },
> + {}
> +};
> +
> +static const struct of_device_id sx150x_of_match[] = {
> + { .compatible = "semtech,sx1508q" },
> + { .compatible = "semtech,sx1509q" },
> + { .compatible = "semtech,sx1506q" },
> + { .compatible = "semtech,sx1502q" },
> + {},
> +};
> +
> +static int sx150x_init_io(struct sx150x_pinctrl *pctl, u8 base, u16 cfg)
> {
> int err = 0;
> - unsigned n;
> + unsigned int n;
>
> - for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n)
> - err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8));
> + for (n = 0; err >= 0 && n < (pctl->data->ngpios / 8); ++n)
> + err = sx150x_i2c_write(pctl->client, base - n, cfg >> (n * 8));
> return err;
> }
>
> -static int sx150x_reset(struct sx150x_chip *chip)
> +static int sx150x_reset(struct sx150x_pinctrl *pctl)
> {
> int err;
>
> - err = i2c_smbus_write_byte_data(chip->client,
> - chip->dev_cfg->pri.x789.reg_reset,
> + err = i2c_smbus_write_byte_data(pctl->client,
> + pctl->data->pri.x789.reg_reset,
> 0x12);
> if (err < 0)
> return err;
>
> - err = i2c_smbus_write_byte_data(chip->client,
> - chip->dev_cfg->pri.x789.reg_reset,
> + err = i2c_smbus_write_byte_data(pctl->client,
> + pctl->data->pri.x789.reg_reset,
> 0x34);
> return err;
> }
>
> -static int sx150x_init_hw(struct sx150x_chip *chip,
> - struct sx150x_platform_data *pdata)
> +static int sx150x_init_hw(struct sx150x_pinctrl *pctl)
> {
> - int err = 0;
> + int err;
>
> - if (pdata->reset_during_probe) {
> - err = sx150x_reset(chip);
> + if (pctl->data->model == SX150X_789 &&
> + of_property_read_bool(pctl->dev->of_node, "semtech,probe-reset")) {
> + err = sx150x_reset(pctl);
> if (err < 0)
> return err;
> }
>
> - if (chip->dev_cfg->model == SX150X_789)
> - err = sx150x_i2c_write(chip->client,
> - chip->dev_cfg->pri.x789.reg_misc,
> + if (pctl->data->model == SX150X_789)
> + err = sx150x_i2c_write(pctl->client,
> + pctl->data->pri.x789.reg_misc,
> 0x01);
> - else if (chip->dev_cfg->model == SX150X_456)
> - err = sx150x_i2c_write(chip->client,
> - chip->dev_cfg->pri.x456.reg_advance,
> + else if (pctl->data->model == SX150X_456)
> + err = sx150x_i2c_write(pctl->client,
> + pctl->data->pri.x456.reg_advance,
> 0x04);
> else
> - err = sx150x_i2c_write(chip->client,
> - chip->dev_cfg->pri.x123.reg_advance,
> + err = sx150x_i2c_write(pctl->client,
> + pctl->data->pri.x123.reg_advance,
> 0x00);
> if (err < 0)
> return err;
>
> - err = sx150x_init_io(chip, chip->dev_cfg->reg_pullup,
> - pdata->io_pullup_ena);
> - if (err < 0)
> - return err;
> -
> - err = sx150x_init_io(chip, chip->dev_cfg->reg_pulldn,
> - pdata->io_pulldn_ena);
> - if (err < 0)
> - return err;
> -
> - if (chip->dev_cfg->model == SX150X_789) {
> - err = sx150x_init_io(chip,
> - chip->dev_cfg->pri.x789.reg_polarity,
> - pdata->io_polarity);
> + /* Set all pins to work in normal mode */
> + if (pctl->data->model == SX150X_789) {
> + err = sx150x_init_io(pctl,
> + pctl->data->pri.x789.reg_polarity,
> + 0);
> if (err < 0)
> return err;
> - } else if (chip->dev_cfg->model == SX150X_456) {
> + } else if (pctl->data->model == SX150X_456) {
> /* Set all pins to work in normal mode */
> - err = sx150x_init_io(chip,
> - chip->dev_cfg->pri.x456.reg_pld_mode,
> + err = sx150x_init_io(pctl,
> + pctl->data->pri.x456.reg_pld_mode,
> 0);
> if (err < 0)
> return err;
> } else {
> /* Set all pins to work in normal mode */
> - err = sx150x_init_io(chip,
> - chip->dev_cfg->pri.x123.reg_pld_mode,
> + err = sx150x_init_io(pctl,
> + pctl->data->pri.x123.reg_pld_mode,
> 0);
> if (err < 0)
> return err;
> }
>
> -
> - if (pdata->oscio_is_gpo)
> - sx150x_set_oscio(chip, 0);
> -
> - return err;
> -}
> -
> -static int sx150x_install_irq_chip(struct sx150x_chip *chip,
> - int irq_summary,
> - int irq_base)
> -{
> - int err;
> -
> - chip->irq_summary = irq_summary;
> - chip->irq_base = irq_base;
> -
> - /* Add gpio chip to irq subsystem */
> - err = gpiochip_irqchip_add(&chip->gpio_chip,
> - &chip->irq_chip, chip->irq_base,
> - handle_edge_irq, IRQ_TYPE_EDGE_BOTH);
> - if (err) {
> - dev_err(&chip->client->dev,
> - "could not connect irqchip to gpiochip\n");
> - return err;
> - }
> -
> - err = devm_request_threaded_irq(&chip->client->dev,
> - irq_summary, NULL, sx150x_irq_thread_fn,
> - IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_FALLING,
> - chip->irq_chip.name, chip);
> - if (err < 0) {
> - chip->irq_summary = -1;
> - chip->irq_base = -1;
> - }
> -
> - return err;
> + return 0;
> }
>
> static int sx150x_probe(struct i2c_client *client,
> - const struct i2c_device_id *id)
> + const struct i2c_device_id *id)
> {
> static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA |
> I2C_FUNC_SMBUS_WRITE_WORD_DATA;
> - struct sx150x_platform_data *pdata;
> - struct sx150x_chip *chip;
> - int rc;
> + struct device *dev = &client->dev;
> + struct sx150x_pinctrl *pctl;
> + int ret;
>
> - pdata = dev_get_platdata(&client->dev);
> - if (!pdata)
> + if (!id->driver_data)
> return -EINVAL;
>
> if (!i2c_check_functionality(client->adapter, i2c_funcs))
> return -ENOSYS;
>
> - chip = devm_kzalloc(&client->dev,
> - sizeof(struct sx150x_chip), GFP_KERNEL);
> - if (!chip)
> + pctl = devm_kzalloc(dev, sizeof(*pctl), GFP_KERNEL);
> + if (!pctl)
> return -ENOMEM;
>
> - sx150x_init_chip(chip, client, id->driver_data, pdata);
> - rc = sx150x_init_hw(chip, pdata);
> - if (rc < 0)
> - return rc;
> -
> - rc = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
> - if (rc)
> - return rc;
> -
> - if (pdata->irq_summary >= 0) {
> - rc = sx150x_install_irq_chip(chip,
> - pdata->irq_summary,
> - pdata->irq_base);
> - if (rc < 0)
> - return rc;
> + pctl->dev = dev;
> + pctl->client = client;
> + pctl->data = (void *)id->driver_data;
> +
> + mutex_init(&pctl->lock);
> +
> + ret = sx150x_init_hw(pctl);
> + if (ret)
> + return ret;
> +
> + /* Register GPIO controller */
> + pctl->gpio.label = devm_kstrdup(dev, client->name, GFP_KERNEL);
> + pctl->gpio.base = -1;
> + pctl->gpio.ngpio = pctl->data->npins;
> + pctl->gpio.get_direction = sx150x_gpio_get_direction;
> + pctl->gpio.direction_input = sx150x_gpio_direction_input;
> + pctl->gpio.direction_output = sx150x_gpio_direction_output;
> + pctl->gpio.get = sx150x_gpio_get;
> + pctl->gpio.set = sx150x_gpio_set;
> + pctl->gpio.set_single_ended = sx150x_gpio_set_single_ended;
> + pctl->gpio.parent = dev;
> +#ifdef CONFIG_OF_GPIO
> + pctl->gpio.of_node = dev->of_node;
> +#endif
> + pctl->gpio.can_sleep = true;
> +
> + ret = devm_gpiochip_add_data(dev, &pctl->gpio, pctl);
> + if (ret)
> + return ret;
> +
> + /* Add Interrupt support if an irq is specified */
> + if (client->irq > 0) {
> + pctl->irq_chip.name = devm_kstrdup(dev, client->name,
> + GFP_KERNEL);
> + pctl->irq_chip.irq_mask = sx150x_irq_mask;
> + pctl->irq_chip.irq_unmask = sx150x_irq_unmask;
> + pctl->irq_chip.irq_set_type = sx150x_irq_set_type;
> + pctl->irq_chip.irq_bus_lock = sx150x_irq_bus_lock;
> + pctl->irq_chip.irq_bus_sync_unlock = sx150x_irq_bus_sync_unlock;
> +
> + pctl->irq.masked = ~0;
> + pctl->irq.sense = 0;
> + pctl->irq.dev_masked = ~0;
> + pctl->irq.dev_sense = 0;
> + pctl->irq.update = -1;
> +
> + ret = gpiochip_irqchip_add(&pctl->gpio,
> + &pctl->irq_chip, 0,
> + handle_edge_irq, IRQ_TYPE_EDGE_BOTH);
Adding irqchip with IRQ_TYPE_EDGE_BOTH triggers a WARN in
drivers/gpio/gpiolib.c:1671, on a custom Vybrid board that I have with
the chip, so maybe it should be replaced with IRQ_TYPE_NONE? That's
what I did for my testing and with that change
Tested-by: Andrey Smirnov <andrew.smirnov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Thanks,
Andrey Smirnov
--
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 v4 2/5] drivers/of: do not add memory for unavailable nodes
From: Reza Arbab @ 2016-10-23 1:51 UTC (permalink / raw)
To: Alistair Popple, Michael Ellerman
Cc: linuxppc-dev, Benjamin Herrenschmidt, Paul Mackerras, Rob Herring,
Frank Rowand, Andrew Morton, Stewart Smith, devicetree,
linux-kernel, Tang Chen, linux-mm, Aneesh Kumar K.V,
Bharata B Rao, Nathan Fontenot
In-Reply-To: <2344394.NlaWgtFOqB@new-mexico>
Hi Alistair,
On Fri, Oct 21, 2016 at 05:22:54PM +1100, Alistair Popple wrote:
>From what I can tell it seems that kernels without this patch will try
>and use this memory even if it is marked in the device-tree as
>status="disabled" which could lead to problems for older kernels when
>we start exporting this property from firmware.
>
>Arguably this might not be such a problem in practice as we probably
>don't have many (if any) existing kernels that will boot on hardware
>exporting these properties.
Yes, I think you've got it right.
>However given this patch seems fairly independent perhaps it is worth
>sending as a separate fix if it is not going to make it into this
>release?
Michael,
If this set as a whole is going to miss the release, would it be helpful
for me to resend 1/5 and 2/5 as a separate set? They are the minimum
needed to prevent the possible forward compatibility issue Alistair
describes.
--
Reza Arbab
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [linux-sunxi] [PATCH v5 0/7] ARM: ASoC: drm: sun8i: Add DE2 HDMI audio and video
From: Chen-Yu Tsai @ 2016-10-23 1:38 UTC (permalink / raw)
To: Jean-Francois Moine
Cc: Dave Airlie, Liam Girdwood, Mark Brown, Maxime Ripard,
Rob Herring, Linux-ALSA, devicetree, dri-devel, linux-arm-kernel,
linux-sunxi
In-Reply-To: <cover.1477142934.git.moinejf-GANU6spQydw@public.gmane.org>
On Sat, Oct 22, 2016 at 9:28 PM, Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org> wrote:
> This patchset series adds HDMI audio and video support to the Allwinner
> sun8i SoCs which include the display engine 2 (DE2).
>
> A first submission in January for video on the H3 could not enter into
> the mainline kernel due to the lack of license headers in Allwinner's
> sources.
>
> Recently, an announce about Tina OS for the R series
> https://www.youtube.com/watch?v=h7KD-6HblAU
> was followed by the upload of a new linux-3.4 source tree
> https://github.com/tinalinux/linux-3.4
> with files containing GPL headers.
>
> Well, I don't know if these sources are really from Allwinner, but
> anyway, this is the opportunity to propose a new version of my DRM
> HDMI driver.
Could you clarify about this bit? Did you just clean up Allwinner's
existing drivers? Or just use them as reference? Either way I think
this deserves some mention in all your copyright headers.
Otherwise what difference does the new release make?
Regards
ChenYu
>
> v5:
> - add overlay plane
> - add audio support
> - add support for the A83T
> - add back the HDMI driver
> - many bug fixes
> v4:
> - drivers/clk/sunxi/Makefile was missing (Emil Velikov)
> v3:
> - add the hardware cursor
> - simplify and fix the DE2 init sequences
> - generation for all SUNXI SoCs (Andre Przywara)
> v2:
> - remove the HDMI driver
> - remarks from Chen-Yu Tsai and Russell King
> - DT documentation added
>
> Jean-Francois Moine (7):
> drm: sunxi: Add a basic DRM driver for Allwinner DE2
> ASoC: sunxi: Add a simple HDMI CODEC
> drm: sunxi: add DE2 HDMI support
> ASoC: sunxi: Add sun8i I2S driver
> ARM: dts: sun8i-h3: add HDMI audio and video nodes
> ARM: dts: sun8i-h3: Add HDMI audio and video to the Banana Pi M2+
> ARM: dts: sun8i-h3: Add HDMI audio and video to the Orange PI 2
>
> .../devicetree/bindings/display/sunxi/hdmi.txt | 52 ++
> .../bindings/display/sunxi/sunxi-de2.txt | 83 ++
> .../devicetree/bindings/sound/sun4i-i2s.txt | 38 +-
> arch/arm/boot/dts/sun8i-h3-bananapi-m2-plus.dts | 17 +
> arch/arm/boot/dts/sun8i-h3-orangepi-2.dts | 17 +
> arch/arm/boot/dts/sun8i-h3.dtsi | 67 ++
> drivers/gpu/drm/Kconfig | 2 +
> drivers/gpu/drm/Makefile | 1 +
> drivers/gpu/drm/sunxi/Kconfig | 29 +
> drivers/gpu/drm/sunxi/Makefile | 9 +
> drivers/gpu/drm/sunxi/de2_crtc.c | 475 +++++++++++
> drivers/gpu/drm/sunxi/de2_crtc.h | 63 ++
> drivers/gpu/drm/sunxi/de2_de.c | 591 +++++++++++++
> drivers/gpu/drm/sunxi/de2_drm.h | 47 ++
> drivers/gpu/drm/sunxi/de2_drv.c | 378 +++++++++
> drivers/gpu/drm/sunxi/de2_hdmi.c | 396 +++++++++
> drivers/gpu/drm/sunxi/de2_hdmi.h | 40 +
> drivers/gpu/drm/sunxi/de2_hdmi_io.c | 927 +++++++++++++++++++++
> drivers/gpu/drm/sunxi/de2_hdmi_io.h | 25 +
> drivers/gpu/drm/sunxi/de2_plane.c | 119 +++
> include/sound/sunxi_hdmi.h | 23 +
> sound/soc/codecs/Kconfig | 9 +
> sound/soc/codecs/Makefile | 2 +
> sound/soc/codecs/sunxi-hdmi.c | 106 +++
> sound/soc/sunxi/Kconfig | 8 +
> sound/soc/sunxi/Makefile | 3 +
> sound/soc/sunxi/sun8i-i2s.c | 700 ++++++++++++++++
> 27 files changed, 4222 insertions(+), 5 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/display/sunxi/hdmi.txt
> create mode 100644 Documentation/devicetree/bindings/display/sunxi/sunxi-de2.txt
> create mode 100644 drivers/gpu/drm/sunxi/Kconfig
> create mode 100644 drivers/gpu/drm/sunxi/Makefile
> create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_crtc.h
> create mode 100644 drivers/gpu/drm/sunxi/de2_de.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_drm.h
> create mode 100644 drivers/gpu/drm/sunxi/de2_drv.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi.h
> create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi_io.c
> create mode 100644 drivers/gpu/drm/sunxi/de2_hdmi_io.h
> create mode 100644 drivers/gpu/drm/sunxi/de2_plane.c
> create mode 100644 include/sound/sunxi_hdmi.h
> create mode 100644 sound/soc/codecs/sunxi-hdmi.c
> create mode 100644 sound/soc/sunxi/sun8i-i2s.c
>
> --
> 2.10.1
>
> --
> 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.
--
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: [linux-sunxi] [PATCH v5 4/7] ASoC: sunxi: Add sun8i I2S driver
From: Chen-Yu Tsai @ 2016-10-23 1:33 UTC (permalink / raw)
To: Jean-Francois Moine
Cc: Dave Airlie, Liam Girdwood, Mark Brown, Maxime Ripard,
Rob Herring, Linux-ALSA, devicetree, dri-devel, linux-arm-kernel,
linux-sunxi
In-Reply-To: <bee315efb909c1ee8b5fe7d3aeb9a33d713cb562.1477142934.git.moinejf-GANU6spQydw@public.gmane.org>
Hi,
On Fri, Oct 21, 2016 at 4:36 PM, Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org> wrote:
> This patch adds I2S support to sun8i SoCs as the A83T and H3.
>
> Signed-off-by: Jean-Francois Moine <moinejf-GANU6spQydw@public.gmane.org>
> ---
> Note: This driver is closed to the sun4i-i2s except that:
> - it handles the H3
If it's close to sun4i-i2s, you should probably rework that one to support
the newer SoCs.
> - it creates the sound card (with sun4i-i2s, the sound card is created
> by the CODECs)
I think this is wrong. I2S is only the DAI. You typically have a separate
platform driver for the whole card, or just use simple-card.
> ---
> .../devicetree/bindings/sound/sun4i-i2s.txt | 38 +-
> sound/soc/sunxi/Kconfig | 8 +
> sound/soc/sunxi/Makefile | 3 +
> sound/soc/sunxi/sun8i-i2s.c | 700 +++++++++++++++++++++
> 4 files changed, 744 insertions(+), 5 deletions(-)
> create mode 100644 sound/soc/sunxi/sun8i-i2s.c
>
> diff --git a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt
> index 7b526ec..2fb0a7a 100644
> --- a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt
> +++ b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt
> @@ -1,4 +1,4 @@
> -* Allwinner A10 I2S controller
> +* Allwinner A10/A38T/H3 I2S controller
>
> The I2S bus (Inter-IC sound bus) is a serial link for digital
> audio data transfer between devices in the system.
> @@ -6,20 +6,30 @@ audio data transfer between devices in the system.
> Required properties:
>
> - compatible: should be one of the followings
> - - "allwinner,sun4i-a10-i2s"
> + - "allwinner,sun4i-a10-i2s"
> + "allwinner,sun8i-a83t-i2s"
> + "allwinner,sun8i-h3-i2s"
> - reg: physical base address of the controller and length of memory mapped
> region.
> -- interrupts: should contain the I2S interrupt.
> - dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
> Documentation/devicetree/bindings/dma/dma.txt
> -- dma-names: should include "tx" and "rx".
> +- dma-names: must include "tx" and/or "rx".
> - clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
> - clock-names: should contain followings:
> - "apb" : clock for the I2S bus interface
> - "mod" : module clock for the I2S controller
> - #sound-dai-cells : Must be equal to 0
>
> -Example:
> +Optional properties:
> +
> +- interrupts: I2S interrupt
> +- resets: phandle to the reset of the device
> +
> +Required nodes:
> +
> + - port: link to the associated CODEC (DAC, HDMI...)
Note here you are changing an existing binding, adding a required node.
If it were truely different, you probably should've started a new binding.
Regards
ChenYu
> +
> +Example 1:
>
> i2s0: i2s@01c22400 {
> #sound-dai-cells = <0>;
> @@ -32,3 +42,21 @@ i2s0: i2s@01c22400 {
> <&dma SUN4I_DMA_NORMAL 3>;
> dma-names = "rx", "tx";
> };
> +
> +Example 2:
> +
> +i2s2: i2s@1c22800 {
> + compatible = "allwinner,sun8i-a83t-i2s";
> + reg = <0x01c22800 0x60>;
> + clocks = <&ccu CLK_BUS_I2S2>, <&ccu CLK_I2S2>;
> + clock-names = "apb", "mod";
> + resets = <&ccu RST_I2S2>;
> + dmas = <&dma 27>;
> + dma-names = "tx";
> + status = "disabled";
> + port {
> + i2s2_hdmi: endpoint {
> + remote-endpoint = <&hdmi_i2s2>;
> + };
> + };
> +};
--
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 v2 7/7] iio: envelope-detector: ADC driver based on a DAC and a comparator
From: Peter Rosin @ 2016-10-22 22:43 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Peter Rosin, Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Rob Herring, Mark Rutland,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1477176226-10566-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
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>
---
.../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 | 463 +++++++++++++++++++++
5 files changed, 512 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..8d890028a649
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
@@ -0,0 +1,36 @@
+What: /sys/bus/iio/devices/iio:deviceX/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/compare_interval_ms
+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 4b6f6ec1b703..0d20f3561700 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6122,7 +6122,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..3842d1a72023
--- /dev/null
+++ b/drivers/iio/adc/envelope-detector.c
@@ -0,0 +1,463 @@
+/*
+ * 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 int envelope_detector_channel_raw_max(struct iio_channel *ch)
+{
+ struct iio_dev *indio_dev = ch->indio_dev;
+ const int *vals;
+ int type;
+ int len;
+ int ret;
+
+ if (!ch->indio_dev->info->read_avail)
+ return -EINVAL;
+
+ ret = ch->indio_dev->info->read_avail(indio_dev, ch->channel,
+ &vals, &type, &len,
+ IIO_CHAN_INFO_RAW);
+
+ if (ret < 0)
+ return ret;
+ if (type != IIO_VAL_INT)
+ return -EINVAL;
+
+ switch (ret) {
+ case IIO_AVAIL_RANGE:
+ if (len != 3)
+ return -EINVAL;
+ return vals[2];
+ }
+ return -EINVAL;
+}
+
+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),
+ .indexed = 1,
+};
+
+static ssize_t envelope_show_invert(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct envelope *env = iio_priv(indio_dev);
+
+ return sprintf(buf, "%u\n", env->invert);
+}
+
+static ssize_t envelope_store_invert(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ 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 IIO_DEVICE_ATTR(invert, 0644,
+ envelope_show_invert,
+ envelope_store_invert, 0);
+
+static ssize_t envelope_show_comp_interval(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct envelope *env = iio_priv(indio_dev);
+
+ return sprintf(buf, "%u\n", env->comp_interval);
+}
+
+static ssize_t envelope_store_comp_interval(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ 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 IIO_DEVICE_ATTR(compare_interval_ms, 0644,
+ envelope_show_comp_interval,
+ envelope_store_comp_interval, 0);
+
+static struct attribute *envelope_detector_attributes[] = {
+ &iio_dev_attr_invert.dev_attr.attr,
+ &iio_dev_attr_compare_interval_ms.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group envelope_detector_attribute_group = {
+ .attrs = envelope_detector_attributes,
+};
+
+static const struct iio_info envelope_detector_info = {
+ .read_raw = &envelope_detector_read_raw,
+ .driver_module = THIS_MODULE,
+ .attrs = &envelope_detector_attribute_group,
+};
+
+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 = envelope_detector_channel_raw_max(env->dac);
+ if (ret < 0) {
+ dev_err(dev, "dac does not indicate its raw maximum value\n");
+ return ret;
+ }
+ env->dac_max = 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");
--
2.1.4
^ permalink raw reply related
* [PATCH v2 6/7] dt-bindings: iio: document envelope-detector bindings
From: Peter Rosin @ 2016-10-22 22:43 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Peter Rosin, Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Rob Herring, Mark Rutland,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1477176226-10566-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
---
.../bindings/iio/adc/envelope-detector.txt | 55 ++++++++++++++++++++++
MAINTAINERS | 6 +++
2 files changed, 61 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..9099531cffff
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
@@ -0,0 +1,55 @@
+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";
+ dpot-dac,max-ohms = <100000>;
+ #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 8c8aae24b96b..4b6f6ec1b703 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6118,6 +6118,12 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
F: drivers/iio/dac/dpot-dac.c
+IIO ENVELOPE DETECTOR
+M: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
+L: linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
+
IIO SUBSYSTEM AND DRIVERS
M: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
R: Hartmut Knaack <knaack.h-Mmb7MZpHnFY@public.gmane.org>
--
2.1.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 5/7] iio: dpot-dac: DAC driver based on a digital potentiometer
From: Peter Rosin @ 2016-10-22 22:43 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Peter Rosin, Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Rob Herring, Mark Rutland,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1477176226-10566-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
It is assumed the 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>
---
MAINTAINERS | 1 +
drivers/iio/dac/Kconfig | 10 ++
drivers/iio/dac/Makefile | 1 +
drivers/iio/dac/dpot-dac.c | 298 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 310 insertions(+)
create mode 100644 drivers/iio/dac/dpot-dac.c
diff --git a/MAINTAINERS b/MAINTAINERS
index c68b72088945..8c8aae24b96b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6116,6 +6116,7 @@ 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
+F: drivers/iio/dac/dpot-dac.c
IIO SUBSYSTEM AND DRIVERS
M: Jonathan Cameron <jic23-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.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..5613eae32347
--- /dev/null
+++ b/drivers/iio/dac/dpot-dac.c
@@ -0,0 +1,298 @@
+/*
+ * IIO DAC emulation driver using a digital potentiometer
+ *
+ * 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.
+ */
+
+/*
+ * 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);
+ struct iio_dev *dpot_dev = dac->dpot->indio_dev;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return dpot_dev->info->read_avail(dpot_dev, dac->dpot->channel,
+ vals, type, length, mask);
+ }
+
+ 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);
+ struct iio_channel *ch = dac->dpot;
+ struct iio_dev *iio_ch_dev = ch->indio_dev;
+ const int *vals;
+ unsigned long long tmp;
+ int type;
+ int len;
+ int ret;
+ int val1;
+ int val2;
+ int max;
+
+ if (!iio_ch_dev->info->read_avail) {
+ dev_err(dev, "dpot does not provide available raw values\n");
+ return -EINVAL;
+ }
+
+ ret = iio_ch_dev->info->read_avail(iio_ch_dev, ch->channel,
+ &vals, &type, &len,
+ IIO_CHAN_INFO_RAW);
+
+ if (ret < 0) {
+ dev_err(dev, "dpot failed to provide available raw values\n");
+ return ret;
+ }
+ if (type != IIO_VAL_INT) {
+ dev_err(dev, "dpot provides strange raw values\n");
+ return -EINVAL;
+ }
+
+ switch (ret) {
+ case IIO_AVAIL_RANGE:
+ if (len != 3) {
+ dev_err(dev, "need a range of available values\n");
+ return -EINVAL;
+ }
+ max = vals[2];
+ break;
+ default:
+ dev_err(dev, "dpot doesn't provide range of available values\n");
+ return -EINVAL;
+ }
+
+ switch (iio_read_channel_scale(dac->dpot, &val1, &val2)) {
+ case IIO_VAL_INT:
+ return max * val1;
+ case IIO_VAL_FRACTIONAL:
+ tmp = (unsigned long long)max * val1;
+ do_div(tmp, val2);
+ return tmp;
+ case IIO_VAL_FRACTIONAL_LOG2:
+ tmp = (s64)vals[0] * 1000000000LL * max >> vals[1];
+ 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);
+
+ 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-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
--
2.1.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH v2 4/7] dt-bindings: iio: document dpot-dac bindings
From: Peter Rosin @ 2016-10-22 22:43 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Rob Herring, Mark Rutland, linux-iio,
devicetree
In-Reply-To: <1477176226-10566-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
.../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..4fd5d63cc2d6
--- /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 the 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 1cd38a7e0064..c68b72088945 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6111,6 +6111,12 @@ L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/rc/iguanair.c
+IIO DIGITAL POTENTIOMETER DAC
+M: Peter Rosin <peda@axentia.se>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/dac/dpot-dac.txt
+
IIO SUBSYSTEM AND DRIVERS
M: Jonathan Cameron <jic23@kernel.org>
R: Hartmut Knaack <knaack.h@gmx.de>
--
2.1.4
^ permalink raw reply related
* [PATCH v2 3/7] dt-bindings: add axentia to vendor-prefixes
From: Peter Rosin @ 2016-10-22 22:43 UTC (permalink / raw)
To: linux-kernel
Cc: Peter Rosin, Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Rob Herring, Mark Rutland, linux-iio,
devicetree
In-Reply-To: <1477176226-10566-1-git-send-email-peda@axentia.se>
Signed-off-by: Peter Rosin <peda@axentia.se>
---
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 f0a48ea78659..a437120a7eee 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -39,6 +39,7 @@ auo AU Optronics Corporation
auvidea Auvidea GmbH
avago Avago Technologies
avic Shanghai AVIC Optoelectronics Co., Ltd.
+axentia Axentia Technologies AB
axis Axis Communications AB
boe BOE Technology Group Co., Ltd.
bosch Bosch Sensortec GmbH
--
2.1.4
^ permalink raw reply related
* [PATCH v2 2/7] iio: mcp4531: provide range of available raw values
From: Peter Rosin @ 2016-10-22 22:43 UTC (permalink / raw)
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: Peter Rosin, Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, Rob Herring, Mark Rutland,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1477176226-10566-1-git-send-email-peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
Signed-off-by: Peter Rosin <peda-koto5C5qi+TLoDKTGw+V6w@public.gmane.org>
---
drivers/iio/potentiometer/mcp4531.c | 104 +++++++++++++++++++++---------------
1 file changed, 62 insertions(+), 42 deletions(-)
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,
};
--
2.1.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
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