From: Jonathan Cameron <jic23@kernel.org>
To: Quentin Schulz <quentin.schulz@free-electrons.com>,
knaack.h@gmx.de, lars@metafoo.de, pmeerw@pmeerw.net,
robh+dt@kernel.org, mark.rutland@arm.com, wens@csie.org,
sre@kernel.org, linux@armlinux.org.uk,
maxime.ripard@free-electrons.com, lee.jones@linaro.org
Cc: linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
thomas.petazzoni@free-electrons.com, icenowy@aosc.xyz,
bonbons@linux-vserver.org
Subject: Re: [PATCH 03/22] iio: adc: add support for X-Powers AXP20X and AXP22X PMICs ADCs
Date: Sat, 7 Jan 2017 14:13:41 -0500 [thread overview]
Message-ID: <7c1c045f-892c-e384-1757-60bef66ec470@kernel.org> (raw)
In-Reply-To: <20170102163723.7939-4-quentin.schulz@free-electrons.com>
On 02/01/17 11:37, Quentin Schulz wrote:
> The X-Powers AXP20X and AXP22X PMICs have multiple ADCs. They expose the
> battery voltage, battery charge and discharge currents, AC-in and VBUS
> voltages and currents, 2 GPIOs muxable in ADC mode and PMIC temperature.
>
> This adds support for most of AXP20X and AXP22X ADCs.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Couple of little bits from me. Peter got the bigger stuff.
Jonathan
> ---
> drivers/iio/adc/Kconfig | 10 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/axp20x_adc.c | 490 +++++++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/axp20x.h | 4 +
> 4 files changed, 505 insertions(+)
> create mode 100644 drivers/iio/adc/axp20x_adc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 38bc319..5c5b51f 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -154,6 +154,16 @@ config AT91_SAMA5D2_ADC
> To compile this driver as a module, choose M here: the module will be
> called at91-sama5d2_adc.
>
> +config AXP20X_ADC
> + tristate "X-Powers AXP20X and AXP22X ADC driver"
> + depends on MFD_AXP20X
> + help
> + Say yes here to have support for X-Powers power management IC (PMIC)
> + AXP20X and AXP22X ADC devices.
> +
> + To compile this driver as a module, choose M here: the module will be
> + called axp20x_adc.
> +
> config AXP288_ADC
> tristate "X-Powers AXP288 ADC driver"
> depends on MFD_AXP20X
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index d36c4be..f5c28a5 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -16,6 +16,7 @@ obj-$(CONFIG_AD7887) += ad7887.o
> obj-$(CONFIG_AD799X) += ad799x.o
> obj-$(CONFIG_AT91_ADC) += at91_adc.o
> obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
> +obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
> new file mode 100644
> index 0000000..8df972a
> --- /dev/null
> +++ b/drivers/iio/adc/axp20x_adc.c
> @@ -0,0 +1,490 @@
> +/* ADC driver for AXP20X and AXP22X PMICs
> + *
> + * Copyright (c) 2016 Free Electrons NextThing Co.
> + * Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
Pet hate : why a blank line here? ;) I'm grumpy - my flight home is delayed.
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/thermal.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/mfd/axp20x.h>
> +
> +#define AXP20X_ADC_EN1_MASK GENMASK(7, 0)
> +
> +#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7))
> +#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
> +#define AXP20X_ADC_EN2_TEMP_ADC BIT(7)
> +#define AXP20X_ADC_EN2_GPIO0_ADC BIT(3)
> +#define AXP20X_ADC_EN2_GPIO1_ADC BIT(2)
> +
> +#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
> +#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
> +#define AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(x) ((x) & BIT(0))
> +#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1)
> +
> +#define AXP20X_ADC_RATE_MASK (3 << 6)
> +#define AXP20X_ADC_RATE_25HZ (0 << 6)
> +#define AXP20X_ADC_RATE_50HZ BIT(6)
> +#define AXP20X_ADC_RATE_100HZ (2 << 6)
> +#define AXP20X_ADC_RATE_200HZ (3 << 6)
> +
> +#define AXP22X_ADC_RATE_100HZ (0 << 6)
> +#define AXP22X_ADC_RATE_200HZ BIT(6)
> +#define AXP22X_ADC_RATE_400HZ (2 << 6)
> +#define AXP22X_ADC_RATE_800HZ (3 << 6)
> +
> +#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \
> + { \
> + .type = _type, \
> + .indexed = 1, \
> + .channel = _channel, \
> + .address = _reg, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_SCALE), \
> + .datasheet_name = _name, \
> + }
> +
> +#define AXP20X_ADC_CHANNEL_OFFSET(_channel, _name, _type, _reg) \
> + { \
> + .type = _type, \
> + .indexed = 1, \
> + .channel = _channel, \
> + .address = _reg, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_SCALE) |\
> + BIT(IIO_CHAN_INFO_OFFSET),\
> + .datasheet_name = _name, \
> + }
> +
> +struct axp20x_adc_iio {
Why? I'm not seeing this used anywhere.
> + struct iio_dev *indio_dev;
> + struct regmap *regmap;
> +};
> +
> +enum axp20x_adc_channel {
> + AXP20X_ACIN_V = 0,
> + AXP20X_ACIN_I,
> + AXP20X_VBUS_V,
> + AXP20X_VBUS_I,
> + AXP20X_TEMP_ADC,
> + AXP20X_GPIO0_V,
> + AXP20X_GPIO1_V,
> + AXP20X_BATT_V,
> + AXP20X_BATT_CHRG_I,
> + AXP20X_BATT_DISCHRG_I,
> + AXP20X_IPSOUT_V,
> +};
> +
> +enum axp22x_adc_channel {
> + AXP22X_TEMP_ADC = 0,
> + AXP22X_BATT_V,
> + AXP22X_BATT_CHRG_I,
> + AXP22X_BATT_DISCHRG_I,
> +};
> +
> +static const struct iio_chan_spec axp20x_adc_channels[] = {
> + AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE,
> + AXP20X_ACIN_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_ACIN_I, "acin_i", IIO_CURRENT,
> + AXP20X_ACIN_I_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_VBUS_V, "vbus_v", IIO_VOLTAGE,
> + AXP20X_VBUS_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_VBUS_I, "vbus_i", IIO_CURRENT,
> + AXP20X_VBUS_I_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_TEMP_ADC, "temp_adc", IIO_TEMP,
> + AXP20X_TEMP_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
> + AXP20X_GPIO0_V_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO1_V, "gpio1_v", IIO_VOLTAGE,
> + AXP20X_GPIO1_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_BATT_V, "batt_v", IIO_VOLTAGE,
> + AXP20X_BATT_V_H),
> + AXP20X_ADC_CHANNEL(AXP20X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
> + AXP20X_BATT_CHRG_I_H),
> + AXP20X_ADC_CHANNEL(AXP20X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
> + AXP20X_BATT_DISCHRG_I_H),
> + AXP20X_ADC_CHANNEL(AXP20X_IPSOUT_V, "ipsout_v", IIO_VOLTAGE,
> + AXP20X_IPSOUT_V_HIGH_H),
> +};
> +
> +static const struct iio_chan_spec axp22x_adc_channels[] = {
> + AXP20X_ADC_CHANNEL_OFFSET(AXP22X_TEMP_ADC, "temp_adc", IIO_TEMP,
> + AXP22X_TEMP_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP22X_BATT_V, "batt_v", IIO_VOLTAGE,
> + AXP20X_BATT_V_H),
> + AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
> + AXP20X_BATT_CHRG_I_H),
> + AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
> + AXP20X_BATT_DISCHRG_I_H),
> +};
> +
> +static int axp20x_adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *channel, int *val,
> + int *val2)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + int size = 12, ret;
> +
> + switch (channel->channel) {
> + case AXP20X_BATT_DISCHRG_I:
> + size = 13;
> + case AXP20X_ACIN_V:
> + case AXP20X_ACIN_I:
> + case AXP20X_VBUS_V:
> + case AXP20X_VBUS_I:
> + case AXP20X_TEMP_ADC:
> + case AXP20X_BATT_V:
> + case AXP20X_BATT_CHRG_I:
> + case AXP20X_IPSOUT_V:
> + case AXP20X_GPIO0_V:
> + case AXP20X_GPIO1_V:
> + ret = axp20x_read_variable_width(info->regmap, channel->address,
> + size);
> + if (ret < 0)
> + return ret;
> + *val = ret;
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp22x_adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *channel, int *val,
> + int *val2)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + int size = 12, ret;
> +
> + switch (channel->channel) {
> + case AXP22X_BATT_DISCHRG_I:
> + size = 13;
> + case AXP22X_TEMP_ADC:
> + case AXP22X_BATT_V:
> + case AXP22X_BATT_CHRG_I:
> + ret = axp20x_read_variable_width(info->regmap, channel->address,
> + size);
> + if (ret < 0)
> + return ret;
> + *val = ret;
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_adc_scale(int channel, int *val, int *val2)
> +{
> + switch (channel) {
> + case AXP20X_ACIN_V:
> + case AXP20X_VBUS_V:
> + *val = 1;
> + *val2 = 700000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_ACIN_I:
> + *val = 0;
> + *val2 = 625000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_VBUS_I:
> + *val = 0;
> + *val2 = 375000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_TEMP_ADC:
> + *val = 100;
> + return IIO_VAL_INT;
> +
> + case AXP20X_GPIO0_V:
> + case AXP20X_GPIO1_V:
> + *val = 0;
> + *val2 = 500000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_BATT_V:
> + *val = 1;
> + *val2 = 100000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_BATT_DISCHRG_I:
> + case AXP20X_BATT_CHRG_I:
> + *val = 0;
> + *val2 = 500000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_IPSOUT_V:
> + *val = 1;
> + *val2 = 400000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp22x_adc_scale(int channel, int *val, int *val2)
> +{
> + switch (channel) {
> + case AXP22X_TEMP_ADC:
> + *val = 100;
> + return IIO_VAL_INT;
> +
> + case AXP22X_BATT_V:
> + *val = 1;
> + *val2 = 100000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP22X_BATT_DISCHRG_I:
> + case AXP22X_BATT_CHRG_I:
> + *val = 0;
> + *val2 = 500000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_adc_offset(struct iio_dev *indio_dev, int channel, int *val)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + int ret, reg;
> +
> + switch (channel) {
> + case AXP20X_TEMP_ADC:
> + *val = -1447;
> + return IIO_VAL_INT;
> +
> + case AXP20X_GPIO0_V:
> + case AXP20X_GPIO1_V:
> + ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, ®);
> + if (ret < 0)
> + return ret;
> +
> + if (channel == AXP20X_GPIO0_V)
> + *val = reg & AXP20X_GPIO10_IN_RANGE_GPIO0;
> + else
> + *val = reg & AXP20X_GPIO10_IN_RANGE_GPIO1;
> +
> + *val = !!(*val) * 700000;
> +
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + switch (mask) {
> + case IIO_CHAN_INFO_OFFSET:
> + return axp20x_adc_offset(indio_dev, chan->channel, val);
> +
> + case IIO_CHAN_INFO_SCALE:
> + return axp20x_adc_scale(chan->channel, val, val2);
> +
> + case IIO_CHAN_INFO_RAW:
> + return axp20x_adc_read_raw(indio_dev, chan, val, val2);
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp22x_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + switch (mask) {
> + case IIO_CHAN_INFO_OFFSET:
> + *val = -2667;
> + return IIO_VAL_INT;
> +
> + case IIO_CHAN_INFO_SCALE:
> + return axp22x_adc_scale(chan->channel, val, val2);
> +
> + case IIO_CHAN_INFO_RAW:
> + return axp22x_adc_read_raw(indio_dev, chan, val, val2);
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int val, int val2,
> + long mask)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> +
> + /*
> + * The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets
> + * for (independently) GPIO0 and GPIO1 when in ADC mode.
> + */
> + if (mask != IIO_CHAN_INFO_OFFSET)
> + return -EINVAL;
> +
> + if (chan->channel != AXP20X_GPIO0_V && chan->channel != AXP20X_GPIO1_V)
> + return -EINVAL;
> +
> + if (val != 0 && val != 700000)
> + return -EINVAL;
> +
> + if (chan->channel == AXP20X_GPIO0_V)
> + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE,
> + AXP20X_GPIO10_IN_RANGE_GPIO0,
> + AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(!!val));
> +
> + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE,
> + AXP20X_GPIO10_IN_RANGE_GPIO1,
> + AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(!!val));
> +}
> +
> +static const struct iio_info axp20x_adc_iio_info = {
> + .read_raw = axp20x_read_raw,
> + .write_raw = axp20x_write_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static const struct iio_info axp22x_adc_iio_info = {
> + .read_raw = axp22x_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static const struct of_device_id axp20x_adc_of_match[] = {
> + { .compatible = "x-powers,axp209-adc", .data = (void *)AXP209_ID, },
> + { .compatible = "x-powers,axp221-adc", .data = (void *)AXP221_ID, },
> + { /* sentinel */ },
> +};
> +
> +static int axp20x_probe(struct platform_device *pdev)
> +{
> + struct axp20x_adc_iio *info;
> + struct iio_dev *indio_dev;
> + struct axp20x_dev *axp20x_dev;
> + int ret, axp20x_id;
> +
> + axp20x_dev = dev_get_drvdata(pdev->dev.parent);
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + info = iio_priv(indio_dev);
> + platform_set_drvdata(pdev, indio_dev);
> +
> + info->regmap = axp20x_dev->regmap;
> + info->indio_dev = indio_dev;
> + indio_dev->name = dev_name(&pdev->dev);
> + indio_dev->dev.parent = &pdev->dev;
> + indio_dev->dev.of_node = pdev->dev.of_node;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + axp20x_id = (int)of_device_get_match_data(&pdev->dev);
> +
> + switch (axp20x_id) {
> + case AXP209_ID:
> + indio_dev->info = &axp20x_adc_iio_info;
> + indio_dev->num_channels = ARRAY_SIZE(axp20x_adc_channels);
> + indio_dev->channels = axp20x_adc_channels;
> +
> + /* Enable the ADCs on IP */
> + regmap_write(info->regmap, AXP20X_ADC_EN1, AXP20X_ADC_EN1_MASK);
> +
> + /* Enable GPIO0/1 and internal temperature ADCs */
> + regmap_update_bits(info->regmap, AXP20X_ADC_EN2,
> + AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
> +
> + /* Configure ADCs rate */
> + regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
> + AXP20X_ADC_RATE_MASK, AXP20X_ADC_RATE_50HZ);
> + break;
> +
> + case AXP221_ID:
> + indio_dev->info = &axp22x_adc_iio_info;
> + indio_dev->num_channels = ARRAY_SIZE(axp22x_adc_channels);
> + indio_dev->channels = axp22x_adc_channels;
> +
> + /* Enable the ADCs on IP */
> + regmap_write(info->regmap, AXP20X_ADC_EN1, AXP22X_ADC_EN1_MASK);
> +
> + /* Configure ADCs rate */
> + regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
> + AXP20X_ADC_RATE_MASK, AXP22X_ADC_RATE_200HZ);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + ret = devm_iio_device_register(&pdev->dev, indio_dev);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "could not register the device\n");
> + regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> + regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int axp20x_remove(struct platform_device *pdev)
> +{
> + struct axp20x_adc_iio *info;
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> + info = iio_priv(indio_dev);
> + regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> + regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> +
> + return 0;
> +}
> +
> +static struct platform_driver axp20x_adc_driver = {
> + .driver = {
> + .name = "axp20x-adc",
> + .of_match_table = axp20x_adc_of_match,
> + },
> + .probe = axp20x_probe,
> + .remove = axp20x_remove,
> +};
> +
> +module_platform_driver(axp20x_adc_driver);
> +
> +MODULE_DESCRIPTION("ADC driver for AXP20X and AXP22X PMICs");
> +MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
> index a4860bc..650c6f6 100644
> --- a/include/linux/mfd/axp20x.h
> +++ b/include/linux/mfd/axp20x.h
> @@ -150,6 +150,10 @@ enum {
> #define AXP20X_VBUS_I_ADC_L 0x5d
> #define AXP20X_TEMP_ADC_H 0x5e
> #define AXP20X_TEMP_ADC_L 0x5f
> +
> +#define AXP22X_TEMP_ADC_H 0x56
> +#define AXP22X_TEMP_ADC_L 0x57
> +
> #define AXP20X_TS_IN_H 0x62
> #define AXP20X_TS_IN_L 0x63
> #define AXP20X_GPIO0_V_ADC_H 0x64
>
WARNING: multiple messages have this Message-ID (diff)
From: Jonathan Cameron <jic23@kernel.org>
To: Quentin Schulz <quentin.schulz@free-electrons.com>,
knaack.h@gmx.de, lars@metafoo.de, pmeerw@pmeerw.net,
robh+dt@kernel.org, mark.rutland@arm.com, wens@csie.org,
sre@kernel.org, linux@armlinux.org.uk,
maxime.ripard@free-electrons.com, lee.jones@linaro.org
Cc: thomas.petazzoni@free-electrons.com, devicetree@vger.kernel.org,
linux-pm@vger.kernel.org, linux-iio@vger.kernel.org,
linux-kernel@vger.kernel.org, bonbons@linux-vserver.org,
icenowy@aosc.xyz, linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH 03/22] iio: adc: add support for X-Powers AXP20X and AXP22X PMICs ADCs
Date: Sat, 7 Jan 2017 14:13:41 -0500 [thread overview]
Message-ID: <7c1c045f-892c-e384-1757-60bef66ec470@kernel.org> (raw)
In-Reply-To: <20170102163723.7939-4-quentin.schulz@free-electrons.com>
On 02/01/17 11:37, Quentin Schulz wrote:
> The X-Powers AXP20X and AXP22X PMICs have multiple ADCs. They expose the
> battery voltage, battery charge and discharge currents, AC-in and VBUS
> voltages and currents, 2 GPIOs muxable in ADC mode and PMIC temperature.
>
> This adds support for most of AXP20X and AXP22X ADCs.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Couple of little bits from me. Peter got the bigger stuff.
Jonathan
> ---
> drivers/iio/adc/Kconfig | 10 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/axp20x_adc.c | 490 +++++++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/axp20x.h | 4 +
> 4 files changed, 505 insertions(+)
> create mode 100644 drivers/iio/adc/axp20x_adc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 38bc319..5c5b51f 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -154,6 +154,16 @@ config AT91_SAMA5D2_ADC
> To compile this driver as a module, choose M here: the module will be
> called at91-sama5d2_adc.
>
> +config AXP20X_ADC
> + tristate "X-Powers AXP20X and AXP22X ADC driver"
> + depends on MFD_AXP20X
> + help
> + Say yes here to have support for X-Powers power management IC (PMIC)
> + AXP20X and AXP22X ADC devices.
> +
> + To compile this driver as a module, choose M here: the module will be
> + called axp20x_adc.
> +
> config AXP288_ADC
> tristate "X-Powers AXP288 ADC driver"
> depends on MFD_AXP20X
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index d36c4be..f5c28a5 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -16,6 +16,7 @@ obj-$(CONFIG_AD7887) += ad7887.o
> obj-$(CONFIG_AD799X) += ad799x.o
> obj-$(CONFIG_AT91_ADC) += at91_adc.o
> obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
> +obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
> new file mode 100644
> index 0000000..8df972a
> --- /dev/null
> +++ b/drivers/iio/adc/axp20x_adc.c
> @@ -0,0 +1,490 @@
> +/* ADC driver for AXP20X and AXP22X PMICs
> + *
> + * Copyright (c) 2016 Free Electrons NextThing Co.
> + * Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
Pet hate : why a blank line here? ;) I'm grumpy - my flight home is delayed.
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/thermal.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/mfd/axp20x.h>
> +
> +#define AXP20X_ADC_EN1_MASK GENMASK(7, 0)
> +
> +#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7))
> +#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
> +#define AXP20X_ADC_EN2_TEMP_ADC BIT(7)
> +#define AXP20X_ADC_EN2_GPIO0_ADC BIT(3)
> +#define AXP20X_ADC_EN2_GPIO1_ADC BIT(2)
> +
> +#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
> +#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
> +#define AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(x) ((x) & BIT(0))
> +#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1)
> +
> +#define AXP20X_ADC_RATE_MASK (3 << 6)
> +#define AXP20X_ADC_RATE_25HZ (0 << 6)
> +#define AXP20X_ADC_RATE_50HZ BIT(6)
> +#define AXP20X_ADC_RATE_100HZ (2 << 6)
> +#define AXP20X_ADC_RATE_200HZ (3 << 6)
> +
> +#define AXP22X_ADC_RATE_100HZ (0 << 6)
> +#define AXP22X_ADC_RATE_200HZ BIT(6)
> +#define AXP22X_ADC_RATE_400HZ (2 << 6)
> +#define AXP22X_ADC_RATE_800HZ (3 << 6)
> +
> +#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \
> + { \
> + .type = _type, \
> + .indexed = 1, \
> + .channel = _channel, \
> + .address = _reg, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_SCALE), \
> + .datasheet_name = _name, \
> + }
> +
> +#define AXP20X_ADC_CHANNEL_OFFSET(_channel, _name, _type, _reg) \
> + { \
> + .type = _type, \
> + .indexed = 1, \
> + .channel = _channel, \
> + .address = _reg, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_SCALE) |\
> + BIT(IIO_CHAN_INFO_OFFSET),\
> + .datasheet_name = _name, \
> + }
> +
> +struct axp20x_adc_iio {
Why? I'm not seeing this used anywhere.
> + struct iio_dev *indio_dev;
> + struct regmap *regmap;
> +};
> +
> +enum axp20x_adc_channel {
> + AXP20X_ACIN_V = 0,
> + AXP20X_ACIN_I,
> + AXP20X_VBUS_V,
> + AXP20X_VBUS_I,
> + AXP20X_TEMP_ADC,
> + AXP20X_GPIO0_V,
> + AXP20X_GPIO1_V,
> + AXP20X_BATT_V,
> + AXP20X_BATT_CHRG_I,
> + AXP20X_BATT_DISCHRG_I,
> + AXP20X_IPSOUT_V,
> +};
> +
> +enum axp22x_adc_channel {
> + AXP22X_TEMP_ADC = 0,
> + AXP22X_BATT_V,
> + AXP22X_BATT_CHRG_I,
> + AXP22X_BATT_DISCHRG_I,
> +};
> +
> +static const struct iio_chan_spec axp20x_adc_channels[] = {
> + AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE,
> + AXP20X_ACIN_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_ACIN_I, "acin_i", IIO_CURRENT,
> + AXP20X_ACIN_I_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_VBUS_V, "vbus_v", IIO_VOLTAGE,
> + AXP20X_VBUS_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_VBUS_I, "vbus_i", IIO_CURRENT,
> + AXP20X_VBUS_I_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_TEMP_ADC, "temp_adc", IIO_TEMP,
> + AXP20X_TEMP_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
> + AXP20X_GPIO0_V_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO1_V, "gpio1_v", IIO_VOLTAGE,
> + AXP20X_GPIO1_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_BATT_V, "batt_v", IIO_VOLTAGE,
> + AXP20X_BATT_V_H),
> + AXP20X_ADC_CHANNEL(AXP20X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
> + AXP20X_BATT_CHRG_I_H),
> + AXP20X_ADC_CHANNEL(AXP20X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
> + AXP20X_BATT_DISCHRG_I_H),
> + AXP20X_ADC_CHANNEL(AXP20X_IPSOUT_V, "ipsout_v", IIO_VOLTAGE,
> + AXP20X_IPSOUT_V_HIGH_H),
> +};
> +
> +static const struct iio_chan_spec axp22x_adc_channels[] = {
> + AXP20X_ADC_CHANNEL_OFFSET(AXP22X_TEMP_ADC, "temp_adc", IIO_TEMP,
> + AXP22X_TEMP_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP22X_BATT_V, "batt_v", IIO_VOLTAGE,
> + AXP20X_BATT_V_H),
> + AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
> + AXP20X_BATT_CHRG_I_H),
> + AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
> + AXP20X_BATT_DISCHRG_I_H),
> +};
> +
> +static int axp20x_adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *channel, int *val,
> + int *val2)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + int size = 12, ret;
> +
> + switch (channel->channel) {
> + case AXP20X_BATT_DISCHRG_I:
> + size = 13;
> + case AXP20X_ACIN_V:
> + case AXP20X_ACIN_I:
> + case AXP20X_VBUS_V:
> + case AXP20X_VBUS_I:
> + case AXP20X_TEMP_ADC:
> + case AXP20X_BATT_V:
> + case AXP20X_BATT_CHRG_I:
> + case AXP20X_IPSOUT_V:
> + case AXP20X_GPIO0_V:
> + case AXP20X_GPIO1_V:
> + ret = axp20x_read_variable_width(info->regmap, channel->address,
> + size);
> + if (ret < 0)
> + return ret;
> + *val = ret;
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp22x_adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *channel, int *val,
> + int *val2)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + int size = 12, ret;
> +
> + switch (channel->channel) {
> + case AXP22X_BATT_DISCHRG_I:
> + size = 13;
> + case AXP22X_TEMP_ADC:
> + case AXP22X_BATT_V:
> + case AXP22X_BATT_CHRG_I:
> + ret = axp20x_read_variable_width(info->regmap, channel->address,
> + size);
> + if (ret < 0)
> + return ret;
> + *val = ret;
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_adc_scale(int channel, int *val, int *val2)
> +{
> + switch (channel) {
> + case AXP20X_ACIN_V:
> + case AXP20X_VBUS_V:
> + *val = 1;
> + *val2 = 700000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_ACIN_I:
> + *val = 0;
> + *val2 = 625000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_VBUS_I:
> + *val = 0;
> + *val2 = 375000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_TEMP_ADC:
> + *val = 100;
> + return IIO_VAL_INT;
> +
> + case AXP20X_GPIO0_V:
> + case AXP20X_GPIO1_V:
> + *val = 0;
> + *val2 = 500000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_BATT_V:
> + *val = 1;
> + *val2 = 100000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_BATT_DISCHRG_I:
> + case AXP20X_BATT_CHRG_I:
> + *val = 0;
> + *val2 = 500000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_IPSOUT_V:
> + *val = 1;
> + *val2 = 400000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp22x_adc_scale(int channel, int *val, int *val2)
> +{
> + switch (channel) {
> + case AXP22X_TEMP_ADC:
> + *val = 100;
> + return IIO_VAL_INT;
> +
> + case AXP22X_BATT_V:
> + *val = 1;
> + *val2 = 100000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP22X_BATT_DISCHRG_I:
> + case AXP22X_BATT_CHRG_I:
> + *val = 0;
> + *val2 = 500000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_adc_offset(struct iio_dev *indio_dev, int channel, int *val)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + int ret, reg;
> +
> + switch (channel) {
> + case AXP20X_TEMP_ADC:
> + *val = -1447;
> + return IIO_VAL_INT;
> +
> + case AXP20X_GPIO0_V:
> + case AXP20X_GPIO1_V:
> + ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, ®);
> + if (ret < 0)
> + return ret;
> +
> + if (channel == AXP20X_GPIO0_V)
> + *val = reg & AXP20X_GPIO10_IN_RANGE_GPIO0;
> + else
> + *val = reg & AXP20X_GPIO10_IN_RANGE_GPIO1;
> +
> + *val = !!(*val) * 700000;
> +
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + switch (mask) {
> + case IIO_CHAN_INFO_OFFSET:
> + return axp20x_adc_offset(indio_dev, chan->channel, val);
> +
> + case IIO_CHAN_INFO_SCALE:
> + return axp20x_adc_scale(chan->channel, val, val2);
> +
> + case IIO_CHAN_INFO_RAW:
> + return axp20x_adc_read_raw(indio_dev, chan, val, val2);
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp22x_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + switch (mask) {
> + case IIO_CHAN_INFO_OFFSET:
> + *val = -2667;
> + return IIO_VAL_INT;
> +
> + case IIO_CHAN_INFO_SCALE:
> + return axp22x_adc_scale(chan->channel, val, val2);
> +
> + case IIO_CHAN_INFO_RAW:
> + return axp22x_adc_read_raw(indio_dev, chan, val, val2);
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int val, int val2,
> + long mask)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> +
> + /*
> + * The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets
> + * for (independently) GPIO0 and GPIO1 when in ADC mode.
> + */
> + if (mask != IIO_CHAN_INFO_OFFSET)
> + return -EINVAL;
> +
> + if (chan->channel != AXP20X_GPIO0_V && chan->channel != AXP20X_GPIO1_V)
> + return -EINVAL;
> +
> + if (val != 0 && val != 700000)
> + return -EINVAL;
> +
> + if (chan->channel == AXP20X_GPIO0_V)
> + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE,
> + AXP20X_GPIO10_IN_RANGE_GPIO0,
> + AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(!!val));
> +
> + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE,
> + AXP20X_GPIO10_IN_RANGE_GPIO1,
> + AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(!!val));
> +}
> +
> +static const struct iio_info axp20x_adc_iio_info = {
> + .read_raw = axp20x_read_raw,
> + .write_raw = axp20x_write_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static const struct iio_info axp22x_adc_iio_info = {
> + .read_raw = axp22x_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static const struct of_device_id axp20x_adc_of_match[] = {
> + { .compatible = "x-powers,axp209-adc", .data = (void *)AXP209_ID, },
> + { .compatible = "x-powers,axp221-adc", .data = (void *)AXP221_ID, },
> + { /* sentinel */ },
> +};
> +
> +static int axp20x_probe(struct platform_device *pdev)
> +{
> + struct axp20x_adc_iio *info;
> + struct iio_dev *indio_dev;
> + struct axp20x_dev *axp20x_dev;
> + int ret, axp20x_id;
> +
> + axp20x_dev = dev_get_drvdata(pdev->dev.parent);
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + info = iio_priv(indio_dev);
> + platform_set_drvdata(pdev, indio_dev);
> +
> + info->regmap = axp20x_dev->regmap;
> + info->indio_dev = indio_dev;
> + indio_dev->name = dev_name(&pdev->dev);
> + indio_dev->dev.parent = &pdev->dev;
> + indio_dev->dev.of_node = pdev->dev.of_node;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + axp20x_id = (int)of_device_get_match_data(&pdev->dev);
> +
> + switch (axp20x_id) {
> + case AXP209_ID:
> + indio_dev->info = &axp20x_adc_iio_info;
> + indio_dev->num_channels = ARRAY_SIZE(axp20x_adc_channels);
> + indio_dev->channels = axp20x_adc_channels;
> +
> + /* Enable the ADCs on IP */
> + regmap_write(info->regmap, AXP20X_ADC_EN1, AXP20X_ADC_EN1_MASK);
> +
> + /* Enable GPIO0/1 and internal temperature ADCs */
> + regmap_update_bits(info->regmap, AXP20X_ADC_EN2,
> + AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
> +
> + /* Configure ADCs rate */
> + regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
> + AXP20X_ADC_RATE_MASK, AXP20X_ADC_RATE_50HZ);
> + break;
> +
> + case AXP221_ID:
> + indio_dev->info = &axp22x_adc_iio_info;
> + indio_dev->num_channels = ARRAY_SIZE(axp22x_adc_channels);
> + indio_dev->channels = axp22x_adc_channels;
> +
> + /* Enable the ADCs on IP */
> + regmap_write(info->regmap, AXP20X_ADC_EN1, AXP22X_ADC_EN1_MASK);
> +
> + /* Configure ADCs rate */
> + regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
> + AXP20X_ADC_RATE_MASK, AXP22X_ADC_RATE_200HZ);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + ret = devm_iio_device_register(&pdev->dev, indio_dev);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "could not register the device\n");
> + regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> + regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int axp20x_remove(struct platform_device *pdev)
> +{
> + struct axp20x_adc_iio *info;
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> + info = iio_priv(indio_dev);
> + regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> + regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> +
> + return 0;
> +}
> +
> +static struct platform_driver axp20x_adc_driver = {
> + .driver = {
> + .name = "axp20x-adc",
> + .of_match_table = axp20x_adc_of_match,
> + },
> + .probe = axp20x_probe,
> + .remove = axp20x_remove,
> +};
> +
> +module_platform_driver(axp20x_adc_driver);
> +
> +MODULE_DESCRIPTION("ADC driver for AXP20X and AXP22X PMICs");
> +MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
> index a4860bc..650c6f6 100644
> --- a/include/linux/mfd/axp20x.h
> +++ b/include/linux/mfd/axp20x.h
> @@ -150,6 +150,10 @@ enum {
> #define AXP20X_VBUS_I_ADC_L 0x5d
> #define AXP20X_TEMP_ADC_H 0x5e
> #define AXP20X_TEMP_ADC_L 0x5f
> +
> +#define AXP22X_TEMP_ADC_H 0x56
> +#define AXP22X_TEMP_ADC_L 0x57
> +
> #define AXP20X_TS_IN_H 0x62
> #define AXP20X_TS_IN_L 0x63
> #define AXP20X_GPIO0_V_ADC_H 0x64
>
WARNING: multiple messages have this Message-ID (diff)
From: jic23@kernel.org (Jonathan Cameron)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 03/22] iio: adc: add support for X-Powers AXP20X and AXP22X PMICs ADCs
Date: Sat, 7 Jan 2017 14:13:41 -0500 [thread overview]
Message-ID: <7c1c045f-892c-e384-1757-60bef66ec470@kernel.org> (raw)
In-Reply-To: <20170102163723.7939-4-quentin.schulz@free-electrons.com>
On 02/01/17 11:37, Quentin Schulz wrote:
> The X-Powers AXP20X and AXP22X PMICs have multiple ADCs. They expose the
> battery voltage, battery charge and discharge currents, AC-in and VBUS
> voltages and currents, 2 GPIOs muxable in ADC mode and PMIC temperature.
>
> This adds support for most of AXP20X and AXP22X ADCs.
>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Couple of little bits from me. Peter got the bigger stuff.
Jonathan
> ---
> drivers/iio/adc/Kconfig | 10 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/axp20x_adc.c | 490 +++++++++++++++++++++++++++++++++++++++++++
> include/linux/mfd/axp20x.h | 4 +
> 4 files changed, 505 insertions(+)
> create mode 100644 drivers/iio/adc/axp20x_adc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 38bc319..5c5b51f 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -154,6 +154,16 @@ config AT91_SAMA5D2_ADC
> To compile this driver as a module, choose M here: the module will be
> called at91-sama5d2_adc.
>
> +config AXP20X_ADC
> + tristate "X-Powers AXP20X and AXP22X ADC driver"
> + depends on MFD_AXP20X
> + help
> + Say yes here to have support for X-Powers power management IC (PMIC)
> + AXP20X and AXP22X ADC devices.
> +
> + To compile this driver as a module, choose M here: the module will be
> + called axp20x_adc.
> +
> config AXP288_ADC
> tristate "X-Powers AXP288 ADC driver"
> depends on MFD_AXP20X
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index d36c4be..f5c28a5 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -16,6 +16,7 @@ obj-$(CONFIG_AD7887) += ad7887.o
> obj-$(CONFIG_AD799X) += ad799x.o
> obj-$(CONFIG_AT91_ADC) += at91_adc.o
> obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
> +obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
> obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
> obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
> diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
> new file mode 100644
> index 0000000..8df972a
> --- /dev/null
> +++ b/drivers/iio/adc/axp20x_adc.c
> @@ -0,0 +1,490 @@
> +/* ADC driver for AXP20X and AXP22X PMICs
> + *
> + * Copyright (c) 2016 Free Electrons NextThing Co.
> + * Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
Pet hate : why a blank line here? ;) I'm grumpy - my flight home is delayed.
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/thermal.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/mfd/axp20x.h>
> +
> +#define AXP20X_ADC_EN1_MASK GENMASK(7, 0)
> +
> +#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7))
> +#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
> +#define AXP20X_ADC_EN2_TEMP_ADC BIT(7)
> +#define AXP20X_ADC_EN2_GPIO0_ADC BIT(3)
> +#define AXP20X_ADC_EN2_GPIO1_ADC BIT(2)
> +
> +#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
> +#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
> +#define AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(x) ((x) & BIT(0))
> +#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1)
> +
> +#define AXP20X_ADC_RATE_MASK (3 << 6)
> +#define AXP20X_ADC_RATE_25HZ (0 << 6)
> +#define AXP20X_ADC_RATE_50HZ BIT(6)
> +#define AXP20X_ADC_RATE_100HZ (2 << 6)
> +#define AXP20X_ADC_RATE_200HZ (3 << 6)
> +
> +#define AXP22X_ADC_RATE_100HZ (0 << 6)
> +#define AXP22X_ADC_RATE_200HZ BIT(6)
> +#define AXP22X_ADC_RATE_400HZ (2 << 6)
> +#define AXP22X_ADC_RATE_800HZ (3 << 6)
> +
> +#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \
> + { \
> + .type = _type, \
> + .indexed = 1, \
> + .channel = _channel, \
> + .address = _reg, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_SCALE), \
> + .datasheet_name = _name, \
> + }
> +
> +#define AXP20X_ADC_CHANNEL_OFFSET(_channel, _name, _type, _reg) \
> + { \
> + .type = _type, \
> + .indexed = 1, \
> + .channel = _channel, \
> + .address = _reg, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_SCALE) |\
> + BIT(IIO_CHAN_INFO_OFFSET),\
> + .datasheet_name = _name, \
> + }
> +
> +struct axp20x_adc_iio {
Why? I'm not seeing this used anywhere.
> + struct iio_dev *indio_dev;
> + struct regmap *regmap;
> +};
> +
> +enum axp20x_adc_channel {
> + AXP20X_ACIN_V = 0,
> + AXP20X_ACIN_I,
> + AXP20X_VBUS_V,
> + AXP20X_VBUS_I,
> + AXP20X_TEMP_ADC,
> + AXP20X_GPIO0_V,
> + AXP20X_GPIO1_V,
> + AXP20X_BATT_V,
> + AXP20X_BATT_CHRG_I,
> + AXP20X_BATT_DISCHRG_I,
> + AXP20X_IPSOUT_V,
> +};
> +
> +enum axp22x_adc_channel {
> + AXP22X_TEMP_ADC = 0,
> + AXP22X_BATT_V,
> + AXP22X_BATT_CHRG_I,
> + AXP22X_BATT_DISCHRG_I,
> +};
> +
> +static const struct iio_chan_spec axp20x_adc_channels[] = {
> + AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE,
> + AXP20X_ACIN_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_ACIN_I, "acin_i", IIO_CURRENT,
> + AXP20X_ACIN_I_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_VBUS_V, "vbus_v", IIO_VOLTAGE,
> + AXP20X_VBUS_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_VBUS_I, "vbus_i", IIO_CURRENT,
> + AXP20X_VBUS_I_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_TEMP_ADC, "temp_adc", IIO_TEMP,
> + AXP20X_TEMP_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
> + AXP20X_GPIO0_V_ADC_H),
> + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO1_V, "gpio1_v", IIO_VOLTAGE,
> + AXP20X_GPIO1_V_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP20X_BATT_V, "batt_v", IIO_VOLTAGE,
> + AXP20X_BATT_V_H),
> + AXP20X_ADC_CHANNEL(AXP20X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
> + AXP20X_BATT_CHRG_I_H),
> + AXP20X_ADC_CHANNEL(AXP20X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
> + AXP20X_BATT_DISCHRG_I_H),
> + AXP20X_ADC_CHANNEL(AXP20X_IPSOUT_V, "ipsout_v", IIO_VOLTAGE,
> + AXP20X_IPSOUT_V_HIGH_H),
> +};
> +
> +static const struct iio_chan_spec axp22x_adc_channels[] = {
> + AXP20X_ADC_CHANNEL_OFFSET(AXP22X_TEMP_ADC, "temp_adc", IIO_TEMP,
> + AXP22X_TEMP_ADC_H),
> + AXP20X_ADC_CHANNEL(AXP22X_BATT_V, "batt_v", IIO_VOLTAGE,
> + AXP20X_BATT_V_H),
> + AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
> + AXP20X_BATT_CHRG_I_H),
> + AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
> + AXP20X_BATT_DISCHRG_I_H),
> +};
> +
> +static int axp20x_adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *channel, int *val,
> + int *val2)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + int size = 12, ret;
> +
> + switch (channel->channel) {
> + case AXP20X_BATT_DISCHRG_I:
> + size = 13;
> + case AXP20X_ACIN_V:
> + case AXP20X_ACIN_I:
> + case AXP20X_VBUS_V:
> + case AXP20X_VBUS_I:
> + case AXP20X_TEMP_ADC:
> + case AXP20X_BATT_V:
> + case AXP20X_BATT_CHRG_I:
> + case AXP20X_IPSOUT_V:
> + case AXP20X_GPIO0_V:
> + case AXP20X_GPIO1_V:
> + ret = axp20x_read_variable_width(info->regmap, channel->address,
> + size);
> + if (ret < 0)
> + return ret;
> + *val = ret;
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp22x_adc_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *channel, int *val,
> + int *val2)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + int size = 12, ret;
> +
> + switch (channel->channel) {
> + case AXP22X_BATT_DISCHRG_I:
> + size = 13;
> + case AXP22X_TEMP_ADC:
> + case AXP22X_BATT_V:
> + case AXP22X_BATT_CHRG_I:
> + ret = axp20x_read_variable_width(info->regmap, channel->address,
> + size);
> + if (ret < 0)
> + return ret;
> + *val = ret;
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_adc_scale(int channel, int *val, int *val2)
> +{
> + switch (channel) {
> + case AXP20X_ACIN_V:
> + case AXP20X_VBUS_V:
> + *val = 1;
> + *val2 = 700000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_ACIN_I:
> + *val = 0;
> + *val2 = 625000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_VBUS_I:
> + *val = 0;
> + *val2 = 375000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_TEMP_ADC:
> + *val = 100;
> + return IIO_VAL_INT;
> +
> + case AXP20X_GPIO0_V:
> + case AXP20X_GPIO1_V:
> + *val = 0;
> + *val2 = 500000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_BATT_V:
> + *val = 1;
> + *val2 = 100000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_BATT_DISCHRG_I:
> + case AXP20X_BATT_CHRG_I:
> + *val = 0;
> + *val2 = 500000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP20X_IPSOUT_V:
> + *val = 1;
> + *val2 = 400000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp22x_adc_scale(int channel, int *val, int *val2)
> +{
> + switch (channel) {
> + case AXP22X_TEMP_ADC:
> + *val = 100;
> + return IIO_VAL_INT;
> +
> + case AXP22X_BATT_V:
> + *val = 1;
> + *val2 = 100000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + case AXP22X_BATT_DISCHRG_I:
> + case AXP22X_BATT_CHRG_I:
> + *val = 0;
> + *val2 = 500000;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_adc_offset(struct iio_dev *indio_dev, int channel, int *val)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> + int ret, reg;
> +
> + switch (channel) {
> + case AXP20X_TEMP_ADC:
> + *val = -1447;
> + return IIO_VAL_INT;
> +
> + case AXP20X_GPIO0_V:
> + case AXP20X_GPIO1_V:
> + ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, ®);
> + if (ret < 0)
> + return ret;
> +
> + if (channel == AXP20X_GPIO0_V)
> + *val = reg & AXP20X_GPIO10_IN_RANGE_GPIO0;
> + else
> + *val = reg & AXP20X_GPIO10_IN_RANGE_GPIO1;
> +
> + *val = !!(*val) * 700000;
> +
> + return IIO_VAL_INT;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + switch (mask) {
> + case IIO_CHAN_INFO_OFFSET:
> + return axp20x_adc_offset(indio_dev, chan->channel, val);
> +
> + case IIO_CHAN_INFO_SCALE:
> + return axp20x_adc_scale(chan->channel, val, val2);
> +
> + case IIO_CHAN_INFO_RAW:
> + return axp20x_adc_read_raw(indio_dev, chan, val, val2);
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp22x_read_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int *val,
> + int *val2, long mask)
> +{
> + switch (mask) {
> + case IIO_CHAN_INFO_OFFSET:
> + *val = -2667;
> + return IIO_VAL_INT;
> +
> + case IIO_CHAN_INFO_SCALE:
> + return axp22x_adc_scale(chan->channel, val, val2);
> +
> + case IIO_CHAN_INFO_RAW:
> + return axp22x_adc_read_raw(indio_dev, chan, val, val2);
> +
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int axp20x_write_raw(struct iio_dev *indio_dev,
> + struct iio_chan_spec const *chan, int val, int val2,
> + long mask)
> +{
> + struct axp20x_adc_iio *info = iio_priv(indio_dev);
> +
> + /*
> + * The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets
> + * for (independently) GPIO0 and GPIO1 when in ADC mode.
> + */
> + if (mask != IIO_CHAN_INFO_OFFSET)
> + return -EINVAL;
> +
> + if (chan->channel != AXP20X_GPIO0_V && chan->channel != AXP20X_GPIO1_V)
> + return -EINVAL;
> +
> + if (val != 0 && val != 700000)
> + return -EINVAL;
> +
> + if (chan->channel == AXP20X_GPIO0_V)
> + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE,
> + AXP20X_GPIO10_IN_RANGE_GPIO0,
> + AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(!!val));
> +
> + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE,
> + AXP20X_GPIO10_IN_RANGE_GPIO1,
> + AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(!!val));
> +}
> +
> +static const struct iio_info axp20x_adc_iio_info = {
> + .read_raw = axp20x_read_raw,
> + .write_raw = axp20x_write_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static const struct iio_info axp22x_adc_iio_info = {
> + .read_raw = axp22x_read_raw,
> + .driver_module = THIS_MODULE,
> +};
> +
> +static const struct of_device_id axp20x_adc_of_match[] = {
> + { .compatible = "x-powers,axp209-adc", .data = (void *)AXP209_ID, },
> + { .compatible = "x-powers,axp221-adc", .data = (void *)AXP221_ID, },
> + { /* sentinel */ },
> +};
> +
> +static int axp20x_probe(struct platform_device *pdev)
> +{
> + struct axp20x_adc_iio *info;
> + struct iio_dev *indio_dev;
> + struct axp20x_dev *axp20x_dev;
> + int ret, axp20x_id;
> +
> + axp20x_dev = dev_get_drvdata(pdev->dev.parent);
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + info = iio_priv(indio_dev);
> + platform_set_drvdata(pdev, indio_dev);
> +
> + info->regmap = axp20x_dev->regmap;
> + info->indio_dev = indio_dev;
> + indio_dev->name = dev_name(&pdev->dev);
> + indio_dev->dev.parent = &pdev->dev;
> + indio_dev->dev.of_node = pdev->dev.of_node;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + axp20x_id = (int)of_device_get_match_data(&pdev->dev);
> +
> + switch (axp20x_id) {
> + case AXP209_ID:
> + indio_dev->info = &axp20x_adc_iio_info;
> + indio_dev->num_channels = ARRAY_SIZE(axp20x_adc_channels);
> + indio_dev->channels = axp20x_adc_channels;
> +
> + /* Enable the ADCs on IP */
> + regmap_write(info->regmap, AXP20X_ADC_EN1, AXP20X_ADC_EN1_MASK);
> +
> + /* Enable GPIO0/1 and internal temperature ADCs */
> + regmap_update_bits(info->regmap, AXP20X_ADC_EN2,
> + AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
> +
> + /* Configure ADCs rate */
> + regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
> + AXP20X_ADC_RATE_MASK, AXP20X_ADC_RATE_50HZ);
> + break;
> +
> + case AXP221_ID:
> + indio_dev->info = &axp22x_adc_iio_info;
> + indio_dev->num_channels = ARRAY_SIZE(axp22x_adc_channels);
> + indio_dev->channels = axp22x_adc_channels;
> +
> + /* Enable the ADCs on IP */
> + regmap_write(info->regmap, AXP20X_ADC_EN1, AXP22X_ADC_EN1_MASK);
> +
> + /* Configure ADCs rate */
> + regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
> + AXP20X_ADC_RATE_MASK, AXP22X_ADC_RATE_200HZ);
> + break;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + ret = devm_iio_device_register(&pdev->dev, indio_dev);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "could not register the device\n");
> + regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> + regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int axp20x_remove(struct platform_device *pdev)
> +{
> + struct axp20x_adc_iio *info;
> + struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> + info = iio_priv(indio_dev);
> + regmap_write(info->regmap, AXP20X_ADC_EN1, 0);
> + regmap_write(info->regmap, AXP20X_ADC_EN2, 0);
> +
> + return 0;
> +}
> +
> +static struct platform_driver axp20x_adc_driver = {
> + .driver = {
> + .name = "axp20x-adc",
> + .of_match_table = axp20x_adc_of_match,
> + },
> + .probe = axp20x_probe,
> + .remove = axp20x_remove,
> +};
> +
> +module_platform_driver(axp20x_adc_driver);
> +
> +MODULE_DESCRIPTION("ADC driver for AXP20X and AXP22X PMICs");
> +MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
> index a4860bc..650c6f6 100644
> --- a/include/linux/mfd/axp20x.h
> +++ b/include/linux/mfd/axp20x.h
> @@ -150,6 +150,10 @@ enum {
> #define AXP20X_VBUS_I_ADC_L 0x5d
> #define AXP20X_TEMP_ADC_H 0x5e
> #define AXP20X_TEMP_ADC_L 0x5f
> +
> +#define AXP22X_TEMP_ADC_H 0x56
> +#define AXP22X_TEMP_ADC_L 0x57
> +
> #define AXP20X_TS_IN_H 0x62
> #define AXP20X_TS_IN_L 0x63
> #define AXP20X_GPIO0_V_ADC_H 0x64
>
next prev parent reply other threads:[~2017-01-08 9:48 UTC|newest]
Thread overview: 212+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-01-02 16:37 [PATCH 00/22] add support for AXP20X and AXP22X power supply drivers Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` [PATCH 01/22] dt-bindings: iio: adc: add AXP20X/AXP22X ADC DT binding Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-03 23:20 ` Rob Herring
2017-01-03 23:20 ` Rob Herring
2017-01-03 23:20 ` Rob Herring
2017-01-05 4:05 ` Chen-Yu Tsai
2017-01-05 4:05 ` Chen-Yu Tsai
2017-01-05 4:05 ` Chen-Yu Tsai
2017-01-05 16:40 ` Maxime Ripard
2017-01-05 16:40 ` Maxime Ripard
2017-01-05 16:40 ` Maxime Ripard
2017-01-02 16:37 ` [PATCH 02/22] mfd: axp20x: add ADC data regs to volatile regs for AXP22X Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-04 11:55 ` Lee Jones
2017-01-04 11:55 ` Lee Jones
2017-01-04 11:55 ` Lee Jones
2017-01-05 4:12 ` Chen-Yu Tsai
2017-01-05 4:12 ` Chen-Yu Tsai
2017-01-05 4:12 ` Chen-Yu Tsai
2017-01-02 16:37 ` [PATCH 03/22] iio: adc: add support for X-Powers AXP20X and AXP22X PMICs ADCs Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 18:37 ` Peter Meerwald-Stadler
2017-01-03 8:16 ` Quentin Schulz
2017-01-07 19:06 ` Jonathan Cameron
2017-01-05 5:42 ` Chen-Yu Tsai
2017-01-05 5:42 ` Chen-Yu Tsai
2017-01-05 5:42 ` Chen-Yu Tsai
2017-01-05 8:06 ` Quentin Schulz
2017-01-05 8:06 ` Quentin Schulz
2017-01-05 8:06 ` Quentin Schulz
2017-01-05 8:27 ` Chen-Yu Tsai
2017-01-05 8:27 ` Chen-Yu Tsai
2017-01-05 8:27 ` Chen-Yu Tsai
2017-01-05 9:50 ` Quentin Schulz
2017-01-05 9:50 ` Quentin Schulz
2017-01-05 9:50 ` Quentin Schulz
2017-01-05 10:28 ` Chen-Yu Tsai
2017-01-05 10:28 ` Chen-Yu Tsai
2017-01-05 10:28 ` Chen-Yu Tsai
2017-01-07 19:23 ` Jonathan Cameron
2017-01-07 19:23 ` Jonathan Cameron
2017-01-07 19:23 ` Jonathan Cameron
2017-01-05 16:46 ` Maxime Ripard
2017-01-05 16:46 ` Maxime Ripard
2017-01-05 16:46 ` Maxime Ripard
2017-01-07 19:20 ` Jonathan Cameron
2017-01-07 19:20 ` Jonathan Cameron
2017-01-07 19:20 ` Jonathan Cameron
2017-01-05 16:51 ` Maxime Ripard
2017-01-05 16:51 ` Maxime Ripard
2017-01-05 16:51 ` Maxime Ripard
2017-01-07 19:13 ` Jonathan Cameron [this message]
2017-01-07 19:13 ` Jonathan Cameron
2017-01-07 19:13 ` Jonathan Cameron
2017-01-02 16:37 ` [PATCH 04/22] mfd: axp20x: add ADC cells for AXP20X and AXP22X PMICs Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-04 11:56 ` Lee Jones
2017-01-04 11:56 ` Lee Jones
2017-01-04 11:56 ` Lee Jones
2017-01-04 11:56 ` Lee Jones
2017-01-04 11:56 ` Lee Jones
2017-01-05 5:51 ` Chen-Yu Tsai
2017-01-05 5:51 ` Chen-Yu Tsai
2017-01-05 5:51 ` Chen-Yu Tsai
2017-01-02 16:37 ` [PATCH 05/22] ARM: dtsi: axp209: add AXP209 ADC subnode Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-05 5:51 ` Chen-Yu Tsai
2017-01-05 5:51 ` Chen-Yu Tsai
2017-01-05 5:51 ` Chen-Yu Tsai
2017-01-05 8:08 ` Quentin Schulz
2017-01-05 8:08 ` Quentin Schulz
2017-01-05 8:08 ` Quentin Schulz
2017-01-05 8:16 ` Chen-Yu Tsai
2017-01-05 8:16 ` Chen-Yu Tsai
2017-01-05 8:16 ` Chen-Yu Tsai
2017-01-02 16:37 ` [PATCH 06/22] ARM: dtsi: axp22x: add AXP22X " Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-05 5:52 ` Chen-Yu Tsai
2017-01-05 5:52 ` Chen-Yu Tsai
2017-01-05 5:52 ` Chen-Yu Tsai
2017-01-02 16:37 ` [PATCH 07/22] dt-bindings: power: supply: add AXP20X/AXP22X AC power supply Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-04 13:14 ` Rob Herring
2017-01-04 13:14 ` Rob Herring
2017-01-05 6:17 ` Chen-Yu Tsai
2017-01-05 6:17 ` Chen-Yu Tsai
2017-01-05 6:17 ` Chen-Yu Tsai
2017-01-07 19:26 ` Jonathan Cameron
2017-01-07 19:26 ` Jonathan Cameron
2017-01-07 19:26 ` Jonathan Cameron
2017-01-02 16:37 ` [PATCH 08/22] power: supply: add AC power supply driver for AXP20X and AXP22X PMICs Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-07 19:31 ` Jonathan Cameron
2017-01-07 19:31 ` Jonathan Cameron
2017-01-07 19:31 ` Jonathan Cameron
2017-01-08 10:41 ` Quentin Schulz
2017-01-08 10:41 ` Quentin Schulz
2017-01-08 10:41 ` Quentin Schulz
2017-01-17 3:00 ` Sebastian Reichel
2017-01-17 3:00 ` Sebastian Reichel
2017-01-17 3:00 ` Sebastian Reichel
2017-01-26 13:32 ` Quentin Schulz
2017-01-26 13:32 ` Quentin Schulz
2017-01-26 13:32 ` Quentin Schulz
2017-01-27 8:20 ` Maxime Ripard
2017-01-27 8:20 ` Maxime Ripard
2017-01-28 14:30 ` Jonathan Cameron
2017-01-28 14:30 ` Jonathan Cameron
2017-01-28 14:30 ` Jonathan Cameron
2017-01-29 15:16 ` Sebastian Reichel
2017-01-29 15:16 ` Sebastian Reichel
2017-01-29 15:16 ` Sebastian Reichel
2017-01-02 16:37 ` [PATCH 09/22] mfd: axp20x: add AC power supply cells for " Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-04 11:57 ` Lee Jones
2017-01-04 11:57 ` Lee Jones
2017-01-02 16:37 ` [PATCH 10/22] ARM: dtsi: axp209: add AC power supply subnode Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` [PATCH 11/22] ARM: dtsi: axp22x: " Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` [PATCH 12/22] ARM: dts: sun8i: sina33: enable ACIN " Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` [PATCH 13/22] ARM: sun5i: chip: " Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` [PATCH 14/22] dt-bindings: power: supply: add AXP20X/AXP22X battery DT binding Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-04 13:21 ` Rob Herring
2017-01-04 13:21 ` Rob Herring
2017-01-07 19:33 ` Jonathan Cameron
2017-01-07 19:33 ` Jonathan Cameron
2017-01-07 19:33 ` Jonathan Cameron
2017-01-08 10:48 ` Quentin Schulz
2017-01-08 10:48 ` Quentin Schulz
2017-01-08 10:48 ` Quentin Schulz
2017-01-08 10:59 ` Jonathan Cameron
2017-01-08 10:59 ` Jonathan Cameron
2017-01-08 10:59 ` Jonathan Cameron
2017-01-02 16:37 ` [PATCH 15/22] mfd: axp20x: add CHRG_CTRL1 to writeable regs for AXP20X/AXP22X Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-04 11:57 ` Lee Jones
2017-01-04 11:57 ` Lee Jones
2017-01-05 6:10 ` Chen-Yu Tsai
2017-01-05 6:10 ` Chen-Yu Tsai
2017-01-05 6:10 ` Chen-Yu Tsai
2017-01-05 8:10 ` Quentin Schulz
2017-01-05 8:10 ` Quentin Schulz
2017-01-05 8:10 ` Quentin Schulz
2017-01-02 16:37 ` [PATCH 16/22] mfd: axp20x: add V_OFF to writeable regs for AXP20X and AXP22X Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-04 11:57 ` Lee Jones
2017-01-04 11:57 ` Lee Jones
2017-01-05 6:02 ` Chen-Yu Tsai
2017-01-05 6:02 ` Chen-Yu Tsai
2017-01-05 6:02 ` Chen-Yu Tsai
2017-01-02 16:37 ` [PATCH 17/22] power: supply: add battery driver for AXP20X and AXP22X PMICs Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-05 17:02 ` Maxime Ripard
2017-01-05 17:02 ` Maxime Ripard
2017-01-05 17:02 ` Maxime Ripard
2017-01-05 17:34 ` Ezequiel Garcia
2017-01-05 17:34 ` Ezequiel Garcia
2017-01-05 17:34 ` Ezequiel Garcia
2017-01-05 17:34 ` Ezequiel Garcia
2017-01-06 2:46 ` Sebastian Reichel
2017-01-06 2:46 ` Sebastian Reichel
2017-01-06 2:46 ` Sebastian Reichel
2017-01-06 3:39 ` Chen-Yu Tsai
2017-01-06 3:39 ` Chen-Yu Tsai
2017-01-06 3:39 ` Chen-Yu Tsai
2017-01-06 8:29 ` Quentin Schulz
2017-01-06 8:29 ` Quentin Schulz
2017-01-06 8:29 ` Quentin Schulz
2017-01-17 3:46 ` Sebastian Reichel
2017-01-17 3:46 ` Sebastian Reichel
2017-01-17 3:46 ` Sebastian Reichel
2017-01-02 16:37 ` [PATCH 18/22] mfd: axp20x: add MFD cells for AXP20X and AXP22X battery driver Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-04 11:57 ` Lee Jones
2017-01-04 11:57 ` Lee Jones
2017-01-04 11:57 ` Lee Jones
2017-01-02 16:37 ` [PATCH 19/22] ARM: dtsi: axp209: add battery power supply subnode Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` [PATCH 20/22] ARM: dtsi: axp22x: " Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` [PATCH 21/22] ARM: dts: sun8i: sina33: enable " Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` [PATCH 22/22] ARM: sun5i: chip: " Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
2017-01-02 16:37 ` Quentin Schulz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=7c1c045f-892c-e384-1757-60bef66ec470@kernel.org \
--to=jic23@kernel.org \
--cc=bonbons@linux-vserver.org \
--cc=devicetree@vger.kernel.org \
--cc=icenowy@aosc.xyz \
--cc=knaack.h@gmx.de \
--cc=lars@metafoo.de \
--cc=lee.jones@linaro.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=mark.rutland@arm.com \
--cc=maxime.ripard@free-electrons.com \
--cc=pmeerw@pmeerw.net \
--cc=quentin.schulz@free-electrons.com \
--cc=robh+dt@kernel.org \
--cc=sre@kernel.org \
--cc=thomas.petazzoni@free-electrons.com \
--cc=wens@csie.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.