aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio/adc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-10-29 10:38:10 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2018-10-29 10:38:10 -0700
commit738b04fba18d35cd352b7b15afefb8a7b798648e (patch)
tree07fabb1a920af5c92bd35e10f9821cf56c8de3e4 /drivers/iio/adc
parentMerge tag 'mailbox-v4.20' of git://git.linaro.org/landing-teams/working/fujitsu/integration (diff)
parentstaging: gasket: Fix sparse "incorrect type in assignment" warnings. (diff)
downloadlinux-dev-738b04fba18d35cd352b7b15afefb8a7b798648e.tar.xz
linux-dev-738b04fba18d35cd352b7b15afefb8a7b798648e.zip
Merge tag 'staging-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging/IIO driver updates from Greg KH: "Here is the big staging and IIO driver pull request for 4.20-rc1. There are lots of things here, we ended up adding more lines than removing, thanks to a large influx of Comedi National Instrument device support. Someday soon we need to get comedi out of staging... Other than the comedi drivers, the "big" things here are: - new iio drivers - delete dgnc driver (no one used it and no one had the hardware anymore) - vbox driver updates and fixes - erofs fixes - tons and tons of tiny checkpatch fixes for almost all staging drivers All of these have been in linux-next, with the last few happening a bit "late" due to them getting stuck on my laptop during travel to the Mantainers summit" * tag 'staging-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (690 commits) staging: gasket: Fix sparse "incorrect type in assignment" warnings. staging: gasket: remove debug logs for callback invocation staging: gasket: remove debug logs in page table mapping calls staging: rtl8188eu: core: Use sizeof(*p) instead of sizeof(struct P) for memory allocation staging: ks7010: Remove extra blank line staging: gasket: Remove extra blank line staging: media: davinci_vpfe: Fix spelling mistake in enum staging: speakup: Add a pair of braces staging: wlan-ng: Replace long int with long staging: MAINTAINERS: remove obsolete IPX staging directory staging: MAINTAINERS: remove NCP filesystem entry staging: rtl8188eu: cleanup comparsions to false staging: gasket: Update device virtual address comment staging: gasket: sysfs: fix attribute release comment staging: gasket: apex: fix sysfs_show staging: gasket: page_table: simplify gasket_components_to_dev_address staging: gasket: page_table: fix comment in components_to_dev_address staging: gasket: page table: fixup error path allocating coherent mem staging: gasket: page_table: rearrange gasket_page_table_entry staging: gasket: page_table: remove unnecessary PTE status set to free ...
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r--drivers/iio/adc/Kconfig30
-rw-r--r--drivers/iio/adc/Makefile2
-rw-r--r--drivers/iio/adc/ad7298.c2
-rw-r--r--drivers/iio/adc/ad7476.c2
-rw-r--r--drivers/iio/adc/ad7793.c2
-rw-r--r--drivers/iio/adc/ad7887.c2
-rw-r--r--drivers/iio/adc/ad7923.c2
-rw-r--r--drivers/iio/adc/ad799x.c2
-rw-r--r--drivers/iio/adc/at91_adc.c6
-rw-r--r--drivers/iio/adc/envelope-detector.c5
-rw-r--r--drivers/iio/adc/fsl-imx25-gcq.c6
-rw-r--r--drivers/iio/adc/max9611.c2
-rw-r--r--drivers/iio/adc/mcp3911.c363
-rw-r--r--drivers/iio/adc/meson_saradc.c70
-rw-r--r--drivers/iio/adc/qcom-pm8xxx-xoadc.c4
-rw-r--r--drivers/iio/adc/qcom-spmi-adc5.c793
-rw-r--r--drivers/iio/adc/qcom-vadc-common.c189
-rw-r--r--drivers/iio/adc/qcom-vadc-common.h54
-rw-r--r--drivers/iio/adc/rcar-gyroadc.c12
-rw-r--r--drivers/iio/adc/sc27xx_adc.c154
-rw-r--r--drivers/iio/adc/ti-ads7950.c53
21 files changed, 1635 insertions, 120 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 4a754921fb6f..a52fea8749a9 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -501,6 +501,16 @@ config MCP3422
This driver can also be built as a module. If so, the module will be
called mcp3422.
+config MCP3911
+ tristate "Microchip Technology MCP3911 driver"
+ depends on SPI
+ help
+ Say yes here to build support for Microchip Technology's MCP3911
+ analog to digital converter.
+
+ This driver can also be built as a module. If so, the module will be
+ called mcp3911.
+
config MEDIATEK_MT6577_AUXADC
tristate "MediaTek AUXADC driver"
depends on ARCH_MEDIATEK || COMPILE_TEST
@@ -596,6 +606,26 @@ config QCOM_SPMI_VADC
To compile this driver as a module, choose M here: the module will
be called qcom-spmi-vadc.
+config QCOM_SPMI_ADC5
+ tristate "Qualcomm Technologies Inc. SPMI PMIC5 ADC"
+ depends on SPMI
+ select REGMAP_SPMI
+ select QCOM_VADC_COMMON
+ help
+ This is the IIO Voltage PMIC5 ADC driver for Qualcomm Technologies Inc.
+
+ The driver supports multiple channels read. The ADC is a 16-bit
+ sigma-delta ADC. The hardware supports calibrated results for
+ conversion requests and clients include reading voltage phone
+ power, on board system thermistors connected to the PMIC ADC,
+ PMIC die temperature, charger temperature, battery current, USB voltage
+ input, voltage signals connected to supported PMIC GPIO inputs. The
+ hardware supports internal pull-up for thermistors and can choose between
+ a 100k, 30k and 400k pull up using the ADC channels.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-spmi-adc5.
+
config RCAR_GYRO_ADC
tristate "Renesas R-Car GyroADC driver"
depends on ARCH_RCAR_GEN2 || COMPILE_TEST
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 03db7b578f9c..a6e6a0b659e2 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -47,12 +47,14 @@ obj-$(CONFIG_MAX1363) += max1363.o
obj-$(CONFIG_MAX9611) += max9611.o
obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
+obj-$(CONFIG_MCP3911) += mcp3911.o
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
+obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c
index 2b20c6c8ec7f..e0220825fde0 100644
--- a/drivers/iio/adc/ad7298.c
+++ b/drivers/iio/adc/ad7298.c
@@ -385,6 +385,6 @@ static struct spi_driver ad7298_driver = {
};
module_spi_driver(ad7298_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7298 ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
index fbaae47746a8..0549686b9ef8 100644
--- a/drivers/iio/adc/ad7476.c
+++ b/drivers/iio/adc/ad7476.c
@@ -328,6 +328,6 @@ static struct spi_driver ad7476_driver = {
};
module_spi_driver(ad7476_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index d4bbe5b53318..4ac3ae62f56f 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -822,6 +822,6 @@ static struct spi_driver ad7793_driver = {
};
module_spi_driver(ad7793_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c
index 205c0f1761aa..9d4c2467d362 100644
--- a/drivers/iio/adc/ad7887.c
+++ b/drivers/iio/adc/ad7887.c
@@ -362,6 +362,6 @@ static struct spi_driver ad7887_driver = {
};
module_spi_driver(ad7887_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7887 ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c
index ffb7e089969c..d62dbb62be45 100644
--- a/drivers/iio/adc/ad7923.c
+++ b/drivers/iio/adc/ad7923.c
@@ -363,7 +363,7 @@ static struct spi_driver ad7923_driver = {
};
module_spi_driver(ad7923_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>");
MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c
index e1da67d5ee22..7a5b5d00a87d 100644
--- a/drivers/iio/adc/ad799x.c
+++ b/drivers/iio/adc/ad799x.c
@@ -892,6 +892,6 @@ static struct i2c_driver ad799x_driver = {
};
module_i2c_driver(ad799x_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD799x ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index 44b516863c9d..75d2f73582a3 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -248,12 +248,14 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *idev = pf->indio_dev;
struct at91_adc_state *st = iio_priv(idev);
+ struct iio_chan_spec const *chan;
int i, j = 0;
for (i = 0; i < idev->masklength; i++) {
if (!test_bit(i, idev->active_scan_mask))
continue;
- st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, i));
+ chan = idev->channels + i;
+ st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel));
j++;
}
@@ -279,6 +281,8 @@ static void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
iio_trigger_poll(idev->trig);
} else {
st->last_value = at91_adc_readl(st, AT91_ADC_CHAN(st, st->chnb));
+ /* Needed to ACK the DRDY interruption */
+ at91_adc_readl(st, AT91_ADC_LCDR);
st->done = true;
wake_up_interruptible(&st->wq_data_avail);
}
diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c
index 4ebda8ab54fe..2f2b563c1162 100644
--- a/drivers/iio/adc/envelope-detector.c
+++ b/drivers/iio/adc/envelope-detector.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Driver for an envelope detector using a DAC and a comparator
*
* Copyright (C) 2016 Axentia Technologies AB
*
* Author: Peter Rosin <peda@axentia.se>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
/*
diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c
index ea264fa9e567..929c617db364 100644
--- a/drivers/iio/adc/fsl-imx25-gcq.c
+++ b/drivers/iio/adc/fsl-imx25-gcq.c
@@ -209,12 +209,14 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
ret = of_property_read_u32(child, "reg", &reg);
if (ret) {
dev_err(dev, "Failed to get reg property\n");
+ of_node_put(child);
return ret;
}
if (reg >= MX25_NUM_CFGS) {
dev_err(dev,
"reg value is greater than the number of available configuration registers\n");
+ of_node_put(child);
return -EINVAL;
}
@@ -228,6 +230,7 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
if (IS_ERR(priv->vref[refp])) {
dev_err(dev, "Error, trying to use external voltage reference without a vref-%s regulator.",
mx25_gcq_refp_names[refp]);
+ of_node_put(child);
return PTR_ERR(priv->vref[refp]);
}
priv->channel_vref_mv[reg] =
@@ -240,6 +243,7 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
break;
default:
dev_err(dev, "Invalid positive reference %d\n", refp);
+ of_node_put(child);
return -EINVAL;
}
@@ -254,10 +258,12 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) {
dev_err(dev, "Invalid fsl,adc-refp property value\n");
+ of_node_put(child);
return -EINVAL;
}
if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) {
dev_err(dev, "Invalid fsl,adc-refn property value\n");
+ of_node_put(child);
return -EINVAL;
}
diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c
index 0538ff8c4ac1..643a4e66eb80 100644
--- a/drivers/iio/adc/max9611.c
+++ b/drivers/iio/adc/max9611.c
@@ -289,7 +289,7 @@ static int max9611_read_csa_voltage(struct max9611_dev *max9611,
return ret;
if (*adc_raw > 0) {
- *csa_gain = gain_selectors[i];
+ *csa_gain = (enum max9611_csa_gain)gain_selectors[i];
return 0;
}
}
diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
new file mode 100644
index 000000000000..dd52f08ec82e
--- /dev/null
+++ b/drivers/iio/adc/mcp3911.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Microchip MCP3911, Two-channel Analog Front End
+ *
+ * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
+ * Copyright (C) 2018 Kent Gustavsson <kent@minoris.se>
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define MCP3911_REG_CHANNEL0 0x00
+#define MCP3911_REG_CHANNEL1 0x03
+#define MCP3911_REG_MOD 0x06
+#define MCP3911_REG_PHASE 0x07
+#define MCP3911_REG_GAIN 0x09
+
+#define MCP3911_REG_STATUSCOM 0x0a
+#define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4)
+#define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3)
+#define MCP3911_STATUSCOM_EN_OFFCAL BIT(2)
+#define MCP3911_STATUSCOM_EN_GAINCAL BIT(1)
+
+#define MCP3911_REG_CONFIG 0x0c
+#define MCP3911_CONFIG_CLKEXT BIT(1)
+#define MCP3911_CONFIG_VREFEXT BIT(2)
+
+#define MCP3911_REG_OFFCAL_CH0 0x0e
+#define MCP3911_REG_GAINCAL_CH0 0x11
+#define MCP3911_REG_OFFCAL_CH1 0x14
+#define MCP3911_REG_GAINCAL_CH1 0x17
+#define MCP3911_REG_VREFCAL 0x1a
+
+#define MCP3911_CHANNEL(x) (MCP3911_REG_CHANNEL0 + x * 3)
+#define MCP3911_OFFCAL(x) (MCP3911_REG_OFFCAL_CH0 + x * 6)
+
+/* Internal voltage reference in uV */
+#define MCP3911_INT_VREF_UV 1200000
+
+#define MCP3911_REG_READ(reg, id) ((((reg) << 1) | ((id) << 5) | (1 << 0)) & 0xff)
+#define MCP3911_REG_WRITE(reg, id) ((((reg) << 1) | ((id) << 5) | (0 << 0)) & 0xff)
+
+#define MCP3911_NUM_CHANNELS 2
+
+struct mcp3911 {
+ struct spi_device *spi;
+ struct mutex lock;
+ struct regulator *vref;
+ struct clk *clki;
+ u32 dev_addr;
+};
+
+static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len)
+{
+ int ret;
+
+ reg = MCP3911_REG_READ(reg, adc->dev_addr);
+ ret = spi_write_then_read(adc->spi, &reg, 1, val, len);
+ if (ret < 0)
+ return ret;
+
+ be32_to_cpus(val);
+ *val >>= ((4 - len) * 8);
+ dev_dbg(&adc->spi->dev, "reading 0x%x from register 0x%x\n", *val,
+ reg >> 1);
+ return ret;
+}
+
+static int mcp3911_write(struct mcp3911 *adc, u8 reg, u32 val, u8 len)
+{
+ dev_dbg(&adc->spi->dev, "writing 0x%x to register 0x%x\n", val, reg);
+
+ val <<= (3 - len) * 8;
+ cpu_to_be32s(&val);
+ val |= MCP3911_REG_WRITE(reg, adc->dev_addr);
+
+ return spi_write(adc->spi, &val, len + 1);
+}
+
+static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask,
+ u32 val, u8 len)
+{
+ u32 tmp;
+ int ret;
+
+ ret = mcp3911_read(adc, reg, &tmp, len);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ val |= tmp & ~mask;
+ return mcp3911_write(adc, reg, val, len);
+}
+
+static int mcp3911_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct mcp3911 *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&adc->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = mcp3911_read(adc,
+ MCP3911_CHANNEL(channel->channel), val, 3);
+ if (ret)
+ goto out;
+
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_OFFSET:
+ ret = mcp3911_read(adc,
+ MCP3911_OFFCAL(channel->channel), val, 3);
+ if (ret)
+ goto out;
+
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ if (adc->vref) {
+ ret = regulator_get_voltage(adc->vref);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "failed to get vref voltage: %d\n",
+ ret);
+ goto out;
+ }
+
+ *val = ret / 1000;
+ } else {
+ *val = MCP3911_INT_VREF_UV;
+ }
+
+ *val2 = 24;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+ }
+
+out:
+ mutex_unlock(&adc->lock);
+ return ret;
+}
+
+static int mcp3911_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int val,
+ int val2, long mask)
+{
+ struct mcp3911 *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&adc->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_OFFSET:
+ if (val2 != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Write offset */
+ ret = mcp3911_write(adc, MCP3911_OFFCAL(channel->channel), val,
+ 3);
+ if (ret)
+ goto out;
+
+ /* Enable offset*/
+ ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM,
+ MCP3911_STATUSCOM_EN_OFFCAL,
+ MCP3911_STATUSCOM_EN_OFFCAL, 2);
+ break;
+ }
+
+out:
+ mutex_unlock(&adc->lock);
+ return ret;
+}
+
+#define MCP3911_CHAN(idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = idx, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec mcp3911_channels[] = {
+ MCP3911_CHAN(0),
+ MCP3911_CHAN(1),
+};
+
+static const struct iio_info mcp3911_info = {
+ .read_raw = mcp3911_read_raw,
+ .write_raw = mcp3911_write_raw,
+};
+
+static int mcp3911_config(struct mcp3911 *adc, struct device_node *of_node)
+{
+ u32 configreg;
+ int ret;
+
+ of_property_read_u32(of_node, "device-addr", &adc->dev_addr);
+ if (adc->dev_addr > 3) {
+ dev_err(&adc->spi->dev,
+ "invalid device address (%i). Must be in range 0-3.\n",
+ adc->dev_addr);
+ return -EINVAL;
+ }
+ dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr);
+
+ ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2);
+ if (ret)
+ return ret;
+
+ if (adc->vref) {
+ dev_dbg(&adc->spi->dev, "use external voltage reference\n");
+ configreg |= MCP3911_CONFIG_VREFEXT;
+ } else {
+ dev_dbg(&adc->spi->dev,
+ "use internal voltage reference (1.2V)\n");
+ configreg &= ~MCP3911_CONFIG_VREFEXT;
+ }
+
+ if (adc->clki) {
+ dev_dbg(&adc->spi->dev, "use external clock as clocksource\n");
+ configreg |= MCP3911_CONFIG_CLKEXT;
+ } else {
+ dev_dbg(&adc->spi->dev,
+ "use crystal oscillator as clocksource\n");
+ configreg &= ~MCP3911_CONFIG_CLKEXT;
+ }
+
+ return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2);
+}
+
+static int mcp3911_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct mcp3911 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+
+ adc->vref = devm_regulator_get_optional(&adc->spi->dev, "vref");
+ if (IS_ERR(adc->vref)) {
+ if (PTR_ERR(adc->vref) == -ENODEV) {
+ adc->vref = NULL;
+ } else {
+ dev_err(&adc->spi->dev,
+ "failed to get regulator (%ld)\n",
+ PTR_ERR(adc->vref));
+ return PTR_ERR(adc->vref);
+ }
+
+ } else {
+ ret = regulator_enable(adc->vref);
+ if (ret)
+ return ret;
+ }
+
+ adc->clki = devm_clk_get(&adc->spi->dev, NULL);
+ if (IS_ERR(adc->clki)) {
+ if (PTR_ERR(adc->clki) == -ENOENT) {
+ adc->clki = NULL;
+ } else {
+ dev_err(&adc->spi->dev,
+ "failed to get adc clk (%ld)\n",
+ PTR_ERR(adc->clki));
+ ret = PTR_ERR(adc->clki);
+ goto reg_disable;
+ }
+ } else {
+ ret = clk_prepare_enable(adc->clki);
+ if (ret < 0) {
+ dev_err(&adc->spi->dev,
+ "Failed to enable clki: %d\n", ret);
+ goto reg_disable;
+ }
+ }
+
+ ret = mcp3911_config(adc, spi->dev.of_node);
+ if (ret)
+ goto clk_disable;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->dev.of_node = spi->dev.of_node;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mcp3911_info;
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->channels = mcp3911_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mcp3911_channels);
+
+ mutex_init(&adc->lock);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto clk_disable;
+
+ return ret;
+
+clk_disable:
+ clk_disable_unprepare(adc->clki);
+reg_disable:
+ if (adc->vref)
+ regulator_disable(adc->vref);
+
+ return ret;
+}
+
+static int mcp3911_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct mcp3911 *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ clk_disable_unprepare(adc->clki);
+ if (adc->vref)
+ regulator_disable(adc->vref);
+
+ return 0;
+}
+
+static const struct of_device_id mcp3911_dt_ids[] = {
+ { .compatible = "microchip,mcp3911" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp3911_dt_ids);
+
+static const struct spi_device_id mcp3911_id[] = {
+ { "mcp3911", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mcp3911_id);
+
+static struct spi_driver mcp3911_driver = {
+ .driver = {
+ .name = "mcp3911",
+ .of_match_table = mcp3911_dt_ids,
+ },
+ .probe = mcp3911_probe,
+ .remove = mcp3911_remove,
+ .id_table = mcp3911_id,
+};
+module_spi_driver(mcp3911_driver);
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_AUTHOR("Kent Gustavsson <kent@minoris.se>");
+MODULE_DESCRIPTION("Microchip Technology MCP3911");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c
index da2d16dfa63e..028ccd218f82 100644
--- a/drivers/iio/adc/meson_saradc.c
+++ b/drivers/iio/adc/meson_saradc.c
@@ -148,7 +148,6 @@
#define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26)
#define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16)
#define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15)
- #define MESON_SAR_ADC_DELTA_10_TS_C_SHIFT 11
#define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11)
#define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10)
#define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0)
@@ -173,6 +172,7 @@
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _chan, \
+ .address = _chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
@@ -235,7 +235,7 @@ struct meson_sar_adc_data {
struct meson_sar_adc_priv {
struct regmap *regmap;
struct regulator *vref;
- const struct meson_sar_adc_data *data;
+ const struct meson_sar_adc_param *param;
struct clk *clkin;
struct clk *core_clk;
struct clk *adc_sel_clk;
@@ -280,7 +280,7 @@ static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val)
/* use val_calib = scale * val_raw + offset calibration function */
tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias;
- return clamp(tmp, 0, (1 << priv->data->param->resolution) - 1);
+ return clamp(tmp, 0, (1 << priv->param->resolution) - 1);
}
static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
@@ -324,15 +324,15 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, &regval);
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval);
- if (fifo_chan != chan->channel) {
+ if (fifo_chan != chan->address) {
dev_err(&indio_dev->dev,
- "ADC FIFO entry belongs to channel %d instead of %d\n",
- fifo_chan, chan->channel);
+ "ADC FIFO entry belongs to channel %d instead of %lu\n",
+ fifo_chan, chan->address);
return -EINVAL;
}
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval);
- fifo_val &= GENMASK(priv->data->param->resolution - 1, 0);
+ fifo_val &= GENMASK(priv->param->resolution - 1, 0);
*val = meson_sar_adc_calib_val(indio_dev, fifo_val);
return 0;
@@ -344,16 +344,16 @@ static void meson_sar_adc_set_averaging(struct iio_dev *indio_dev,
enum meson_sar_adc_num_samples samples)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
- int val, channel = chan->channel;
+ int val, address = chan->address;
- val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(channel);
+ val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
- MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(channel),
+ MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(address),
val);
- val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(channel);
+ val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
- MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(channel), val);
+ MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(address), val);
}
static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
@@ -373,23 +373,23 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
/* map channel index 0 to the channel which we want to read */
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0),
- chan->channel);
+ chan->address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval);
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
- chan->channel);
+ chan->address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
regval);
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
- chan->channel);
+ chan->address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
regval);
- if (chan->channel == 6)
+ if (chan->address == 6)
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0);
}
@@ -451,7 +451,7 @@ static int meson_sar_adc_lock(struct iio_dev *indio_dev)
mutex_lock(&indio_dev->mlock);
- if (priv->data->param->has_bl30_integration) {
+ if (priv->param->has_bl30_integration) {
/* prevent BL30 from using the SAR ADC while we are using it */
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
MESON_SAR_ADC_DELAY_KERNEL_BUSY,
@@ -479,7 +479,7 @@ static void meson_sar_adc_unlock(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
- if (priv->data->param->has_bl30_integration)
+ if (priv->param->has_bl30_integration)
/* allow BL30 to use the SAR ADC again */
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0);
@@ -527,8 +527,8 @@ static int meson_sar_adc_get_sample(struct iio_dev *indio_dev,
if (ret) {
dev_warn(indio_dev->dev.parent,
- "failed to read sample for channel %d: %d\n",
- chan->channel, ret);
+ "failed to read sample for channel %lu: %d\n",
+ chan->address, ret);
return ret;
}
@@ -563,7 +563,7 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
}
*val = ret / 1000;
- *val2 = priv->data->param->resolution;
+ *val2 = priv->param->resolution;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_CALIBBIAS:
@@ -636,7 +636,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
*/
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
- if (priv->data->param->has_bl30_integration) {
+ if (priv->param->has_bl30_integration) {
/*
* leave sampling delay and the input clocks as configured by
* BL30 to make sure BL30 gets the values it expects when
@@ -716,7 +716,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
return ret;
}
- ret = clk_set_rate(priv->adc_clk, priv->data->param->clock_rate);
+ ret = clk_set_rate(priv->adc_clk, priv->param->clock_rate);
if (ret) {
dev_err(indio_dev->dev.parent,
"failed to set adc clock rate\n");
@@ -729,7 +729,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
- const struct meson_sar_adc_param *param = priv->data->param;
+ const struct meson_sar_adc_param *param = priv->param;
u32 enable_mask;
if (param->bandgap_reg == MESON_SAR_ADC_REG11)
@@ -849,13 +849,13 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
int ret, nominal0, nominal1, value0, value1;
/* use points 25% and 75% for calibration */
- nominal0 = (1 << priv->data->param->resolution) / 4;
- nominal1 = (1 << priv->data->param->resolution) * 3 / 4;
+ nominal0 = (1 << priv->param->resolution) / 4;
+ nominal1 = (1 << priv->param->resolution) * 3 / 4;
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
usleep_range(10, 20);
ret = meson_sar_adc_get_sample(indio_dev,
- &meson_sar_adc_iio_channels[7],
+ &indio_dev->channels[7],
MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
if (ret < 0)
goto out;
@@ -863,7 +863,7 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev)
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4);
usleep_range(10, 20);
ret = meson_sar_adc_get_sample(indio_dev,
- &meson_sar_adc_iio_channels[7],
+ &indio_dev->channels[7],
MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
if (ret < 0)
goto out;
@@ -979,11 +979,11 @@ MODULE_DEVICE_TABLE(of, meson_sar_adc_of_match);
static int meson_sar_adc_probe(struct platform_device *pdev)
{
+ const struct meson_sar_adc_data *match_data;
struct meson_sar_adc_priv *priv;
struct iio_dev *indio_dev;
struct resource *res;
void __iomem *base;
- const struct of_device_id *match;
int irq, ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
@@ -995,15 +995,15 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
priv = iio_priv(indio_dev);
init_completion(&priv->done);
- match = of_match_device(meson_sar_adc_of_match, &pdev->dev);
- if (!match) {
- dev_err(&pdev->dev, "failed to match device\n");
+ match_data = of_device_get_match_data(&pdev->dev);
+ if (!match_data) {
+ dev_err(&pdev->dev, "failed to get match data\n");
return -ENODEV;
}
- priv->data = match->data;
+ priv->param = match_data->param;
- indio_dev->name = priv->data->name;
+ indio_dev->name = match_data->name;
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->modes = INDIO_DIRECT_MODE;
@@ -1027,7 +1027,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
return ret;
priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- priv->data->param->regmap_config);
+ priv->param->regmap_config);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
index b093ecddf1a8..c30c002f1fef 100644
--- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c
+++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
@@ -708,8 +708,8 @@ static int pm8xxx_of_xlate(struct iio_dev *indio_dev,
* mux.
*/
if (iiospec->args_count != 2) {
- dev_err(&indio_dev->dev, "wrong number of arguments for %s need 2 got %d\n",
- iiospec->np->name,
+ dev_err(&indio_dev->dev, "wrong number of arguments for %pOFn need 2 got %d\n",
+ iiospec->np,
iiospec->args_count);
return -EINVAL;
}
diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
new file mode 100644
index 000000000000..f9af6b082916
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -0,0 +1,793 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ */
+
+#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/log2.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+#include "qcom-vadc-common.h"
+
+#define ADC5_USR_REVISION1 0x0
+#define ADC5_USR_STATUS1 0x8
+#define ADC5_USR_STATUS1_REQ_STS BIT(1)
+#define ADC5_USR_STATUS1_EOC BIT(0)
+#define ADC5_USR_STATUS1_REQ_STS_EOC_MASK 0x3
+
+#define ADC5_USR_STATUS2 0x9
+#define ADC5_USR_STATUS2_CONV_SEQ_MASK 0x70
+#define ADC5_USR_STATUS2_CONV_SEQ_MASK_SHIFT 0x5
+
+#define ADC5_USR_IBAT_MEAS 0xf
+#define ADC5_USR_IBAT_MEAS_SUPPORTED BIT(0)
+
+#define ADC5_USR_DIG_PARAM 0x42
+#define ADC5_USR_DIG_PARAM_CAL_VAL BIT(6)
+#define ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT 6
+#define ADC5_USR_DIG_PARAM_CAL_SEL 0x30
+#define ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT 4
+#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL 0xc
+#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT 2
+
+#define ADC5_USR_FAST_AVG_CTL 0x43
+#define ADC5_USR_FAST_AVG_CTL_EN BIT(7)
+#define ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK 0x7
+
+#define ADC5_USR_CH_SEL_CTL 0x44
+
+#define ADC5_USR_DELAY_CTL 0x45
+#define ADC5_USR_HW_SETTLE_DELAY_MASK 0xf
+
+#define ADC5_USR_EN_CTL1 0x46
+#define ADC5_USR_EN_CTL1_ADC_EN BIT(7)
+
+#define ADC5_USR_CONV_REQ 0x47
+#define ADC5_USR_CONV_REQ_REQ BIT(7)
+
+#define ADC5_USR_DATA0 0x50
+
+#define ADC5_USR_DATA1 0x51
+
+#define ADC5_USR_IBAT_DATA0 0x52
+
+#define ADC5_USR_IBAT_DATA1 0x53
+
+/*
+ * Conversion time varies based on the decimation, clock rate, fast average
+ * samples and measurements queued across different VADC peripherals.
+ * Set the timeout to a max of 100ms.
+ */
+#define ADC5_CONV_TIME_MIN_US 263
+#define ADC5_CONV_TIME_MAX_US 264
+#define ADC5_CONV_TIME_RETRY 400
+#define ADC5_CONV_TIMEOUT msecs_to_jiffies(100)
+
+/* Digital version >= 5.3 supports hw_settle_2 */
+#define ADC5_HW_SETTLE_DIFF_MINOR 3
+#define ADC5_HW_SETTLE_DIFF_MAJOR 5
+
+enum adc5_cal_method {
+ ADC5_NO_CAL = 0,
+ ADC5_RATIOMETRIC_CAL,
+ ADC5_ABSOLUTE_CAL
+};
+
+enum adc5_cal_val {
+ ADC5_TIMER_CAL = 0,
+ ADC5_NEW_CAL
+};
+
+/**
+ * struct adc5_channel_prop - ADC channel property.
+ * @channel: channel number, refer to the channel list.
+ * @cal_method: calibration method.
+ * @cal_val: calibration value
+ * @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.
+ * @scale_fn_type: Represents the scaling function to convert voltage
+ * physical units desired by the client for the channel.
+ * @datasheet_name: Channel name used in device tree.
+ */
+struct adc5_channel_prop {
+ unsigned int channel;
+ enum adc5_cal_method cal_method;
+ enum adc5_cal_val cal_val;
+ unsigned int decimation;
+ unsigned int prescale;
+ unsigned int hw_settle_time;
+ unsigned int avg_samples;
+ enum vadc_scale_fn_type scale_fn_type;
+ const char *datasheet_name;
+};
+
+/**
+ * struct adc5_chip - ADC private structure.
+ * @regmap: SPMI ADC5 peripheral register map field.
+ * @dev: SPMI ADC5 device.
+ * @base: base address for the ADC peripheral.
+ * @nchannels: number of ADC channels.
+ * @chan_props: array of ADC channel properties.
+ * @iio_chans: array of IIO channels specification.
+ * @poll_eoc: use polling instead of interrupt.
+ * @complete: ADC result notification after interrupt is received.
+ * @lock: ADC lock for access to the peripheral.
+ * @data: software configuration data.
+ */
+struct adc5_chip {
+ struct regmap *regmap;
+ struct device *dev;
+ u16 base;
+ unsigned int nchannels;
+ struct adc5_channel_prop *chan_props;
+ struct iio_chan_spec *iio_chans;
+ bool poll_eoc;
+ struct completion complete;
+ struct mutex lock;
+ const struct adc5_data *data;
+};
+
+static const struct vadc_prescale_ratio adc5_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},
+ {.num = 1, .den = 16}
+};
+
+static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
+{
+ return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
+}
+
+static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
+{
+ return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
+}
+
+static int adc5_prescaling_from_dt(u32 num, u32 den)
+{
+ unsigned int pre;
+
+ for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
+ if (adc5_prescale_ratios[pre].num == num &&
+ adc5_prescale_ratios[pre].den == den)
+ break;
+
+ if (pre == ARRAY_SIZE(adc5_prescale_ratios))
+ return -EINVAL;
+
+ return pre;
+}
+
+static int adc5_hw_settle_time_from_dt(u32 value,
+ const unsigned int *hw_settle)
+{
+ unsigned int i;
+
+ for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
+ if (value == hw_settle[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int adc5_avg_samples_from_dt(u32 value)
+{
+ if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
+ return -EINVAL;
+
+ return __ffs(value);
+}
+
+static int adc5_decimation_from_dt(u32 value,
+ const unsigned int *decimation)
+{
+ unsigned int i;
+
+ for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
+ if (value == decimation[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
+{
+ int ret;
+ u8 rslt_lsb, rslt_msb;
+
+ ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, sizeof(rslt_lsb));
+ if (ret)
+ return ret;
+
+ ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb));
+ if (ret)
+ return ret;
+
+ *data = (rslt_msb << 8) | rslt_lsb;
+
+ if (*data == ADC5_USR_DATA_CHECK) {
+ pr_err("Invalid data:0x%x\n", *data);
+ return -EINVAL;
+ }
+
+ pr_debug("voltage raw code:0x%x\n", *data);
+
+ return 0;
+}
+
+static int adc5_poll_wait_eoc(struct adc5_chip *adc)
+{
+ unsigned int count, retry = ADC5_CONV_TIME_RETRY;
+ u8 status1;
+ int ret;
+
+ for (count = 0; count < retry; count++) {
+ ret = adc5_read(adc, ADC5_USR_STATUS1, &status1,
+ sizeof(status1));
+ if (ret)
+ return ret;
+
+ status1 &= ADC5_USR_STATUS1_REQ_STS_EOC_MASK;
+ if (status1 == ADC5_USR_STATUS1_EOC)
+ return 0;
+
+ usleep_range(ADC5_CONV_TIME_MIN_US, ADC5_CONV_TIME_MAX_US);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static void adc5_update_dig_param(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop, u8 *data)
+{
+ /* Update calibration value */
+ *data &= ~ADC5_USR_DIG_PARAM_CAL_VAL;
+ *data |= (prop->cal_val << ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT);
+
+ /* Update calibration select */
+ *data &= ~ADC5_USR_DIG_PARAM_CAL_SEL;
+ *data |= (prop->cal_method << ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT);
+
+ /* Update decimation ratio select */
+ *data &= ~ADC5_USR_DIG_PARAM_DEC_RATIO_SEL;
+ *data |= (prop->decimation << ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT);
+}
+
+static int adc5_configure(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop)
+{
+ int ret;
+ u8 buf[6];
+
+ /* Read registers 0x42 through 0x46 */
+ ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ /* Digital param selection */
+ adc5_update_dig_param(adc, prop, &buf[0]);
+
+ /* Update fast average sample value */
+ buf[1] &= (u8) ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
+ buf[1] |= prop->avg_samples;
+
+ /* Select ADC channel */
+ buf[2] = prop->channel;
+
+ /* Select HW settle delay for channel */
+ buf[3] &= (u8) ~ADC5_USR_HW_SETTLE_DELAY_MASK;
+ buf[3] |= prop->hw_settle_time;
+
+ /* Select ADC enable */
+ buf[4] |= ADC5_USR_EN_CTL1_ADC_EN;
+
+ /* Select CONV request */
+ buf[5] |= ADC5_USR_CONV_REQ_REQ;
+
+ if (!adc->poll_eoc)
+ reinit_completion(&adc->complete);
+
+ return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+}
+
+static int adc5_do_conversion(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop,
+ struct iio_chan_spec const *chan,
+ u16 *data_volt, u16 *data_cur)
+{
+ int ret;
+
+ mutex_lock(&adc->lock);
+
+ ret = adc5_configure(adc, prop);
+ if (ret) {
+ pr_err("ADC configure failed with %d\n", ret);
+ goto unlock;
+ }
+
+ if (adc->poll_eoc) {
+ ret = adc5_poll_wait_eoc(adc);
+ if (ret < 0) {
+ pr_err("EOC bit not set\n");
+ goto unlock;
+ }
+ } else {
+ ret = wait_for_completion_timeout(&adc->complete,
+ ADC5_CONV_TIMEOUT);
+ if (!ret) {
+ pr_debug("Did not get completion timeout.\n");
+ ret = adc5_poll_wait_eoc(adc);
+ if (ret < 0) {
+ pr_err("EOC bit not set\n");
+ goto unlock;
+ }
+ }
+ }
+
+ ret = adc5_read_voltage_data(adc, data_volt);
+unlock:
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+static irqreturn_t adc5_isr(int irq, void *dev_id)
+{
+ struct adc5_chip *adc = dev_id;
+
+ complete(&adc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int adc5_of_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ int i;
+
+ for (i = 0; i < adc->nchannels; i++)
+ if (adc->chan_props[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+static int adc5_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ struct adc5_channel_prop *prop;
+ u16 adc_code_volt, adc_code_cur;
+ int ret;
+
+ prop = &adc->chan_props[chan->address];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = adc5_do_conversion(adc, prop, chan,
+ &adc_code_volt, &adc_code_cur);
+ if (ret)
+ return ret;
+
+ ret = qcom_adc5_hw_scale(prop->scale_fn_type,
+ &adc5_prescale_ratios[prop->prescale],
+ adc->data,
+ adc_code_volt, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct iio_info adc5_info = {
+ .read_raw = adc5_read_raw,
+ .of_xlate = adc5_of_xlate,
+};
+
+struct adc5_channels {
+ const char *datasheet_name;
+ unsigned int prescale_index;
+ enum iio_chan_type type;
+ long info_mask;
+ enum vadc_scale_fn_type scale_fn_type;
+};
+
+#define ADC5_CHAN(_dname, _type, _mask, _pre, _scale) \
+ { \
+ .datasheet_name = _dname, \
+ .prescale_index = _pre, \
+ .type = _type, \
+ .info_mask = _mask, \
+ .scale_fn_type = _scale, \
+ }, \
+
+#define ADC5_CHAN_TEMP(_dname, _pre, _scale) \
+ ADC5_CHAN(_dname, IIO_TEMP, \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ _pre, _scale) \
+
+#define ADC5_CHAN_VOLT(_dname, _pre, _scale) \
+ ADC5_CHAN(_dname, IIO_VOLTAGE, \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ _pre, _scale) \
+
+static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
+ [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1,
+ SCALE_HW_CALIB_PMIC_THERM)
+ [ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 16,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 1,
+ SCALE_HW_CALIB_PM5_CHG_TEMP)
+ /* Charger prescales SBUx and MID_CHG to fit within 1.8V upper unit */
+ [ADC5_SBUx] = ADC5_CHAN_VOLT("chg_sbux", 3,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_MID_CHG_DIV6] = ADC5_CHAN_VOLT("chg_mid_chg", 6,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 1,
+ SCALE_HW_CALIB_XOTHERM)
+ [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 1,
+ SCALE_HW_CALIB_PM5_SMB_TEMP)
+};
+
+static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
+ [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 3,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1,
+ SCALE_HW_CALIB_PMIC_THERM)
+ [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_100k_pu", 1,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_100k_pu", 1,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 1,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+};
+
+static int adc5_get_dt_channel_data(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop,
+ struct device_node *node,
+ const struct adc5_data *data)
+{
+ const char *name = node->name, *channel_name;
+ u32 chan, value, varr[2];
+ int ret;
+ struct device *dev = adc->dev;
+
+ ret = of_property_read_u32(node, "reg", &chan);
+ if (ret) {
+ dev_err(dev, "invalid channel number %s\n", name);
+ return ret;
+ }
+
+ if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA ||
+ !data->adc_chans[chan].datasheet_name) {
+ dev_err(dev, "%s invalid channel number %d\n", name, chan);
+ return -EINVAL;
+ }
+
+ /* the channel has DT description */
+ prop->channel = chan;
+
+ channel_name = of_get_property(node,
+ "label", NULL) ? : node->name;
+ if (!channel_name) {
+ pr_err("Invalid channel name\n");
+ return -EINVAL;
+ }
+ prop->datasheet_name = channel_name;
+
+ ret = of_property_read_u32(node, "qcom,decimation", &value);
+ if (!ret) {
+ ret = adc5_decimation_from_dt(value, data->decimation);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid decimation %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->decimation = ret;
+ } else {
+ prop->decimation = ADC5_DECIMATION_DEFAULT;
+ }
+
+ ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
+ if (!ret) {
+ ret = adc5_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;
+ }
+
+ ret = of_property_read_u32(node, "qcom,hw-settle-time", &value);
+ if (!ret) {
+ u8 dig_version[2];
+
+ ret = adc5_read(adc, ADC5_USR_REVISION1, dig_version,
+ sizeof(dig_version));
+ if (ret < 0) {
+ dev_err(dev, "Invalid dig version read %d\n", ret);
+ return ret;
+ }
+
+ pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0],
+ dig_version[1]);
+ /* Digital controller >= 5.3 have hw_settle_2 option */
+ if (dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
+ dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR)
+ ret = adc5_hw_settle_time_from_dt(value,
+ data->hw_settle_2);
+ else
+ ret = adc5_hw_settle_time_from_dt(value,
+ data->hw_settle_1);
+
+ 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 = adc5_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->cal_method = ADC5_RATIOMETRIC_CAL;
+ else
+ prop->cal_method = ADC5_ABSOLUTE_CAL;
+
+ /*
+ * Default to using timer calibration. Using a fresh calibration value
+ * for every conversion will increase the overall time for a request.
+ */
+ prop->cal_val = ADC5_TIMER_CAL;
+
+ dev_dbg(dev, "%02x name %s\n", chan, name);
+
+ return 0;
+}
+
+static const struct adc5_data adc5_data_pmic = {
+ .full_scale_code_volt = 0x70e4,
+ .full_scale_code_cur = 0x2710,
+ .adc_chans = adc5_chans_pmic,
+ .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
+ {250, 420, 840},
+ .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 800, 900, 1, 2, 4, 6, 8, 10},
+ .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 1, 2, 4, 8, 16, 32, 64, 128},
+};
+
+static const struct adc5_data adc5_data_pmic_rev2 = {
+ .full_scale_code_volt = 0x4000,
+ .full_scale_code_cur = 0x1800,
+ .adc_chans = adc5_chans_rev2,
+ .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
+ {256, 512, 1024},
+ .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {0, 100, 200, 300, 400, 500, 600, 700,
+ 800, 900, 1, 2, 4, 6, 8, 10},
+ .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 1, 2, 4, 8, 16, 32, 64, 128},
+};
+
+static const struct of_device_id adc5_match_table[] = {
+ {
+ .compatible = "qcom,spmi-adc5",
+ .data = &adc5_data_pmic,
+ },
+ {
+ .compatible = "qcom,spmi-adc-rev2",
+ .data = &adc5_data_pmic_rev2,
+ },
+ { }
+};
+
+static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
+{
+ const struct adc5_channels *adc_chan;
+ struct iio_chan_spec *iio_chan;
+ struct adc5_channel_prop prop, *chan_props;
+ struct device_node *child;
+ unsigned int index = 0;
+ const struct of_device_id *id;
+ const struct adc5_data *data;
+ int ret;
+
+ adc->nchannels = of_get_available_child_count(node);
+ if (!adc->nchannels)
+ return -EINVAL;
+
+ adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels,
+ sizeof(*adc->iio_chans), GFP_KERNEL);
+ if (!adc->iio_chans)
+ return -ENOMEM;
+
+ adc->chan_props = devm_kcalloc(adc->dev, adc->nchannels,
+ sizeof(*adc->chan_props), GFP_KERNEL);
+ if (!adc->chan_props)
+ return -ENOMEM;
+
+ chan_props = adc->chan_props;
+ iio_chan = adc->iio_chans;
+ id = of_match_node(adc5_match_table, node);
+ if (id)
+ data = id->data;
+ else
+ data = &adc5_data_pmic;
+ adc->data = data;
+
+ for_each_available_child_of_node(node, child) {
+ ret = adc5_get_dt_channel_data(adc, &prop, child, data);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+
+ prop.scale_fn_type =
+ data->adc_chans[prop.channel].scale_fn_type;
+ *chan_props = prop;
+ adc_chan = &data->adc_chans[prop.channel];
+
+ iio_chan->channel = prop.channel;
+ iio_chan->datasheet_name = prop.datasheet_name;
+ iio_chan->extend_name = prop.datasheet_name;
+ iio_chan->info_mask_separate = adc_chan->info_mask;
+ iio_chan->type = adc_chan->type;
+ iio_chan->address = index;
+ iio_chan++;
+ chan_props++;
+ index++;
+ }
+
+ return 0;
+}
+
+static int adc5_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct adc5_chip *adc;
+ 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(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->regmap = regmap;
+ adc->dev = dev;
+ adc->base = reg;
+ init_completion(&adc->complete);
+ mutex_init(&adc->lock);
+
+ ret = adc5_get_dt_data(adc, node);
+ if (ret) {
+ pr_err("adc get dt data failed\n");
+ return ret;
+ }
+
+ irq_eoc = platform_get_irq(pdev, 0);
+ if (irq_eoc < 0) {
+ if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
+ return irq_eoc;
+ adc->poll_eoc = true;
+ } else {
+ ret = devm_request_irq(dev, irq_eoc, adc5_isr, 0,
+ "pm-adc5", adc);
+ 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 = &adc5_info;
+ indio_dev->channels = adc->iio_chans;
+ indio_dev->num_channels = adc->nchannels;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct platform_driver adc5_driver = {
+ .driver = {
+ .name = "qcom-spmi-adc5.c",
+ .of_match_table = adc5_match_table,
+ },
+ .probe = adc5_probe,
+};
+module_platform_driver(adc5_driver);
+
+MODULE_ALIAS("platform:qcom-spmi-adc5");
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
index fe3d7826783c..dcd7fb5b9fb2 100644
--- a/drivers/iio/adc/qcom-vadc-common.c
+++ b/drivers/iio/adc/qcom-vadc-common.c
@@ -47,8 +47,79 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
{44, 125}
};
+/*
+ * Voltage to temperature table for 100k pull up for NTCG104EF104 with
+ * 1.875V reference.
+ */
+static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
+ { 1831, -40000 },
+ { 1814, -35000 },
+ { 1791, -30000 },
+ { 1761, -25000 },
+ { 1723, -20000 },
+ { 1675, -15000 },
+ { 1616, -10000 },
+ { 1545, -5000 },
+ { 1463, 0 },
+ { 1370, 5000 },
+ { 1268, 10000 },
+ { 1160, 15000 },
+ { 1049, 20000 },
+ { 937, 25000 },
+ { 828, 30000 },
+ { 726, 35000 },
+ { 630, 40000 },
+ { 544, 45000 },
+ { 467, 50000 },
+ { 399, 55000 },
+ { 340, 60000 },
+ { 290, 65000 },
+ { 247, 70000 },
+ { 209, 75000 },
+ { 179, 80000 },
+ { 153, 85000 },
+ { 130, 90000 },
+ { 112, 95000 },
+ { 96, 100000 },
+ { 82, 105000 },
+ { 71, 110000 },
+ { 62, 115000 },
+ { 53, 120000 },
+ { 46, 125000 },
+};
+
+static int qcom_vadc_scale_hw_calib_volt(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_uv);
+static int qcom_vadc_scale_hw_calib_therm(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_smb_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_chg5_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_calib_die_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+
+static struct qcom_adc5_scale_type scale_adc5_fn[] = {
+ [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
+ [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
+ [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
+ [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
+ [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
+ [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
+};
+
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
- u32 tablesize, s32 input, s64 *output)
+ u32 tablesize, s32 input, int *output)
{
bool descending = 1;
u32 i = 0;
@@ -128,7 +199,7 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
bool absolute, u16 adc_code,
int *result_mdec)
{
- s64 voltage = 0, result = 0;
+ s64 voltage = 0;
int ret;
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
@@ -138,12 +209,11 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
- voltage, &result);
+ voltage, result_mdec);
if (ret)
return ret;
- result *= 1000;
- *result_mdec = result;
+ *result_mdec *= 1000;
return 0;
}
@@ -191,6 +261,99 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
return 0;
}
+static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ unsigned int factor)
+{
+ s64 voltage, temp, adc_vdd_ref_mv = 1875;
+
+ /*
+ * The normal data range is between 0V to 1.875V. On cases where
+ * we read low voltage values, the ADC code can go beyond the
+ * range and the scale result is incorrect so we clamp the values
+ * for the cases where the code represents a value below 0V
+ */
+ if (adc_code > VADC5_MAX_CODE)
+ adc_code = 0;
+
+ /* (ADC code * vref_vadc (1.875V)) / full_scale_code */
+ voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
+ voltage = div64_s64(voltage, data->full_scale_code_volt);
+ if (voltage > 0) {
+ voltage *= prescale->den;
+ temp = prescale->num * factor;
+ voltage = div64_s64(voltage, temp);
+ } else {
+ voltage = 0;
+ }
+
+ return (int) voltage;
+}
+
+static int qcom_vadc_scale_hw_calib_volt(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_uv)
+{
+ *result_uv = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 1);
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_calib_therm(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ int voltage;
+
+ voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 1000);
+
+ /* Map voltage to temperature from look-up table */
+ return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+ voltage, result_mdec);
+}
+
+static int qcom_vadc_scale_hw_calib_die_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 2);
+ *result_mdec -= KELVINMIL_CELSIUSMIL;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_smb_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100,
+ prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR);
+ *result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_chg5_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 4);
+ *result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec;
+
+ return 0;
+}
+
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
@@ -221,6 +384,22 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
}
EXPORT_SYMBOL(qcom_vadc_scale);
+int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result)
+{
+ if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
+ scaletype < SCALE_HW_CALIB_INVALID)) {
+ pr_err("Invalid scale type %d\n", scaletype);
+ return -EINVAL;
+ }
+
+ return scale_adc5_fn[scaletype].scale_fn(prescale, data,
+ adc_code, result);
+}
+EXPORT_SYMBOL(qcom_adc5_hw_scale);
+
int qcom_vadc_decimation_from_dt(u32 value)
{
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h
index 1d5354ff5c72..bbb1fa02b382 100644
--- a/drivers/iio/adc/qcom-vadc-common.h
+++ b/drivers/iio/adc/qcom-vadc-common.h
@@ -25,15 +25,31 @@
#define VADC_DECIMATION_MIN 512
#define VADC_DECIMATION_MAX 4096
+#define ADC5_DEF_VBAT_PRESCALING 1 /* 1:3 */
+#define ADC5_DECIMATION_SHORT 250
+#define ADC5_DECIMATION_MEDIUM 420
+#define ADC5_DECIMATION_LONG 840
+/* Default decimation - 1024 for rev2, 840 for pmic5 */
+#define ADC5_DECIMATION_DEFAULT 2
+#define ADC5_DECIMATION_SAMPLES_MAX 3
#define VADC_HW_SETTLE_DELAY_MAX 10000
+#define VADC_HW_SETTLE_SAMPLES_MAX 16
#define VADC_AVG_SAMPLES_MAX 512
+#define ADC5_AVG_SAMPLES_MAX 16
#define KELVINMIL_CELSIUSMIL 273150
+#define PMIC5_CHG_TEMP_SCALE_FACTOR 377500
+#define PMIC5_SMB_TEMP_CONSTANT 419400
+#define PMIC5_SMB_TEMP_SCALE_FACTOR 356
#define PMI_CHG_SCALE_1 -138890
#define PMI_CHG_SCALE_2 391750000000LL
+#define VADC5_MAX_CODE 0x7fff
+#define ADC5_FULL_SCALE_CODE 0x70e4
+#define ADC5_USR_DATA_CHECK 0x8000
+
/**
* struct vadc_map_pt - Map the graph representation for ADC channel
* @x: Represent the ADC digitized code.
@@ -89,6 +105,18 @@ struct vadc_prescale_ratio {
* SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
* SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
* SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
+ * SCALE_HW_CALIB_DEFAULT: Default scaling to convert raw adc code to
+ * voltage (uV) with hardware applied offset/slope values to adc code.
+ * SCALE_HW_CALIB_THERM_100K_PULLUP: Returns temperature in millidegC using
+ * lookup table. The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_XOTHERM: Returns XO thermistor voltage in millidegC using
+ * 100k pullup. The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
+ * The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5
+ * charger temperature.
+ * SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5
+ * SMB1390 temperature.
*/
enum vadc_scale_fn_type {
SCALE_DEFAULT = 0,
@@ -96,6 +124,22 @@ enum vadc_scale_fn_type {
SCALE_PMIC_THERM,
SCALE_XOTHERM,
SCALE_PMI_CHG_TEMP,
+ SCALE_HW_CALIB_DEFAULT,
+ SCALE_HW_CALIB_THERM_100K_PULLUP,
+ SCALE_HW_CALIB_XOTHERM,
+ SCALE_HW_CALIB_PMIC_THERM,
+ SCALE_HW_CALIB_PM5_CHG_TEMP,
+ SCALE_HW_CALIB_PM5_SMB_TEMP,
+ SCALE_HW_CALIB_INVALID,
+};
+
+struct adc5_data {
+ const u32 full_scale_code_volt;
+ const u32 full_scale_code_cur;
+ const struct adc5_channels *adc_chans;
+ unsigned int *decimation;
+ unsigned int *hw_settle_1;
+ unsigned int *hw_settle_2;
};
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
@@ -104,6 +148,16 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
bool absolute,
u16 adc_code, int *result_mdec);
+struct qcom_adc5_scale_type {
+ int (*scale_fn)(const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data, u16 adc_code, int *result);
+};
+
+int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+
int qcom_vadc_decimation_from_dt(u32 value);
#endif /* QCOM_VADC_COMMON_H */
diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
index dcb50172186f..4e982b51bcda 100644
--- a/drivers/iio/adc/rcar-gyroadc.c
+++ b/drivers/iio/adc/rcar-gyroadc.c
@@ -343,8 +343,8 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
for_each_child_of_node(np, child) {
of_id = of_match_node(rcar_gyroadc_child_match, child);
if (!of_id) {
- dev_err(dev, "Ignoring unsupported ADC \"%s\".",
- child->name);
+ dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".",
+ child);
continue;
}
@@ -381,16 +381,16 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
ret = of_property_read_u32(child, "reg", &reg);
if (ret) {
dev_err(dev,
- "Failed to get child reg property of ADC \"%s\".\n",
- child->name);
+ "Failed to get child reg property of ADC \"%pOFn\".\n",
+ child);
return ret;
}
/* Channel number is too high. */
if (reg >= num_channels) {
dev_err(dev,
- "Only %i channels supported with %s, but reg = <%i>.\n",
- num_channels, child->name, reg);
+ "Only %i channels supported with %pOFn, but reg = <%i>.\n",
+ num_channels, child, reg);
return ret;
}
}
diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c
index 2b60efea0c39..7940b23dcad9 100644
--- a/drivers/iio/adc/sc27xx_adc.c
+++ b/drivers/iio/adc/sc27xx_adc.c
@@ -5,10 +5,12 @@
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/slab.h>
/* PMIC global registers definition */
#define SC27XX_MODULE_EN 0xc08
@@ -87,16 +89,73 @@ struct sc27xx_adc_linear_graph {
* should use the small-scale graph, and if more than 1.2v, we should use the
* big-scale graph.
*/
-static const struct sc27xx_adc_linear_graph big_scale_graph = {
+static struct sc27xx_adc_linear_graph big_scale_graph = {
4200, 3310,
3600, 2832,
};
-static const struct sc27xx_adc_linear_graph small_scale_graph = {
+static struct sc27xx_adc_linear_graph small_scale_graph = {
1000, 3413,
100, 341,
};
+static const struct sc27xx_adc_linear_graph big_scale_graph_calib = {
+ 4200, 856,
+ 3600, 733,
+};
+
+static const struct sc27xx_adc_linear_graph small_scale_graph_calib = {
+ 1000, 833,
+ 100, 80,
+};
+
+static int sc27xx_adc_get_calib_data(u32 calib_data, int calib_adc)
+{
+ return ((calib_data & 0xff) + calib_adc - 128) * 4;
+}
+
+static int sc27xx_adc_scale_calibration(struct sc27xx_adc_data *data,
+ bool big_scale)
+{
+ const struct sc27xx_adc_linear_graph *calib_graph;
+ struct sc27xx_adc_linear_graph *graph;
+ struct nvmem_cell *cell;
+ const char *cell_name;
+ u32 calib_data = 0;
+ void *buf;
+ size_t len;
+
+ if (big_scale) {
+ calib_graph = &big_scale_graph_calib;
+ graph = &big_scale_graph;
+ cell_name = "big_scale_calib";
+ } else {
+ calib_graph = &small_scale_graph_calib;
+ graph = &small_scale_graph;
+ cell_name = "small_scale_calib";
+ }
+
+ cell = nvmem_cell_get(data->dev, cell_name);
+ if (IS_ERR(cell))
+ return PTR_ERR(cell);
+
+ buf = nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ memcpy(&calib_data, buf, min(len, sizeof(u32)));
+
+ /* Only need to calibrate the adc values in the linear graph. */
+ graph->adc0 = sc27xx_adc_get_calib_data(calib_data, calib_graph->adc0);
+ graph->adc1 = sc27xx_adc_get_calib_data(calib_data >> 8,
+ calib_graph->adc1);
+
+ kfree(buf);
+ return 0;
+}
+
static int sc27xx_adc_get_ratio(int channel, int scale)
{
switch (channel) {
@@ -209,7 +268,7 @@ static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data,
*div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK;
}
-static int sc27xx_adc_to_volt(const struct sc27xx_adc_linear_graph *graph,
+static int sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph,
int raw_adc)
{
int tmp;
@@ -273,6 +332,17 @@ static int sc27xx_adc_read_raw(struct iio_dev *indio_dev,
int ret, tmp;
switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = sc27xx_adc_read(data, chan->channel, scale, &tmp);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret)
+ return ret;
+
+ *val = tmp;
+ return IIO_VAL_INT;
+
case IIO_CHAN_INFO_PROCESSED:
mutex_lock(&indio_dev->mlock);
ret = sc27xx_adc_read_processed(data, chan->channel, scale,
@@ -315,48 +385,47 @@ static const struct iio_info sc27xx_info = {
.write_raw = &sc27xx_adc_write_raw,
};
-#define SC27XX_ADC_CHANNEL(index) { \
+#define SC27XX_ADC_CHANNEL(index, mask) { \
.type = IIO_VOLTAGE, \
.channel = index, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
- BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate = mask | BIT(IIO_CHAN_INFO_SCALE), \
.datasheet_name = "CH##index", \
.indexed = 1, \
}
static const struct iio_chan_spec sc27xx_channels[] = {
- SC27XX_ADC_CHANNEL(0),
- SC27XX_ADC_CHANNEL(1),
- SC27XX_ADC_CHANNEL(2),
- SC27XX_ADC_CHANNEL(3),
- SC27XX_ADC_CHANNEL(4),
- SC27XX_ADC_CHANNEL(5),
- SC27XX_ADC_CHANNEL(6),
- SC27XX_ADC_CHANNEL(7),
- SC27XX_ADC_CHANNEL(8),
- SC27XX_ADC_CHANNEL(9),
- SC27XX_ADC_CHANNEL(10),
- SC27XX_ADC_CHANNEL(11),
- SC27XX_ADC_CHANNEL(12),
- SC27XX_ADC_CHANNEL(13),
- SC27XX_ADC_CHANNEL(14),
- SC27XX_ADC_CHANNEL(15),
- SC27XX_ADC_CHANNEL(16),
- SC27XX_ADC_CHANNEL(17),
- SC27XX_ADC_CHANNEL(18),
- SC27XX_ADC_CHANNEL(19),
- SC27XX_ADC_CHANNEL(20),
- SC27XX_ADC_CHANNEL(21),
- SC27XX_ADC_CHANNEL(22),
- SC27XX_ADC_CHANNEL(23),
- SC27XX_ADC_CHANNEL(24),
- SC27XX_ADC_CHANNEL(25),
- SC27XX_ADC_CHANNEL(26),
- SC27XX_ADC_CHANNEL(27),
- SC27XX_ADC_CHANNEL(28),
- SC27XX_ADC_CHANNEL(29),
- SC27XX_ADC_CHANNEL(30),
- SC27XX_ADC_CHANNEL(31),
+ SC27XX_ADC_CHANNEL(0, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(1, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(2, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(3, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(4, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(5, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(6, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(7, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(8, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(9, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(10, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(11, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(12, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(13, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(14, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(15, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(16, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(17, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(18, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(19, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(20, BIT(IIO_CHAN_INFO_RAW)),
+ SC27XX_ADC_CHANNEL(21, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(22, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(23, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(24, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(25, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(26, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(27, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(28, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(29, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(30, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(31, BIT(IIO_CHAN_INFO_PROCESSED)),
};
static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
@@ -380,6 +449,15 @@ static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
if (ret)
goto disable_clk;
+ /* ADC channel scales' calibration from nvmem device */
+ ret = sc27xx_adc_scale_calibration(data, true);
+ if (ret)
+ goto disable_clk;
+
+ ret = sc27xx_adc_scale_calibration(data, false);
+ if (ret)
+ goto disable_clk;
+
return 0;
disable_clk:
diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c
index a5bd5944bc66..0ad63592cc3c 100644
--- a/drivers/iio/adc/ti-ads7950.c
+++ b/drivers/iio/adc/ti-ads7950.c
@@ -51,7 +51,7 @@
struct ti_ads7950_state {
struct spi_device *spi;
- struct spi_transfer ring_xfer[TI_ADS7950_MAX_CHAN + 2];
+ struct spi_transfer ring_xfer;
struct spi_transfer scan_single_xfer[3];
struct spi_message ring_msg;
struct spi_message scan_single_msg;
@@ -65,11 +65,11 @@ struct ti_ads7950_state {
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
- __be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE]
+ u16 rx_buf[TI_ADS7950_MAX_CHAN + 2 + TI_ADS7950_TIMESTAMP_SIZE]
____cacheline_aligned;
- __be16 tx_buf[TI_ADS7950_MAX_CHAN];
- __be16 single_tx;
- __be16 single_rx;
+ u16 tx_buf[TI_ADS7950_MAX_CHAN + 2];
+ u16 single_tx;
+ u16 single_rx;
};
@@ -108,7 +108,7 @@ enum ti_ads7950_id {
.realbits = bits, \
.storagebits = 16, \
.shift = 12 - (bits), \
- .endianness = IIO_BE, \
+ .endianness = IIO_CPU, \
}, \
}
@@ -249,23 +249,14 @@ static int ti_ads7950_update_scan_mode(struct iio_dev *indio_dev,
len = 0;
for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) {
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(i) | st->settings;
- st->tx_buf[len++] = cpu_to_be16(cmd);
+ st->tx_buf[len++] = cmd;
}
/* Data for the 1st channel is not returned until the 3rd transfer */
- len += 2;
- for (i = 0; i < len; i++) {
- if ((i + 2) < len)
- st->ring_xfer[i].tx_buf = &st->tx_buf[i];
- if (i >= 2)
- st->ring_xfer[i].rx_buf = &st->rx_buf[i - 2];
- st->ring_xfer[i].len = 2;
- st->ring_xfer[i].cs_change = 1;
- }
- /* make sure last transfer's cs_change is not set */
- st->ring_xfer[len - 1].cs_change = 0;
+ st->tx_buf[len++] = 0;
+ st->tx_buf[len++] = 0;
- spi_message_init_with_transfers(&st->ring_msg, st->ring_xfer, len);
+ st->ring_xfer.len = len * 2;
return 0;
}
@@ -281,7 +272,7 @@ static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p)
if (ret < 0)
goto out;
- iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
+ iio_push_to_buffers_with_timestamp(indio_dev, &st->rx_buf[2],
iio_get_time_ns(indio_dev));
out:
@@ -298,13 +289,13 @@ static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
mutex_lock(&indio_dev->mlock);
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings;
- st->single_tx = cpu_to_be16(cmd);
+ st->single_tx = cmd;
ret = spi_sync(st->spi, &st->scan_single_msg);
if (ret)
goto out;
- ret = be16_to_cpu(st->single_rx);
+ ret = st->single_rx;
out:
mutex_unlock(&indio_dev->mlock);
@@ -378,6 +369,14 @@ static int ti_ads7950_probe(struct spi_device *spi)
const struct ti_ads7950_chip_info *info;
int ret;
+ spi->bits_per_word = 16;
+ spi->mode |= SPI_CS_WORD;
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Error in spi setup\n");
+ return ret;
+ }
+
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
@@ -398,6 +397,16 @@ static int ti_ads7950_probe(struct spi_device *spi)
indio_dev->num_channels = info->num_channels;
indio_dev->info = &ti_ads7950_info;
+ /* build spi ring message */
+ spi_message_init(&st->ring_msg);
+
+ st->ring_xfer.tx_buf = &st->tx_buf[0];
+ st->ring_xfer.rx_buf = &st->rx_buf[0];
+ /* len will be set later */
+ st->ring_xfer.cs_change = true;
+
+ spi_message_add_tail(&st->ring_xfer, &st->ring_msg);
+
/*
* Setup default message. The sample is read at the end of the first
* transfer, then it takes one full cycle to convert the sample and one