summaryrefslogtreecommitdiffstats
path: root/drivers/iio/adc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r--drivers/iio/adc/Kconfig62
-rw-r--r--drivers/iio/adc/Makefile5
-rw-r--r--drivers/iio/adc/ad7793.c2
-rw-r--r--drivers/iio/adc/ad799x.c15
-rw-r--r--drivers/iio/adc/at91_adc.c5
-rw-r--r--drivers/iio/adc/axp288_adc.c261
-rw-r--r--drivers/iio/adc/cc10001_adc.c423
-rw-r--r--drivers/iio/adc/da9150-gpadc.c407
-rw-r--r--drivers/iio/adc/exynos_adc.c62
-rw-r--r--drivers/iio/adc/mcp320x.c222
-rw-r--r--drivers/iio/adc/mcp3422.c17
-rw-r--r--drivers/iio/adc/men_z188_adc.c1
-rw-r--r--drivers/iio/adc/qcom-spmi-iadc.c596
-rw-r--r--drivers/iio/adc/qcom-spmi-vadc.c1016
-rw-r--r--drivers/iio/adc/rockchip_saradc.c65
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c19
-rw-r--r--drivers/iio/adc/vf610_adc.c141
17 files changed, 3180 insertions, 139 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 88bdc8f612e2..e36a73e7c3a8 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -127,6 +127,35 @@ config AT91_ADC
help
Say yes here to build support for Atmel AT91 ADC.
+config AXP288_ADC
+ tristate "X-Powers AXP288 ADC driver"
+ depends on MFD_AXP20X
+ help
+ Say yes here to have support for X-Powers power management IC (PMIC) ADC
+ device. Depending on platform configuration, this general purpose ADC can
+ be used for sampling sensors such as thermal resistors.
+
+config DA9150_GPADC
+ tristate "Dialog DA9150 GPADC driver support"
+ depends on MFD_DA9150
+ help
+ Say yes here to build support for Dialog DA9150 GPADC.
+
+ This driver can also be built as a module. If chosen, the module name
+ will be da9150-gpadc.
+
+config CC10001_ADC
+ tristate "Cosmic Circuits 10001 ADC driver"
+ depends on HAVE_CLK || REGULATOR
+ depends on HAS_IOMEM
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Cosmic Circuits 10001 ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called cc10001_adc.
+
config EXYNOS_ADC
tristate "Exynos ADC driver support"
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
@@ -167,10 +196,11 @@ config MAX1363
data via the iio dev interface.
config MCP320X
- tristate "Microchip Technology MCP3204/08"
+ tristate "Microchip Technology MCP3x01/02/04/08"
depends on SPI
help
- Say yes here to build support for Microchip Technology's MCP3204 or
+ Say yes here to build support for Microchip Technology's
+ MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204 or
MCP3208 analog to digital converter.
This driver can also be built as a module. If so, the module will be
@@ -206,6 +236,34 @@ config NAU7802
To compile this driver as a module, choose M here: the
module will be called nau7802.
+config QCOM_SPMI_IADC
+ tristate "Qualcomm SPMI PMIC current ADC"
+ depends on SPMI
+ select REGMAP_SPMI
+ help
+ This is the IIO Current ADC driver for Qualcomm QPNP IADC Chip.
+
+ The driver supports single mode operation to read from one of two
+ channels (external or internal). Hardware have additional
+ channels internally used for gain and offset calibration.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-spmi-iadc.
+
+config QCOM_SPMI_VADC
+ tristate "Qualcomm SPMI PMIC voltage ADC"
+ depends on SPMI
+ select REGMAP_SPMI
+ help
+ This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
+
+ The driver supports multiple channels read. The VADC is a 15-bit
+ sigma-delta ADC. Some of the channels are internally used for
+ calibration.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-spmi-vadc.
+
config ROCKCHIP_SARADC
tristate "Rockchip SARADC driver"
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index cb88a6a23b8f..3930e63e84bc 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -14,6 +14,9 @@ obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
+obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
+obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1027) += max1027.o
@@ -22,6 +25,8 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
+obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
+obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index 4dddeabdfbb0..b84922a4b32e 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -861,5 +861,5 @@ static struct spi_driver ad7793_driver = {
module_spi_driver(ad7793_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("Analog Devices AD7793 and simialr ADCs");
+MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c
index e37412da15f5..b99de00e57b8 100644
--- a/drivers/iio/adc/ad799x.c
+++ b/drivers/iio/adc/ad799x.c
@@ -143,9 +143,15 @@ static int ad799x_write_config(struct ad799x_state *st, u16 val)
case ad7998:
return i2c_smbus_write_word_swapped(st->client, AD7998_CONF_REG,
val);
- default:
+ case ad7992:
+ case ad7993:
+ case ad7994:
return i2c_smbus_write_byte_data(st->client, AD7998_CONF_REG,
val);
+ default:
+ /* Will be written when doing a conversion */
+ st->config = val;
+ return 0;
}
}
@@ -155,8 +161,13 @@ static int ad799x_read_config(struct ad799x_state *st)
case ad7997:
case ad7998:
return i2c_smbus_read_word_swapped(st->client, AD7998_CONF_REG);
- default:
+ case ad7992:
+ case ad7993:
+ case ad7994:
return i2c_smbus_read_byte_data(st->client, AD7998_CONF_REG);
+ default:
+ /* No readback support */
+ return st->config;
}
}
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index ff61ae55dd3f..8a0eb4a04fb5 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -544,7 +544,6 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
{
struct iio_dev *idev = iio_trigger_get_drvdata(trig);
struct at91_adc_state *st = iio_priv(idev);
- struct iio_buffer *buffer = idev->buffer;
struct at91_adc_reg_desc *reg = st->registers;
u32 status = at91_adc_readl(st, reg->trigger_register);
int value;
@@ -564,7 +563,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
at91_adc_writel(st, reg->trigger_register,
status | value);
- for_each_set_bit(bit, buffer->scan_mask,
+ for_each_set_bit(bit, idev->active_scan_mask,
st->num_channels) {
struct iio_chan_spec const *chan = idev->channels + bit;
at91_adc_writel(st, AT91_ADC_CHER,
@@ -579,7 +578,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
at91_adc_writel(st, reg->trigger_register,
status & ~value);
- for_each_set_bit(bit, buffer->scan_mask,
+ for_each_set_bit(bit, idev->active_scan_mask,
st->num_channels) {
struct iio_chan_spec const *chan = idev->channels + bit;
at91_adc_writel(st, AT91_ADC_CHDR,
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
new file mode 100644
index 000000000000..08bcfb061ca5
--- /dev/null
+++ b/drivers/iio/adc/axp288_adc.c
@@ -0,0 +1,261 @@
+/*
+ * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/platform_device.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+#define AXP288_ADC_EN_MASK 0xF1
+#define AXP288_ADC_TS_PIN_GPADC 0xF2
+#define AXP288_ADC_TS_PIN_ON 0xF3
+
+enum axp288_adc_id {
+ AXP288_ADC_TS,
+ AXP288_ADC_PMIC,
+ AXP288_ADC_GP,
+ AXP288_ADC_BATT_CHRG_I,
+ AXP288_ADC_BATT_DISCHRG_I,
+ AXP288_ADC_BATT_V,
+ AXP288_ADC_NR_CHAN,
+};
+
+struct axp288_adc_info {
+ int irq;
+ struct regmap *regmap;
+};
+
+static const struct iio_chan_spec const axp288_adc_channels[] = {
+ {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 0,
+ .address = AXP288_TS_ADC_H,
+ .datasheet_name = "TS_PIN",
+ }, {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 1,
+ .address = AXP288_PMIC_ADC_H,
+ .datasheet_name = "PMIC_TEMP",
+ }, {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 2,
+ .address = AXP288_GP_ADC_H,
+ .datasheet_name = "GPADC",
+ }, {
+ .indexed = 1,
+ .type = IIO_CURRENT,
+ .channel = 3,
+ .address = AXP20X_BATT_CHRG_I_H,
+ .datasheet_name = "BATT_CHG_I",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ }, {
+ .indexed = 1,
+ .type = IIO_CURRENT,
+ .channel = 4,
+ .address = AXP20X_BATT_DISCHRG_I_H,
+ .datasheet_name = "BATT_DISCHRG_I",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ }, {
+ .indexed = 1,
+ .type = IIO_VOLTAGE,
+ .channel = 5,
+ .address = AXP20X_BATT_V_H,
+ .datasheet_name = "BATT_V",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+};
+
+#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \
+ _consumer_channel) \
+ { \
+ .adc_channel_label = _adc_channel_label, \
+ .consumer_dev_name = _consumer_dev_name, \
+ .consumer_channel = _consumer_channel, \
+ }
+
+/* for consumer drivers */
+static struct iio_map axp288_adc_default_maps[] = {
+ AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"),
+ AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"),
+ AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"),
+ AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"),
+ AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"),
+ AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"),
+ {},
+};
+
+static int axp288_adc_read_channel(int *val, unsigned long address,
+ struct regmap *regmap)
+{
+ u8 buf[2];
+
+ if (regmap_bulk_read(regmap, address, buf, 2))
+ return -EIO;
+ *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F);
+
+ return IIO_VAL_INT;
+}
+
+static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
+ unsigned long address)
+{
+ /* channels other than GPADC do not need to switch TS pin */
+ if (address != AXP288_GP_ADC_H)
+ return 0;
+
+ return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
+}
+
+static int axp288_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ struct axp288_adc_info *info = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
+ chan->address)) {
+ dev_err(&indio_dev->dev, "GPADC mode\n");
+ ret = -EINVAL;
+ break;
+ }
+ ret = axp288_adc_read_channel(val, chan->address, info->regmap);
+ if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
+ chan->address))
+ dev_err(&indio_dev->dev, "TS pin restore\n");
+ break;
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = axp288_adc_read_channel(val, chan->address, info->regmap);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int axp288_adc_set_state(struct regmap *regmap)
+{
+ /* ADC should be always enabled for internal FG to function */
+ if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
+ return -EIO;
+
+ return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
+}
+
+static const struct iio_info axp288_adc_iio_info = {
+ .read_raw = &axp288_adc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int axp288_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct axp288_adc_info *info;
+ struct iio_dev *indio_dev;
+ struct axp20x_dev *axp20x = 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);
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return info->irq;
+ }
+ platform_set_drvdata(pdev, indio_dev);
+ info->regmap = axp20x->regmap;
+ /*
+ * Set ADC to enabled state at all time, including system suspend.
+ * otherwise internal fuel gauge functionality may be affected.
+ */
+ ret = axp288_adc_set_state(axp20x->regmap);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable ADC device\n");
+ return ret;
+ }
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = pdev->name;
+ indio_dev->channels = axp288_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels);
+ indio_dev->info = &axp288_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ ret = iio_map_array_register(indio_dev, axp288_adc_default_maps);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to register iio device\n");
+ goto err_array_unregister;
+ }
+ return 0;
+
+err_array_unregister:
+ iio_map_array_unregister(indio_dev);
+
+ return ret;
+}
+
+static int axp288_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_device_unregister(indio_dev);
+ iio_map_array_unregister(indio_dev);
+
+ return 0;
+}
+
+static struct platform_device_id axp288_adc_id_table[] = {
+ { .name = "axp288_adc" },
+ {},
+};
+
+static struct platform_driver axp288_adc_driver = {
+ .probe = axp288_adc_probe,
+ .remove = axp288_adc_remove,
+ .id_table = axp288_adc_id_table,
+ .driver = {
+ .name = "axp288_adc",
+ },
+};
+
+MODULE_DEVICE_TABLE(platform, axp288_adc_id_table);
+
+module_platform_driver(axp288_adc_driver);
+
+MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
+MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c
new file mode 100644
index 000000000000..51e2a83c9404
--- /dev/null
+++ b/drivers/iio/adc/cc10001_adc.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 2014-2015 Imagination Technologies Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/* Registers */
+#define CC10001_ADC_CONFIG 0x00
+#define CC10001_ADC_START_CONV BIT(4)
+#define CC10001_ADC_MODE_SINGLE_CONV BIT(5)
+
+#define CC10001_ADC_DDATA_OUT 0x04
+#define CC10001_ADC_EOC 0x08
+#define CC10001_ADC_EOC_SET BIT(0)
+
+#define CC10001_ADC_CHSEL_SAMPLED 0x0c
+#define CC10001_ADC_POWER_UP 0x10
+#define CC10001_ADC_POWER_UP_SET BIT(0)
+#define CC10001_ADC_DEBUG 0x14
+#define CC10001_ADC_DATA_COUNT 0x20
+
+#define CC10001_ADC_DATA_MASK GENMASK(9, 0)
+#define CC10001_ADC_NUM_CHANNELS 8
+#define CC10001_ADC_CH_MASK GENMASK(2, 0)
+
+#define CC10001_INVALID_SAMPLED 0xffff
+#define CC10001_MAX_POLL_COUNT 20
+
+/*
+ * As per device specification, wait six clock cycles after power-up to
+ * activate START. Since adding two more clock cycles delay does not
+ * impact the performance too much, we are adding two additional cycles delay
+ * intentionally here.
+ */
+#define CC10001_WAIT_CYCLES 8
+
+struct cc10001_adc_device {
+ void __iomem *reg_base;
+ struct clk *adc_clk;
+ struct regulator *reg;
+ u16 *buf;
+
+ struct mutex lock;
+ unsigned long channel_map;
+ unsigned int start_delay_ns;
+ unsigned int eoc_delay_ns;
+};
+
+static inline void cc10001_adc_write_reg(struct cc10001_adc_device *adc_dev,
+ u32 reg, u32 val)
+{
+ writel(val, adc_dev->reg_base + reg);
+}
+
+static inline u32 cc10001_adc_read_reg(struct cc10001_adc_device *adc_dev,
+ u32 reg)
+{
+ return readl(adc_dev->reg_base + reg);
+}
+
+static void cc10001_adc_start(struct cc10001_adc_device *adc_dev,
+ unsigned int channel)
+{
+ u32 val;
+
+ /* Channel selection and mode of operation */
+ val = (channel & CC10001_ADC_CH_MASK) | CC10001_ADC_MODE_SINGLE_CONV;
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
+
+ val = cc10001_adc_read_reg(adc_dev, CC10001_ADC_CONFIG);
+ val = val | CC10001_ADC_START_CONV;
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
+}
+
+static u16 cc10001_adc_poll_done(struct iio_dev *indio_dev,
+ unsigned int channel,
+ unsigned int delay)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int poll_count = 0;
+
+ while (!(cc10001_adc_read_reg(adc_dev, CC10001_ADC_EOC) &
+ CC10001_ADC_EOC_SET)) {
+
+ ndelay(delay);
+ if (poll_count++ == CC10001_MAX_POLL_COUNT)
+ return CC10001_INVALID_SAMPLED;
+ }
+
+ poll_count = 0;
+ while ((cc10001_adc_read_reg(adc_dev, CC10001_ADC_CHSEL_SAMPLED) &
+ CC10001_ADC_CH_MASK) != channel) {
+
+ ndelay(delay);
+ if (poll_count++ == CC10001_MAX_POLL_COUNT)
+ return CC10001_INVALID_SAMPLED;
+ }
+
+ /* Read the 10 bit output register */
+ return cc10001_adc_read_reg(adc_dev, CC10001_ADC_DDATA_OUT) &
+ CC10001_ADC_DATA_MASK;
+}
+
+static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
+{
+ struct cc10001_adc_device *adc_dev;
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev;
+ unsigned int delay_ns;
+ unsigned int channel;
+ bool sample_invalid;
+ u16 *data;
+ int i;
+
+ indio_dev = pf->indio_dev;
+ adc_dev = iio_priv(indio_dev);
+ data = adc_dev->buf;
+
+ mutex_lock(&adc_dev->lock);
+
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
+ CC10001_ADC_POWER_UP_SET);
+
+ /* Wait for 8 (6+2) clock cycles before activating START */
+ ndelay(adc_dev->start_delay_ns);
+
+ /* Calculate delay step for eoc and sampled data */
+ delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
+
+ i = 0;
+ sample_invalid = false;
+ for_each_set_bit(channel, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+
+ cc10001_adc_start(adc_dev, channel);
+
+ data[i] = cc10001_adc_poll_done(indio_dev, channel, delay_ns);
+ if (data[i] == CC10001_INVALID_SAMPLED) {
+ dev_warn(&indio_dev->dev,
+ "invalid sample on channel %d\n", channel);
+ sample_invalid = true;
+ goto done;
+ }
+ i++;
+ }
+
+done:
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
+
+ mutex_unlock(&adc_dev->lock);
+
+ if (!sample_invalid)
+ iio_push_to_buffers_with_timestamp(indio_dev, data,
+ iio_get_time_ns());
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int delay_ns;
+ u16 val;
+
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
+ CC10001_ADC_POWER_UP_SET);
+
+ /* Wait for 8 (6+2) clock cycles before activating START */
+ ndelay(adc_dev->start_delay_ns);
+
+ /* Calculate delay step for eoc and sampled data */
+ delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
+
+ cc10001_adc_start(adc_dev, chan->channel);
+
+ val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns);
+
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
+
+ return val;
+}
+
+static int cc10001_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+ mutex_lock(&adc_dev->lock);
+ *val = cc10001_adc_read_raw_voltage(indio_dev, chan);
+ mutex_unlock(&adc_dev->lock);
+
+ if (*val == CC10001_INVALID_SAMPLED)
+ return -EIO;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(adc_dev->reg);
+ if (ret)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cc10001_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+
+ kfree(adc_dev->buf);
+ adc_dev->buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!adc_dev->buf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const struct iio_info cc10001_adc_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &cc10001_adc_read_raw,
+ .update_scan_mode = &cc10001_update_scan_mode,
+};
+
+static int cc10001_adc_channel_init(struct iio_dev *indio_dev)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+ struct iio_chan_spec *chan_array, *timestamp;
+ unsigned int bit, idx = 0;
+
+ indio_dev->num_channels = bitmap_weight(&adc_dev->channel_map,
+ CC10001_ADC_NUM_CHANNELS);
+
+ chan_array = devm_kcalloc(&indio_dev->dev, indio_dev->num_channels + 1,
+ sizeof(struct iio_chan_spec),
+ GFP_KERNEL);
+ if (!chan_array)
+ return -ENOMEM;
+
+ for_each_set_bit(bit, &adc_dev->channel_map, CC10001_ADC_NUM_CHANNELS) {
+ struct iio_chan_spec *chan = &chan_array[idx];
+
+ chan->type = IIO_VOLTAGE;
+ chan->indexed = 1;
+ chan->channel = bit;
+ chan->scan_index = idx;
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = 10;
+ chan->scan_type.storagebits = 16;
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ idx++;
+ }
+
+ timestamp = &chan_array[idx];
+ timestamp->type = IIO_TIMESTAMP;
+ timestamp->channel = -1;
+ timestamp->scan_index = idx;
+ timestamp->scan_type.sign = 's';
+ timestamp->scan_type.realbits = 64;
+ timestamp->scan_type.storagebits = 64;
+
+ indio_dev->channels = chan_array;
+
+ return 0;
+}
+
+static int cc10001_adc_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct cc10001_adc_device *adc_dev;
+ unsigned long adc_clk_rate;
+ struct resource *res;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ adc_dev = iio_priv(indio_dev);
+
+ adc_dev->channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0);
+ if (!of_property_read_u32(node, "adc-reserved-channels", &ret))
+ adc_dev->channel_map &= ~ret;
+
+ adc_dev->reg = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(adc_dev->reg))
+ return PTR_ERR(adc_dev->reg);
+
+ ret = regulator_enable(adc_dev->reg);
+ if (ret)
+ return ret;
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &cc10001_adc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(adc_dev->reg_base)) {
+ ret = PTR_ERR(adc_dev->reg_base);
+ goto err_disable_reg;
+ }
+
+ adc_dev->adc_clk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(adc_dev->adc_clk)) {
+ dev_err(&pdev->dev, "failed to get the clock\n");
+ ret = PTR_ERR(adc_dev->adc_clk);
+ goto err_disable_reg;
+ }
+
+ ret = clk_prepare_enable(adc_dev->adc_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable the clock\n");
+ goto err_disable_reg;
+ }
+
+ adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
+ if (!adc_clk_rate) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "null clock rate!\n");
+ goto err_disable_clk;
+ }
+
+ adc_dev->eoc_delay_ns = NSEC_PER_SEC / adc_clk_rate;
+ adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES;
+
+ /* Setup the ADC channels available on the device */
+ ret = cc10001_adc_channel_init(indio_dev);
+ if (ret < 0)
+ goto err_disable_clk;
+
+ mutex_init(&adc_dev->lock);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &cc10001_adc_trigger_h, NULL);
+ if (ret < 0)
+ goto err_disable_clk;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto err_cleanup_buffer;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ return 0;
+
+err_cleanup_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_disable_clk:
+ clk_disable_unprepare(adc_dev->adc_clk);
+err_disable_reg:
+ regulator_disable(adc_dev->reg);
+ return ret;
+}
+
+static int cc10001_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ clk_disable_unprepare(adc_dev->adc_clk);
+ regulator_disable(adc_dev->reg);
+
+ return 0;
+}
+
+static const struct of_device_id cc10001_adc_dt_ids[] = {
+ { .compatible = "cosmic,10001-adc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cc10001_adc_dt_ids);
+
+static struct platform_driver cc10001_adc_driver = {
+ .driver = {
+ .name = "cc10001-adc",
+ .of_match_table = cc10001_adc_dt_ids,
+ },
+ .probe = cc10001_adc_probe,
+ .remove = cc10001_adc_remove,
+};
+module_platform_driver(cc10001_adc_driver);
+
+MODULE_AUTHOR("Phani Movva <Phani.Movva@imgtec.com>");
+MODULE_DESCRIPTION("Cosmic Circuits ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c
new file mode 100644
index 000000000000..3445107e10b7
--- /dev/null
+++ b/drivers/iio/adc/da9150-gpadc.c
@@ -0,0 +1,407 @@
+/*
+ * DA9150 GPADC Driver
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/mfd/da9150/core.h>
+#include <linux/mfd/da9150/registers.h>
+
+/* Channels */
+enum da9150_gpadc_hw_channel {
+ DA9150_GPADC_HW_CHAN_GPIOA_2V = 0,
+ DA9150_GPADC_HW_CHAN_GPIOA_2V_,
+ DA9150_GPADC_HW_CHAN_GPIOB_2V,
+ DA9150_GPADC_HW_CHAN_GPIOB_2V_,
+ DA9150_GPADC_HW_CHAN_GPIOC_2V,
+ DA9150_GPADC_HW_CHAN_GPIOC_2V_,
+ DA9150_GPADC_HW_CHAN_GPIOD_2V,
+ DA9150_GPADC_HW_CHAN_GPIOD_2V_,
+ DA9150_GPADC_HW_CHAN_IBUS_SENSE,
+ DA9150_GPADC_HW_CHAN_IBUS_SENSE_,
+ DA9150_GPADC_HW_CHAN_VBUS_DIV,
+ DA9150_GPADC_HW_CHAN_VBUS_DIV_,
+ DA9150_GPADC_HW_CHAN_ID,
+ DA9150_GPADC_HW_CHAN_ID_,
+ DA9150_GPADC_HW_CHAN_VSYS,
+ DA9150_GPADC_HW_CHAN_VSYS_,
+ DA9150_GPADC_HW_CHAN_GPIOA_6V,
+ DA9150_GPADC_HW_CHAN_GPIOA_6V_,
+ DA9150_GPADC_HW_CHAN_GPIOB_6V,
+ DA9150_GPADC_HW_CHAN_GPIOB_6V_,
+ DA9150_GPADC_HW_CHAN_GPIOC_6V,
+ DA9150_GPADC_HW_CHAN_GPIOC_6V_,
+ DA9150_GPADC_HW_CHAN_GPIOD_6V,
+ DA9150_GPADC_HW_CHAN_GPIOD_6V_,
+ DA9150_GPADC_HW_CHAN_VBAT,
+ DA9150_GPADC_HW_CHAN_VBAT_,
+ DA9150_GPADC_HW_CHAN_TBAT,
+ DA9150_GPADC_HW_CHAN_TBAT_,
+ DA9150_GPADC_HW_CHAN_TJUNC_CORE,
+ DA9150_GPADC_HW_CHAN_TJUNC_CORE_,
+ DA9150_GPADC_HW_CHAN_TJUNC_OVP,
+ DA9150_GPADC_HW_CHAN_TJUNC_OVP_,
+};
+
+enum da9150_gpadc_channel {
+ DA9150_GPADC_CHAN_GPIOA = 0,
+ DA9150_GPADC_CHAN_GPIOB,
+ DA9150_GPADC_CHAN_GPIOC,
+ DA9150_GPADC_CHAN_GPIOD,
+ DA9150_GPADC_CHAN_IBUS,
+ DA9150_GPADC_CHAN_VBUS,
+ DA9150_GPADC_CHAN_VSYS,
+ DA9150_GPADC_CHAN_VBAT,
+ DA9150_GPADC_CHAN_TBAT,
+ DA9150_GPADC_CHAN_TJUNC_CORE,
+ DA9150_GPADC_CHAN_TJUNC_OVP,
+};
+
+/* Private data */
+struct da9150_gpadc {
+ struct da9150 *da9150;
+ struct device *dev;
+
+ struct mutex lock;
+ struct completion complete;
+};
+
+
+static irqreturn_t da9150_gpadc_irq(int irq, void *data)
+{
+
+ struct da9150_gpadc *gpadc = data;
+
+ complete(&gpadc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int da9150_gpadc_read_adc(struct da9150_gpadc *gpadc, int hw_chan)
+{
+ u8 result_regs[2];
+ int result;
+
+ mutex_lock(&gpadc->lock);
+
+ /* Set channel & enable measurement */
+ da9150_reg_write(gpadc->da9150, DA9150_GPADC_MAN,
+ (DA9150_GPADC_EN_MASK |
+ hw_chan << DA9150_GPADC_MUX_SHIFT));
+
+ /* Consume left-over completion from a previous timeout */
+ try_wait_for_completion(&gpadc->complete);
+
+ /* Check for actual completion */
+ wait_for_completion_timeout(&gpadc->complete, msecs_to_jiffies(5));
+
+ /* Read result and status from device */
+ da9150_bulk_read(gpadc->da9150, DA9150_GPADC_RES_A, 2, result_regs);
+
+ mutex_unlock(&gpadc->lock);
+
+ /* Check to make sure device really has completed reading */
+ if (result_regs[1] & DA9150_GPADC_RUN_MASK) {
+ dev_err(gpadc->dev, "Timeout on channel %d of GPADC\n",
+ hw_chan);
+ return -ETIMEDOUT;
+ }
+
+ /* LSBs - 2 bits */
+ result = (result_regs[1] & DA9150_GPADC_RES_L_MASK) >>
+ DA9150_GPADC_RES_L_SHIFT;
+ /* MSBs - 8 bits */
+ result |= result_regs[0] << DA9150_GPADC_RES_L_BITS;
+
+ return result;
+}
+
+static inline int da9150_gpadc_gpio_6v_voltage_now(int raw_val)
+{
+ /* Convert to mV */
+ return (6 * ((raw_val * 1000) + 500)) / 1024;
+}
+
+static inline int da9150_gpadc_ibus_current_avg(int raw_val)
+{
+ /* Convert to mA */
+ return (4 * ((raw_val * 1000) + 500)) / 2048;
+}
+
+static inline int da9150_gpadc_vbus_21v_voltage_now(int raw_val)
+{
+ /* Convert to mV */
+ return (21 * ((raw_val * 1000) + 500)) / 1024;
+}
+
+static inline int da9150_gpadc_vsys_6v_voltage_now(int raw_val)
+{
+ /* Convert to mV */
+ return (3 * ((raw_val * 1000) + 500)) / 512;
+}
+
+static int da9150_gpadc_read_processed(struct da9150_gpadc *gpadc, int channel,
+ int hw_chan, int *val)
+{
+ int raw_val;
+
+ raw_val = da9150_gpadc_read_adc(gpadc, hw_chan);
+ if (raw_val < 0)
+ return raw_val;
+
+ switch (channel) {
+ case DA9150_GPADC_CHAN_GPIOA:
+ case DA9150_GPADC_CHAN_GPIOB:
+ case DA9150_GPADC_CHAN_GPIOC:
+ case DA9150_GPADC_CHAN_GPIOD:
+ *val = da9150_gpadc_gpio_6v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_IBUS:
+ *val = da9150_gpadc_ibus_current_avg(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_VBUS:
+ *val = da9150_gpadc_vbus_21v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_VSYS:
+ *val = da9150_gpadc_vsys_6v_voltage_now(raw_val);
+ break;
+ default:
+ /* No processing for other channels so return raw value */
+ *val = raw_val;
+ break;
+ }
+
+ return IIO_VAL_INT;
+}
+
+static int da9150_gpadc_read_scale(int channel, int *val, int *val2)
+{
+ switch (channel) {
+ case DA9150_GPADC_CHAN_VBAT:
+ *val = 2932;
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+ case DA9150_GPADC_CHAN_TJUNC_CORE:
+ case DA9150_GPADC_CHAN_TJUNC_OVP:
+ *val = 1000000;
+ *val2 = 4420;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da9150_gpadc_read_offset(int channel, int *val)
+{
+ switch (channel) {
+ case DA9150_GPADC_CHAN_VBAT:
+ *val = 1500000 / 2932;
+ return IIO_VAL_INT;
+ case DA9150_GPADC_CHAN_TJUNC_CORE:
+ case DA9150_GPADC_CHAN_TJUNC_OVP:
+ *val = -144;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da9150_gpadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct da9150_gpadc *gpadc = iio_priv(indio_dev);
+
+ if ((chan->channel < DA9150_GPADC_CHAN_GPIOA) ||
+ (chan->channel > DA9150_GPADC_CHAN_TJUNC_OVP))
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ return da9150_gpadc_read_processed(gpadc, chan->channel,
+ chan->address, val);
+ case IIO_CHAN_INFO_SCALE:
+ return da9150_gpadc_read_scale(chan->channel, val, val2);
+ case IIO_CHAN_INFO_OFFSET:
+ return da9150_gpadc_read_offset(chan->channel, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info da9150_gpadc_info = {
+ .read_raw = &da9150_gpadc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define DA9150_GPADC_CHANNEL(_id, _hw_id, _type, chan_info, \
+ _ext_name) { \
+ .type = _type, \
+ .indexed = 1, \
+ .channel = DA9150_GPADC_CHAN_##_id, \
+ .address = DA9150_GPADC_HW_CHAN_##_hw_id, \
+ .info_mask_separate = chan_info, \
+ .extend_name = _ext_name, \
+ .datasheet_name = #_id, \
+}
+
+#define DA9150_GPADC_CHANNEL_RAW(_id, _hw_id, _type, _ext_name) \
+ DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \
+ BIT(IIO_CHAN_INFO_RAW), _ext_name)
+
+#define DA9150_GPADC_CHANNEL_SCALED(_id, _hw_id, _type, _ext_name) \
+ DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ _ext_name)
+
+#define DA9150_GPADC_CHANNEL_PROCESSED(_id, _hw_id, _type, _ext_name) \
+ DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \
+ BIT(IIO_CHAN_INFO_PROCESSED), _ext_name)
+
+/* Supported channels */
+static const struct iio_chan_spec da9150_gpadc_channels[] = {
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOA, GPIOA_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOB, GPIOB_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOC, GPIOC_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOD, GPIOD_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(IBUS, IBUS_SENSE, IIO_CURRENT, "ibus"),
+ DA9150_GPADC_CHANNEL_PROCESSED(VBUS, VBUS_DIV_, IIO_VOLTAGE, "vbus"),
+ DA9150_GPADC_CHANNEL_PROCESSED(VSYS, VSYS, IIO_VOLTAGE, "vsys"),
+ DA9150_GPADC_CHANNEL_SCALED(VBAT, VBAT, IIO_VOLTAGE, "vbat"),
+ DA9150_GPADC_CHANNEL_RAW(TBAT, TBAT, IIO_VOLTAGE, "tbat"),
+ DA9150_GPADC_CHANNEL_SCALED(TJUNC_CORE, TJUNC_CORE, IIO_TEMP,
+ "tjunc_core"),
+ DA9150_GPADC_CHANNEL_SCALED(TJUNC_OVP, TJUNC_OVP, IIO_TEMP,
+ "tjunc_ovp"),
+};
+
+/* Default maps used by da9150-charger */
+static struct iio_map da9150_gpadc_default_maps[] = {
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_IBUS",
+ .adc_channel_label = "IBUS",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_VBUS",
+ .adc_channel_label = "VBUS",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_TJUNC",
+ .adc_channel_label = "TJUNC_CORE",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_VBAT",
+ .adc_channel_label = "VBAT",
+ },
+ {},
+};
+
+static int da9150_gpadc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct da9150 *da9150 = dev_get_drvdata(dev->parent);
+ struct da9150_gpadc *gpadc;
+ struct iio_dev *indio_dev;
+ int irq, ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "Failed to allocate IIO device\n");
+ return -ENOMEM;
+ }
+ gpadc = iio_priv(indio_dev);
+
+ platform_set_drvdata(pdev, indio_dev);
+ gpadc->da9150 = da9150;
+ gpadc->dev = dev;
+ mutex_init(&gpadc->lock);
+ init_completion(&gpadc->complete);
+
+ irq = platform_get_irq_byname(pdev, "GPADC");
+ if (irq < 0) {
+ dev_err(dev, "Failed to get IRQ: %d\n", irq);
+ return irq;
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, da9150_gpadc_irq,
+ IRQF_ONESHOT, "GPADC", gpadc);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
+ return ret;
+ }
+
+ ret = iio_map_array_register(indio_dev, da9150_gpadc_default_maps);
+ if (ret) {
+ dev_err(dev, "Failed to register IIO maps: %d\n", ret);
+ return ret;
+ }
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &da9150_gpadc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = da9150_gpadc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "Failed to register IIO device: %d\n", ret);
+ goto iio_map_unreg;
+ }
+
+ return 0;
+
+iio_map_unreg:
+ iio_map_array_unregister(indio_dev);
+
+ return ret;
+}
+
+static int da9150_gpadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_device_unregister(indio_dev);
+ iio_map_array_unregister(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver da9150_gpadc_driver = {
+ .driver = {
+ .name = "da9150-gpadc",
+ },
+ .probe = da9150_gpadc_probe,
+ .remove = da9150_gpadc_remove,
+};
+
+module_platform_driver(da9150_gpadc_driver);
+
+MODULE_DESCRIPTION("GPADC Driver for DA9150");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index 43620fd4c66a..3a2dbb3b4926 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -39,6 +39,8 @@
#include <linux/iio/iio.h>
#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
/* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */
#define ADC_V1_CON(x) ((x) + 0x00)
@@ -90,11 +92,14 @@
#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100))
+#define EXYNOS_ADCV1_PHY_OFFSET 0x0718
+#define EXYNOS_ADCV2_PHY_OFFSET 0x0720
+
struct exynos_adc {
struct exynos_adc_data *data;
struct device *dev;
void __iomem *regs;
- void __iomem *enable_reg;
+ struct regmap *pmu_map;
struct clk *clk;
struct clk *sclk;
unsigned int irq;
@@ -110,6 +115,7 @@ struct exynos_adc_data {
int num_channels;
bool needs_sclk;
bool needs_adc_phy;
+ int phy_offset;
u32 mask;
void (*init_hw)(struct exynos_adc *info);
@@ -183,7 +189,7 @@ static void exynos_adc_v1_init_hw(struct exynos_adc *info)
u32 con1;
if (info->data->needs_adc_phy)
- writel(1, info->enable_reg);
+ regmap_write(info->pmu_map, info->data->phy_offset, 1);
/* set default prescaler values and Enable prescaler */
con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
@@ -198,7 +204,7 @@ static void exynos_adc_v1_exit_hw(struct exynos_adc *info)
u32 con;
if (info->data->needs_adc_phy)
- writel(0, info->enable_reg);
+ regmap_write(info->pmu_map, info->data->phy_offset, 0);
con = readl(ADC_V1_CON(info->regs));
con |= ADC_V1_CON_STANDBY;
@@ -225,6 +231,7 @@ static const struct exynos_adc_data exynos_adc_v1_data = {
.num_channels = MAX_ADC_V1_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.needs_adc_phy = true,
+ .phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
.init_hw = exynos_adc_v1_init_hw,
.exit_hw = exynos_adc_v1_exit_hw,
@@ -314,7 +321,7 @@ static void exynos_adc_v2_init_hw(struct exynos_adc *info)
u32 con1, con2;
if (info->data->needs_adc_phy)
- writel(1, info->enable_reg);
+ regmap_write(info->pmu_map, info->data->phy_offset, 1);
con1 = ADC_V2_CON1_SOFT_RESET;
writel(con1, ADC_V2_CON1(info->regs));
@@ -332,7 +339,7 @@ static void exynos_adc_v2_exit_hw(struct exynos_adc *info)
u32 con;
if (info->data->needs_adc_phy)
- writel(0, info->enable_reg);
+ regmap_write(info->pmu_map, info->data->phy_offset, 0);
con = readl(ADC_V2_CON1(info->regs));
con &= ~ADC_CON_EN_START;
@@ -362,6 +369,7 @@ static const struct exynos_adc_data exynos_adc_v2_data = {
.num_channels = MAX_ADC_V2_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.needs_adc_phy = true,
+ .phy_offset = EXYNOS_ADCV2_PHY_OFFSET,
.init_hw = exynos_adc_v2_init_hw,
.exit_hw = exynos_adc_v2_exit_hw,
@@ -374,6 +382,7 @@ static const struct exynos_adc_data exynos3250_adc_data = {
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.needs_sclk = true,
.needs_adc_phy = true,
+ .phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
.init_hw = exynos_adc_v2_init_hw,
.exit_hw = exynos_adc_v2_exit_hw,
@@ -381,6 +390,35 @@ static const struct exynos_adc_data exynos3250_adc_data = {
.start_conv = exynos_adc_v2_start_conv,
};
+static void exynos_adc_exynos7_init_hw(struct exynos_adc *info)
+{
+ u32 con1, con2;
+
+ if (info->data->needs_adc_phy)
+ regmap_write(info->pmu_map, info->data->phy_offset, 1);
+
+ con1 = ADC_V2_CON1_SOFT_RESET;
+ writel(con1, ADC_V2_CON1(info->regs));
+
+ con2 = readl(ADC_V2_CON2(info->regs));
+ con2 &= ~ADC_V2_CON2_C_TIME(7);
+ con2 |= ADC_V2_CON2_C_TIME(0);
+ writel(con2, ADC_V2_CON2(info->regs));
+
+ /* Enable interrupts */
+ writel(1, ADC_V2_INT_EN(info->regs));
+}
+
+static const struct exynos_adc_data exynos7_adc_data = {
+ .num_channels = MAX_ADC_V1_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+
+ .init_hw = exynos_adc_exynos7_init_hw,
+ .exit_hw = exynos_adc_v2_exit_hw,
+ .clear_irq = exynos_adc_v2_clear_irq,
+ .start_conv = exynos_adc_v2_start_conv,
+};
+
static const struct of_device_id exynos_adc_match[] = {
{
.compatible = "samsung,s3c2410-adc",
@@ -406,6 +444,9 @@ static const struct of_device_id exynos_adc_match[] = {
}, {
.compatible = "samsung,exynos3250-adc",
.data = &exynos3250_adc_data,
+ }, {
+ .compatible = "samsung,exynos7-adc",
+ .data = &exynos7_adc_data,
},
{},
};
@@ -558,10 +599,13 @@ static int exynos_adc_probe(struct platform_device *pdev)
if (info->data->needs_adc_phy) {
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- info->enable_reg = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(info->enable_reg))
- return PTR_ERR(info->enable_reg);
+ info->pmu_map = syscon_regmap_lookup_by_phandle(
+ pdev->dev.of_node,
+ "samsung,syscon-phandle");
+ if (IS_ERR(info->pmu_map)) {
+ dev_err(&pdev->dev, "syscon regmap lookup failed.\n");
+ return PTR_ERR(info->pmu_map);
+ }
}
irq = platform_get_irq(pdev, 0);
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index 28a086e48776..efbfd12a4bfd 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -1,9 +1,30 @@
/*
* Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
+ * Copyright (C) 2014 Rose Technology
+ * Allan Bendorff Jensen <abj@rosetechnology.dk>
+ * Soren Andersen <san@rosetechnology.dk>
+ *
+ * Driver for following ADC chips from Microchip Technology's:
+ * 10 Bit converter
+ * MCP3001
+ * MCP3002
+ * MCP3004
+ * MCP3008
+ * ------------
+ * 12 bit converter
+ * MCP3201
+ * MCP3202
+ * MCP3204
+ * MCP3208
+ * ------------
*
- * Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips.
* Datasheet can be found here:
- * http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf mcp3001
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf mcp3002
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf mcp3004/08
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf mcp3201
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf mcp3202
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08
*
* 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
@@ -11,19 +32,29 @@
*/
#include <linux/err.h>
+#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/regulator/consumer.h>
-#define MCP_SINGLE_ENDED (1 << 3)
-#define MCP_START_BIT (1 << 4)
-
enum {
+ mcp3001,
+ mcp3002,
+ mcp3004,
+ mcp3008,
+ mcp3201,
+ mcp3202,
mcp3204,
mcp3208,
};
+struct mcp320x_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ unsigned int resolution;
+};
+
struct mcp320x {
struct spi_device *spi;
struct spi_message msg;
@@ -34,19 +65,69 @@ struct mcp320x {
struct regulator *reg;
struct mutex lock;
+ const struct mcp320x_chip_info *chip_info;
};
-static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg)
+static int mcp320x_channel_to_tx_data(int device_index,
+ const unsigned int channel, bool differential)
+{
+ int start_bit = 1;
+
+ switch (device_index) {
+ case mcp3001:
+ case mcp3201:
+ return 0;
+ case mcp3002:
+ case mcp3202:
+ return ((start_bit << 4) | (!differential << 3) |
+ (channel << 2));
+ case mcp3004:
+ case mcp3204:
+ case mcp3008:
+ case mcp3208:
+ return ((start_bit << 6) | (!differential << 5) |
+ (channel << 2));
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
+ bool differential, int device_index)
{
int ret;
- adc->tx_buf = msg;
- ret = spi_sync(adc->spi, &adc->msg);
- if (ret < 0)
- return ret;
+ adc->rx_buf[0] = 0;
+ adc->rx_buf[1] = 0;
+ adc->tx_buf = mcp320x_channel_to_tx_data(device_index,
+ channel, differential);
+
+ if (device_index != mcp3001 && device_index != mcp3201) {
+ ret = spi_sync(adc->spi, &adc->msg);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = spi_read(adc->spi, &adc->rx_buf, sizeof(adc->rx_buf));
+ if (ret < 0)
+ return ret;
+ }
- return ((adc->rx_buf[0] & 0x3f) << 6) |
- (adc->rx_buf[1] >> 2);
+ switch (device_index) {
+ case mcp3001:
+ return (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
+ case mcp3002:
+ case mcp3004:
+ case mcp3008:
+ return (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
+ case mcp3201:
+ return (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);
+ case mcp3202:
+ case mcp3204:
+ case mcp3208:
+ return (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
+ default:
+ return -EINVAL;
+ }
}
static int mcp320x_read_raw(struct iio_dev *indio_dev,
@@ -55,18 +136,17 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
{
struct mcp320x *adc = iio_priv(indio_dev);
int ret = -EINVAL;
+ int device_index = 0;
mutex_lock(&adc->lock);
+ device_index = spi_get_device_id(adc->spi)->driver_data;
+
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (channel->differential)
- ret = mcp320x_adc_conversion(adc,
- MCP_START_BIT | channel->address);
- else
- ret = mcp320x_adc_conversion(adc,
- MCP_START_BIT | MCP_SINGLE_ENDED |
- channel->address);
+ ret = mcp320x_adc_conversion(adc, channel->address,
+ channel->differential, device_index);
+
if (ret < 0)
goto out;
@@ -75,18 +155,15 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
break;
case IIO_CHAN_INFO_SCALE:
- /* Digital output code = (4096 * Vin) / Vref */
ret = regulator_get_voltage(adc->reg);
if (ret < 0)
goto out;
+ /* convert regulator output voltage to mV */
*val = ret / 1000;
- *val2 = 12;
+ *val2 = adc->chip_info->resolution;
ret = IIO_VAL_FRACTIONAL_LOG2;
break;
-
- default:
- break;
}
out:
@@ -117,6 +194,16 @@ out:
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
}
+static const struct iio_chan_spec mcp3201_channels[] = {
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+};
+
+static const struct iio_chan_spec mcp3202_channels[] = {
+ MCP320X_VOLTAGE_CHANNEL(0),
+ MCP320X_VOLTAGE_CHANNEL(1),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+};
+
static const struct iio_chan_spec mcp3204_channels[] = {
MCP320X_VOLTAGE_CHANNEL(0),
MCP320X_VOLTAGE_CHANNEL(1),
@@ -146,19 +233,46 @@ static const struct iio_info mcp320x_info = {
.driver_module = THIS_MODULE,
};
-struct mcp3208_chip_info {
- const struct iio_chan_spec *channels;
- unsigned int num_channels;
-};
-
-static const struct mcp3208_chip_info mcp3208_chip_infos[] = {
+static const struct mcp320x_chip_info mcp320x_chip_infos[] = {
+ [mcp3001] = {
+ .channels = mcp3201_channels,
+ .num_channels = ARRAY_SIZE(mcp3201_channels),
+ .resolution = 10
+ },
+ [mcp3002] = {
+ .channels = mcp3202_channels,
+ .num_channels = ARRAY_SIZE(mcp3202_channels),
+ .resolution = 10
+ },
+ [mcp3004] = {
+ .channels = mcp3204_channels,
+ .num_channels = ARRAY_SIZE(mcp3204_channels),
+ .resolution = 10
+ },
+ [mcp3008] = {
+ .channels = mcp3208_channels,
+ .num_channels = ARRAY_SIZE(mcp3208_channels),
+ .resolution = 10
+ },
+ [mcp3201] = {
+ .channels = mcp3201_channels,
+ .num_channels = ARRAY_SIZE(mcp3201_channels),
+ .resolution = 12
+ },
+ [mcp3202] = {
+ .channels = mcp3202_channels,
+ .num_channels = ARRAY_SIZE(mcp3202_channels),
+ .resolution = 12
+ },
[mcp3204] = {
.channels = mcp3204_channels,
- .num_channels = ARRAY_SIZE(mcp3204_channels)
+ .num_channels = ARRAY_SIZE(mcp3204_channels),
+ .resolution = 12
},
[mcp3208] = {
.channels = mcp3208_channels,
- .num_channels = ARRAY_SIZE(mcp3208_channels)
+ .num_channels = ARRAY_SIZE(mcp3208_channels),
+ .resolution = 12
},
};
@@ -166,7 +280,7 @@ static int mcp320x_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct mcp320x *adc;
- const struct mcp3208_chip_info *chip_info;
+ const struct mcp320x_chip_info *chip_info;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
@@ -181,7 +295,7 @@ static int mcp320x_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mcp320x_info;
- chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data];
+ chip_info = &mcp320x_chip_infos[spi_get_device_id(spi)->driver_data];
indio_dev->channels = chip_info->channels;
indio_dev->num_channels = chip_info->num_channels;
@@ -226,7 +340,45 @@ static int mcp320x_remove(struct spi_device *spi)
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id mcp320x_dt_ids[] = {
+ {
+ .compatible = "mcp3001",
+ .data = &mcp320x_chip_infos[mcp3001],
+ }, {
+ .compatible = "mcp3002",
+ .data = &mcp320x_chip_infos[mcp3002],
+ }, {
+ .compatible = "mcp3004",
+ .data = &mcp320x_chip_infos[mcp3004],
+ }, {
+ .compatible = "mcp3008",
+ .data = &mcp320x_chip_infos[mcp3008],
+ }, {
+ .compatible = "mcp3201",
+ .data = &mcp320x_chip_infos[mcp3201],
+ }, {
+ .compatible = "mcp3202",
+ .data = &mcp320x_chip_infos[mcp3202],
+ }, {
+ .compatible = "mcp3204",
+ .data = &mcp320x_chip_infos[mcp3204],
+ }, {
+ .compatible = "mcp3208",
+ .data = &mcp320x_chip_infos[mcp3208],
+ }, {
+ }
+};
+MODULE_DEVICE_TABLE(of, mcp320x_dt_ids);
+#endif
+
static const struct spi_device_id mcp320x_id[] = {
+ { "mcp3001", mcp3001 },
+ { "mcp3002", mcp3002 },
+ { "mcp3004", mcp3004 },
+ { "mcp3008", mcp3008 },
+ { "mcp3201", mcp3201 },
+ { "mcp3202", mcp3202 },
{ "mcp3204", mcp3204 },
{ "mcp3208", mcp3208 },
{ }
@@ -245,5 +397,5 @@ static struct spi_driver mcp320x_driver = {
module_spi_driver(mcp320x_driver);
MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
-MODULE_DESCRIPTION("Microchip Technology MCP3204/08");
+MODULE_DESCRIPTION("Microchip Technology MCP3x01/02/04/08");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c
index 51672256072b..b96c636470ef 100644
--- a/drivers/iio/adc/mcp3422.c
+++ b/drivers/iio/adc/mcp3422.c
@@ -58,20 +58,11 @@
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}
-/* LSB is in nV to eliminate floating point */
-static const u32 rates_to_lsb[] = {1000000, 250000, 62500, 15625};
-
-/*
- * scales calculated as:
- * rates_to_lsb[sample_rate] / (1 << pga);
- * pga is 1 for 0, 2
- */
-
static const int mcp3422_scales[4][4] = {
- { 1000000, 250000, 62500, 15625 },
- { 500000 , 125000, 31250, 7812 },
- { 250000 , 62500 , 15625, 3906 },
- { 125000 , 31250 , 7812 , 1953 } };
+ { 1000000, 500000, 250000, 125000 },
+ { 250000 , 125000, 62500 , 31250 },
+ { 62500 , 31250 , 15625 , 7812 },
+ { 15625 , 7812 , 3906 , 1953 } };
/* Constant msleep times for data acquisitions */
static const int mcp3422_read_times[4] = {
diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c
index b58d6302521f..d095efe1ba14 100644
--- a/drivers/iio/adc/men_z188_adc.c
+++ b/drivers/iio/adc/men_z188_adc.c
@@ -152,6 +152,7 @@ static void men_z188_remove(struct mcb_device *dev)
static const struct mcb_device_id men_z188_ids[] = {
{ .device = 0xbc },
+ { }
};
MODULE_DEVICE_TABLE(mcb, men_z188_ids);
diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c
new file mode 100644
index 000000000000..fabd24edc2a1
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-iadc.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/* IADC register and bit definition */
+#define IADC_REVISION2 0x1
+#define IADC_REVISION2_SUPPORTED_IADC 1
+
+#define IADC_PERPH_TYPE 0x4
+#define IADC_PERPH_TYPE_ADC 8
+
+#define IADC_PERPH_SUBTYPE 0x5
+#define IADC_PERPH_SUBTYPE_IADC 3
+
+#define IADC_STATUS1 0x8
+#define IADC_STATUS1_OP_MODE 4
+#define IADC_STATUS1_REQ_STS BIT(1)
+#define IADC_STATUS1_EOC BIT(0)
+#define IADC_STATUS1_REQ_STS_EOC_MASK 0x3
+
+#define IADC_MODE_CTL 0x40
+#define IADC_OP_MODE_SHIFT 3
+#define IADC_OP_MODE_NORMAL 0
+#define IADC_TRIM_EN BIT(0)
+
+#define IADC_EN_CTL1 0x46
+#define IADC_EN_CTL1_SET BIT(7)
+
+#define IADC_CH_SEL_CTL 0x48
+
+#define IADC_DIG_PARAM 0x50
+#define IADC_DIG_DEC_RATIO_SEL_SHIFT 2
+
+#define IADC_HW_SETTLE_DELAY 0x51
+
+#define IADC_CONV_REQ 0x52
+#define IADC_CONV_REQ_SET BIT(7)
+
+#define IADC_FAST_AVG_CTL 0x5a
+#define IADC_FAST_AVG_EN 0x5b
+#define IADC_FAST_AVG_EN_SET BIT(7)
+
+#define IADC_PERH_RESET_CTL3 0xda
+#define IADC_FOLLOW_WARM_RB BIT(2)
+
+#define IADC_DATA 0x60 /* 16 bits */
+
+#define IADC_SEC_ACCESS 0xd0
+#define IADC_SEC_ACCESS_DATA 0xa5
+
+#define IADC_NOMINAL_RSENSE 0xf4
+#define IADC_NOMINAL_RSENSE_SIGN_MASK BIT(7)
+
+#define IADC_REF_GAIN_MICRO_VOLTS 17857
+
+#define IADC_INT_RSENSE_DEVIATION 15625 /* nano Ohms per bit */
+
+#define IADC_INT_RSENSE_IDEAL_VALUE 10000 /* micro Ohms */
+#define IADC_INT_RSENSE_DEFAULT_VALUE 7800 /* micro Ohms */
+#define IADC_INT_RSENSE_DEFAULT_GF 9000 /* micro Ohms */
+#define IADC_INT_RSENSE_DEFAULT_SMIC 9700 /* micro Ohms */
+
+#define IADC_CONV_TIME_MIN_US 2000
+#define IADC_CONV_TIME_MAX_US 2100
+
+#define IADC_DEF_PRESCALING 0 /* 1:1 */
+#define IADC_DEF_DECIMATION 0 /* 512 */
+#define IADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
+#define IADC_DEF_AVG_SAMPLES 0 /* 1 sample */
+
+/* IADC channel list */
+#define IADC_INT_RSENSE 0
+#define IADC_EXT_RSENSE 1
+#define IADC_GAIN_17P857MV 3
+#define IADC_EXT_OFFSET_CSP_CSN 5
+#define IADC_INT_OFFSET_CSP2_CSN2 6
+
+/**
+ * struct iadc_chip - IADC Current ADC device structure.
+ * @regmap: regmap for register read/write.
+ * @dev: This device pointer.
+ * @base: base offset for the ADC peripheral.
+ * @rsense: Values of the internal and external sense resister in micro Ohms.
+ * @poll_eoc: Poll for end of conversion instead of waiting for IRQ.
+ * @offset: Raw offset values for the internal and external channels.
+ * @gain: Raw gain of the channels.
+ * @lock: ADC lock for access to the peripheral.
+ * @complete: ADC notification after end of conversion interrupt is received.
+ */
+struct iadc_chip {
+ struct regmap *regmap;
+ struct device *dev;
+ u16 base;
+ bool poll_eoc;
+ u32 rsense[2];
+ u16 offset[2];
+ u16 gain;
+ struct mutex lock;
+ struct completion complete;
+};
+
+static int iadc_read(struct iadc_chip *iadc, u16 offset, u8 *data)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(iadc->regmap, iadc->base + offset, &val);
+ if (ret < 0)
+ return ret;
+
+ *data = val;
+ return 0;
+}
+
+static int iadc_write(struct iadc_chip *iadc, u16 offset, u8 data)
+{
+ return regmap_write(iadc->regmap, iadc->base + offset, data);
+}
+
+static int iadc_reset(struct iadc_chip *iadc)
+{
+ u8 data;
+ int ret;
+
+ ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_read(iadc, IADC_PERH_RESET_CTL3, &data);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
+ if (ret < 0)
+ return ret;
+
+ data |= IADC_FOLLOW_WARM_RB;
+
+ return iadc_write(iadc, IADC_PERH_RESET_CTL3, data);
+}
+
+static int iadc_set_state(struct iadc_chip *iadc, bool state)
+{
+ return iadc_write(iadc, IADC_EN_CTL1, state ? IADC_EN_CTL1_SET : 0);
+}
+
+static void iadc_status_show(struct iadc_chip *iadc)
+{
+ u8 mode, sta1, chan, dig, en, req;
+ int ret;
+
+ ret = iadc_read(iadc, IADC_MODE_CTL, &mode);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_DIG_PARAM, &dig);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_CH_SEL_CTL, &chan);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_CONV_REQ, &req);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_STATUS1, &sta1);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_EN_CTL1, &en);
+ if (ret < 0)
+ return;
+
+ dev_err(iadc->dev,
+ "mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n",
+ mode, en, chan, dig, req, sta1);
+}
+
+static int iadc_configure(struct iadc_chip *iadc, int channel)
+{
+ u8 decim, mode;
+ int ret;
+
+ /* Mode selection */
+ mode = (IADC_OP_MODE_NORMAL << IADC_OP_MODE_SHIFT) | IADC_TRIM_EN;
+ ret = iadc_write(iadc, IADC_MODE_CTL, mode);
+ if (ret < 0)
+ return ret;
+
+ /* Channel selection */
+ ret = iadc_write(iadc, IADC_CH_SEL_CTL, channel);
+ if (ret < 0)
+ return ret;
+
+ /* Digital parameter setup */
+ decim = IADC_DEF_DECIMATION << IADC_DIG_DEC_RATIO_SEL_SHIFT;
+ ret = iadc_write(iadc, IADC_DIG_PARAM, decim);
+ if (ret < 0)
+ return ret;
+
+ /* HW settle time delay */
+ ret = iadc_write(iadc, IADC_HW_SETTLE_DELAY, IADC_DEF_HW_SETTLE_TIME);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_write(iadc, IADC_FAST_AVG_CTL, IADC_DEF_AVG_SAMPLES);
+ if (ret < 0)
+ return ret;
+
+ if (IADC_DEF_AVG_SAMPLES)
+ ret = iadc_write(iadc, IADC_FAST_AVG_EN, IADC_FAST_AVG_EN_SET);
+ else
+ ret = iadc_write(iadc, IADC_FAST_AVG_EN, 0);
+
+ if (ret < 0)
+ return ret;
+
+ if (!iadc->poll_eoc)
+ reinit_completion(&iadc->complete);
+
+ ret = iadc_set_state(iadc, true);
+ if (ret < 0)
+ return ret;
+
+ /* Request conversion */
+ return iadc_write(iadc, IADC_CONV_REQ, IADC_CONV_REQ_SET);
+}
+
+static int iadc_poll_wait_eoc(struct iadc_chip *iadc, unsigned int interval_us)
+{
+ unsigned int count, retry;
+ int ret;
+ u8 sta1;
+
+ retry = interval_us / IADC_CONV_TIME_MIN_US;
+
+ for (count = 0; count < retry; count++) {
+ ret = iadc_read(iadc, IADC_STATUS1, &sta1);
+ if (ret < 0)
+ return ret;
+
+ sta1 &= IADC_STATUS1_REQ_STS_EOC_MASK;
+ if (sta1 == IADC_STATUS1_EOC)
+ return 0;
+
+ usleep_range(IADC_CONV_TIME_MIN_US, IADC_CONV_TIME_MAX_US);
+ }
+
+ iadc_status_show(iadc);
+
+ return -ETIMEDOUT;
+}
+
+static int iadc_read_result(struct iadc_chip *iadc, u16 *data)
+{
+ return regmap_bulk_read(iadc->regmap, iadc->base + IADC_DATA, data, 2);
+}
+
+static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data)
+{
+ unsigned int wait;
+ int ret;
+
+ ret = iadc_configure(iadc, chan);
+ if (ret < 0)
+ goto exit;
+
+ wait = BIT(IADC_DEF_AVG_SAMPLES) * IADC_CONV_TIME_MIN_US * 2;
+
+ if (iadc->poll_eoc) {
+ ret = iadc_poll_wait_eoc(iadc, wait);
+ } else {
+ ret = wait_for_completion_timeout(&iadc->complete,
+ usecs_to_jiffies(wait));
+ if (!ret)
+ ret = -ETIMEDOUT;
+ else
+ /* double check conversion status */
+ ret = iadc_poll_wait_eoc(iadc, IADC_CONV_TIME_MIN_US);
+ }
+
+ if (!ret)
+ ret = iadc_read_result(iadc, data);
+exit:
+ iadc_set_state(iadc, false);
+ if (ret < 0)
+ dev_err(iadc->dev, "conversion failed\n");
+
+ return ret;
+}
+
+static int iadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct iadc_chip *iadc = iio_priv(indio_dev);
+ s32 isense_ua, vsense_uv;
+ u16 adc_raw, vsense_raw;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&iadc->lock);
+ ret = iadc_do_conversion(iadc, chan->channel, &adc_raw);
+ mutex_unlock(&iadc->lock);
+ if (ret < 0)
+ return ret;
+
+ vsense_raw = adc_raw - iadc->offset[chan->channel];
+
+ vsense_uv = vsense_raw * IADC_REF_GAIN_MICRO_VOLTS;
+ vsense_uv /= (s32)iadc->gain - iadc->offset[chan->channel];
+
+ isense_ua = vsense_uv / iadc->rsense[chan->channel];
+
+ dev_dbg(iadc->dev, "off %d gain %d adc %d %duV I %duA\n",
+ iadc->offset[chan->channel], iadc->gain,
+ adc_raw, vsense_uv, isense_ua);
+
+ *val = isense_ua;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info iadc_info = {
+ .read_raw = iadc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static irqreturn_t iadc_isr(int irq, void *dev_id)
+{
+ struct iadc_chip *iadc = dev_id;
+
+ complete(&iadc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int iadc_update_offset(struct iadc_chip *iadc)
+{
+ int ret;
+
+ ret = iadc_do_conversion(iadc, IADC_GAIN_17P857MV, &iadc->gain);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_do_conversion(iadc, IADC_INT_OFFSET_CSP2_CSN2,
+ &iadc->offset[IADC_INT_RSENSE]);
+ if (ret < 0)
+ return ret;
+
+ if (iadc->gain == iadc->offset[IADC_INT_RSENSE]) {
+ dev_err(iadc->dev, "error: internal offset == gain %d\n",
+ iadc->gain);
+ return -EINVAL;
+ }
+
+ ret = iadc_do_conversion(iadc, IADC_EXT_OFFSET_CSP_CSN,
+ &iadc->offset[IADC_EXT_RSENSE]);
+ if (ret < 0)
+ return ret;
+
+ if (iadc->gain == iadc->offset[IADC_EXT_RSENSE]) {
+ dev_err(iadc->dev, "error: external offset == gain %d\n",
+ iadc->gain);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int iadc_version_check(struct iadc_chip *iadc)
+{
+ u8 val;
+ int ret;
+
+ ret = iadc_read(iadc, IADC_PERPH_TYPE, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val < IADC_PERPH_TYPE_ADC) {
+ dev_err(iadc->dev, "%d is not ADC\n", val);
+ return -EINVAL;
+ }
+
+ ret = iadc_read(iadc, IADC_PERPH_SUBTYPE, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val < IADC_PERPH_SUBTYPE_IADC) {
+ dev_err(iadc->dev, "%d is not IADC\n", val);
+ return -EINVAL;
+ }
+
+ ret = iadc_read(iadc, IADC_REVISION2, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val < IADC_REVISION2_SUPPORTED_IADC) {
+ dev_err(iadc->dev, "revision %d not supported\n", val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int iadc_rsense_read(struct iadc_chip *iadc, struct device_node *node)
+{
+ int ret, sign, int_sense;
+ u8 deviation;
+
+ ret = of_property_read_u32(node, "qcom,external-resistor-micro-ohms",
+ &iadc->rsense[IADC_EXT_RSENSE]);
+ if (ret < 0)
+ iadc->rsense[IADC_EXT_RSENSE] = IADC_INT_RSENSE_IDEAL_VALUE;
+
+ if (!iadc->rsense[IADC_EXT_RSENSE]) {
+ dev_err(iadc->dev, "external resistor can't be zero Ohms");
+ return -EINVAL;
+ }
+
+ ret = iadc_read(iadc, IADC_NOMINAL_RSENSE, &deviation);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Deviation value stored is an offset from 10 mili Ohms, bit 7 is
+ * the sign, the remaining bits have an LSB of 15625 nano Ohms.
+ */
+ sign = (deviation & IADC_NOMINAL_RSENSE_SIGN_MASK) ? -1 : 1;
+
+ deviation &= ~IADC_NOMINAL_RSENSE_SIGN_MASK;
+
+ /* Scale it to nono Ohms */
+ int_sense = IADC_INT_RSENSE_IDEAL_VALUE * 1000;
+ int_sense += sign * deviation * IADC_INT_RSENSE_DEVIATION;
+ int_sense /= 1000; /* micro Ohms */
+
+ iadc->rsense[IADC_INT_RSENSE] = int_sense;
+ return 0;
+}
+
+static const struct iio_chan_spec iadc_channels[] = {
+ {
+ .type = IIO_CURRENT,
+ .datasheet_name = "INTERNAL_RSENSE",
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ },
+ {
+ .type = IIO_CURRENT,
+ .datasheet_name = "EXTERNAL_RSENSE",
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ },
+};
+
+static int iadc_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct iadc_chip *iadc;
+ int ret, irq_eoc;
+ u32 res;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*iadc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ iadc = iio_priv(indio_dev);
+ iadc->dev = dev;
+
+ iadc->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!iadc->regmap)
+ return -ENODEV;
+
+ init_completion(&iadc->complete);
+ mutex_init(&iadc->lock);
+
+ ret = of_property_read_u32(node, "reg", &res);
+ if (ret < 0)
+ return -ENODEV;
+
+ iadc->base = res;
+
+ ret = iadc_version_check(iadc);
+ if (ret < 0)
+ return -ENODEV;
+
+ ret = iadc_rsense_read(iadc, node);
+ if (ret < 0)
+ return -ENODEV;
+
+ dev_dbg(iadc->dev, "sense resistors %d and %d micro Ohm\n",
+ iadc->rsense[IADC_INT_RSENSE],
+ iadc->rsense[IADC_EXT_RSENSE]);
+
+ irq_eoc = platform_get_irq(pdev, 0);
+ if (irq_eoc == -EPROBE_DEFER)
+ return irq_eoc;
+
+ if (irq_eoc < 0)
+ iadc->poll_eoc = true;
+
+ ret = iadc_reset(iadc);
+ if (ret < 0) {
+ dev_err(dev, "reset failed\n");
+ return ret;
+ }
+
+ if (!iadc->poll_eoc) {
+ ret = devm_request_irq(dev, irq_eoc, iadc_isr, 0,
+ "spmi-iadc", iadc);
+ if (!ret)
+ enable_irq_wake(irq_eoc);
+ else
+ return ret;
+ } else {
+ device_init_wakeup(iadc->dev, 1);
+ }
+
+ ret = iadc_update_offset(iadc);
+ if (ret < 0) {
+ dev_err(dev, "failed offset calibration\n");
+ return ret;
+ }
+
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = node;
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &iadc_info;
+ indio_dev->channels = iadc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(iadc_channels);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id iadc_match_table[] = {
+ { .compatible = "qcom,spmi-iadc" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, iadc_match_table);
+
+static struct platform_driver iadc_driver = {
+ .driver = {
+ .name = "qcom-spmi-iadc",
+ .of_match_table = iadc_match_table,
+ },
+ .probe = iadc_probe,
+};
+
+module_platform_driver(iadc_driver);
+
+MODULE_ALIAS("platform:qcom-spmi-iadc");
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC current ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
new file mode 100644
index 000000000000..3211729bcb0b
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -0,0 +1,1016 @@
+/*
+ * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/log2.h>
+
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+
+/* VADC register and bit definitions */
+#define VADC_REVISION2 0x1
+#define VADC_REVISION2_SUPPORTED_VADC 1
+
+#define VADC_PERPH_TYPE 0x4
+#define VADC_PERPH_TYPE_ADC 8
+
+#define VADC_PERPH_SUBTYPE 0x5
+#define VADC_PERPH_SUBTYPE_VADC 1
+
+#define VADC_STATUS1 0x8
+#define VADC_STATUS1_OP_MODE 4
+#define VADC_STATUS1_REQ_STS BIT(1)
+#define VADC_STATUS1_EOC BIT(0)
+#define VADC_STATUS1_REQ_STS_EOC_MASK 0x3
+
+#define VADC_MODE_CTL 0x40
+#define VADC_OP_MODE_SHIFT 3
+#define VADC_OP_MODE_NORMAL 0
+#define VADC_AMUX_TRIM_EN BIT(1)
+#define VADC_ADC_TRIM_EN BIT(0)
+
+#define VADC_EN_CTL1 0x46
+#define VADC_EN_CTL1_SET BIT(7)
+
+#define VADC_ADC_CH_SEL_CTL 0x48
+
+#define VADC_ADC_DIG_PARAM 0x50
+#define VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT 2
+
+#define VADC_HW_SETTLE_DELAY 0x51
+
+#define VADC_CONV_REQ 0x52
+#define VADC_CONV_REQ_SET BIT(7)
+
+#define VADC_FAST_AVG_CTL 0x5a
+#define VADC_FAST_AVG_EN 0x5b
+#define VADC_FAST_AVG_EN_SET BIT(7)
+
+#define VADC_ACCESS 0xd0
+#define VADC_ACCESS_DATA 0xa5
+
+#define VADC_PERH_RESET_CTL3 0xda
+#define VADC_FOLLOW_WARM_RB BIT(2)
+
+#define VADC_DATA 0x60 /* 16 bits */
+
+#define VADC_CONV_TIME_MIN_US 2000
+#define VADC_CONV_TIME_MAX_US 2100
+
+/* Min ADC code represents 0V */
+#define VADC_MIN_ADC_CODE 0x6000
+/* Max ADC code represents full-scale range of 1.8V */
+#define VADC_MAX_ADC_CODE 0xa800
+
+#define VADC_ABSOLUTE_RANGE_UV 625000
+#define VADC_RATIOMETRIC_RANGE_UV 1800000
+
+#define VADC_DEF_PRESCALING 0 /* 1:1 */
+#define VADC_DEF_DECIMATION 0 /* 512 */
+#define VADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
+#define VADC_DEF_AVG_SAMPLES 0 /* 1 sample */
+#define VADC_DEF_CALIB_TYPE VADC_CALIB_ABSOLUTE
+
+#define VADC_DECIMATION_MIN 512
+#define VADC_DECIMATION_MAX 4096
+
+#define VADC_HW_SETTLE_DELAY_MAX 10000
+#define VADC_AVG_SAMPLES_MAX 512
+
+#define KELVINMIL_CELSIUSMIL 273150
+
+#define VADC_CHAN_MIN VADC_USBIN
+#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
+
+/*
+ * VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
+ * VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
+ * calibration.
+ */
+enum vadc_calibration {
+ VADC_CALIB_ABSOLUTE = 0,
+ VADC_CALIB_RATIOMETRIC
+};
+
+/**
+ * struct vadc_linear_graph - Represent ADC characteristics.
+ * @dy: numerator slope to calculate the gain.
+ * @dx: denominator slope to calculate the gain.
+ * @gnd: A/D word of the ground reference used for the channel.
+ *
+ * Each ADC device has different offset and gain parameters which are
+ * computed to calibrate the device.
+ */
+struct vadc_linear_graph {
+ s32 dy;
+ s32 dx;
+ s32 gnd;
+};
+
+/**
+ * struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
+ * @num: the inverse numerator of the gain applied to the input channel.
+ * @den: the inverse denominator of the gain applied to the input channel.
+ */
+struct vadc_prescale_ratio {
+ u32 num;
+ u32 den;
+};
+
+/**
+ * struct vadc_channel_prop - VADC channel property.
+ * @channel: channel number, refer to the channel list.
+ * @calibration: calibration type.
+ * @decimation: sampling rate supported for the channel.
+ * @prescale: channel scaling performed on the input signal.
+ * @hw_settle_time: the time between AMUX being configured and the
+ * start of conversion.
+ * @avg_samples: ability to provide single result from the ADC
+ * that is an average of multiple measurements.
+ */
+struct vadc_channel_prop {
+ unsigned int channel;
+ enum vadc_calibration calibration;
+ unsigned int decimation;
+ unsigned int prescale;
+ unsigned int hw_settle_time;
+ unsigned int avg_samples;
+};
+
+/**
+ * struct vadc_priv - VADC private structure.
+ * @regmap: pointer to struct regmap.
+ * @dev: pointer to struct device.
+ * @base: base address for the ADC peripheral.
+ * @nchannels: number of VADC channels.
+ * @chan_props: array of VADC channel properties.
+ * @iio_chans: array of IIO channels specification.
+ * @are_ref_measured: are reference points measured.
+ * @poll_eoc: use polling instead of interrupt.
+ * @complete: VADC result notification after interrupt is received.
+ * @graph: store parameters for calibration.
+ * @lock: ADC lock for access to the peripheral.
+ */
+struct vadc_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ u16 base;
+ unsigned int nchannels;
+ struct vadc_channel_prop *chan_props;
+ struct iio_chan_spec *iio_chans;
+ bool are_ref_measured;
+ bool poll_eoc;
+ struct completion complete;
+ struct vadc_linear_graph graph[2];
+ struct mutex lock;
+};
+
+static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
+ {.num = 1, .den = 1},
+ {.num = 1, .den = 3},
+ {.num = 1, .den = 4},
+ {.num = 1, .den = 6},
+ {.num = 1, .den = 20},
+ {.num = 1, .den = 8},
+ {.num = 10, .den = 81},
+ {.num = 1, .den = 10}
+};
+
+static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
+{
+ return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
+}
+
+static int vadc_write(struct vadc_priv *vadc, u16 offset, u8 data)
+{
+ return regmap_write(vadc->regmap, vadc->base + offset, data);
+}
+
+static int vadc_reset(struct vadc_priv *vadc)
+{
+ u8 data;
+ int ret;
+
+ ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA);
+ if (ret)
+ return ret;
+
+ ret = vadc_read(vadc, VADC_PERH_RESET_CTL3, &data);
+ if (ret)
+ return ret;
+
+ ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA);
+ if (ret)
+ return ret;
+
+ data |= VADC_FOLLOW_WARM_RB;
+
+ return vadc_write(vadc, VADC_PERH_RESET_CTL3, data);
+}
+
+static int vadc_set_state(struct vadc_priv *vadc, bool state)
+{
+ return vadc_write(vadc, VADC_EN_CTL1, state ? VADC_EN_CTL1_SET : 0);
+}
+
+static void vadc_show_status(struct vadc_priv *vadc)
+{
+ u8 mode, sta1, chan, dig, en, req;
+ int ret;
+
+ ret = vadc_read(vadc, VADC_MODE_CTL, &mode);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_ADC_DIG_PARAM, &dig);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_ADC_CH_SEL_CTL, &chan);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_CONV_REQ, &req);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_STATUS1, &sta1);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_EN_CTL1, &en);
+ if (ret)
+ return;
+
+ dev_err(vadc->dev,
+ "mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n",
+ mode, en, chan, dig, req, sta1);
+}
+
+static int vadc_configure(struct vadc_priv *vadc,
+ struct vadc_channel_prop *prop)
+{
+ u8 decimation, mode_ctrl;
+ int ret;
+
+ /* Mode selection */
+ mode_ctrl = (VADC_OP_MODE_NORMAL << VADC_OP_MODE_SHIFT) |
+ VADC_ADC_TRIM_EN | VADC_AMUX_TRIM_EN;
+ ret = vadc_write(vadc, VADC_MODE_CTL, mode_ctrl);
+ if (ret)
+ return ret;
+
+ /* Channel selection */
+ ret = vadc_write(vadc, VADC_ADC_CH_SEL_CTL, prop->channel);
+ if (ret)
+ return ret;
+
+ /* Digital parameter setup */
+ decimation = prop->decimation << VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT;
+ ret = vadc_write(vadc, VADC_ADC_DIG_PARAM, decimation);
+ if (ret)
+ return ret;
+
+ /* HW settle time delay */
+ ret = vadc_write(vadc, VADC_HW_SETTLE_DELAY, prop->hw_settle_time);
+ if (ret)
+ return ret;
+
+ ret = vadc_write(vadc, VADC_FAST_AVG_CTL, prop->avg_samples);
+ if (ret)
+ return ret;
+
+ if (prop->avg_samples)
+ ret = vadc_write(vadc, VADC_FAST_AVG_EN, VADC_FAST_AVG_EN_SET);
+ else
+ ret = vadc_write(vadc, VADC_FAST_AVG_EN, 0);
+
+ return ret;
+}
+
+static int vadc_poll_wait_eoc(struct vadc_priv *vadc, unsigned int interval_us)
+{
+ unsigned int count, retry;
+ u8 sta1;
+ int ret;
+
+ retry = interval_us / VADC_CONV_TIME_MIN_US;
+
+ for (count = 0; count < retry; count++) {
+ ret = vadc_read(vadc, VADC_STATUS1, &sta1);
+ if (ret)
+ return ret;
+
+ sta1 &= VADC_STATUS1_REQ_STS_EOC_MASK;
+ if (sta1 == VADC_STATUS1_EOC)
+ return 0;
+
+ usleep_range(VADC_CONV_TIME_MIN_US, VADC_CONV_TIME_MAX_US);
+ }
+
+ vadc_show_status(vadc);
+
+ return -ETIMEDOUT;
+}
+
+static int vadc_read_result(struct vadc_priv *vadc, u16 *data)
+{
+ int ret;
+
+ ret = regmap_bulk_read(vadc->regmap, vadc->base + VADC_DATA, data, 2);
+ if (ret)
+ return ret;
+
+ *data = clamp_t(u16, *data, VADC_MIN_ADC_CODE, VADC_MAX_ADC_CODE);
+
+ return 0;
+}
+
+static struct vadc_channel_prop *vadc_get_channel(struct vadc_priv *vadc,
+ unsigned int num)
+{
+ unsigned int i;
+
+ for (i = 0; i < vadc->nchannels; i++)
+ if (vadc->chan_props[i].channel == num)
+ return &vadc->chan_props[i];
+
+ dev_dbg(vadc->dev, "no such channel %02x\n", num);
+
+ return NULL;
+}
+
+static int vadc_do_conversion(struct vadc_priv *vadc,
+ struct vadc_channel_prop *prop, u16 *data)
+{
+ unsigned int timeout;
+ int ret;
+
+ mutex_lock(&vadc->lock);
+
+ ret = vadc_configure(vadc, prop);
+ if (ret)
+ goto unlock;
+
+ if (!vadc->poll_eoc)
+ reinit_completion(&vadc->complete);
+
+ ret = vadc_set_state(vadc, true);
+ if (ret)
+ goto unlock;
+
+ ret = vadc_write(vadc, VADC_CONV_REQ, VADC_CONV_REQ_SET);
+ if (ret)
+ goto err_disable;
+
+ timeout = BIT(prop->avg_samples) * VADC_CONV_TIME_MIN_US * 2;
+
+ if (vadc->poll_eoc) {
+ ret = vadc_poll_wait_eoc(vadc, timeout);
+ } else {
+ ret = wait_for_completion_timeout(&vadc->complete, timeout);
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto err_disable;
+ }
+
+ /* Double check conversion status */
+ ret = vadc_poll_wait_eoc(vadc, VADC_CONV_TIME_MIN_US);
+ if (ret)
+ goto err_disable;
+ }
+
+ ret = vadc_read_result(vadc, data);
+
+err_disable:
+ vadc_set_state(vadc, false);
+ if (ret)
+ dev_err(vadc->dev, "conversion failed\n");
+unlock:
+ mutex_unlock(&vadc->lock);
+ return ret;
+}
+
+static int vadc_measure_ref_points(struct vadc_priv *vadc)
+{
+ struct vadc_channel_prop *prop;
+ u16 read_1, read_2;
+ int ret;
+
+ vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE_UV;
+ vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV;
+
+ prop = vadc_get_channel(vadc, VADC_REF_1250MV);
+ ret = vadc_do_conversion(vadc, prop, &read_1);
+ if (ret)
+ goto err;
+
+ /* Try with buffered 625mV channel first */
+ prop = vadc_get_channel(vadc, VADC_SPARE1);
+ if (!prop)
+ prop = vadc_get_channel(vadc, VADC_REF_625MV);
+
+ ret = vadc_do_conversion(vadc, prop, &read_2);
+ if (ret)
+ goto err;
+
+ if (read_1 == read_2) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ vadc->graph[VADC_CALIB_ABSOLUTE].dy = read_1 - read_2;
+ vadc->graph[VADC_CALIB_ABSOLUTE].gnd = read_2;
+
+ /* Ratiometric calibration */
+ prop = vadc_get_channel(vadc, VADC_VDD_VADC);
+ ret = vadc_do_conversion(vadc, prop, &read_1);
+ if (ret)
+ goto err;
+
+ prop = vadc_get_channel(vadc, VADC_GND_REF);
+ ret = vadc_do_conversion(vadc, prop, &read_2);
+ if (ret)
+ goto err;
+
+ if (read_1 == read_2) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ vadc->graph[VADC_CALIB_RATIOMETRIC].dy = read_1 - read_2;
+ vadc->graph[VADC_CALIB_RATIOMETRIC].gnd = read_2;
+err:
+ if (ret)
+ dev_err(vadc->dev, "measure reference points failed\n");
+
+ return ret;
+}
+
+static s32 vadc_calibrate(struct vadc_priv *vadc,
+ const struct vadc_channel_prop *prop, u16 adc_code)
+{
+ const struct vadc_prescale_ratio *prescale;
+ s32 voltage;
+
+ voltage = adc_code - vadc->graph[prop->calibration].gnd;
+ voltage *= vadc->graph[prop->calibration].dx;
+ voltage = voltage / vadc->graph[prop->calibration].dy;
+
+ if (prop->calibration == VADC_CALIB_ABSOLUTE)
+ voltage += vadc->graph[prop->calibration].dx;
+
+ if (voltage < 0)
+ voltage = 0;
+
+ prescale = &vadc_prescale_ratios[prop->prescale];
+
+ voltage = voltage * prescale->den;
+
+ return voltage / prescale->num;
+}
+
+static int vadc_decimation_from_dt(u32 value)
+{
+ if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
+ value > VADC_DECIMATION_MAX)
+ return -EINVAL;
+
+ return __ffs64(value / VADC_DECIMATION_MIN);
+}
+
+static int vadc_prescaling_from_dt(u32 num, u32 den)
+{
+ unsigned int pre;
+
+ for (pre = 0; pre < ARRAY_SIZE(vadc_prescale_ratios); pre++)
+ if (vadc_prescale_ratios[pre].num == num &&
+ vadc_prescale_ratios[pre].den == den)
+ break;
+
+ if (pre == ARRAY_SIZE(vadc_prescale_ratios))
+ return -EINVAL;
+
+ return pre;
+}
+
+static int vadc_hw_settle_time_from_dt(u32 value)
+{
+ if ((value <= 1000 && value % 100) || (value > 1000 && value % 2000))
+ return -EINVAL;
+
+ if (value <= 1000)
+ value /= 100;
+ else
+ value = value / 2000 + 10;
+
+ return value;
+}
+
+static int vadc_avg_samples_from_dt(u32 value)
+{
+ if (!is_power_of_2(value) || value > VADC_AVG_SAMPLES_MAX)
+ return -EINVAL;
+
+ return __ffs64(value);
+}
+
+static int vadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct vadc_priv *vadc = iio_priv(indio_dev);
+ struct vadc_channel_prop *prop;
+ u16 adc_code;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ prop = &vadc->chan_props[chan->address];
+ ret = vadc_do_conversion(vadc, prop, &adc_code);
+ if (ret)
+ break;
+
+ *val = vadc_calibrate(vadc, prop, adc_code);
+
+ /* 2mV/K, return milli Celsius */
+ *val /= 2;
+ *val -= KELVINMIL_CELSIUSMIL;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_RAW:
+ prop = &vadc->chan_props[chan->address];
+ ret = vadc_do_conversion(vadc, prop, &adc_code);
+ if (ret)
+ break;
+
+ *val = vadc_calibrate(vadc, prop, adc_code);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int vadc_of_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ struct vadc_priv *vadc = iio_priv(indio_dev);
+ unsigned int i;
+
+ for (i = 0; i < vadc->nchannels; i++)
+ if (vadc->iio_chans[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+static const struct iio_info vadc_info = {
+ .read_raw = vadc_read_raw,
+ .of_xlate = vadc_of_xlate,
+ .driver_module = THIS_MODULE,
+};
+
+struct vadc_channels {
+ const char *datasheet_name;
+ unsigned int prescale_index;
+ enum iio_chan_type type;
+ long info_mask;
+};
+
+#define VADC_CHAN(_dname, _type, _mask, _pre) \
+ [VADC_##_dname] = { \
+ .datasheet_name = __stringify(_dname), \
+ .prescale_index = _pre, \
+ .type = _type, \
+ .info_mask = _mask \
+ }, \
+
+#define VADC_CHAN_TEMP(_dname, _pre) \
+ VADC_CHAN(_dname, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre) \
+
+#define VADC_CHAN_VOLT(_dname, _pre) \
+ VADC_CHAN(_dname, IIO_VOLTAGE, \
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
+ _pre) \
+
+/*
+ * The array represents all possible ADC channels found in the supported PMICs.
+ * Every index in the array is equal to the channel number per datasheet. The
+ * gaps in the array should be treated as reserved channels.
+ */
+static const struct vadc_channels vadc_chans[] = {
+ VADC_CHAN_VOLT(USBIN, 4)
+ VADC_CHAN_VOLT(DCIN, 4)
+ VADC_CHAN_VOLT(VCHG_SNS, 3)
+ VADC_CHAN_VOLT(SPARE1_03, 1)
+ VADC_CHAN_VOLT(USB_ID_MV, 1)
+ VADC_CHAN_VOLT(VCOIN, 1)
+ VADC_CHAN_VOLT(VBAT_SNS, 1)
+ VADC_CHAN_VOLT(VSYS, 1)
+ VADC_CHAN_TEMP(DIE_TEMP, 0)
+ VADC_CHAN_VOLT(REF_625MV, 0)
+ VADC_CHAN_VOLT(REF_1250MV, 0)
+ VADC_CHAN_VOLT(CHG_TEMP, 0)
+ VADC_CHAN_VOLT(SPARE1, 0)
+ VADC_CHAN_VOLT(SPARE2, 0)
+ VADC_CHAN_VOLT(GND_REF, 0)
+ VADC_CHAN_VOLT(VDD_VADC, 0)
+
+ VADC_CHAN_VOLT(P_MUX1_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX2_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX3_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX4_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX5_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX6_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX7_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX8_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX9_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX10_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX11_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX12_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX13_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX14_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX15_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX16_1_1, 0)
+
+ VADC_CHAN_VOLT(P_MUX1_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX2_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX3_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX4_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX5_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX6_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX7_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX8_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX9_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX10_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX11_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX12_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX13_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX14_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX15_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX16_1_3, 1)
+
+ VADC_CHAN_VOLT(LR_MUX1_BAT_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX2_BAT_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_XO_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX4_AMUX_THM1, 0)
+ VADC_CHAN_VOLT(LR_MUX5_AMUX_THM2, 0)
+ VADC_CHAN_VOLT(LR_MUX6_AMUX_THM3, 0)
+ VADC_CHAN_VOLT(LR_MUX7_HW_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX8_AMUX_THM4, 0)
+ VADC_CHAN_VOLT(LR_MUX9_AMUX_THM5, 0)
+ VADC_CHAN_VOLT(LR_MUX10_USB_ID, 0)
+ VADC_CHAN_VOLT(AMUX_PU1, 0)
+ VADC_CHAN_VOLT(AMUX_PU2, 0)
+ VADC_CHAN_VOLT(LR_MUX3_BUF_XO_THERM, 0)
+
+ VADC_CHAN_VOLT(LR_MUX1_PU1_BAT_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX2_PU1_BAT_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_PU1_XO_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX4_PU1_AMUX_THM1, 0)
+ VADC_CHAN_VOLT(LR_MUX5_PU1_AMUX_THM2, 0)
+ VADC_CHAN_VOLT(LR_MUX6_PU1_AMUX_THM3, 0)
+ VADC_CHAN_VOLT(LR_MUX7_PU1_AMUX_HW_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX8_PU1_AMUX_THM4, 0)
+ VADC_CHAN_VOLT(LR_MUX9_PU1_AMUX_THM5, 0)
+ VADC_CHAN_VOLT(LR_MUX10_PU1_AMUX_USB_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_XO_THERM, 0)
+
+ VADC_CHAN_VOLT(LR_MUX1_PU2_BAT_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX2_PU2_BAT_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_PU2_XO_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX4_PU2_AMUX_THM1, 0)
+ VADC_CHAN_VOLT(LR_MUX5_PU2_AMUX_THM2, 0)
+ VADC_CHAN_VOLT(LR_MUX6_PU2_AMUX_THM3, 0)
+ VADC_CHAN_VOLT(LR_MUX7_PU2_AMUX_HW_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX8_PU2_AMUX_THM4, 0)
+ VADC_CHAN_VOLT(LR_MUX9_PU2_AMUX_THM5, 0)
+ VADC_CHAN_VOLT(LR_MUX10_PU2_AMUX_USB_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_BUF_PU2_XO_THERM, 0)
+
+ VADC_CHAN_VOLT(LR_MUX1_PU1_PU2_BAT_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX2_PU1_PU2_BAT_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_PU1_PU2_XO_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
+ VADC_CHAN_VOLT(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
+ VADC_CHAN_VOLT(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
+ VADC_CHAN_VOLT(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
+ VADC_CHAN_VOLT(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
+ VADC_CHAN_VOLT(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
+};
+
+static int vadc_get_dt_channel_data(struct device *dev,
+ struct vadc_channel_prop *prop,
+ struct device_node *node)
+{
+ const char *name = node->name;
+ u32 chan, value, varr[2];
+ int ret;
+
+ ret = of_property_read_u32(node, "reg", &chan);
+ if (ret) {
+ dev_err(dev, "invalid channel number %s\n", name);
+ return ret;
+ }
+
+ if (chan > VADC_CHAN_MAX || chan < VADC_CHAN_MIN) {
+ dev_err(dev, "%s invalid channel number %d\n", name, chan);
+ return -EINVAL;
+ }
+
+ /* the channel has DT description */
+ prop->channel = chan;
+
+ ret = of_property_read_u32(node, "qcom,decimation", &value);
+ if (!ret) {
+ ret = vadc_decimation_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid decimation %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->decimation = ret;
+ } else {
+ prop->decimation = VADC_DEF_DECIMATION;
+ }
+
+ ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
+ if (!ret) {
+ ret = vadc_prescaling_from_dt(varr[0], varr[1]);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
+ chan, varr[0], varr[1]);
+ return ret;
+ }
+ prop->prescale = ret;
+ } else {
+ prop->prescale = vadc_chans[prop->channel].prescale_index;
+ }
+
+ ret = of_property_read_u32(node, "qcom,hw-settle-time", &value);
+ if (!ret) {
+ ret = vadc_hw_settle_time_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid hw-settle-time %d us\n",
+ chan, value);
+ return ret;
+ }
+ prop->hw_settle_time = ret;
+ } else {
+ prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
+ }
+
+ ret = of_property_read_u32(node, "qcom,avg-samples", &value);
+ if (!ret) {
+ ret = vadc_avg_samples_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid avg-samples %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->avg_samples = ret;
+ } else {
+ prop->avg_samples = VADC_DEF_AVG_SAMPLES;
+ }
+
+ if (of_property_read_bool(node, "qcom,ratiometric"))
+ prop->calibration = VADC_CALIB_RATIOMETRIC;
+ else
+ prop->calibration = VADC_CALIB_ABSOLUTE;
+
+ dev_dbg(dev, "%02x name %s\n", chan, name);
+
+ return 0;
+}
+
+static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
+{
+ const struct vadc_channels *vadc_chan;
+ struct iio_chan_spec *iio_chan;
+ struct vadc_channel_prop prop;
+ struct device_node *child;
+ unsigned int index = 0;
+ int ret;
+
+ vadc->nchannels = of_get_available_child_count(node);
+ if (!vadc->nchannels)
+ return -EINVAL;
+
+ vadc->iio_chans = devm_kcalloc(vadc->dev, vadc->nchannels,
+ sizeof(*vadc->iio_chans), GFP_KERNEL);
+ if (!vadc->iio_chans)
+ return -ENOMEM;
+
+ vadc->chan_props = devm_kcalloc(vadc->dev, vadc->nchannels,
+ sizeof(*vadc->chan_props), GFP_KERNEL);
+ if (!vadc->chan_props)
+ return -ENOMEM;
+
+ iio_chan = vadc->iio_chans;
+
+ for_each_available_child_of_node(node, child) {
+ ret = vadc_get_dt_channel_data(vadc->dev, &prop, child);
+ if (ret)
+ return ret;
+
+ vadc->chan_props[index] = prop;
+
+ vadc_chan = &vadc_chans[prop.channel];
+
+ iio_chan->channel = prop.channel;
+ iio_chan->datasheet_name = vadc_chan->datasheet_name;
+ iio_chan->info_mask_separate = vadc_chan->info_mask;
+ iio_chan->type = vadc_chan->type;
+ iio_chan->indexed = 1;
+ iio_chan->address = index++;
+
+ iio_chan++;
+ }
+
+ /* These channels are mandatory, they are used as reference points */
+ if (!vadc_get_channel(vadc, VADC_REF_1250MV)) {
+ dev_err(vadc->dev, "Please define 1.25V channel\n");
+ return -ENODEV;
+ }
+
+ if (!vadc_get_channel(vadc, VADC_REF_625MV)) {
+ dev_err(vadc->dev, "Please define 0.625V channel\n");
+ return -ENODEV;
+ }
+
+ if (!vadc_get_channel(vadc, VADC_VDD_VADC)) {
+ dev_err(vadc->dev, "Please define VDD channel\n");
+ return -ENODEV;
+ }
+
+ if (!vadc_get_channel(vadc, VADC_GND_REF)) {
+ dev_err(vadc->dev, "Please define GND channel\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static irqreturn_t vadc_isr(int irq, void *dev_id)
+{
+ struct vadc_priv *vadc = dev_id;
+
+ complete(&vadc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int vadc_check_revision(struct vadc_priv *vadc)
+{
+ u8 val;
+ int ret;
+
+ ret = vadc_read(vadc, VADC_PERPH_TYPE, &val);
+ if (ret)
+ return ret;
+
+ if (val < VADC_PERPH_TYPE_ADC) {
+ dev_err(vadc->dev, "%d is not ADC\n", val);
+ return -ENODEV;
+ }
+
+ ret = vadc_read(vadc, VADC_PERPH_SUBTYPE, &val);
+ if (ret)
+ return ret;
+
+ if (val < VADC_PERPH_SUBTYPE_VADC) {
+ dev_err(vadc->dev, "%d is not VADC\n", val);
+ return -ENODEV;
+ }
+
+ ret = vadc_read(vadc, VADC_REVISION2, &val);
+ if (ret)
+ return ret;
+
+ if (val < VADC_REVISION2_SUPPORTED_VADC) {
+ dev_err(vadc->dev, "revision %d not supported\n", val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int vadc_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct vadc_priv *vadc;
+ struct regmap *regmap;
+ int ret, irq_eoc;
+ u32 reg;
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ ret = of_property_read_u32(node, "reg", &reg);
+ if (ret < 0)
+ return ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*vadc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ vadc = iio_priv(indio_dev);
+ vadc->regmap = regmap;
+ vadc->dev = dev;
+ vadc->base = reg;
+ vadc->are_ref_measured = false;
+ init_completion(&vadc->complete);
+ mutex_init(&vadc->lock);
+
+ ret = vadc_check_revision(vadc);
+ if (ret)
+ return ret;
+
+ ret = vadc_get_dt_data(vadc, node);
+ if (ret)
+ return ret;
+
+ irq_eoc = platform_get_irq(pdev, 0);
+ if (irq_eoc < 0) {
+ if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
+ return irq_eoc;
+ vadc->poll_eoc = true;
+ } else {
+ ret = devm_request_irq(dev, irq_eoc, vadc_isr, 0,
+ "spmi-vadc", vadc);
+ if (ret)
+ return ret;
+ }
+
+ ret = vadc_reset(vadc);
+ if (ret) {
+ dev_err(dev, "reset failed\n");
+ return ret;
+ }
+
+ ret = vadc_measure_ref_points(vadc);
+ if (ret)
+ return ret;
+
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = node;
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &vadc_info;
+ indio_dev->channels = vadc->iio_chans;
+ indio_dev->num_channels = vadc->nchannels;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id vadc_match_table[] = {
+ { .compatible = "qcom,spmi-vadc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, vadc_match_table);
+
+static struct platform_driver vadc_driver = {
+ .driver = {
+ .name = "qcom-spmi-vadc",
+ .of_match_table = vadc_match_table,
+ },
+ .probe = vadc_probe,
+};
+module_platform_driver(vadc_driver);
+
+MODULE_ALIAS("platform:qcom-spmi-vadc");
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC voltage ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>");
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index e074a0b03f28..8d4e019ea4ca 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -18,13 +18,13 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
#define SARADC_DATA 0x00
-#define SARADC_DATA_MASK 0x3ff
#define SARADC_STAS 0x04
#define SARADC_STAS_BUSY BIT(0)
@@ -38,15 +38,22 @@
#define SARADC_DLY_PU_SOC 0x0c
#define SARADC_DLY_PU_SOC_MASK 0x3f
-#define SARADC_BITS 10
#define SARADC_TIMEOUT msecs_to_jiffies(100)
+struct rockchip_saradc_data {
+ int num_bits;
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ unsigned long clk_rate;
+};
+
struct rockchip_saradc {
void __iomem *regs;
struct clk *pclk;
struct clk *clk;
struct completion completion;
struct regulator *vref;
+ const struct rockchip_saradc_data *data;
u16 last_val;
};
@@ -90,7 +97,7 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
}
*val = ret / 1000;
- *val2 = SARADC_BITS;
+ *val2 = info->data->num_bits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
@@ -103,7 +110,7 @@ static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
/* Read value */
info->last_val = readl_relaxed(info->regs + SARADC_DATA);
- info->last_val &= SARADC_DATA_MASK;
+ info->last_val &= GENMASK(info->data->num_bits - 1, 0);
/* Clear irq & power down adc */
writel_relaxed(0, info->regs + SARADC_CTRL);
@@ -133,12 +140,44 @@ static const struct iio_chan_spec rockchip_saradc_iio_channels[] = {
ADC_CHANNEL(2, "adc2"),
};
+static const struct rockchip_saradc_data saradc_data = {
+ .num_bits = 10,
+ .channels = rockchip_saradc_iio_channels,
+ .num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels),
+ .clk_rate = 1000000,
+};
+
+static const struct iio_chan_spec rockchip_rk3066_tsadc_iio_channels[] = {
+ ADC_CHANNEL(0, "adc0"),
+ ADC_CHANNEL(1, "adc1"),
+};
+
+static const struct rockchip_saradc_data rk3066_tsadc_data = {
+ .num_bits = 12,
+ .channels = rockchip_rk3066_tsadc_iio_channels,
+ .num_channels = ARRAY_SIZE(rockchip_rk3066_tsadc_iio_channels),
+ .clk_rate = 50000,
+};
+
+static const struct of_device_id rockchip_saradc_match[] = {
+ {
+ .compatible = "rockchip,saradc",
+ .data = &saradc_data,
+ }, {
+ .compatible = "rockchip,rk3066-tsadc",
+ .data = &rk3066_tsadc_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
+
static int rockchip_saradc_probe(struct platform_device *pdev)
{
struct rockchip_saradc *info = NULL;
struct device_node *np = pdev->dev.of_node;
struct iio_dev *indio_dev = NULL;
struct resource *mem;
+ const struct of_device_id *match;
int ret;
int irq;
@@ -152,6 +191,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
}
info = iio_priv(indio_dev);
+ match = of_match_device(rockchip_saradc_match, &pdev->dev);
+ info->data = match->data;
+
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
info->regs = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(info->regs))
@@ -192,10 +234,10 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
}
/*
- * Use a default of 1MHz for the converter clock.
+ * Use a default value for the converter clock.
* This may become user-configurable in the future.
*/
- ret = clk_set_rate(info->clk, 1000000);
+ ret = clk_set_rate(info->clk, info->data->clk_rate);
if (ret < 0) {
dev_err(&pdev->dev, "failed to set adc clk rate, %d\n", ret);
return ret;
@@ -227,8 +269,8 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
indio_dev->info = &rockchip_saradc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = rockchip_saradc_iio_channels;
- indio_dev->num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels);
+ indio_dev->channels = info->data->channels;
+ indio_dev->num_channels = info->data->num_channels;
ret = iio_device_register(indio_dev);
if (ret)
@@ -296,18 +338,11 @@ static int rockchip_saradc_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops,
rockchip_saradc_suspend, rockchip_saradc_resume);
-static const struct of_device_id rockchip_saradc_match[] = {
- { .compatible = "rockchip,saradc" },
- {},
-};
-MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
-
static struct platform_driver rockchip_saradc_driver = {
.probe = rockchip_saradc_probe,
.remove = rockchip_saradc_remove,
.driver = {
.name = "rockchip-saradc",
- .owner = THIS_MODULE,
.of_match_table = rockchip_saradc_match,
.pm = &rockchip_saradc_pm_ops,
},
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index b730864731e8..a0e7161f040c 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -86,19 +86,18 @@ static void tiadc_step_config(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
unsigned int stepconfig;
- int i, steps;
+ int i, steps = 0;
/*
* There are 16 configurable steps and 8 analog input
* lines available which are shared between Touchscreen and ADC.
*
- * Steps backwards i.e. from 16 towards 0 are used by ADC
+ * Steps forwards i.e. from 0 towards 16 are used by ADC
* depending on number of input lines needed.
* Channel would represent which analog input
* needs to be given to ADC to digitalize data.
*/
- steps = TOTAL_STEPS - adc_dev->channels;
if (iio_buffer_enabled(indio_dev))
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
| STEPCONFIG_MODE_SWCNT;
@@ -189,12 +188,11 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
- struct iio_buffer *buffer = indio_dev->buffer;
unsigned int enb = 0;
u8 bit;
tiadc_step_config(indio_dev);
- for_each_set_bit(bit, buffer->scan_mask, adc_dev->channels)
+ for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
adc_dev->buffer_en_ch_steps = enb;
@@ -250,7 +248,7 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
struct iio_buffer *buffer;
int ret;
- buffer = iio_kfifo_allocate(indio_dev);
+ buffer = iio_kfifo_allocate();
if (!buffer)
return -ENOMEM;
@@ -264,16 +262,8 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
indio_dev->setup_ops = setup_ops;
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
- ret = iio_buffer_register(indio_dev,
- indio_dev->channels,
- indio_dev->num_channels);
- if (ret)
- goto error_free_irq;
-
return 0;
-error_free_irq:
- free_irq(irq, indio_dev);
error_kfifo_free:
iio_kfifo_free(indio_dev->buffer);
return ret;
@@ -285,7 +275,6 @@ static void tiadc_iio_buffered_hardware_remove(struct iio_dev *indio_dev)
free_irq(adc_dev->mfd_tscadc->irq, indio_dev);
iio_kfifo_free(indio_dev->buffer);
- iio_buffer_unregister(indio_dev);
}
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index 4a10ae97dbf2..56292ae4538d 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -91,7 +91,7 @@
#define VF610_ADC_CAL 0x80
/* Other field define */
-#define VF610_ADC_ADCHC(x) ((x) & 0xF)
+#define VF610_ADC_ADCHC(x) ((x) & 0x1F)
#define VF610_ADC_AIEN (0x1 << 7)
#define VF610_ADC_CONV_DISABLE 0x1F
#define VF610_ADC_HS_COCO0 0x1
@@ -141,9 +141,13 @@ struct vf610_adc {
struct regulator *vref;
struct vf610_adc_feature adc_feature;
+ u32 sample_freq_avail[5];
+
struct completion completion;
};
+static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
+
#define VF610_ADC_CHAN(_idx, _chan_type) { \
.type = (_chan_type), \
.indexed = 1, \
@@ -153,6 +157,12 @@ struct vf610_adc {
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}
+#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
+ .type = (_chan_type), \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+}
+
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
VF610_ADC_CHAN(0, IIO_VOLTAGE),
VF610_ADC_CHAN(1, IIO_VOLTAGE),
@@ -170,38 +180,51 @@ static const struct iio_chan_spec vf610_adc_iio_channels[] = {
VF610_ADC_CHAN(13, IIO_VOLTAGE),
VF610_ADC_CHAN(14, IIO_VOLTAGE),
VF610_ADC_CHAN(15, IIO_VOLTAGE),
+ VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
/* sentinel */
};
-/*
- * ADC sample frequency, unit is ADCK cycles.
- * ADC clk source is ipg clock, which is the same as bus clock.
- *
- * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
- * SFCAdder: fixed to 6 ADCK cycles
- * AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
- * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
- * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
- *
- * By default, enable 12 bit resolution mode, clock source
- * set to ipg clock, So get below frequency group:
- */
-static const u32 vf610_sample_freq_avail[5] =
-{1941176, 559332, 286957, 145374, 73171};
+static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
+{
+ unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
+ int i;
+
+ /*
+ * Calculate ADC sample frequencies
+ * Sample time unit is ADCK cycles. ADCK clk source is ipg clock,
+ * which is the same as bus clock.
+ *
+ * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
+ * SFCAdder: fixed to 6 ADCK cycles
+ * AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
+ * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
+ * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
+ */
+ adck_rate = ipg_rate / info->adc_feature.clk_div;
+ for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
+ info->sample_freq_avail[i] =
+ adck_rate / (6 + vf610_hw_avgs[i] * (25 + 3));
+}
static inline void vf610_adc_cfg_init(struct vf610_adc *info)
{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
+
/* set default Configuration for ADC controller */
- info->adc_feature.clk_sel = VF610_ADCIOC_BUSCLK_SET;
- info->adc_feature.vol_ref = VF610_ADCIOC_VR_VREF_SET;
+ adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET;
+ adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET;
- info->adc_feature.calibration = true;
- info->adc_feature.ovwren = true;
+ adc_feature->calibration = true;
+ adc_feature->ovwren = true;
- info->adc_feature.clk_div = 1;
- info->adc_feature.res_mode = 12;
- info->adc_feature.sample_rate = 1;
- info->adc_feature.lpm = true;
+ adc_feature->res_mode = 12;
+ adc_feature->sample_rate = 1;
+ adc_feature->lpm = true;
+
+ /* Use a save ADCK which is below 20MHz on all devices */
+ adc_feature->clk_div = 8;
+
+ vf610_adc_calculate_rates(info);
}
static void vf610_adc_cfg_post_set(struct vf610_adc *info)
@@ -252,7 +275,6 @@ static void vf610_adc_cfg_post_set(struct vf610_adc *info)
static void vf610_adc_calibration(struct vf610_adc *info)
{
int adc_gc, hc_cfg;
- int timeout;
if (!info->adc_feature.calibration)
return;
@@ -264,9 +286,7 @@ static void vf610_adc_calibration(struct vf610_adc *info)
adc_gc = readl(info->regs + VF610_REG_ADC_GC);
writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC);
- timeout = wait_for_completion_timeout
- (&info->completion, VF610_ADC_TIMEOUT);
- if (timeout == 0)
+ if (!wait_for_completion_timeout(&info->completion, VF610_ADC_TIMEOUT))
dev_err(info->dev, "Timeout for adc calibration\n");
adc_gc = readl(info->regs + VF610_REG_ADC_GS);
@@ -283,12 +303,10 @@ static void vf610_adc_cfg_set(struct vf610_adc *info)
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
- /* low power configuration */
cfg_data &= ~VF610_ADC_ADLPC_EN;
if (adc_feature->lpm)
cfg_data |= VF610_ADC_ADLPC_EN;
- /* disable high speed */
cfg_data &= ~VF610_ADC_ADHSC_EN;
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
@@ -428,10 +446,27 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1941176, 559332, 286957, 145374, 73171");
+static ssize_t vf610_show_samp_freq_avail(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vf610_adc *info = iio_priv(dev_to_iio_dev(dev));
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(info->sample_freq_avail); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "%u ", info->sample_freq_avail[i]);
+
+ /* replace trailing space by newline */
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(vf610_show_samp_freq_avail);
static struct attribute *vf610_attributes[] = {
- &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
@@ -451,6 +486,7 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
mutex_lock(&indio_dev->mlock);
reinit_completion(&info->completion);
@@ -468,7 +504,23 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
return ret;
}
- *val = info->value;
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = info->value;
+ break;
+ case IIO_TEMP:
+ /*
+ * Calculate in degree Celsius times 1000
+ * Using sensor slope of 1.84 mV/°C and
+ * V at 25°C of 696 mV
+ */
+ *val = 25000 - ((int)info->value - 864) * 1000000 / 1840;
+ break;
+ default:
+ mutex_unlock(&indio_dev->mlock);
+ return -EINVAL;
+ }
+
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT;
@@ -478,7 +530,7 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
- *val = vf610_sample_freq_avail[info->adc_feature.sample_rate];
+ *val = info->sample_freq_avail[info->adc_feature.sample_rate];
*val2 = 0;
return IIO_VAL_INT;
@@ -501,9 +553,9 @@ static int vf610_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
for (i = 0;
- i < ARRAY_SIZE(vf610_sample_freq_avail);
+ i < ARRAY_SIZE(info->sample_freq_avail);
i++)
- if (val == vf610_sample_freq_avail[i]) {
+ if (val == info->sample_freq_avail[i]) {
info->adc_feature.sample_rate = i;
vf610_adc_sample_set(info);
return 0;
@@ -569,9 +621,9 @@ static int vf610_adc_probe(struct platform_device *pdev)
return PTR_ERR(info->regs);
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
+ if (irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
- return -EINVAL;
+ return irq;
}
ret = devm_request_irq(info->dev, irq,
@@ -586,8 +638,7 @@ static int vf610_adc_probe(struct platform_device *pdev)
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
PTR_ERR(info->clk));
- ret = PTR_ERR(info->clk);
- return ret;
+ return PTR_ERR(info->clk);
}
info->vref = devm_regulator_get(&pdev->dev, "vref");
@@ -681,17 +732,19 @@ static int vf610_adc_resume(struct device *dev)
ret = clk_prepare_enable(info->clk);
if (ret)
- return ret;
+ goto disable_reg;
vf610_adc_hw_init(info);
return 0;
+
+disable_reg:
+ regulator_disable(info->vref);
+ return ret;
}
#endif
-static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops,
- vf610_adc_suspend,
- vf610_adc_resume);
+static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, vf610_adc_resume);
static struct platform_driver vf610_adc_driver = {
.probe = vf610_adc_probe,