summaryrefslogtreecommitdiff
path: root/drivers/iio/adc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r--drivers/iio/adc/Kconfig112
-rw-r--r--drivers/iio/adc/Makefile10
-rw-r--r--drivers/iio/adc/ad799x.c2
-rw-r--r--drivers/iio/adc/aspeed_adc.c295
-rw-r--r--drivers/iio/adc/cpcap-adc.c1007
-rw-r--r--drivers/iio/adc/exynos_adc.c2
-rw-r--r--drivers/iio/adc/hx711.c2
-rw-r--r--drivers/iio/adc/imx7d_adc.c2
-rw-r--r--drivers/iio/adc/ina2xx-adc.c34
-rw-r--r--drivers/iio/adc/lpc32xx_adc.c219
-rw-r--r--drivers/iio/adc/ltc2497.c279
-rw-r--r--drivers/iio/adc/max1027.c2
-rw-r--r--drivers/iio/adc/max11100.c5
-rw-r--r--drivers/iio/adc/max1118.c307
-rw-r--r--drivers/iio/adc/max1363.c2
-rw-r--r--drivers/iio/adc/max9611.c585
-rw-r--r--drivers/iio/adc/meson_saradc.c165
-rw-r--r--drivers/iio/adc/qcom-pm8xxx-xoadc.c1036
-rw-r--r--drivers/iio/adc/qcom-spmi-vadc.c325
-rw-r--r--drivers/iio/adc/qcom-vadc-common.c230
-rw-r--r--drivers/iio/adc/qcom-vadc-common.h108
-rw-r--r--drivers/iio/adc/rockchip_saradc.c2
-rw-r--r--drivers/iio/adc/spear_adc.c395
-rw-r--r--drivers/iio/adc/stm32-adc.c50
-rw-r--r--drivers/iio/adc/stx104.c3
-rw-r--r--drivers/iio/adc/sun4i-gpadc-iio.c719
-rw-r--r--drivers/iio/adc/ti-ads1015.c24
-rw-r--r--drivers/iio/adc/vf610_adc.c2
28 files changed, 5573 insertions, 351 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index ff5ad3be55b4..401f47b51d83 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -130,6 +130,17 @@ config AD799X
To compile this driver as a module, choose M here: the module will be
called ad799x.
+config ASPEED_ADC
+ tristate "Aspeed ADC"
+ depends on ARCH_ASPEED || COMPILE_TEST
+ depends on COMMON_CLK
+ help
+ If you say yes here you get support for the ADC included in Aspeed
+ BMC SoCs.
+
+ To compile this driver as a module, choose M here: the module will be
+ called aspeed_adc.
+
config AT91_ADC
tristate "Atmel AT91 ADC"
depends on ARCH_AT91
@@ -205,6 +216,17 @@ config CC10001_ADC
This driver can also be built as a module. If so, the module will be
called cc10001_adc.
+config CPCAP_ADC
+ tristate "Motorola CPCAP PMIC ADC driver"
+ depends on MFD_CPCAP
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Motorola CPCAP PMIC ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called cpcap-adc.
+
config DA9150_GPADC
tristate "Dialog DA9150 GPADC driver support"
depends on MFD_DA9150
@@ -328,6 +350,18 @@ config LPC18XX_ADC
To compile this driver as a module, choose M here: the module will be
called lpc18xx_adc.
+config LPC32XX_ADC
+ tristate "NXP LPC32XX ADC"
+ depends on ARCH_LPC32XX || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for the integrated ADC inside the
+ LPC32XX SoC. Note that this feature uses the same hardware as the
+ touchscreen driver, so you should either select only one of the two
+ drivers (lpc32xx_adc or lpc32xx_ts) or, in the OpenFirmware case,
+ activate only one via device tree selection. Provides direct access
+ via sysfs.
+
config LTC2485
tristate "Linear Technology LTC2485 ADC driver"
depends on I2C
@@ -337,6 +371,16 @@ config LTC2485
To compile this driver as a module, choose M here: the module will be
called ltc2485.
+config LTC2497
+ tristate "Linear Technology LTC2497 ADC driver"
+ depends on I2C
+ help
+ Say yes here to build support for Linear Technology LTC2497
+ 16-Bit 8-/16-Channel Delta Sigma ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ltc2497.
+
config MAX1027
tristate "Maxim max1027 ADC driver"
depends on SPI
@@ -358,6 +402,18 @@ config MAX11100
To compile this driver as a module, choose M here: the module will be
called max11100.
+config MAX1118
+ tristate "Maxim max1117/max1118/max1119 ADCs driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Maxim max1117/max1118/max1119
+ 8-bit, dual-channel ADCs.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max1118.
+
config MAX1363
tristate "Maxim max1363 ADC driver"
depends on I2C
@@ -377,6 +433,16 @@ config MAX1363
To compile this driver as a module, choose M here: the module will be
called max1363.
+config MAX9611
+ tristate "Maxim max9611/max9612 ADC driver"
+ depends on I2C
+ help
+ Say yes here to build support for Maxim max9611/max9612 current sense
+ amplifier with 12-bits ADC interface.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max9611.
+
config MCP320X
tristate "Microchip Technology MCP3x01/02/04/08"
depends on SPI
@@ -451,6 +517,20 @@ config PALMAS_GPADC
is used in smartphones and tablets and supports a 16 channel
general purpose ADC.
+config QCOM_VADC_COMMON
+ tristate
+
+config QCOM_PM8XXX_XOADC
+ tristate "Qualcomm SSBI PM8xxx PMIC XOADCs"
+ depends on MFD_PM8XXX
+ select QCOM_VADC_COMMON
+ help
+ ADC driver for the XOADC portions of the Qualcomm PM8xxx PMICs
+ using SSBI transport: PM8018, PM8038, PM8058, PM8921.
+
+ To compile this driver as a module, choose M here: the module
+ will be called qcom-pm8xxx-xoadc.
+
config QCOM_SPMI_IADC
tristate "Qualcomm SPMI PMIC current ADC"
depends on SPMI
@@ -469,6 +549,7 @@ config QCOM_SPMI_VADC
tristate "Qualcomm SPMI PMIC voltage ADC"
depends on SPMI
select REGMAP_SPMI
+ select QCOM_VADC_COMMON
help
This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
@@ -503,6 +584,17 @@ config ROCKCHIP_SARADC
To compile this driver as a module, choose M here: the
module will be called rockchip_saradc.
+config SPEAR_ADC
+ tristate "ST SPEAr ADC"
+ depends on PLAT_SPEAR || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for the integrated ADC inside the
+ ST SPEAr SoC. Provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called spear_adc.
+
config STM32_ADC_CORE
tristate "STMicroelectronics STM32 adc core"
depends on ARCH_STM32 || COMPILE_TEST
@@ -532,7 +624,7 @@ config STM32_ADC
config STX104
tristate "Apex Embedded Systems STX104 driver"
- depends on X86 && ISA_BUS_API
+ depends on PC104 && X86 && ISA_BUS_API
select GPIOLIB
help
Say yes here to build support for the Apex Embedded Systems STX104
@@ -545,6 +637,24 @@ config STX104
The base port addresses for the devices may be configured via the base
array module parameter.
+config SUN4I_GPADC
+ tristate "Support for the Allwinner SoCs GPADC"
+ depends on IIO
+ depends on MFD_SUN4I_GPADC || MACH_SUN8I
+ depends on THERMAL || !THERMAL_OF
+ help
+ Say yes here to build support for Allwinner (A10, A13 and A31) SoCs
+ GPADC. This ADC provides 4 channels which can be used as an ADC or as
+ a touchscreen input and one channel for thermal sensor.
+
+ The thermal sensor slows down ADC readings and can be disabled by
+ disabling CONFIG_THERMAL_OF. However, the thermal sensor should be
+ enabled by default since the SoC temperature is usually more critical
+ than ADC readings.
+
+ To compile this driver as a module, choose M here: the module will be
+ called sun4i-gpadc-iio.
+
config TI_ADC081C
tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index a01de757f42c..9339bec4babe 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
+obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
@@ -21,6 +22,7 @@ obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
+obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
@@ -31,10 +33,14 @@ obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
+obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
obj-$(CONFIG_LTC2485) += ltc2485.o
+obj-$(CONFIG_LTC2497) += ltc2497.o
obj-$(CONFIG_MAX1027) += max1027.o
obj-$(CONFIG_MAX11100) += max11100.o
+obj-$(CONFIG_MAX1118) += max1118.o
obj-$(CONFIG_MAX1363) += max1363.o
+obj-$(CONFIG_MAX9611) += max9611.o
obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
@@ -44,10 +50,14 @@ 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_IADC) += qcom-spmi-iadc.o
+obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
+obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
+obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
obj-$(CONFIG_STX104) += stx104.o
+obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c
index 9704090b7908..22426ae4af97 100644
--- a/drivers/iio/adc/ad799x.c
+++ b/drivers/iio/adc/ad799x.c
@@ -520,7 +520,7 @@ static struct attribute *ad799x_event_attributes[] = {
NULL,
};
-static struct attribute_group ad799x_event_attrs_group = {
+static const struct attribute_group ad799x_event_attrs_group = {
.attrs = ad799x_event_attributes,
};
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
new file mode 100644
index 000000000000..62670cbfa2bb
--- /dev/null
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -0,0 +1,295 @@
+/*
+ * Aspeed AST2400/2500 ADC
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+
+#define ASPEED_RESOLUTION_BITS 10
+#define ASPEED_CLOCKS_PER_SAMPLE 12
+
+#define ASPEED_REG_ENGINE_CONTROL 0x00
+#define ASPEED_REG_INTERRUPT_CONTROL 0x04
+#define ASPEED_REG_VGA_DETECT_CONTROL 0x08
+#define ASPEED_REG_CLOCK_CONTROL 0x0C
+#define ASPEED_REG_MAX 0xC0
+
+#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1)
+#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1)
+#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1)
+
+#define ASPEED_ENGINE_ENABLE BIT(0)
+
+struct aspeed_adc_model_data {
+ const char *model_name;
+ unsigned int min_sampling_rate; // Hz
+ unsigned int max_sampling_rate; // Hz
+ unsigned int vref_voltage; // mV
+};
+
+struct aspeed_adc_data {
+ struct device *dev;
+ void __iomem *base;
+ spinlock_t clk_lock;
+ struct clk_hw *clk_prescaler;
+ struct clk_hw *clk_scaler;
+};
+
+#define ASPEED_CHAN(_idx, _data_reg_addr) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .address = (_data_reg_addr), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec aspeed_adc_iio_channels[] = {
+ ASPEED_CHAN(0, 0x10),
+ ASPEED_CHAN(1, 0x12),
+ ASPEED_CHAN(2, 0x14),
+ ASPEED_CHAN(3, 0x16),
+ ASPEED_CHAN(4, 0x18),
+ ASPEED_CHAN(5, 0x1A),
+ ASPEED_CHAN(6, 0x1C),
+ ASPEED_CHAN(7, 0x1E),
+ ASPEED_CHAN(8, 0x20),
+ ASPEED_CHAN(9, 0x22),
+ ASPEED_CHAN(10, 0x24),
+ ASPEED_CHAN(11, 0x26),
+ ASPEED_CHAN(12, 0x28),
+ ASPEED_CHAN(13, 0x2A),
+ ASPEED_CHAN(14, 0x2C),
+ ASPEED_CHAN(15, 0x2E),
+};
+
+static int aspeed_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+ const struct aspeed_adc_model_data *model_data =
+ of_device_get_match_data(data->dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = readw(data->base + chan->address);
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = model_data->vref_voltage;
+ *val2 = ASPEED_RESOLUTION_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = clk_get_rate(data->clk_scaler->clk) /
+ ASPEED_CLOCKS_PER_SAMPLE;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int aspeed_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+ const struct aspeed_adc_model_data *model_data =
+ of_device_get_match_data(data->dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val < model_data->min_sampling_rate ||
+ val > model_data->max_sampling_rate)
+ return -EINVAL;
+
+ clk_set_rate(data->clk_scaler->clk,
+ val * ASPEED_CLOCKS_PER_SAMPLE);
+ return 0;
+
+ case IIO_CHAN_INFO_SCALE:
+ case IIO_CHAN_INFO_RAW:
+ /*
+ * Technically, these could be written but the only reasons
+ * for doing so seem better handled in userspace. EPERM is
+ * returned to signal this is a policy choice rather than a
+ * hardware limitation.
+ */
+ return -EPERM;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int aspeed_adc_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+
+ if (!readval || reg % 4 || reg > ASPEED_REG_MAX)
+ return -EINVAL;
+
+ *readval = readl(data->base + reg);
+
+ return 0;
+}
+
+static const struct iio_info aspeed_adc_iio_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = aspeed_adc_read_raw,
+ .write_raw = aspeed_adc_write_raw,
+ .debugfs_reg_access = aspeed_adc_reg_access,
+};
+
+static int aspeed_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct aspeed_adc_data *data;
+ const struct aspeed_adc_model_data *model_data;
+ struct resource *res;
+ const char *clk_parent_name;
+ int ret;
+ u32 adc_engine_control_reg_val;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(data->base))
+ return PTR_ERR(data->base);
+
+ /* Register ADC clock prescaler with source specified by device tree. */
+ spin_lock_init(&data->clk_lock);
+ clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
+
+ data->clk_prescaler = clk_hw_register_divider(
+ &pdev->dev, "prescaler", clk_parent_name, 0,
+ data->base + ASPEED_REG_CLOCK_CONTROL,
+ 17, 15, 0, &data->clk_lock);
+ if (IS_ERR(data->clk_prescaler))
+ return PTR_ERR(data->clk_prescaler);
+
+ /*
+ * Register ADC clock scaler downstream from the prescaler. Allow rate
+ * setting to adjust the prescaler as well.
+ */
+ data->clk_scaler = clk_hw_register_divider(
+ &pdev->dev, "scaler", "prescaler",
+ CLK_SET_RATE_PARENT,
+ data->base + ASPEED_REG_CLOCK_CONTROL,
+ 0, 10, 0, &data->clk_lock);
+ if (IS_ERR(data->clk_scaler)) {
+ ret = PTR_ERR(data->clk_scaler);
+ goto scaler_error;
+ }
+
+ /* Start all channels in normal mode. */
+ clk_prepare_enable(data->clk_scaler->clk);
+ adc_engine_control_reg_val = GENMASK(31, 16) |
+ ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE;
+ writel(adc_engine_control_reg_val,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+
+ model_data = of_device_get_match_data(&pdev->dev);
+ indio_dev->name = model_data->model_name;
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &aspeed_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = aspeed_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto iio_register_error;
+
+ return 0;
+
+iio_register_error:
+ writel(ASPEED_OPERATION_MODE_POWER_DOWN,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ clk_disable_unprepare(data->clk_scaler->clk);
+ clk_hw_unregister_divider(data->clk_scaler);
+
+scaler_error:
+ clk_hw_unregister_divider(data->clk_prescaler);
+ return ret;
+}
+
+static int aspeed_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct aspeed_adc_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ writel(ASPEED_OPERATION_MODE_POWER_DOWN,
+ data->base + ASPEED_REG_ENGINE_CONTROL);
+ clk_disable_unprepare(data->clk_scaler->clk);
+ clk_hw_unregister_divider(data->clk_scaler);
+ clk_hw_unregister_divider(data->clk_prescaler);
+
+ return 0;
+}
+
+static const struct aspeed_adc_model_data ast2400_model_data = {
+ .model_name = "ast2400-adc",
+ .vref_voltage = 2500, // mV
+ .min_sampling_rate = 10000,
+ .max_sampling_rate = 500000,
+};
+
+static const struct aspeed_adc_model_data ast2500_model_data = {
+ .model_name = "ast2500-adc",
+ .vref_voltage = 1800, // mV
+ .min_sampling_rate = 1,
+ .max_sampling_rate = 1000000,
+};
+
+static const struct of_device_id aspeed_adc_matches[] = {
+ { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data },
+ { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data },
+ {},
+};
+MODULE_DEVICE_TABLE(of, aspeed_adc_matches);
+
+static struct platform_driver aspeed_adc_driver = {
+ .probe = aspeed_adc_probe,
+ .remove = aspeed_adc_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = aspeed_adc_matches,
+ }
+};
+
+module_platform_driver(aspeed_adc_driver);
+
+MODULE_AUTHOR("Rick Altherr <raltherr@google.com>");
+MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c
new file mode 100644
index 000000000000..62d37f8725b8
--- /dev/null
+++ b/drivers/iio/adc/cpcap-adc.c
@@ -0,0 +1,1007 @@
+/*
+ * Copyright (C) 2017 Tony Lindgren <tony@atomide.com>
+ *
+ * Rewritten for Linux IIO framework with some code based on
+ * earlier driver found in the Motorola Linux kernel:
+ *
+ * Copyright (C) 2009-2010 Motorola, Inc.
+ *
+ * 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.
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/mfd/motorola-cpcap.h>
+
+/* Register CPCAP_REG_ADCC1 bits */
+#define CPCAP_BIT_ADEN_AUTO_CLR BIT(15) /* Currently unused */
+#define CPCAP_BIT_CAL_MODE BIT(14) /* Set with BIT_RAND0 */
+#define CPCAP_BIT_ADC_CLK_SEL1 BIT(13) /* Currently unused */
+#define CPCAP_BIT_ADC_CLK_SEL0 BIT(12) /* Currently unused */
+#define CPCAP_BIT_ATOX BIT(11)
+#define CPCAP_BIT_ATO3 BIT(10)
+#define CPCAP_BIT_ATO2 BIT(9)
+#define CPCAP_BIT_ATO1 BIT(8)
+#define CPCAP_BIT_ATO0 BIT(7)
+#define CPCAP_BIT_ADA2 BIT(6)
+#define CPCAP_BIT_ADA1 BIT(5)
+#define CPCAP_BIT_ADA0 BIT(4)
+#define CPCAP_BIT_AD_SEL1 BIT(3) /* Set for bank1 */
+#define CPCAP_BIT_RAND1 BIT(2) /* Set for channel 16 & 17 */
+#define CPCAP_BIT_RAND0 BIT(1) /* Set with CAL_MODE */
+#define CPCAP_BIT_ADEN BIT(0) /* Currently unused */
+
+/* Register CPCAP_REG_ADCC2 bits */
+#define CPCAP_BIT_CAL_FACTOR_ENABLE BIT(15) /* Currently unused */
+#define CPCAP_BIT_BATDETB_EN BIT(14) /* Currently unused */
+#define CPCAP_BIT_ADTRIG_ONESHOT BIT(13) /* Set for !TIMING_IMM */
+#define CPCAP_BIT_ASC BIT(12) /* Set for TIMING_IMM */
+#define CPCAP_BIT_ATOX_PS_FACTOR BIT(11)
+#define CPCAP_BIT_ADC_PS_FACTOR1 BIT(10)
+#define CPCAP_BIT_ADC_PS_FACTOR0 BIT(9)
+#define CPCAP_BIT_AD4_SELECT BIT(8) /* Currently unused */
+#define CPCAP_BIT_ADC_BUSY BIT(7) /* Currently unused */
+#define CPCAP_BIT_THERMBIAS_EN BIT(6) /* Currently unused */
+#define CPCAP_BIT_ADTRIG_DIS BIT(5) /* Disable interrupt */
+#define CPCAP_BIT_LIADC BIT(4) /* Currently unused */
+#define CPCAP_BIT_TS_REFEN BIT(3) /* Currently unused */
+#define CPCAP_BIT_TS_M2 BIT(2) /* Currently unused */
+#define CPCAP_BIT_TS_M1 BIT(1) /* Currently unused */
+#define CPCAP_BIT_TS_M0 BIT(0) /* Currently unused */
+
+#define CPCAP_MAX_TEMP_LVL 27
+#define CPCAP_FOUR_POINT_TWO_ADC 801
+#define ST_ADC_CAL_CHRGI_HIGH_THRESHOLD 530
+#define ST_ADC_CAL_CHRGI_LOW_THRESHOLD 494
+#define ST_ADC_CAL_BATTI_HIGH_THRESHOLD 530
+#define ST_ADC_CAL_BATTI_LOW_THRESHOLD 494
+#define ST_ADC_CALIBRATE_DIFF_THRESHOLD 3
+
+#define CPCAP_ADC_MAX_RETRIES 5 /* Calibration and quirk */
+
+/**
+ * struct cpcap_adc_ato - timing settings for cpcap adc
+ *
+ * Unfortunately no cpcap documentation available, please document when
+ * using these.
+ */
+struct cpcap_adc_ato {
+ unsigned short ato_in;
+ unsigned short atox_in;
+ unsigned short adc_ps_factor_in;
+ unsigned short atox_ps_factor_in;
+ unsigned short ato_out;
+ unsigned short atox_out;
+ unsigned short adc_ps_factor_out;
+ unsigned short atox_ps_factor_out;
+};
+
+/**
+ * struct cpcap-adc - cpcap adc device driver data
+ * @reg: cpcap regmap
+ * @dev: struct device
+ * @vendor: cpcap vendor
+ * @irq: interrupt
+ * @lock: mutex
+ * @ato: request timings
+ * @wq_data_avail: work queue
+ * @done: work done
+ */
+struct cpcap_adc {
+ struct regmap *reg;
+ struct device *dev;
+ u16 vendor;
+ int irq;
+ struct mutex lock; /* ADC register access lock */
+ const struct cpcap_adc_ato *ato;
+ wait_queue_head_t wq_data_avail;
+ bool done;
+};
+
+/**
+ * enum cpcap_adc_channel - cpcap adc channels
+ */
+enum cpcap_adc_channel {
+ /* Bank0 channels */
+ CPCAP_ADC_AD0_BATTDETB, /* Battery detection */
+ CPCAP_ADC_BATTP, /* Battery voltage */
+ CPCAP_ADC_VBUS, /* USB VBUS voltage */
+ CPCAP_ADC_AD3, /* Battery temperature when charging */
+ CPCAP_ADC_BPLUS_AD4, /* Another battery or system voltage */
+ CPCAP_ADC_CHG_ISENSE, /* Calibrated charge current */
+ CPCAP_ADC_BATTI, /* Calibrated system current */
+ CPCAP_ADC_USB_ID, /* USB OTG ID, unused on droid 4? */
+
+ /* Bank1 channels */
+ CPCAP_ADC_AD8, /* Seems unused */
+ CPCAP_ADC_AD9, /* Seems unused */
+ CPCAP_ADC_LICELL, /* Maybe system voltage? Always 3V */
+ CPCAP_ADC_HV_BATTP, /* Another battery detection? */
+ CPCAP_ADC_TSX1_AD12, /* Seems unused, for touchscreen? */
+ CPCAP_ADC_TSX2_AD13, /* Seems unused, for touchscreen? */
+ CPCAP_ADC_TSY1_AD14, /* Seems unused, for touchscreen? */
+ CPCAP_ADC_TSY2_AD15, /* Seems unused, for touchscreen? */
+
+ /* Remuxed channels using bank0 entries */
+ CPCAP_ADC_BATTP_PI16, /* Alternative mux mode for BATTP */
+ CPCAP_ADC_BATTI_PI17, /* Alternative mux mode for BATTI */
+
+ CPCAP_ADC_CHANNEL_NUM,
+};
+
+/**
+ * enum cpcap_adc_timing - cpcap adc timing options
+ *
+ * CPCAP_ADC_TIMING_IMM seems to be immediate with no timings.
+ * Please document when using.
+ */
+enum cpcap_adc_timing {
+ CPCAP_ADC_TIMING_IMM,
+ CPCAP_ADC_TIMING_IN,
+ CPCAP_ADC_TIMING_OUT,
+};
+
+/**
+ * struct cpcap_adc_phasing_tbl - cpcap phasing table
+ * @offset: offset in the phasing table
+ * @multiplier: multiplier in the phasing table
+ * @divider: divider in the phasing table
+ * @min: minimum value
+ * @max: maximum value
+ */
+struct cpcap_adc_phasing_tbl {
+ short offset;
+ unsigned short multiplier;
+ unsigned short divider;
+ short min;
+ short max;
+};
+
+/**
+ * struct cpcap_adc_conversion_tbl - cpcap conversion table
+ * @conv_type: conversion type
+ * @align_offset: align offset
+ * @conv_offset: conversion offset
+ * @cal_offset: calibration offset
+ * @multiplier: conversion multiplier
+ * @divider: conversion divider
+ */
+struct cpcap_adc_conversion_tbl {
+ enum iio_chan_info_enum conv_type;
+ int align_offset;
+ int conv_offset;
+ int cal_offset;
+ int multiplier;
+ int divider;
+};
+
+/**
+ * struct cpcap_adc_request - cpcap adc request
+ * @channel: request channel
+ * @phase_tbl: channel phasing table
+ * @conv_tbl: channel conversion table
+ * @bank_index: channel index within the bank
+ * @timing: timing settings
+ * @result: result
+ */
+struct cpcap_adc_request {
+ int channel;
+ const struct cpcap_adc_phasing_tbl *phase_tbl;
+ const struct cpcap_adc_conversion_tbl *conv_tbl;
+ int bank_index;
+ enum cpcap_adc_timing timing;
+ int result;
+};
+
+/* Phasing table for channels. Note that channels 16 & 17 use BATTP and BATTI */
+static const struct cpcap_adc_phasing_tbl bank_phasing[] = {
+ /* Bank0 */
+ [CPCAP_ADC_AD0_BATTDETB] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_BATTP] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_VBUS] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_AD3] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_BPLUS_AD4] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_CHG_ISENSE] = {0, 0x80, 0x80, -512, 511},
+ [CPCAP_ADC_BATTI] = {0, 0x80, 0x80, -512, 511},
+ [CPCAP_ADC_USB_ID] = {0, 0x80, 0x80, 0, 1023},
+
+ /* Bank1 */
+ [CPCAP_ADC_AD8] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_AD9] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_LICELL] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_HV_BATTP] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSX1_AD12] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSX2_AD13] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSY1_AD14] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSY2_AD15] = {0, 0x80, 0x80, 0, 1023},
+};
+
+/*
+ * Conversion table for channels. Updated during init based on calibration.
+ * Here too channels 16 & 17 use BATTP and BATTI.
+ */
+static struct cpcap_adc_conversion_tbl bank_conversion[] = {
+ /* Bank0 */
+ [CPCAP_ADC_AD0_BATTDETB] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_BATTP] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023,
+ },
+ [CPCAP_ADC_VBUS] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 10000, 1023,
+ },
+ [CPCAP_ADC_AD3] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_BPLUS_AD4] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023,
+ },
+ [CPCAP_ADC_CHG_ISENSE] = {
+ IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023,
+ },
+ [CPCAP_ADC_BATTI] = {
+ IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023,
+ },
+ [CPCAP_ADC_USB_ID] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+
+ /* Bank1 */
+ [CPCAP_ADC_AD8] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_AD9] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_LICELL] = {
+ IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 3400, 1023,
+ },
+ [CPCAP_ADC_HV_BATTP] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_TSX1_AD12] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_TSX2_AD13] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_TSY1_AD14] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+ [CPCAP_ADC_TSY2_AD15] = {
+ IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1,
+ },
+};
+
+/*
+ * Temperature lookup table of register values to milliCelcius.
+ * REVISIT: Check the duplicate 0x3ff entry in a freezer
+ */
+static const int temp_map[CPCAP_MAX_TEMP_LVL][2] = {
+ { 0x03ff, -40000 },
+ { 0x03ff, -35000 },
+ { 0x03ef, -30000 },
+ { 0x03b2, -25000 },
+ { 0x036c, -20000 },
+ { 0x0320, -15000 },
+ { 0x02d0, -10000 },
+ { 0x027f, -5000 },
+ { 0x022f, 0 },
+ { 0x01e4, 5000 },
+ { 0x019f, 10000 },
+ { 0x0161, 15000 },
+ { 0x012b, 20000 },
+ { 0x00fc, 25000 },
+ { 0x00d4, 30000 },
+ { 0x00b2, 35000 },
+ { 0x0095, 40000 },
+ { 0x007d, 45000 },
+ { 0x0069, 50000 },
+ { 0x0059, 55000 },
+ { 0x004b, 60000 },
+ { 0x003f, 65000 },
+ { 0x0036, 70000 },
+ { 0x002e, 75000 },
+ { 0x0027, 80000 },
+ { 0x0022, 85000 },
+ { 0x001d, 90000 },
+};
+
+#define CPCAP_CHAN(_type, _index, _address, _datasheet_name) { \
+ .type = (_type), \
+ .address = (_address), \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ .scan_index = (_index), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 10, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+ .datasheet_name = (_datasheet_name), \
+}
+
+/*
+ * The datasheet names are from Motorola mapphone Linux kernel except
+ * for the last two which might be uncalibrated charge voltage and
+ * current.
+ */
+static const struct iio_chan_spec cpcap_adc_channels[] = {
+ /* Bank0 */
+ CPCAP_CHAN(IIO_TEMP, 0, CPCAP_REG_ADCD0, "battdetb"),
+ CPCAP_CHAN(IIO_VOLTAGE, 1, CPCAP_REG_ADCD1, "battp"),
+ CPCAP_CHAN(IIO_VOLTAGE, 2, CPCAP_REG_ADCD2, "vbus"),
+ CPCAP_CHAN(IIO_TEMP, 3, CPCAP_REG_ADCD3, "ad3"),
+ CPCAP_CHAN(IIO_VOLTAGE, 4, CPCAP_REG_ADCD4, "ad4"),
+ CPCAP_CHAN(IIO_CURRENT, 5, CPCAP_REG_ADCD5, "chg_isense"),
+ CPCAP_CHAN(IIO_CURRENT, 6, CPCAP_REG_ADCD6, "batti"),
+ CPCAP_CHAN(IIO_VOLTAGE, 7, CPCAP_REG_ADCD7, "usb_id"),
+
+ /* Bank1 */
+ CPCAP_CHAN(IIO_CURRENT, 8, CPCAP_REG_ADCD0, "ad8"),
+ CPCAP_CHAN(IIO_VOLTAGE, 9, CPCAP_REG_ADCD1, "ad9"),
+ CPCAP_CHAN(IIO_VOLTAGE, 10, CPCAP_REG_ADCD2, "licell"),
+ CPCAP_CHAN(IIO_VOLTAGE, 11, CPCAP_REG_ADCD3, "hv_battp"),
+ CPCAP_CHAN(IIO_VOLTAGE, 12, CPCAP_REG_ADCD4, "tsx1_ad12"),
+ CPCAP_CHAN(IIO_VOLTAGE, 13, CPCAP_REG_ADCD5, "tsx2_ad13"),
+ CPCAP_CHAN(IIO_VOLTAGE, 14, CPCAP_REG_ADCD6, "tsy1_ad14"),
+ CPCAP_CHAN(IIO_VOLTAGE, 15, CPCAP_REG_ADCD7, "tsy2_ad15"),
+
+ /* There are two registers with multiplexed functionality */
+ CPCAP_CHAN(IIO_VOLTAGE, 16, CPCAP_REG_ADCD0, "chg_vsense"),
+ CPCAP_CHAN(IIO_CURRENT, 17, CPCAP_REG_ADCD1, "batti2"),
+};
+
+static irqreturn_t cpcap_adc_irq_thread(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct cpcap_adc *ddata = iio_priv(indio_dev);
+ int error;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS,
+ CPCAP_BIT_ADTRIG_DIS);
+ if (error)
+ return IRQ_NONE;
+
+ ddata->done = true;
+ wake_up_interruptible(&ddata->wq_data_avail);
+
+ return IRQ_HANDLED;
+}
+
+/* ADC calibration functions */
+static void cpcap_adc_setup_calibrate(struct cpcap_adc *ddata,
+ enum cpcap_adc_channel chan)
+{
+ unsigned int value = 0;
+ unsigned long timeout = jiffies + msecs_to_jiffies(3000);
+ int error;
+
+ if ((chan != CPCAP_ADC_CHG_ISENSE) &&
+ (chan != CPCAP_ADC_BATTI))
+ return;
+
+ value |= CPCAP_BIT_CAL_MODE | CPCAP_BIT_RAND0;
+ value |= ((chan << 4) &
+ (CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | CPCAP_BIT_ADA0));
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1,
+ CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX |
+ CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 |
+ CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 |
+ CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 |
+ CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 |
+ CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0,
+ value);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ATOX_PS_FACTOR |
+ CPCAP_BIT_ADC_PS_FACTOR1 |
+ CPCAP_BIT_ADC_PS_FACTOR0,
+ 0);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS,
+ CPCAP_BIT_ADTRIG_DIS);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ASC,
+ CPCAP_BIT_ASC);
+ if (error)
+ return;
+
+ do {
+ schedule_timeout_uninterruptible(1);
+ error = regmap_read(ddata->reg, CPCAP_REG_ADCC2, &value);
+ if (error)
+ return;
+ } while ((value & CPCAP_BIT_ASC) && time_before(jiffies, timeout));
+
+ if (value & CPCAP_BIT_ASC)
+ dev_err(ddata->dev,
+ "Timeout waiting for calibration to complete\n");
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1,
+ CPCAP_BIT_CAL_MODE, 0);
+ if (error)
+ return;
+}
+
+static int cpcap_adc_calibrate_one(struct cpcap_adc *ddata,
+ int channel,
+ u16 calibration_register,
+ int lower_threshold,
+ int upper_threshold)
+{
+ unsigned int calibration_data[2];
+ unsigned short cal_data_diff;
+ int i, error;
+
+ for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) {
+ calibration_data[0] = 0;
+ calibration_data[1] = 0;
+ cal_data_diff = 0;
+ cpcap_adc_setup_calibrate(ddata, channel);
+ error = regmap_read(ddata->reg, calibration_register,
+ &calibration_data[0]);
+ if (error)
+ return error;
+ cpcap_adc_setup_calibrate(ddata, channel);
+ error = regmap_read(ddata->reg, calibration_register,
+ &calibration_data[1]);
+ if (error)
+ return error;
+
+ if (calibration_data[0] > calibration_data[1])
+ cal_data_diff =
+ calibration_data[0] - calibration_data[1];
+ else
+ cal_data_diff =
+ calibration_data[1] - calibration_data[0];
+
+ if (((calibration_data[1] >= lower_threshold) &&
+ (calibration_data[1] <= upper_threshold) &&
+ (cal_data_diff <= ST_ADC_CALIBRATE_DIFF_THRESHOLD)) ||
+ (ddata->vendor == CPCAP_VENDOR_TI)) {
+ bank_conversion[channel].cal_offset =
+ ((short)calibration_data[1] * -1) + 512;
+ dev_dbg(ddata->dev, "ch%i calibration complete: %i\n",
+ channel, bank_conversion[channel].cal_offset);
+ break;
+ }
+ usleep_range(5000, 10000);
+ }
+
+ return 0;
+}
+
+static int cpcap_adc_calibrate(struct cpcap_adc *ddata)
+{
+ int error;
+
+ error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_CHG_ISENSE,
+ CPCAP_REG_ADCAL1,
+ ST_ADC_CAL_CHRGI_LOW_THRESHOLD,
+ ST_ADC_CAL_CHRGI_HIGH_THRESHOLD);
+ if (error)
+ return error;
+
+ error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_BATTI,
+ CPCAP_REG_ADCAL2,
+ ST_ADC_CAL_BATTI_LOW_THRESHOLD,
+ ST_ADC_CAL_BATTI_HIGH_THRESHOLD);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+/* ADC setup, read and scale functions */
+static void cpcap_adc_setup_bank(struct cpcap_adc *ddata,
+ struct cpcap_adc_request *req)
+{
+ const struct cpcap_adc_ato *ato = ddata->ato;
+ unsigned short value1 = 0;
+ unsigned short value2 = 0;
+ int error;
+
+ if (!ato)
+ return;
+
+ switch (req->channel) {
+ case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15:
+ value1 |= CPCAP_BIT_AD_SEL1;
+ break;
+ case CPCAP_ADC_BATTP_PI16 ... CPCAP_ADC_BATTI_PI17:
+ value1 |= CPCAP_BIT_RAND1;
+ default:
+ break;
+ }
+
+ switch (req->timing) {
+ case CPCAP_ADC_TIMING_IN:
+ value1 |= ato->ato_in;
+ value1 |= ato->atox_in;
+ value2 |= ato->adc_ps_factor_in;
+ value2 |= ato->atox_ps_factor_in;
+ break;
+ case CPCAP_ADC_TIMING_OUT:
+ value1 |= ato->ato_out;
+ value1 |= ato->atox_out;
+ value2 |= ato->adc_ps_factor_out;
+ value2 |= ato->atox_ps_factor_out;
+ break;
+
+ case CPCAP_ADC_TIMING_IMM:
+ default:
+ break;
+ }
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1,
+ CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX |
+ CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 |
+ CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 |
+ CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 |
+ CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 |
+ CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0,
+ value1);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ATOX_PS_FACTOR |
+ CPCAP_BIT_ADC_PS_FACTOR1 |
+ CPCAP_BIT_ADC_PS_FACTOR0,
+ value2);
+ if (error)
+ return;
+
+ if (req->timing == CPCAP_ADC_TIMING_IMM) {
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS,
+ CPCAP_BIT_ADTRIG_DIS);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ASC,
+ CPCAP_BIT_ASC);
+ if (error)
+ return;
+ } else {
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_ONESHOT,
+ CPCAP_BIT_ADTRIG_ONESHOT);
+ if (error)
+ return;
+
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS, 0);
+ if (error)
+ return;
+ }
+}
+
+/*
+ * Occasionally the ADC does not seem to start and there will be no
+ * interrupt. Let's re-init interrupt to prevent the ADC from hanging
+ * for the next request. It is unclear why this happens, but the next
+ * request will usually work after doing this.
+ */
+static void cpcap_adc_quirk_reset_lost_irq(struct cpcap_adc *ddata)
+{
+ int error;
+
+ dev_info(ddata->dev, "lost ADC irq, attempting to reinit\n");
+ disable_irq(ddata->irq);
+ error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS,
+ CPCAP_BIT_ADTRIG_DIS);
+ if (error)
+ dev_warn(ddata->dev, "%s reset failed: %i\n",
+ __func__, error);
+ enable_irq(ddata->irq);
+}
+
+static int cpcap_adc_start_bank(struct cpcap_adc *ddata,
+ struct cpcap_adc_request *req)
+{
+ int i, error;
+
+ req->timing = CPCAP_ADC_TIMING_IMM;
+ ddata->done = false;
+
+ for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) {
+ cpcap_adc_setup_bank(ddata, req);
+ error = wait_event_interruptible_timeout(ddata->wq_data_avail,
+ ddata->done,
+ msecs_to_jiffies(50));
+ if (error > 0)
+ return 0;
+
+ if (error == 0) {
+ cpcap_adc_quirk_reset_lost_irq(ddata);
+ error = -ETIMEDOUT;
+ continue;
+ }
+
+ if (error < 0)
+ return error;
+ }
+
+ return error;
+}
+
+static void cpcap_adc_phase(struct cpcap_adc_request *req)
+{
+ const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl;
+ const struct cpcap_adc_phasing_tbl *phase_tbl = req->phase_tbl;
+ int index = req->channel;
+
+ /* Remuxed channels 16 and 17 use BATTP and BATTI entries */
+ switch (req->channel) {
+ case CPCAP_ADC_BATTP:
+ case CPCAP_ADC_BATTP_PI16:
+ index = req->bank_index;
+ req->result -= phase_tbl[index].offset;
+ req->result -= CPCAP_FOUR_POINT_TWO_ADC;
+ req->result *= phase_tbl[index].multiplier;
+ if (phase_tbl[index].divider == 0)
+ return;
+ req->result /= phase_tbl[index].divider;
+ req->result += CPCAP_FOUR_POINT_TWO_ADC;
+ break;
+ case CPCAP_ADC_BATTI_PI17:
+ index = req->bank_index;
+ /* fallthrough */
+ default:
+ req->result += conv_tbl[index].cal_offset;
+ req->result += conv_tbl[index].align_offset;
+ req->result *= phase_tbl[index].multiplier;
+ if (phase_tbl[index].divider == 0)
+ return;
+ req->result /= phase_tbl[index].divider;
+ req->result += phase_tbl[index].offset;
+ break;
+ }
+
+ if (req->result < phase_tbl[index].min)
+ req->result = phase_tbl[index].min;
+ else if (req->result > phase_tbl[index].max)
+ req->result = phase_tbl[index].max;
+}
+
+/* Looks up temperatures in a table and calculates averages if needed */
+static int cpcap_adc_table_to_millicelcius(unsigned short value)
+{
+ int i, result = 0, alpha;
+
+ if (value <= temp_map[CPCAP_MAX_TEMP_LVL - 1][0])
+ return temp_map[CPCAP_MAX_TEMP_LVL - 1][1];
+
+ if (value >= temp_map[0][0])
+ return temp_map[0][1];
+
+ for (i = 0; i < CPCAP_MAX_TEMP_LVL - 1; i++) {
+ if ((value <= temp_map[i][0]) &&
+ (value >= temp_map[i + 1][0])) {
+ if (value == temp_map[i][0]) {
+ result = temp_map[i][1];
+ } else if (value == temp_map[i + 1][0]) {
+ result = temp_map[i + 1][1];
+ } else {
+ alpha = ((value - temp_map[i][0]) * 1000) /
+ (temp_map[i + 1][0] - temp_map[i][0]);
+
+ result = temp_map[i][1] +
+ ((alpha * (temp_map[i + 1][1] -
+ temp_map[i][1])) / 1000);
+ }
+ break;
+ }
+ }
+
+ return result;
+}
+
+static void cpcap_adc_convert(struct cpcap_adc_request *req)
+{
+ const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl;
+ int index = req->channel;
+
+ /* Remuxed channels 16 and 17 use BATTP and BATTI entries */
+ switch (req->channel) {
+ case CPCAP_ADC_BATTP_PI16:
+ index = CPCAP_ADC_BATTP;
+ break;
+ case CPCAP_ADC_BATTI_PI17:
+ index = CPCAP_ADC_BATTI;
+ break;
+ default:
+ break;
+ }
+
+ /* No conversion for raw channels */
+ if (conv_tbl[index].conv_type == IIO_CHAN_INFO_RAW)
+ return;
+
+ /* Temperatures use a lookup table instead of conversion table */
+ if ((req->channel == CPCAP_ADC_AD0_BATTDETB) ||
+ (req->channel == CPCAP_ADC_AD3)) {
+ req->result =
+ cpcap_adc_table_to_millicelcius(req->result);
+
+ return;
+ }
+
+ /* All processed channels use a conversion table */
+ req->result *= conv_tbl[index].multiplier;
+ if (conv_tbl[index].divider == 0)
+ return;
+ req->result /= conv_tbl[index].divider;
+ req->result += conv_tbl[index].conv_offset;
+}
+
+/*
+ * REVISIT: Check if timed sampling can use multiple channels at the
+ * same time. If not, replace channel_mask with just channel.
+ */
+static int cpcap_adc_read_bank_scaled(struct cpcap_adc *ddata,
+ struct cpcap_adc_request *req)
+{
+ int calibration_data, error, addr;
+
+ if (ddata->vendor == CPCAP_VENDOR_TI) {
+ error = regmap_read(ddata->reg, CPCAP_REG_ADCAL1,
+ &calibration_data);
+ if (error)
+ return error;
+ bank_conversion[CPCAP_ADC_CHG_ISENSE].cal_offset =
+ ((short)calibration_data * -1) + 512;
+
+ error = regmap_read(ddata->reg, CPCAP_REG_ADCAL2,
+ &calibration_data);
+ if (error)
+ return error;
+ bank_conversion[CPCAP_ADC_BATTI].cal_offset =
+ ((short)calibration_data * -1) + 512;
+ }
+
+ addr = CPCAP_REG_ADCD0 + req->bank_index * 4;
+
+ error = regmap_read(ddata->reg, addr, &req->result);
+ if (error)
+ return error;
+
+ req->result &= 0x3ff;
+ cpcap_adc_phase(req);
+ cpcap_adc_convert(req);
+
+ return 0;
+}
+
+static int cpcap_adc_init_request(struct cpcap_adc_request *req,
+ int channel)
+{
+ req->channel = channel;
+ req->phase_tbl = bank_phasing;
+ req->conv_tbl = bank_conversion;
+
+ switch (channel) {
+ case CPCAP_ADC_AD0_BATTDETB ... CPCAP_ADC_USB_ID:
+ req->bank_index = channel;
+ break;
+ case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15:
+ req->bank_index = channel - 8;
+ break;
+ case CPCAP_ADC_BATTP_PI16:
+ req->bank_index = CPCAP_ADC_BATTP;
+ break;
+ case CPCAP_ADC_BATTI_PI17:
+ req->bank_index = CPCAP_ADC_BATTI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cpcap_adc_read(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cpcap_adc *ddata = iio_priv(indio_dev);
+ struct cpcap_adc_request req;
+ int error;
+
+ error = cpcap_adc_init_request(&req, chan->channel);
+ if (error)
+ return error;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&ddata->lock);
+ error = cpcap_adc_start_bank(ddata, &req);
+ if (error)
+ goto err_unlock;
+ error = regmap_read(ddata->reg, chan->address, val);
+ if (error)
+ goto err_unlock;
+ mutex_unlock(&ddata->lock);
+ break;
+ case IIO_CHAN_INFO_PROCESSED:
+ mutex_lock(&ddata->lock);
+ error = cpcap_adc_start_bank(ddata, &req);
+ if (error)
+ goto err_unlock;
+ error = cpcap_adc_read_bank_scaled(ddata, &req);
+ if (error)
+ goto err_unlock;
+ mutex_unlock(&ddata->lock);
+ *val = req.result;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return IIO_VAL_INT;
+
+err_unlock:
+ mutex_unlock(&ddata->lock);
+ dev_err(ddata->dev, "error reading ADC: %i\n", error);
+
+ return error;
+}
+
+static const struct iio_info cpcap_adc_info = {
+ .read_raw = &cpcap_adc_read,
+ .driver_module = THIS_MODULE,
+};
+
+/*
+ * Configuration for Motorola mapphone series such as droid 4.
+ * Copied from the Motorola mapphone kernel tree.
+ */
+static const struct cpcap_adc_ato mapphone_adc = {
+ .ato_in = 0x0480,
+ .atox_in = 0,
+ .adc_ps_factor_in = 0x0200,
+ .atox_ps_factor_in = 0,
+ .ato_out = 0,
+ .atox_out = 0,
+ .adc_ps_factor_out = 0,
+ .atox_ps_factor_out = 0,
+};
+
+static const struct of_device_id cpcap_adc_id_table[] = {
+ {
+ .compatible = "motorola,cpcap-adc",
+ },
+ {
+ .compatible = "motorola,mapphone-cpcap-adc",
+ .data = &mapphone_adc,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, cpcap_adc_id_table);
+
+static int cpcap_adc_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct cpcap_adc *ddata;
+ struct iio_dev *indio_dev;
+ int error;
+
+ match = of_match_device(of_match_ptr(cpcap_adc_id_table),
+ &pdev->dev);
+ if (!match)
+ return -EINVAL;
+
+ if (!match->data) {
+ dev_err(&pdev->dev, "no configuration data found\n");
+
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ddata));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed to allocate iio device\n");
+
+ return -ENOMEM;
+ }
+ ddata = iio_priv(indio_dev);
+ ddata->ato = match->data;
+ ddata->dev = &pdev->dev;
+
+ mutex_init(&ddata->lock);
+ init_waitqueue_head(&ddata->wq_data_avail);
+
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->channels = cpcap_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(cpcap_adc_channels);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &cpcap_adc_info;
+
+ ddata->reg = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!ddata->reg)
+ return -ENODEV;
+
+ error = cpcap_get_vendor(ddata->dev, ddata->reg, &ddata->vendor);
+ if (error)
+ return error;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ ddata->irq = platform_get_irq_byname(pdev, "adcdone");
+ if (!ddata->irq)
+ return -ENODEV;
+
+ error = devm_request_threaded_irq(&pdev->dev, ddata->irq, NULL,
+ cpcap_adc_irq_thread,
+ IRQF_TRIGGER_NONE,
+ "cpcap-adc", indio_dev);
+ if (error) {
+ dev_err(&pdev->dev, "could not get irq: %i\n",
+ error);
+
+ return error;
+ }
+
+ error = cpcap_adc_calibrate(ddata);
+ if (error)
+ return error;
+
+ dev_info(&pdev->dev, "CPCAP ADC device probed\n");
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static struct platform_driver cpcap_adc_driver = {
+ .driver = {
+ .name = "cpcap_adc",
+ .of_match_table = of_match_ptr(cpcap_adc_id_table),
+ },
+ .probe = cpcap_adc_probe,
+};
+
+module_platform_driver(cpcap_adc_driver);
+
+MODULE_ALIAS("platform:cpcap_adc");
+MODULE_DESCRIPTION("CPCAP ADC driver");
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index ad1775b5f83c..6c5a7be9f8c1 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -579,7 +579,7 @@ static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y)
static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
{
- struct exynos_adc *info = (struct exynos_adc *)dev_id;
+ struct exynos_adc *info = dev_id;
u32 mask = info->data->mask;
/* Read value */
diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c
index 139639f73769..27005d84ed73 100644
--- a/drivers/iio/adc/hx711.c
+++ b/drivers/iio/adc/hx711.c
@@ -369,7 +369,7 @@ static struct attribute *hx711_attributes[] = {
NULL,
};
-static struct attribute_group hx711_attribute_group = {
+static const struct attribute_group hx711_attribute_group = {
.attrs = hx711_attributes,
};
diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c
index e2241ee94783..254b29a68b9d 100644
--- a/drivers/iio/adc/imx7d_adc.c
+++ b/drivers/iio/adc/imx7d_adc.c
@@ -365,7 +365,7 @@ static int imx7d_adc_read_data(struct imx7d_adc *info)
static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
{
- struct imx7d_adc *info = (struct imx7d_adc *)dev_id;
+ struct imx7d_adc *info = dev_id;
int status;
status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
index 3263231276ca..db9838230257 100644
--- a/drivers/iio/adc/ina2xx-adc.c
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -28,6 +28,7 @@
#include <linux/iio/sysfs.h>
#include <linux/kthread.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/util_macros.h>
@@ -635,6 +636,7 @@ static int ina2xx_probe(struct i2c_client *client,
struct iio_dev *indio_dev;
struct iio_buffer *buffer;
unsigned int val;
+ enum ina2xx_ids type;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
@@ -652,7 +654,11 @@ static int ina2xx_probe(struct i2c_client *client,
return PTR_ERR(chip->regmap);
}
- chip->config = &ina2xx_config[id->driver_data];
+ if (client->dev.of_node)
+ type = (enum ina2xx_ids)of_device_get_match_data(&client->dev);
+ else
+ type = id->driver_data;
+ chip->config = &ina2xx_config[type];
mutex_init(&chip->state_lock);
@@ -726,9 +732,35 @@ static const struct i2c_device_id ina2xx_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
+static const struct of_device_id ina2xx_of_match[] = {
+ {
+ .compatible = "ti,ina219",
+ .data = (void *)ina219
+ },
+ {
+ .compatible = "ti,ina220",
+ .data = (void *)ina219
+ },
+ {
+ .compatible = "ti,ina226",
+ .data = (void *)ina226
+ },
+ {
+ .compatible = "ti,ina230",
+ .data = (void *)ina226
+ },
+ {
+ .compatible = "ti,ina231",
+ .data = (void *)ina226
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ina2xx_of_match);
+
static struct i2c_driver ina2xx_driver = {
.driver = {
.name = KBUILD_MODNAME,
+ .of_match_table = ina2xx_of_match,
},
.probe = ina2xx_probe,
.remove = ina2xx_remove,
diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c
new file mode 100644
index 000000000000..0de709b4288b
--- /dev/null
+++ b/drivers/iio/adc/lpc32xx_adc.c
@@ -0,0 +1,219 @@
+/*
+ * lpc32xx_adc.c - Support for ADC in LPC32XX
+ *
+ * 3-channel, 10-bit ADC
+ *
+ * Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de>
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/*
+ * LPC32XX registers definitions
+ */
+#define LPC32XXAD_SELECT(x) ((x) + 0x04)
+#define LPC32XXAD_CTRL(x) ((x) + 0x08)
+#define LPC32XXAD_VALUE(x) ((x) + 0x48)
+
+/* Bit definitions for LPC32XXAD_SELECT: */
+/* constant, always write this value! */
+#define LPC32XXAD_REFm 0x00000200
+/* constant, always write this value! */
+#define LPC32XXAD_REFp 0x00000080
+ /* multiple of this is the channel number: 0, 1, 2 */
+#define LPC32XXAD_IN 0x00000010
+/* constant, always write this value! */
+#define LPC32XXAD_INTERNAL 0x00000004
+
+/* Bit definitions for LPC32XXAD_CTRL: */
+#define LPC32XXAD_STROBE 0x00000002
+#define LPC32XXAD_PDN_CTRL 0x00000004
+
+/* Bit definitions for LPC32XXAD_VALUE: */
+#define LPC32XXAD_VALUE_MASK 0x000003FF
+
+#define LPC32XXAD_NAME "lpc32xx-adc"
+
+struct lpc32xx_adc_state {
+ void __iomem *adc_base;
+ struct clk *clk;
+ struct completion completion;
+
+ u32 value;
+};
+
+static int lpc32xx_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct lpc32xx_adc_state *st = iio_priv(indio_dev);
+
+ if (mask == IIO_CHAN_INFO_RAW) {
+ mutex_lock(&indio_dev->mlock);
+ clk_prepare_enable(st->clk);
+ /* Measurement setup */
+ __raw_writel(LPC32XXAD_INTERNAL | (chan->address) |
+ LPC32XXAD_REFp | LPC32XXAD_REFm,
+ LPC32XXAD_SELECT(st->adc_base));
+ /* Trigger conversion */
+ __raw_writel(LPC32XXAD_PDN_CTRL | LPC32XXAD_STROBE,
+ LPC32XXAD_CTRL(st->adc_base));
+ wait_for_completion(&st->completion); /* set by ISR */
+ clk_disable_unprepare(st->clk);
+ *val = st->value;
+ mutex_unlock(&indio_dev->mlock);
+
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info lpc32xx_adc_iio_info = {
+ .read_raw = &lpc32xx_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define LPC32XX_ADC_CHANNEL(_index) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .address = LPC32XXAD_IN * _index, \
+ .scan_index = _index, \
+}
+
+static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
+ LPC32XX_ADC_CHANNEL(0),
+ LPC32XX_ADC_CHANNEL(1),
+ LPC32XX_ADC_CHANNEL(2),
+};
+
+static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id)
+{
+ struct lpc32xx_adc_state *st = dev_id;
+
+ /* Read value and clear irq */
+ st->value = __raw_readl(LPC32XXAD_VALUE(st->adc_base)) &
+ LPC32XXAD_VALUE_MASK;
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int lpc32xx_adc_probe(struct platform_device *pdev)
+{
+ struct lpc32xx_adc_state *st = NULL;
+ struct resource *res;
+ int retval = -ENODEV;
+ struct iio_dev *iodev = NULL;
+ int irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get platform I/O memory\n");
+ return -ENXIO;
+ }
+
+ iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
+ if (!iodev)
+ return -ENOMEM;
+
+ st = iio_priv(iodev);
+
+ st->adc_base = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!st->adc_base) {
+ dev_err(&pdev->dev, "failed mapping memory\n");
+ return -EBUSY;
+ }
+
+ st->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(st->clk)) {
+ dev_err(&pdev->dev, "failed getting clock\n");
+ return PTR_ERR(st->clk);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(&pdev->dev, "failed getting interrupt resource\n");
+ return -ENXIO;
+ }
+
+ retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
+ LPC32XXAD_NAME, st);
+ if (retval < 0) {
+ dev_err(&pdev->dev, "failed requesting interrupt\n");
+ return retval;
+ }
+
+ platform_set_drvdata(pdev, iodev);
+
+ init_completion(&st->completion);
+
+ iodev->name = LPC32XXAD_NAME;
+ iodev->dev.parent = &pdev->dev;
+ iodev->info = &lpc32xx_adc_iio_info;
+ iodev->modes = INDIO_DIRECT_MODE;
+ iodev->channels = lpc32xx_adc_iio_channels;
+ iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels);
+
+ retval = devm_iio_device_register(&pdev->dev, iodev);
+ if (retval)
+ return retval;
+
+ dev_info(&pdev->dev, "LPC32XX ADC driver loaded, IRQ %d\n", irq);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lpc32xx_adc_match[] = {
+ { .compatible = "nxp,lpc3220-adc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_adc_match);
+#endif
+
+static struct platform_driver lpc32xx_adc_driver = {
+ .probe = lpc32xx_adc_probe,
+ .driver = {
+ .name = LPC32XXAD_NAME,
+ .of_match_table = of_match_ptr(lpc32xx_adc_match),
+ },
+};
+
+module_platform_driver(lpc32xx_adc_driver);
+
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_DESCRIPTION("LPC32XX ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c
new file mode 100644
index 000000000000..2691b10023f5
--- /dev/null
+++ b/drivers/iio/adc/ltc2497.c
@@ -0,0 +1,279 @@
+/*
+ * ltc2497.c - Driver for Analog Devices/Linear Technology LTC2497 ADC
+ *
+ * Copyright (C) 2017 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ *
+ * Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#define LTC2497_ENABLE 0xA0
+#define LTC2497_SGL BIT(4)
+#define LTC2497_DIFF 0
+#define LTC2497_SIGN BIT(3)
+#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE
+#define LTC2497_CONVERSION_TIME_MS 150ULL
+
+struct ltc2497_st {
+ struct i2c_client *client;
+ struct regulator *ref;
+ ktime_t time_prev;
+ u8 addr_prev;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ __be32 buf ____cacheline_aligned;
+};
+
+static int ltc2497_wait_conv(struct ltc2497_st *st)
+{
+ s64 time_elapsed;
+
+ time_elapsed = ktime_ms_delta(ktime_get(), st->time_prev);
+
+ if (time_elapsed < LTC2497_CONVERSION_TIME_MS) {
+ /* delay if conversion time not passed
+ * since last read or write
+ */
+ if (msleep_interruptible(
+ LTC2497_CONVERSION_TIME_MS - time_elapsed))
+ return -ERESTARTSYS;
+
+ return 0;
+ }
+
+ if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) {
+ /* We're in automatic mode -
+ * so the last reading is stil not outdated
+ */
+ return 0;
+ }
+
+ return 1;
+}
+
+static int ltc2497_read(struct ltc2497_st *st, u8 address, int *val)
+{
+ struct i2c_client *client = st->client;
+ int ret;
+
+ ret = ltc2497_wait_conv(st);
+ if (ret < 0)
+ return ret;
+
+ if (ret || st->addr_prev != address) {
+ ret = i2c_smbus_write_byte(st->client,
+ LTC2497_ENABLE | address);
+ if (ret < 0)
+ return ret;
+ st->addr_prev = address;
+ if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS))
+ return -ERESTARTSYS;
+ }
+ ret = i2c_master_recv(client, (char *)&st->buf, 3);
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c_master_recv failed\n");
+ return ret;
+ }
+ st->time_prev = ktime_get();
+
+ /* convert and shift the result,
+ * and finally convert from offset binary to signed integer
+ */
+ *val = (be32_to_cpu(st->buf) >> 14) - (1 << 17);
+
+ return ret;
+}
+
+static int ltc2497_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ltc2497_st *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = ltc2497_read(st, chan->address, val);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(st->ref);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = 17;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+#define LTC2497_CHAN(_chan, _addr) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_chan), \
+ .address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+#define LTC2497_CHAN_DIFF(_chan, _addr) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \
+ .channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\
+ .address = (_addr | _chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .differential = 1, \
+}
+
+static const struct iio_chan_spec ltc2497_channel[] = {
+ LTC2497_CHAN(0, LTC2497_SGL),
+ LTC2497_CHAN(1, LTC2497_SGL),
+ LTC2497_CHAN(2, LTC2497_SGL),
+ LTC2497_CHAN(3, LTC2497_SGL),
+ LTC2497_CHAN(4, LTC2497_SGL),
+ LTC2497_CHAN(5, LTC2497_SGL),
+ LTC2497_CHAN(6, LTC2497_SGL),
+ LTC2497_CHAN(7, LTC2497_SGL),
+ LTC2497_CHAN(8, LTC2497_SGL),
+ LTC2497_CHAN(9, LTC2497_SGL),
+ LTC2497_CHAN(10, LTC2497_SGL),
+ LTC2497_CHAN(11, LTC2497_SGL),
+ LTC2497_CHAN(12, LTC2497_SGL),
+ LTC2497_CHAN(13, LTC2497_SGL),
+ LTC2497_CHAN(14, LTC2497_SGL),
+ LTC2497_CHAN(15, LTC2497_SGL),
+ LTC2497_CHAN_DIFF(0, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(1, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(2, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(3, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(4, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(5, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(6, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(7, LTC2497_DIFF),
+ LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN),
+ LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN),
+};
+
+static const struct iio_info ltc2497_info = {
+ .read_raw = ltc2497_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ltc2497_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct ltc2497_st *st;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_WRITE_BYTE))
+ return -EOPNOTSUPP;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ st->client = client;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->info = &ltc2497_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ltc2497_channel;
+ indio_dev->num_channels = ARRAY_SIZE(ltc2497_channel);
+
+ st->ref = devm_regulator_get(&client->dev, "vref");
+ if (IS_ERR(st->ref))
+ return PTR_ERR(st->ref);
+
+ ret = regulator_enable(st->ref);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte(st->client, LTC2497_CONFIG_DEFAULT);
+ if (ret < 0)
+ goto err_regulator_disable;
+
+ st->addr_prev = LTC2497_CONFIG_DEFAULT;
+ st->time_prev = ktime_get();
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto err_regulator_disable;
+
+ return 0;
+
+err_regulator_disable:
+ regulator_disable(st->ref);
+
+ return ret;
+}
+
+static int ltc2497_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct ltc2497_st *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ regulator_disable(st->ref);
+
+ return 0;
+}
+
+static const struct i2c_device_id ltc2497_id[] = {
+ { "ltc2497", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc2497_id);
+
+static const struct of_device_id ltc2497_of_match[] = {
+ { .compatible = "lltc,ltc2497", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ltc2497_of_match);
+
+static struct i2c_driver ltc2497_driver = {
+ .driver = {
+ .name = "ltc2497",
+ .of_match_table = of_match_ptr(ltc2497_of_match),
+ },
+ .probe = ltc2497_probe,
+ .remove = ltc2497_remove,
+ .id_table = ltc2497_id,
+};
+module_i2c_driver(ltc2497_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Linear Technology LTC2497 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c
index 3b7c4f78f37a..ebc715927e63 100644
--- a/drivers/iio/adc/max1027.c
+++ b/drivers/iio/adc/max1027.c
@@ -364,7 +364,7 @@ static int max1027_set_trigger_state(struct iio_trigger *trig, bool state)
static irqreturn_t max1027_trigger_handler(int irq, void *private)
{
- struct iio_poll_func *pf = (struct iio_poll_func *)private;
+ struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct max1027_state *st = iio_priv(indio_dev);
diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c
index a088cf99bfe1..1180bcc22ff1 100644
--- a/drivers/iio/adc/max11100.c
+++ b/drivers/iio/adc/max11100.c
@@ -124,8 +124,8 @@ static int max11100_probe(struct spi_device *spi)
indio_dev->name = "max11100";
indio_dev->info = &max11100_info;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = max11100_channels,
- indio_dev->num_channels = ARRAY_SIZE(max11100_channels),
+ indio_dev->channels = max11100_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max11100_channels);
state->vref_reg = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(state->vref_reg))
@@ -167,7 +167,6 @@ MODULE_DEVICE_TABLE(of, max11100_ids);
static struct spi_driver max11100_driver = {
.driver = {
.name = "max11100",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(max11100_ids),
},
.probe = max11100_probe,
diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c
new file mode 100644
index 000000000000..2e9648a078c4
--- /dev/null
+++ b/drivers/iio/adc/max1118.c
@@ -0,0 +1,307 @@
+/*
+ * MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs driver
+ *
+ * Copyright (c) 2017 Akinobu Mita <akinobu.mita@gmail.com>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1117-MAX1119.pdf
+ *
+ * SPI interface connections
+ *
+ * SPI MAXIM
+ * Master Direction MAX1117/8/9
+ * ------ --------- -----------
+ * nCS --> CNVST
+ * SCK --> SCLK
+ * MISO <-- DOUT
+ * ------ --------- -----------
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/regulator/consumer.h>
+
+enum max1118_id {
+ max1117,
+ max1118,
+ max1119,
+};
+
+struct max1118 {
+ struct spi_device *spi;
+ struct mutex lock;
+ struct regulator *reg;
+
+ u8 data ____cacheline_aligned;
+};
+
+#define MAX1118_CHANNEL(ch) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (ch), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = ch, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ }, \
+ }
+
+static const struct iio_chan_spec max1118_channels[] = {
+ MAX1118_CHANNEL(0),
+ MAX1118_CHANNEL(1),
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static int max1118_read(struct spi_device *spi, int channel)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct max1118 *adc = iio_priv(indio_dev);
+ struct spi_transfer xfers[] = {
+ /*
+ * To select CH1 for conversion, CNVST pin must be brought high
+ * and low for a second time.
+ */
+ {
+ .len = 0,
+ .delay_usecs = 1, /* > CNVST Low Time 100 ns */
+ .cs_change = 1,
+ },
+ /*
+ * The acquisition interval begins with the falling edge of
+ * CNVST. The total acquisition and conversion process takes
+ * <7.5us.
+ */
+ {
+ .len = 0,
+ .delay_usecs = 8,
+ },
+ {
+ .rx_buf = &adc->data,
+ .len = 1,
+ },
+ };
+ int ret;
+
+ if (channel == 0)
+ ret = spi_sync_transfer(spi, xfers + 1, 2);
+ else
+ ret = spi_sync_transfer(spi, xfers, 3);
+
+ if (ret)
+ return ret;
+
+ return adc->data;
+}
+
+static int max1118_get_vref_mV(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct max1118 *adc = iio_priv(indio_dev);
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ int vref_uV;
+
+ switch (id->driver_data) {
+ case max1117:
+ return 2048;
+ case max1119:
+ return 4096;
+ case max1118:
+ vref_uV = regulator_get_voltage(adc->reg);
+ if (vref_uV < 0)
+ return vref_uV;
+ return vref_uV / 1000;
+ }
+
+ return -ENODEV;
+}
+
+static int max1118_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max1118 *adc = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+ *val = max1118_read(adc->spi, chan->channel);
+ mutex_unlock(&adc->lock);
+ if (*val < 0)
+ return *val;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = max1118_get_vref_mV(adc->spi);
+ if (*val < 0)
+ return *val;
+ *val2 = 8;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info max1118_info = {
+ .read_raw = max1118_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static irqreturn_t max1118_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct max1118 *adc = iio_priv(indio_dev);
+ u8 data[16] = { }; /* 2x 8-bit ADC data + padding + 8 bytes timestamp */
+ int scan_index;
+ int i = 0;
+
+ mutex_lock(&adc->lock);
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ const struct iio_chan_spec *scan_chan =
+ &indio_dev->channels[scan_index];
+ int ret = max1118_read(adc->spi, scan_chan->channel);
+
+ if (ret < 0) {
+ dev_warn(&adc->spi->dev,
+ "failed to get conversion data\n");
+ goto out;
+ }
+
+ data[i] = ret;
+ i++;
+ }
+ iio_push_to_buffers_with_timestamp(indio_dev, data,
+ iio_get_time_ns(indio_dev));
+out:
+ mutex_unlock(&adc->lock);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int max1118_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct max1118 *adc;
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ 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;
+ mutex_init(&adc->lock);
+
+ if (id->driver_data == max1118) {
+ adc->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(adc->reg)) {
+ dev_err(&spi->dev, "failed to get vref regulator\n");
+ return PTR_ERR(adc->reg);
+ }
+ ret = regulator_enable(adc->reg);
+ if (ret)
+ return ret;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &max1118_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = max1118_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max1118_channels);
+
+ /*
+ * To reinitiate a conversion on CH0, it is necessary to allow for a
+ * conversion to be complete and all of the data to be read out. Once
+ * a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go
+ * into AutoShutdown mode until the next conversion is initiated.
+ */
+ max1118_read(spi, 0);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ max1118_trigger_handler, NULL);
+ if (ret)
+ goto err_reg_disable;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_buffer_cleanup;
+
+ return 0;
+
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_reg_disable:
+ if (id->driver_data == max1118)
+ regulator_disable(adc->reg);
+
+ return ret;
+}
+
+static int max1118_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct max1118 *adc = iio_priv(indio_dev);
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (id->driver_data == max1118)
+ return regulator_disable(adc->reg);
+
+ return 0;
+}
+
+static const struct spi_device_id max1118_id[] = {
+ { "max1117", max1117 },
+ { "max1118", max1118 },
+ { "max1119", max1119 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, max1118_id);
+
+#ifdef CONFIG_OF
+
+static const struct of_device_id max1118_dt_ids[] = {
+ { .compatible = "maxim,max1117" },
+ { .compatible = "maxim,max1118" },
+ { .compatible = "maxim,max1119" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, max1118_dt_ids);
+
+#endif
+
+static struct spi_driver max1118_spi_driver = {
+ .driver = {
+ .name = "max1118",
+ .of_match_table = of_match_ptr(max1118_dt_ids),
+ },
+ .probe = max1118_probe,
+ .remove = max1118_remove,
+ .id_table = max1118_id,
+};
+module_spi_driver(max1118_spi_driver);
+
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
+MODULE_DESCRIPTION("MAXIM MAX1117/MAX1118/MAX1119 ADCs driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c
index c6c12feb4a08..80eada4886b3 100644
--- a/drivers/iio/adc/max1363.c
+++ b/drivers/iio/adc/max1363.c
@@ -1007,7 +1007,7 @@ static struct attribute *max1363_event_attributes[] = {
NULL,
};
-static struct attribute_group max1363_event_attribute_group = {
+static const struct attribute_group max1363_event_attribute_group = {
.attrs = max1363_event_attributes,
};
diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c
new file mode 100644
index 000000000000..ec82106480e1
--- /dev/null
+++ b/drivers/iio/adc/max9611.c
@@ -0,0 +1,585 @@
+/*
+ * iio/adc/max9611.c
+ *
+ * Maxim max9611/max9612 high side current sense amplifier with
+ * 12-bit ADC interface.
+ *
+ * Copyright (C) 2017 Jacopo Mondi
+ *
+ * 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.
+ */
+
+/*
+ * This driver supports input common-mode voltage, current-sense
+ * amplifier with programmable gains and die temperature reading from
+ * Maxim max9611/max9612.
+ *
+ * Op-amp, analog comparator, and watchdog functionalities are not
+ * supported by this driver.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#define DRIVER_NAME "max9611"
+
+/* max9611 register addresses */
+#define MAX9611_REG_CSA_DATA 0x00
+#define MAX9611_REG_RS_DATA 0x02
+#define MAX9611_REG_TEMP_DATA 0x08
+#define MAX9611_REG_CTRL1 0x0a
+#define MAX9611_REG_CTRL2 0x0b
+
+/* max9611 REG1 mux configuration options */
+#define MAX9611_MUX_MASK GENMASK(3, 0)
+#define MAX9611_MUX_SENSE_1x 0x00
+#define MAX9611_MUX_SENSE_4x 0x01
+#define MAX9611_MUX_SENSE_8x 0x02
+#define MAX9611_INPUT_VOLT 0x03
+#define MAX9611_MUX_TEMP 0x06
+
+/* max9611 voltage (both csa and input) helper macros */
+#define MAX9611_VOLTAGE_SHIFT 0x04
+#define MAX9611_VOLTAGE_RAW(_r) ((_r) >> MAX9611_VOLTAGE_SHIFT)
+
+/*
+ * max9611 current sense amplifier voltage output:
+ * LSB and offset values depends on selected gain (1x, 4x, 8x)
+ *
+ * GAIN LSB (nV) OFFSET (LSB steps)
+ * 1x 107500 1
+ * 4x 26880 1
+ * 8x 13440 3
+ *
+ * The complete formula to calculate current sense voltage is:
+ * (((adc_read >> 4) - offset) / ((1 / LSB) * 10^-3)
+ */
+#define MAX9611_CSA_1X_LSB_nV 107500
+#define MAX9611_CSA_4X_LSB_nV 26880
+#define MAX9611_CSA_8X_LSB_nV 13440
+
+#define MAX9611_CSA_1X_OFFS_RAW 1
+#define MAX9611_CSA_4X_OFFS_RAW 1
+#define MAX9611_CSA_8X_OFFS_RAW 3
+
+/*
+ * max9611 common input mode (CIM): LSB is 14mV, with 14mV offset at 25 C
+ *
+ * The complete formula to calculate input common voltage is:
+ * (((adc_read >> 4) * 1000) - offset) / (1 / 14 * 1000)
+ */
+#define MAX9611_CIM_LSB_mV 14
+#define MAX9611_CIM_OFFSET_RAW 1
+
+/*
+ * max9611 temperature reading: LSB is 480 milli degrees Celsius
+ *
+ * The complete formula to calculate temperature is:
+ * ((adc_read >> 7) * 1000) / (1 / 480 * 1000)
+ */
+#define MAX9611_TEMP_MAX_POS 0x7f80
+#define MAX9611_TEMP_MAX_NEG 0xff80
+#define MAX9611_TEMP_MIN_NEG 0xd980
+#define MAX9611_TEMP_MASK GENMASK(7, 15)
+#define MAX9611_TEMP_SHIFT 0x07
+#define MAX9611_TEMP_RAW(_r) ((_r) >> MAX9611_TEMP_SHIFT)
+#define MAX9611_TEMP_SCALE_NUM 1000000
+#define MAX9611_TEMP_SCALE_DIV 2083
+
+struct max9611_dev {
+ struct device *dev;
+ struct i2c_client *i2c_client;
+ struct mutex lock;
+ unsigned int shunt_resistor_uohm;
+};
+
+enum max9611_conf_ids {
+ CONF_SENSE_1x,
+ CONF_SENSE_4x,
+ CONF_SENSE_8x,
+ CONF_IN_VOLT,
+ CONF_TEMP,
+};
+
+/**
+ * max9611_mux_conf - associate ADC mux configuration with register address
+ * where data shall be read from
+ */
+static const unsigned int max9611_mux_conf[][2] = {
+ /* CONF_SENSE_1x */
+ { MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA },
+ /* CONF_SENSE_4x */
+ { MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA },
+ /* CONF_SENSE_8x */
+ { MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA },
+ /* CONF_IN_VOLT */
+ { MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA },
+ /* CONF_TEMP */
+ { MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA },
+};
+
+enum max9611_csa_gain {
+ CSA_GAIN_1x,
+ CSA_GAIN_4x,
+ CSA_GAIN_8x,
+};
+
+enum max9611_csa_gain_params {
+ CSA_GAIN_LSB_nV,
+ CSA_GAIN_OFFS_RAW,
+};
+
+/**
+ * max9611_csa_gain_conf - associate gain multiplier with LSB and
+ * offset values.
+ *
+ * Group together parameters associated with configurable gain
+ * on current sense amplifier path to ADC interface.
+ * Current sense read routine adjusts gain until it gets a meaningful
+ * value; use this structure to retrieve the correct LSB and offset values.
+ */
+static const unsigned int max9611_gain_conf[][2] = {
+ { /* [0] CSA_GAIN_1x */
+ MAX9611_CSA_1X_LSB_nV,
+ MAX9611_CSA_1X_OFFS_RAW,
+ },
+ { /* [1] CSA_GAIN_4x */
+ MAX9611_CSA_4X_LSB_nV,
+ MAX9611_CSA_4X_OFFS_RAW,
+ },
+ { /* [2] CSA_GAIN_8x */
+ MAX9611_CSA_8X_LSB_nV,
+ MAX9611_CSA_8X_OFFS_RAW,
+ },
+};
+
+enum max9611_chan_addrs {
+ MAX9611_CHAN_VOLTAGE_INPUT,
+ MAX9611_CHAN_VOLTAGE_SENSE,
+ MAX9611_CHAN_TEMPERATURE,
+ MAX9611_CHAN_CURRENT_LOAD,
+ MAX9611_CHAN_POWER_LOAD,
+};
+
+static const struct iio_chan_spec max9611_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .address = MAX9611_CHAN_TEMPERATURE,
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = MAX9611_CHAN_VOLTAGE_SENSE,
+ .indexed = 1,
+ .channel = 0,
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .address = MAX9611_CHAN_VOLTAGE_INPUT,
+ .indexed = 1,
+ .channel = 1,
+ },
+ {
+ .type = IIO_CURRENT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = MAX9611_CHAN_CURRENT_LOAD,
+ },
+ {
+ .type = IIO_POWER,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = MAX9611_CHAN_POWER_LOAD
+ },
+};
+
+/**
+ * max9611_read_single() - read a single value from ADC interface
+ *
+ * Data registers are 16 bit long, spread between two 8 bit registers
+ * with consecutive addresses.
+ * Configure ADC mux first, then read register at address "reg_addr".
+ * The smbus_read_word routine asks for 16 bits and the ADC is kind enough
+ * to return values from "reg_addr" and "reg_addr + 1" consecutively.
+ * Data are transmitted with big-endian ordering: MSB arrives first.
+ *
+ * @max9611: max9611 device
+ * @selector: index for mux and register configuration
+ * @raw_val: the value returned from ADC
+ */
+static int max9611_read_single(struct max9611_dev *max9611,
+ enum max9611_conf_ids selector,
+ u16 *raw_val)
+{
+ int ret;
+
+ u8 mux_conf = max9611_mux_conf[selector][0] & MAX9611_MUX_MASK;
+ u8 reg_addr = max9611_mux_conf[selector][1];
+
+ /*
+ * Keep mutex lock held during read-write to avoid mux register
+ * (CTRL1) re-configuration.
+ */
+ mutex_lock(&max9611->lock);
+ ret = i2c_smbus_write_byte_data(max9611->i2c_client,
+ MAX9611_REG_CTRL1, mux_conf);
+ if (ret) {
+ dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
+ MAX9611_REG_CTRL1, mux_conf);
+ mutex_unlock(&max9611->lock);
+ return ret;
+ }
+
+ /*
+ * need a delay here to make register configuration
+ * stabilize. 1 msec at least, from empirical testing.
+ */
+ usleep_range(1000, 2000);
+
+ ret = i2c_smbus_read_word_swapped(max9611->i2c_client, reg_addr);
+ if (ret < 0) {
+ dev_err(max9611->dev, "i2c read word from 0x%2x failed\n",
+ reg_addr);
+ mutex_unlock(&max9611->lock);
+ return ret;
+ }
+
+ *raw_val = ret;
+ mutex_unlock(&max9611->lock);
+
+ return 0;
+}
+
+/**
+ * max9611_read_csa_voltage() - read current sense amplifier output voltage
+ *
+ * Current sense amplifier output voltage is read through a configurable
+ * 1x, 4x or 8x gain.
+ * Start with plain 1x gain, and adjust gain control properly until a
+ * meaningful value is read from ADC output.
+ *
+ * @max9611: max9611 device
+ * @adc_raw: raw value read from ADC output
+ * @csa_gain: gain configuration option selector
+ */
+static int max9611_read_csa_voltage(struct max9611_dev *max9611,
+ u16 *adc_raw,
+ enum max9611_csa_gain *csa_gain)
+{
+ enum max9611_conf_ids gain_selectors[] = {
+ CONF_SENSE_1x,
+ CONF_SENSE_4x,
+ CONF_SENSE_8x
+ };
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(gain_selectors); ++i) {
+ ret = max9611_read_single(max9611, gain_selectors[i], adc_raw);
+ if (ret)
+ return ret;
+
+ if (*adc_raw > 0) {
+ *csa_gain = gain_selectors[i];
+ return 0;
+ }
+ }
+
+ return -EIO;
+}
+
+static int max9611_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max9611_dev *dev = iio_priv(indio_dev);
+ enum max9611_csa_gain gain_selector;
+ const unsigned int *csa_gain;
+ u16 adc_data;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+
+ switch (chan->address) {
+ case MAX9611_CHAN_TEMPERATURE:
+ ret = max9611_read_single(dev, CONF_TEMP,
+ &adc_data);
+ if (ret)
+ return -EINVAL;
+
+ *val = MAX9611_TEMP_RAW(adc_data);
+ return IIO_VAL_INT;
+
+ case MAX9611_CHAN_VOLTAGE_INPUT:
+ ret = max9611_read_single(dev, CONF_IN_VOLT,
+ &adc_data);
+ if (ret)
+ return -EINVAL;
+
+ *val = MAX9611_VOLTAGE_RAW(adc_data);
+ return IIO_VAL_INT;
+ }
+
+ break;
+
+ case IIO_CHAN_INFO_OFFSET:
+ /* MAX9611_CHAN_VOLTAGE_INPUT */
+ *val = MAX9611_CIM_OFFSET_RAW;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+
+ switch (chan->address) {
+ case MAX9611_CHAN_TEMPERATURE:
+ *val = MAX9611_TEMP_SCALE_NUM;
+ *val2 = MAX9611_TEMP_SCALE_DIV;
+
+ return IIO_VAL_FRACTIONAL;
+
+ case MAX9611_CHAN_VOLTAGE_INPUT:
+ *val = MAX9611_CIM_LSB_mV;
+
+ return IIO_VAL_INT;
+ }
+
+ break;
+
+ case IIO_CHAN_INFO_PROCESSED:
+
+ switch (chan->address) {
+ case MAX9611_CHAN_VOLTAGE_SENSE:
+ /*
+ * processed (mV): (raw - offset) * LSB (nV) / 10^6
+ *
+ * Even if max9611 can output raw csa voltage readings,
+ * use a produced value as scale depends on gain.
+ */
+ ret = max9611_read_csa_voltage(dev, &adc_data,
+ &gain_selector);
+ if (ret)
+ return -EINVAL;
+
+ csa_gain = max9611_gain_conf[gain_selector];
+
+ adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
+ *val = MAX9611_VOLTAGE_RAW(adc_data) *
+ csa_gain[CSA_GAIN_LSB_nV];
+ *val2 = 1000000;
+
+ return IIO_VAL_FRACTIONAL;
+
+ case MAX9611_CHAN_CURRENT_LOAD:
+ /* processed (mA): Vcsa (nV) / Rshunt (uOhm) */
+ ret = max9611_read_csa_voltage(dev, &adc_data,
+ &gain_selector);
+ if (ret)
+ return -EINVAL;
+
+ csa_gain = max9611_gain_conf[gain_selector];
+
+ adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
+ *val = MAX9611_VOLTAGE_RAW(adc_data) *
+ csa_gain[CSA_GAIN_LSB_nV];
+ *val2 = dev->shunt_resistor_uohm;
+
+ return IIO_VAL_FRACTIONAL;
+
+ case MAX9611_CHAN_POWER_LOAD:
+ /*
+ * processed (mW): Vin (mV) * Vcsa (uV) /
+ * Rshunt (uOhm)
+ */
+ ret = max9611_read_single(dev, CONF_IN_VOLT,
+ &adc_data);
+ if (ret)
+ return -EINVAL;
+
+ adc_data -= MAX9611_CIM_OFFSET_RAW;
+ *val = MAX9611_VOLTAGE_RAW(adc_data) *
+ MAX9611_CIM_LSB_mV;
+
+ ret = max9611_read_csa_voltage(dev, &adc_data,
+ &gain_selector);
+ if (ret)
+ return -EINVAL;
+
+ csa_gain = max9611_gain_conf[gain_selector];
+
+ /* divide by 10^3 here to avoid 32bit overflow */
+ adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
+ *val *= MAX9611_VOLTAGE_RAW(adc_data) *
+ csa_gain[CSA_GAIN_LSB_nV] / 1000;
+ *val2 = dev->shunt_resistor_uohm;
+
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t max9611_shunt_resistor_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct max9611_dev *max9611 = iio_priv(dev_to_iio_dev(dev));
+ unsigned int i, r;
+
+ i = max9611->shunt_resistor_uohm / 1000;
+ r = max9611->shunt_resistor_uohm % 1000;
+
+ return sprintf(buf, "%u.%03u\n", i, r);
+}
+
+static IIO_DEVICE_ATTR(in_power_shunt_resistor, 0444,
+ max9611_shunt_resistor_show, NULL, 0);
+static IIO_DEVICE_ATTR(in_current_shunt_resistor, 0444,
+ max9611_shunt_resistor_show, NULL, 0);
+
+static struct attribute *max9611_attributes[] = {
+ &iio_dev_attr_in_power_shunt_resistor.dev_attr.attr,
+ &iio_dev_attr_in_current_shunt_resistor.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group max9611_attribute_group = {
+ .attrs = max9611_attributes,
+};
+
+static const struct iio_info indio_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = max9611_read_raw,
+ .attrs = &max9611_attribute_group,
+};
+
+static int max9611_init(struct max9611_dev *max9611)
+{
+ struct i2c_client *client = max9611->i2c_client;
+ u16 regval;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE |
+ I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+ dev_err(max9611->dev,
+ "I2c adapter does not support smbus write_byte or read_word functionalities: aborting probe.\n");
+ return -EINVAL;
+ }
+
+ /* Make sure die temperature is in range to test communications. */
+ ret = max9611_read_single(max9611, CONF_TEMP, &regval);
+ if (ret)
+ return ret;
+
+ regval = ret & MAX9611_TEMP_MASK;
+
+ if ((regval > MAX9611_TEMP_MAX_POS &&
+ regval < MAX9611_TEMP_MIN_NEG) ||
+ regval > MAX9611_TEMP_MAX_NEG) {
+ dev_err(max9611->dev,
+ "Invalid value received from ADC 0x%4x: aborting\n",
+ regval);
+ return -EIO;
+ }
+
+ /* Mux shall be zeroed back before applying other configurations */
+ ret = i2c_smbus_write_byte_data(max9611->i2c_client,
+ MAX9611_REG_CTRL1, 0);
+ if (ret) {
+ dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
+ MAX9611_REG_CTRL1, 0);
+ return ret;
+ }
+
+ ret = i2c_smbus_write_byte_data(max9611->i2c_client,
+ MAX9611_REG_CTRL2, 0);
+ if (ret) {
+ dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
+ MAX9611_REG_CTRL2, 0);
+ return ret;
+ }
+ usleep_range(1000, 2000);
+
+ return 0;
+}
+
+static const struct of_device_id max9611_of_table[] = {
+ {.compatible = "maxim,max9611", .data = "max9611"},
+ {.compatible = "maxim,max9612", .data = "max9612"},
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, max9611_of_table);
+static int max9611_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const char * const shunt_res_prop = "shunt-resistor-micro-ohms";
+ const struct device_node *of_node = client->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(max9611_of_table, &client->dev);
+ struct max9611_dev *max9611;
+ struct iio_dev *indio_dev;
+ unsigned int of_shunt;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*max9611));
+ if (IS_ERR(indio_dev))
+ return PTR_ERR(indio_dev);
+
+ i2c_set_clientdata(client, indio_dev);
+
+ max9611 = iio_priv(indio_dev);
+ max9611->dev = &client->dev;
+ max9611->i2c_client = client;
+ mutex_init(&max9611->lock);
+
+ ret = of_property_read_u32(of_node, shunt_res_prop, &of_shunt);
+ if (ret) {
+ dev_err(&client->dev,
+ "Missing %s property for %s node\n",
+ shunt_res_prop, of_node->full_name);
+ return ret;
+ }
+ max9611->shunt_resistor_uohm = of_shunt;
+
+ ret = max9611_init(max9611);
+ if (ret)
+ return ret;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->dev.of_node = client->dev.of_node;
+ indio_dev->name = of_id->data;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &indio_info;
+ indio_dev->channels = max9611_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max9611_channels);
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static struct i2c_driver max9611_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = max9611_of_table,
+ },
+ .probe = max9611_probe,
+};
+module_i2c_driver(max9611_driver);
+
+MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>");
+MODULE_DESCRIPTION("Maxim max9611/12 current sense amplifier with 12bit ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c
index 89def6034f40..dd4190b50df6 100644
--- a/drivers/iio/adc/meson_saradc.c
+++ b/drivers/iio/adc/meson_saradc.c
@@ -18,7 +18,9 @@
#include <linux/io.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
+#include <linux/interrupt.h>
#include <linux/of.h>
+#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -163,6 +165,9 @@
#define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8)
#define MESON_SAR_ADC_MAX_FIFO_SIZE 32
+#define MESON_SAR_ADC_TIMEOUT 100 /* ms */
+/* for use with IIO_VAL_INT_PLUS_MICRO */
+#define MILLION 1000000
#define MESON_SAR_ADC_CHAN(_chan) { \
.type = IIO_VOLTAGE, \
@@ -170,7 +175,9 @@
.channel = _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), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE), \
.datasheet_name = "SAR_ADC_CH"#_chan, \
}
@@ -229,6 +236,9 @@ struct meson_sar_adc_priv {
struct clk_gate clk_gate;
struct clk *adc_div_clk;
struct clk_divider clk_div;
+ struct completion done;
+ int calibbias;
+ int calibscale;
};
static const struct regmap_config meson_sar_adc_regmap_config = {
@@ -248,6 +258,17 @@ static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev)
return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval);
}
+static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ int tmp;
+
+ /* 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->resolution) - 1);
+}
+
static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
@@ -274,33 +295,31 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev,
int *val)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
- int ret, regval, fifo_chan, fifo_val, sum = 0, count = 0;
+ int regval, fifo_chan, fifo_val, count;
- ret = meson_sar_adc_wait_busy_clear(indio_dev);
- if (ret)
- return ret;
-
- while (meson_sar_adc_get_fifo_count(indio_dev) > 0 &&
- count < MESON_SAR_ADC_MAX_FIFO_SIZE) {
- 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)
- continue;
-
- fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK,
- regval);
- fifo_val &= (BIT(priv->data->resolution) - 1);
+ if(!wait_for_completion_timeout(&priv->done,
+ msecs_to_jiffies(MESON_SAR_ADC_TIMEOUT)))
+ return -ETIMEDOUT;
- sum += fifo_val;
- count++;
+ count = meson_sar_adc_get_fifo_count(indio_dev);
+ if (count != 1) {
+ dev_err(&indio_dev->dev,
+ "ADC FIFO has %d element(s) instead of one\n", count);
+ return -EINVAL;
}
- if (!count)
- return -ENOENT;
+ 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) {
+ dev_err(&indio_dev->dev,
+ "ADC FIFO entry belongs to channel %d instead of %d\n",
+ fifo_chan, chan->channel);
+ return -EINVAL;
+ }
- *val = sum / count;
+ fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval);
+ fifo_val &= GENMASK(priv->data->resolution - 1, 0);
+ *val = meson_sar_adc_calib_val(indio_dev, fifo_val);
return 0;
}
@@ -378,6 +397,12 @@ static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ reinit_completion(&priv->done);
+
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
+ MESON_SAR_ADC_REG0_FIFO_IRQ_EN,
+ MESON_SAR_ADC_REG0_FIFO_IRQ_EN);
+
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE,
MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE);
@@ -392,6 +417,9 @@ static void meson_sar_adc_stop_sample_engine(struct iio_dev *indio_dev)
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
+ MESON_SAR_ADC_REG0_FIFO_IRQ_EN, 0);
+
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
MESON_SAR_ADC_REG0_SAMPLING_STOP,
MESON_SAR_ADC_REG0_SAMPLING_STOP);
@@ -516,6 +544,15 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
*val2 = priv->data->resolution;
return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ *val = priv->calibbias;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_CALIBSCALE:
+ *val = priv->calibscale / MILLION;
+ *val2 = priv->calibscale % MILLION;
+ return IIO_VAL_INT_PLUS_MICRO;
+
default:
return -EINVAL;
}
@@ -643,6 +680,7 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
int ret;
+ u32 regval;
ret = meson_sar_adc_lock(indio_dev);
if (ret)
@@ -667,6 +705,9 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev)
goto err_sana_clk;
}
+ regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
+ MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
MESON_SAR_ADC_REG11_BANDGAP_EN,
MESON_SAR_ADC_REG11_BANDGAP_EN);
@@ -728,6 +769,66 @@ static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev)
return 0;
}
+static irqreturn_t meson_sar_adc_irq(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ unsigned int cnt, threshold;
+ u32 regval;
+
+ regmap_read(priv->regmap, MESON_SAR_ADC_REG0, &regval);
+ cnt = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval);
+ threshold = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval);
+
+ if (cnt < threshold)
+ return IRQ_NONE;
+
+ complete(&priv->done);
+
+ return IRQ_HANDLED;
+}
+
+static int meson_sar_adc_calib(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ int ret, nominal0, nominal1, value0, value1;
+
+ /* use points 25% and 75% for calibration */
+ nominal0 = (1 << priv->data->resolution) / 4;
+ nominal1 = (1 << priv->data->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],
+ MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
+ if (ret < 0)
+ goto out;
+
+ 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],
+ MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
+ if (ret < 0)
+ goto out;
+
+ if (value1 <= value0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ priv->calibscale = div_s64((nominal1 - nominal0) * (s64)MILLION,
+ value1 - value0);
+ priv->calibbias = nominal0 - div_s64((s64)value0 * priv->calibscale,
+ MILLION);
+ ret = 0;
+out:
+ meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
+
+ return ret;
+}
+
static const struct iio_info meson_sar_adc_iio_info = {
.read_raw = meson_sar_adc_iio_info_read_raw,
.driver_module = THIS_MODULE,
@@ -770,7 +871,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *base;
const struct of_device_id *match;
- int ret;
+ int irq, ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
if (!indio_dev) {
@@ -779,6 +880,7 @@ 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);
priv->data = match->data;
@@ -797,6 +899,15 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
+ irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ if (!irq)
+ return -EINVAL;
+
+ ret = devm_request_irq(&pdev->dev, irq, meson_sar_adc_irq, IRQF_SHARED,
+ dev_name(&pdev->dev), indio_dev);
+ if (ret)
+ return ret;
+
priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&meson_sar_adc_regmap_config);
if (IS_ERR(priv->regmap))
@@ -857,6 +968,8 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
return PTR_ERR(priv->vref);
}
+ priv->calibscale = MILLION;
+
ret = meson_sar_adc_init(indio_dev);
if (ret)
goto err;
@@ -865,6 +978,10 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
if (ret)
goto err;
+ ret = meson_sar_adc_calib(indio_dev);
+ if (ret)
+ dev_warn(&pdev->dev, "calibration failed\n");
+
platform_set_drvdata(pdev, indio_dev);
ret = iio_device_register(indio_dev);
diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
new file mode 100644
index 000000000000..cea8f1fb444a
--- /dev/null
+++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
@@ -0,0 +1,1036 @@
+/*
+ * Qualcomm PM8xxx PMIC XOADC driver
+ *
+ * These ADCs are known as HK/XO (house keeping / chrystal oscillator)
+ * "XO" in "XOADC" means Chrystal Oscillator. It's a bunch of
+ * specific-purpose and general purpose ADC converters and channels.
+ *
+ * Copyright (C) 2017 Linaro Ltd.
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.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/init.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+
+#include "qcom-vadc-common.h"
+
+/*
+ * Definitions for the "user processor" registers lifted from the v3.4
+ * Qualcomm tree. Their kernel has two out-of-tree drivers for the ADC:
+ * drivers/misc/pmic8058-xoadc.c
+ * drivers/hwmon/pm8xxx-adc.c
+ * None of them contain any complete register specification, so this is
+ * a best effort of combining the information.
+ */
+
+/* These appear to be "battery monitor" registers */
+#define ADC_ARB_BTM_CNTRL1 0x17e
+#define ADC_ARB_BTM_CNTRL1_EN_BTM BIT(0)
+#define ADC_ARB_BTM_CNTRL1_SEL_OP_MODE BIT(1)
+#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL1 BIT(2)
+#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL2 BIT(3)
+#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL3 BIT(4)
+#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL4 BIT(5)
+#define ADC_ARB_BTM_CNTRL1_EOC BIT(6)
+#define ADC_ARB_BTM_CNTRL1_REQ BIT(7)
+
+#define ADC_ARB_BTM_AMUX_CNTRL 0x17f
+#define ADC_ARB_BTM_ANA_PARAM 0x180
+#define ADC_ARB_BTM_DIG_PARAM 0x181
+#define ADC_ARB_BTM_RSV 0x182
+#define ADC_ARB_BTM_DATA1 0x183
+#define ADC_ARB_BTM_DATA0 0x184
+#define ADC_ARB_BTM_BAT_COOL_THR1 0x185
+#define ADC_ARB_BTM_BAT_COOL_THR0 0x186
+#define ADC_ARB_BTM_BAT_WARM_THR1 0x187
+#define ADC_ARB_BTM_BAT_WARM_THR0 0x188
+#define ADC_ARB_BTM_CNTRL2 0x18c
+
+/* Proper ADC registers */
+
+#define ADC_ARB_USRP_CNTRL 0x197
+#define ADC_ARB_USRP_CNTRL_EN_ARB BIT(0)
+#define ADC_ARB_USRP_CNTRL_RSV1 BIT(1)
+#define ADC_ARB_USRP_CNTRL_RSV2 BIT(2)
+#define ADC_ARB_USRP_CNTRL_RSV3 BIT(3)
+#define ADC_ARB_USRP_CNTRL_RSV4 BIT(4)
+#define ADC_ARB_USRP_CNTRL_RSV5 BIT(5)
+#define ADC_ARB_USRP_CNTRL_EOC BIT(6)
+#define ADC_ARB_USRP_CNTRL_REQ BIT(7)
+
+#define ADC_ARB_USRP_AMUX_CNTRL 0x198
+/*
+ * The channel mask includes the bits selecting channel mux and prescaler
+ * on PM8058, or channel mux and premux on PM8921.
+ */
+#define ADC_ARB_USRP_AMUX_CNTRL_CHAN_MASK 0xfc
+#define ADC_ARB_USRP_AMUX_CNTRL_RSV0 BIT(0)
+#define ADC_ARB_USRP_AMUX_CNTRL_RSV1 BIT(1)
+/* On PM8058 this is prescaling, on PM8921 this is premux */
+#define ADC_ARB_USRP_AMUX_CNTRL_PRESCALEMUX0 BIT(2)
+#define ADC_ARB_USRP_AMUX_CNTRL_PRESCALEMUX1 BIT(3)
+#define ADC_ARB_USRP_AMUX_CNTRL_SEL0 BIT(4)
+#define ADC_ARB_USRP_AMUX_CNTRL_SEL1 BIT(5)
+#define ADC_ARB_USRP_AMUX_CNTRL_SEL2 BIT(6)
+#define ADC_ARB_USRP_AMUX_CNTRL_SEL3 BIT(7)
+#define ADC_AMUX_PREMUX_SHIFT 2
+#define ADC_AMUX_SEL_SHIFT 4
+
+/* We know very little about the bits in this register */
+#define ADC_ARB_USRP_ANA_PARAM 0x199
+#define ADC_ARB_USRP_ANA_PARAM_DIS 0xFE
+#define ADC_ARB_USRP_ANA_PARAM_EN 0xFF
+
+#define ADC_ARB_USRP_DIG_PARAM 0x19A
+#define ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0 BIT(0)
+#define ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1 BIT(1)
+#define ADC_ARB_USRP_DIG_PARAM_CLK_RATE0 BIT(2)
+#define ADC_ARB_USRP_DIG_PARAM_CLK_RATE1 BIT(3)
+#define ADC_ARB_USRP_DIG_PARAM_EOC BIT(4)
+/*
+ * On a later ADC the decimation factors are defined as
+ * 00 = 512, 01 = 1024, 10 = 2048, 11 = 4096 so assume this
+ * holds also for this older XOADC.
+ */
+#define ADC_ARB_USRP_DIG_PARAM_DEC_RATE0 BIT(5)
+#define ADC_ARB_USRP_DIG_PARAM_DEC_RATE1 BIT(6)
+#define ADC_ARB_USRP_DIG_PARAM_EN BIT(7)
+#define ADC_DIG_PARAM_DEC_SHIFT 5
+
+#define ADC_ARB_USRP_RSV 0x19B
+#define ADC_ARB_USRP_RSV_RST BIT(0)
+#define ADC_ARB_USRP_RSV_DTEST0 BIT(1)
+#define ADC_ARB_USRP_RSV_DTEST1 BIT(2)
+#define ADC_ARB_USRP_RSV_OP BIT(3)
+#define ADC_ARB_USRP_RSV_IP_SEL0 BIT(4)
+#define ADC_ARB_USRP_RSV_IP_SEL1 BIT(5)
+#define ADC_ARB_USRP_RSV_IP_SEL2 BIT(6)
+#define ADC_ARB_USRP_RSV_TRM BIT(7)
+#define ADC_RSV_IP_SEL_SHIFT 4
+
+#define ADC_ARB_USRP_DATA0 0x19D
+#define ADC_ARB_USRP_DATA1 0x19C
+
+/**
+ * Physical channels which MUST exist on all PM variants in order to provide
+ * proper reference points for calibration.
+ *
+ * @PM8XXX_CHANNEL_INTERNAL: 625mV reference channel
+ * @PM8XXX_CHANNEL_125V: 1250mV reference channel
+ * @PM8XXX_CHANNEL_INTERNAL_2: 325mV reference channel
+ * @PM8XXX_CHANNEL_MUXOFF: channel to reduce input load on mux, apparently also
+ * measures XO temperature
+ */
+#define PM8XXX_CHANNEL_INTERNAL 0x0c
+#define PM8XXX_CHANNEL_125V 0x0d
+#define PM8XXX_CHANNEL_INTERNAL_2 0x0e
+#define PM8XXX_CHANNEL_MUXOFF 0x0f
+
+/*
+ * PM8058 AMUX premux scaling, two bits. This is done of the channel before
+ * reaching the AMUX.
+ */
+#define PM8058_AMUX_PRESCALE_0 0x0 /* No scaling on the signal */
+#define PM8058_AMUX_PRESCALE_1 0x1 /* Unity scaling selected by the user */
+#define PM8058_AMUX_PRESCALE_1_DIV3 0x2 /* 1/3 prescaler on the input */
+
+/* Defines reference voltage for the XOADC */
+#define AMUX_RSV0 0x0 /* XO_IN/XOADC_GND, special selection to read XO temp */
+#define AMUX_RSV1 0x1 /* PMIC_IN/XOADC_GND */
+#define AMUX_RSV2 0x2 /* PMIC_IN/BMS_CSP */
+#define AMUX_RSV3 0x3 /* not used */
+#define AMUX_RSV4 0x4 /* XOADC_GND/XOADC_GND */
+#define AMUX_RSV5 0x5 /* XOADC_VREF/XOADC_GND */
+#define XOADC_RSV_MAX 5 /* 3 bits 0..7, 3 and 6,7 are invalid */
+
+/**
+ * struct xoadc_channel - encodes channel properties and defaults
+ * @datasheet_name: the hardwarename of this channel
+ * @pre_scale_mux: prescale (PM8058) or premux (PM8921) for selecting
+ * this channel. Both this and the amux channel is needed to uniquely
+ * identify a channel. Values 0..3.
+ * @amux_channel: value of the ADC_ARB_USRP_AMUX_CNTRL register for this
+ * channel, bits 4..7, selects the amux, values 0..f
+ * @prescale: the channels have hard-coded prescale ratios defined
+ * by the hardware, this tells us what it is
+ * @type: corresponding IIO channel type, usually IIO_VOLTAGE or
+ * IIO_TEMP
+ * @scale_fn_type: the liner interpolation etc to convert the
+ * ADC code to the value that IIO expects, in uV or millicelsius
+ * etc. This scale function can be pretty elaborate if different
+ * thermistors are connected or other hardware characteristics are
+ * deployed.
+ * @amux_ip_rsv: ratiometric scale value used by the analog muxer: this
+ * selects the reference voltage for ratiometric scaling
+ */
+struct xoadc_channel {
+ const char *datasheet_name;
+ u8 pre_scale_mux:2;
+ u8 amux_channel:4;
+ const struct vadc_prescale_ratio prescale;
+ enum iio_chan_type type;
+ enum vadc_scale_fn_type scale_fn_type;
+ u8 amux_ip_rsv:3;
+};
+
+/**
+ * struct xoadc_variant - encodes the XOADC variant characteristics
+ * @name: name of this PMIC variant
+ * @channels: the hardware channels and respective settings and defaults
+ * @broken_ratiometric: if the PMIC has broken ratiometric scaling (this
+ * is a known problem on PM8058)
+ * @prescaling: this variant uses AMUX bits 2 & 3 for prescaling (PM8058)
+ * @second_level_mux: this variant uses AMUX bits 2 & 3 for a second level
+ * mux
+ */
+struct xoadc_variant {
+ const char name[16];
+ const struct xoadc_channel *channels;
+ bool broken_ratiometric;
+ bool prescaling;
+ bool second_level_mux;
+};
+
+/*
+ * XOADC_CHAN macro parameters:
+ * _dname: the name of the channel
+ * _presmux: prescaler (PM8058) or premux (PM8921) setting for this channel
+ * _amux: the value in bits 2..7 of the ADC_ARB_USRP_AMUX_CNTRL register
+ * for this channel. On some PMICs some of the bits select a prescaler, and
+ * on some PMICs some of the bits select various complex multiplex settings.
+ * _type: IIO channel type
+ * _prenum: prescaler numerator (dividend)
+ * _preden: prescaler denominator (divisor)
+ * _scale: scaling function type, this selects how the raw valued is mangled
+ * to output the actual processed measurement
+ * _amip: analog mux input parent when using ratiometric measurements
+ */
+#define XOADC_CHAN(_dname, _presmux, _amux, _type, _prenum, _preden, _scale, _amip) \
+ { \
+ .datasheet_name = __stringify(_dname), \
+ .pre_scale_mux = _presmux, \
+ .amux_channel = _amux, \
+ .prescale = { .num = _prenum, .den = _preden }, \
+ .type = _type, \
+ .scale_fn_type = _scale, \
+ .amux_ip_rsv = _amip, \
+ }
+
+/*
+ * Taken from arch/arm/mach-msm/board-9615.c in the vendor tree:
+ * TODO: incomplete, needs testing.
+ */
+static const struct xoadc_channel pm8018_xoadc_channels[] = {
+ XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VPH_PWR, 0x00, 0x02, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1),
+ /* Used for battery ID or battery temperature */
+ XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV2),
+ XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0),
+ { }, /* Sentinel */
+};
+
+/*
+ * Taken from arch/arm/mach-msm/board-8930-pmic.c in the vendor tree:
+ * TODO: needs testing.
+ */
+static const struct xoadc_channel pm8038_xoadc_channels[] = {
+ XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ICHG, 0x00, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX5, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX6, 0x00, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX7, 0x00, 0x07, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* AMUX8 used for battery temperature in most cases */
+ XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_TEMP, 1, 1, SCALE_THERM_100K_PULLUP, AMUX_RSV2),
+ XOADC_CHAN(AMUX9, 0x00, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 4, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1),
+ XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(INTERNAL_2, 0x00, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0),
+ { }, /* Sentinel */
+};
+
+/*
+ * This was created by cross-referencing the vendor tree
+ * arch/arm/mach-msm/board-msm8x60.c msm_adc_channels_data[]
+ * with the "channel types" (first field) to find the right
+ * configuration for these channels on an MSM8x60 i.e. PM8058
+ * setup.
+ */
+static const struct xoadc_channel pm8058_xoadc_channels[] = {
+ XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 10, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ICHG, 0x00, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ /*
+ * AMUX channels 5 thru 9 are referred to as MPP5 thru MPP9 in
+ * some code and documentation. But they are really just 5
+ * channels just like any other. They are connected to a switching
+ * matrix where they can be routed to any of the MPPs, not just
+ * 1-to-1 onto MPP5 thru 9, so naming them MPP5 thru MPP9 is
+ * very confusing.
+ */
+ XOADC_CHAN(AMUX5, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX6, 0x00, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX7, 0x00, 0x07, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX9, 0x00, 0x09, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1),
+ XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(INTERNAL_2, 0x00, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0),
+ /* There are also "unity" and divided by 3 channels (prescaler) but noone is using them */
+ { }, /* Sentinel */
+};
+
+/*
+ * The PM8921 has some pre-muxing on its channels, this comes from the vendor tree
+ * include/linux/mfd/pm8xxx/pm8xxx-adc.h
+ * board-flo-pmic.c (Nexus 7) and board-8064-pmic.c
+ */
+static const struct xoadc_channel pm8921_xoadc_channels[] = {
+ XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1),
+ /* channel "ICHG" is reserved and not used on PM8921 */
+ XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(IBAT, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* CHAN 6 & 7 (MPP1 & MPP2) are reserved for MPP channels on PM8921 */
+ XOADC_CHAN(BATT_THERM, 0x00, 0x08, IIO_TEMP, 1, 1, SCALE_THERM_100K_PULLUP, AMUX_RSV1),
+ XOADC_CHAN(BATT_ID, 0x00, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 4, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1),
+ XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* FIXME: look into the scaling of this temperature */
+ XOADC_CHAN(CHG_TEMP, 0x00, 0x0e, IIO_TEMP, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0),
+ /* The following channels have premux bit 0 set to 1 (all end in 4) */
+ XOADC_CHAN(ATEST_8, 0x01, 0x00, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* Set scaling to 1/2 based on the name for these two */
+ XOADC_CHAN(USB_SNS_DIV20, 0x01, 0x01, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DCIN_SNS_DIV20, 0x01, 0x02, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX3, 0x01, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX4, 0x01, 0x04, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX5, 0x01, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX6, 0x01, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX7, 0x01, 0x07, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX8, 0x01, 0x08, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* Internal test signals, I think */
+ XOADC_CHAN(ATEST_1, 0x01, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_2, 0x01, 0x0a, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_3, 0x01, 0x0b, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_4, 0x01, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_5, 0x01, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_6, 0x01, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_7, 0x01, 0x0f, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1),
+ /* The following channels have premux bit 1 set to 1 (all end in 8) */
+ /* I guess even ATEST8 will be divided by 3 here */
+ XOADC_CHAN(ATEST_8, 0x02, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ /* I guess div 2 div 3 becomes div 6 */
+ XOADC_CHAN(USB_SNS_DIV20_DIV3, 0x02, 0x01, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(DCIN_SNS_DIV20_DIV3, 0x02, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX3_DIV3, 0x02, 0x03, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX4_DIV3, 0x02, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX5_DIV3, 0x02, 0x05, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX6_DIV3, 0x02, 0x06, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX7_DIV3, 0x02, 0x07, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(AMUX8_DIV3, 0x02, 0x08, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_1_DIV3, 0x02, 0x09, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_2_DIV3, 0x02, 0x0a, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_3_DIV3, 0x02, 0x0b, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_4_DIV3, 0x02, 0x0c, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_5_DIV3, 0x02, 0x0d, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_6_DIV3, 0x02, 0x0e, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ XOADC_CHAN(ATEST_7_DIV3, 0x02, 0x0f, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1),
+ { }, /* Sentinel */
+};
+
+/**
+ * struct pm8xxx_chan_info - ADC channel information
+ * @name: name of this channel
+ * @hwchan: pointer to hardware channel information (muxing & scaling settings)
+ * @calibration: whether to use absolute or ratiometric calibration
+ * @scale_fn_type: scaling function type
+ * @decimation: 0,1,2,3
+ * @amux_ip_rsv: ratiometric scale value if using ratiometric
+ * calibration: 0, 1, 2, 4, 5.
+ */
+struct pm8xxx_chan_info {
+ const char *name;
+ const struct xoadc_channel *hwchan;
+ enum vadc_calibration calibration;
+ u8 decimation:2;
+ u8 amux_ip_rsv:3;
+};
+
+/**
+ * struct pm8xxx_xoadc - state container for the XOADC
+ * @dev: pointer to device
+ * @map: regmap to access registers
+ * @vref: reference voltage regulator
+ * characteristics of the channels, and sensible default settings
+ * @nchans: number of channels, configured by the device tree
+ * @chans: the channel information per-channel, configured by the device tree
+ * @iio_chans: IIO channel specifiers
+ * @graph: linear calibration parameters for absolute and
+ * ratiometric measurements
+ * @complete: completion to indicate end of conversion
+ * @lock: lock to restrict access to the hardware to one client at the time
+ */
+struct pm8xxx_xoadc {
+ struct device *dev;
+ struct regmap *map;
+ const struct xoadc_variant *variant;
+ struct regulator *vref;
+ unsigned int nchans;
+ struct pm8xxx_chan_info *chans;
+ struct iio_chan_spec *iio_chans;
+ struct vadc_linear_graph graph[2];
+ struct completion complete;
+ struct mutex lock;
+};
+
+static irqreturn_t pm8xxx_eoc_irq(int irq, void *d)
+{
+ struct iio_dev *indio_dev = d;
+ struct pm8xxx_xoadc *adc = iio_priv(indio_dev);
+
+ complete(&adc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static struct pm8xxx_chan_info *
+pm8xxx_get_channel(struct pm8xxx_xoadc *adc, u8 chan)
+{
+ struct pm8xxx_chan_info *ch;
+ int i;
+
+ for (i = 0; i < adc->nchans; i++) {
+ ch = &adc->chans[i];
+ if (ch->hwchan->amux_channel == chan)
+ break;
+ }
+ if (i == adc->nchans)
+ return NULL;
+
+ return ch;
+}
+
+static int pm8xxx_read_channel_rsv(struct pm8xxx_xoadc *adc,
+ const struct pm8xxx_chan_info *ch,
+ u8 rsv, u16 *adc_code,
+ bool force_ratiometric)
+{
+ int ret;
+ unsigned int val;
+ u8 rsvmask, rsvval;
+ u8 lsb, msb;
+
+ dev_dbg(adc->dev, "read channel \"%s\", amux %d, prescale/mux: %d, rsv %d\n",
+ ch->name, ch->hwchan->amux_channel, ch->hwchan->pre_scale_mux, rsv);
+
+ mutex_lock(&adc->lock);
+
+ /* Mux in this channel */
+ val = ch->hwchan->amux_channel << ADC_AMUX_SEL_SHIFT;
+ val |= ch->hwchan->pre_scale_mux << ADC_AMUX_PREMUX_SHIFT;
+ ret = regmap_write(adc->map, ADC_ARB_USRP_AMUX_CNTRL, val);
+ if (ret)
+ goto unlock;
+
+ /* Set up ratiometric scale value, mask off all bits except these */
+ rsvmask = (ADC_ARB_USRP_RSV_RST | ADC_ARB_USRP_RSV_DTEST0 |
+ ADC_ARB_USRP_RSV_DTEST1 | ADC_ARB_USRP_RSV_OP);
+ if (adc->variant->broken_ratiometric && !force_ratiometric) {
+ /*
+ * Apparently the PM8058 has some kind of bug which is
+ * reflected in the vendor tree drivers/misc/pmix8058-xoadc.c
+ * which just hardcodes the RSV selector to SEL1 (0x20) for
+ * most cases and SEL0 (0x10) for the MUXOFF channel only.
+ * If we force ratiometric (currently only done when attempting
+ * to do ratiometric calibration) this doesn't seem to work
+ * very well and I suspect ratiometric conversion is simply
+ * broken or not supported on the PM8058.
+ *
+ * Maybe IO_SEL2 doesn't exist on PM8058 and bits 4 & 5 select
+ * the mode alone.
+ *
+ * Some PM8058 register documentation would be nice to get
+ * this right.
+ */
+ if (ch->hwchan->amux_channel == PM8XXX_CHANNEL_MUXOFF)
+ rsvval = ADC_ARB_USRP_RSV_IP_SEL0;
+ else
+ rsvval = ADC_ARB_USRP_RSV_IP_SEL1;
+ } else {
+ if (rsv == 0xff)
+ rsvval = (ch->amux_ip_rsv << ADC_RSV_IP_SEL_SHIFT) |
+ ADC_ARB_USRP_RSV_TRM;
+ else
+ rsvval = (rsv << ADC_RSV_IP_SEL_SHIFT) |
+ ADC_ARB_USRP_RSV_TRM;
+ }
+
+ ret = regmap_update_bits(adc->map,
+ ADC_ARB_USRP_RSV,
+ ~rsvmask,
+ rsvval);
+ if (ret)
+ goto unlock;
+
+ ret = regmap_write(adc->map, ADC_ARB_USRP_ANA_PARAM,
+ ADC_ARB_USRP_ANA_PARAM_DIS);
+ if (ret)
+ goto unlock;
+
+ /* Decimation factor */
+ ret = regmap_write(adc->map, ADC_ARB_USRP_DIG_PARAM,
+ ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0 |
+ ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1 |
+ ch->decimation << ADC_DIG_PARAM_DEC_SHIFT);
+ if (ret)
+ goto unlock;
+
+ ret = regmap_write(adc->map, ADC_ARB_USRP_ANA_PARAM,
+ ADC_ARB_USRP_ANA_PARAM_EN);
+ if (ret)
+ goto unlock;
+
+ /* Enable the arbiter, the Qualcomm code does it twice like this */
+ ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL,
+ ADC_ARB_USRP_CNTRL_EN_ARB);
+ if (ret)
+ goto unlock;
+ ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL,
+ ADC_ARB_USRP_CNTRL_EN_ARB);
+ if (ret)
+ goto unlock;
+
+
+ /* Fire a request! */
+ reinit_completion(&adc->complete);
+ ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL,
+ ADC_ARB_USRP_CNTRL_EN_ARB |
+ ADC_ARB_USRP_CNTRL_REQ);
+ if (ret)
+ goto unlock;
+
+ /* Next the interrupt occurs */
+ ret = wait_for_completion_timeout(&adc->complete,
+ VADC_CONV_TIME_MAX_US);
+ if (!ret) {
+ dev_err(adc->dev, "conversion timed out\n");
+ ret = -ETIMEDOUT;
+ goto unlock;
+ }
+
+ ret = regmap_read(adc->map, ADC_ARB_USRP_DATA0, &val);
+ if (ret)
+ goto unlock;
+ lsb = val;
+ ret = regmap_read(adc->map, ADC_ARB_USRP_DATA1, &val);
+ if (ret)
+ goto unlock;
+ msb = val;
+ *adc_code = (msb << 8) | lsb;
+
+ /* Turn off the ADC by setting the arbiter to 0 twice */
+ ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, 0);
+ if (ret)
+ goto unlock;
+ ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, 0);
+ if (ret)
+ goto unlock;
+
+unlock:
+ mutex_unlock(&adc->lock);
+ return ret;
+}
+
+static int pm8xxx_read_channel(struct pm8xxx_xoadc *adc,
+ const struct pm8xxx_chan_info *ch,
+ u16 *adc_code)
+{
+ /*
+ * Normally we just use the ratiometric scale value (RSV) predefined
+ * for the channel, but during calibration we need to modify this
+ * so this wrapper is a helper hiding the more complex version.
+ */
+ return pm8xxx_read_channel_rsv(adc, ch, 0xff, adc_code, false);
+}
+
+static int pm8xxx_calibrate_device(struct pm8xxx_xoadc *adc)
+{
+ const struct pm8xxx_chan_info *ch;
+ u16 read_1250v;
+ u16 read_0625v;
+ u16 read_nomux_rsv5;
+ u16 read_nomux_rsv4;
+ int ret;
+
+ adc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV;
+ adc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE;
+
+ /* Common reference channel calibration */
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_125V);
+ if (!ch)
+ return -ENODEV;
+ ret = pm8xxx_read_channel(adc, ch, &read_1250v);
+ if (ret) {
+ dev_err(adc->dev, "could not read 1.25V reference channel\n");
+ return -ENODEV;
+ }
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_INTERNAL);
+ if (!ch)
+ return -ENODEV;
+ ret = pm8xxx_read_channel(adc, ch, &read_0625v);
+ if (ret) {
+ dev_err(adc->dev, "could not read 0.625V reference channel\n");
+ return -ENODEV;
+ }
+ if (read_1250v == read_0625v) {
+ dev_err(adc->dev, "read same ADC code for 1.25V and 0.625V\n");
+ return -ENODEV;
+ }
+
+ adc->graph[VADC_CALIB_ABSOLUTE].dy = read_1250v - read_0625v;
+ adc->graph[VADC_CALIB_ABSOLUTE].gnd = read_0625v;
+
+ dev_info(adc->dev, "absolute calibration dx = %d uV, dy = %d units\n",
+ VADC_ABSOLUTE_RANGE_UV, adc->graph[VADC_CALIB_ABSOLUTE].dy);
+
+ /* Ratiometric calibration */
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_MUXOFF);
+ if (!ch)
+ return -ENODEV;
+ ret = pm8xxx_read_channel_rsv(adc, ch, AMUX_RSV5,
+ &read_nomux_rsv5, true);
+ if (ret) {
+ dev_err(adc->dev, "could not read MUXOFF reference channel\n");
+ return -ENODEV;
+ }
+ ret = pm8xxx_read_channel_rsv(adc, ch, AMUX_RSV4,
+ &read_nomux_rsv4, true);
+ if (ret) {
+ dev_err(adc->dev, "could not read MUXOFF reference channel\n");
+ return -ENODEV;
+ }
+ adc->graph[VADC_CALIB_RATIOMETRIC].dy =
+ read_nomux_rsv5 - read_nomux_rsv4;
+ adc->graph[VADC_CALIB_RATIOMETRIC].gnd = read_nomux_rsv4;
+
+ dev_info(adc->dev, "ratiometric calibration dx = %d, dy = %d units\n",
+ VADC_RATIOMETRIC_RANGE,
+ adc->graph[VADC_CALIB_RATIOMETRIC].dy);
+
+ return 0;
+}
+
+static int pm8xxx_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct pm8xxx_xoadc *adc = iio_priv(indio_dev);
+ const struct pm8xxx_chan_info *ch;
+ u16 adc_code;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ch = pm8xxx_get_channel(adc, chan->address);
+ if (!ch) {
+ dev_err(adc->dev, "no such channel %lu\n",
+ chan->address);
+ return -EINVAL;
+ }
+ ret = pm8xxx_read_channel(adc, ch, &adc_code);
+ if (ret)
+ return ret;
+
+ ret = qcom_vadc_scale(ch->hwchan->scale_fn_type,
+ &adc->graph[ch->calibration],
+ &ch->hwchan->prescale,
+ (ch->calibration == VADC_CALIB_ABSOLUTE),
+ adc_code, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_RAW:
+ ch = pm8xxx_get_channel(adc, chan->address);
+ if (!ch) {
+ dev_err(adc->dev, "no such channel %lu\n",
+ chan->address);
+ return -EINVAL;
+ }
+ ret = pm8xxx_read_channel(adc, ch, &adc_code);
+ if (ret)
+ return ret;
+
+ *val = (int)adc_code;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int pm8xxx_of_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ struct pm8xxx_xoadc *adc = iio_priv(indio_dev);
+ u8 pre_scale_mux;
+ u8 amux_channel;
+ unsigned int i;
+
+ /*
+ * First cell is prescaler or premux, second cell is analog
+ * 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,
+ iiospec->args_count);
+ return -EINVAL;
+ }
+ pre_scale_mux = (u8)iiospec->args[0];
+ amux_channel = (u8)iiospec->args[1];
+ dev_dbg(&indio_dev->dev, "pre scale/mux: %02x, amux: %02x\n",
+ pre_scale_mux, amux_channel);
+
+ /* We need to match exactly on the prescale/premux and channel */
+ for (i = 0; i < adc->nchans; i++)
+ if (adc->chans[i].hwchan->pre_scale_mux == pre_scale_mux &&
+ adc->chans[i].hwchan->amux_channel == amux_channel)
+ return i;
+
+ return -EINVAL;
+}
+
+static const struct iio_info pm8xxx_xoadc_info = {
+ .driver_module = THIS_MODULE,
+ .of_xlate = pm8xxx_of_xlate,
+ .read_raw = pm8xxx_read_raw,
+};
+
+static int pm8xxx_xoadc_parse_channel(struct device *dev,
+ struct device_node *np,
+ const struct xoadc_channel *hw_channels,
+ struct iio_chan_spec *iio_chan,
+ struct pm8xxx_chan_info *ch)
+{
+ const char *name = np->name;
+ const struct xoadc_channel *hwchan;
+ u32 pre_scale_mux, amux_channel;
+ u32 rsv, dec;
+ int ret;
+ int chid;
+
+ ret = of_property_read_u32_index(np, "reg", 0, &pre_scale_mux);
+ if (ret) {
+ dev_err(dev, "invalid pre scale/mux number %s\n", name);
+ return ret;
+ }
+ ret = of_property_read_u32_index(np, "reg", 1, &amux_channel);
+ if (ret) {
+ dev_err(dev, "invalid amux channel number %s\n", name);
+ return ret;
+ }
+
+ /* Find the right channel setting */
+ chid = 0;
+ hwchan = &hw_channels[0];
+ while (hwchan && hwchan->datasheet_name) {
+ if (hwchan->pre_scale_mux == pre_scale_mux &&
+ hwchan->amux_channel == amux_channel)
+ break;
+ hwchan++;
+ chid++;
+ }
+ /* The sentinel does not have a name assigned */
+ if (!hwchan->datasheet_name) {
+ dev_err(dev, "could not locate channel %02x/%02x\n",
+ pre_scale_mux, amux_channel);
+ return -EINVAL;
+ }
+ ch->name = name;
+ ch->hwchan = hwchan;
+ /* Everyone seems to use absolute calibration except in special cases */
+ ch->calibration = VADC_CALIB_ABSOLUTE;
+ /* Everyone seems to use default ("type 2") decimation */
+ ch->decimation = VADC_DEF_DECIMATION;
+
+ if (!of_property_read_u32(np, "qcom,ratiometric", &rsv)) {
+ ch->calibration = VADC_CALIB_RATIOMETRIC;
+ if (rsv > XOADC_RSV_MAX) {
+ dev_err(dev, "%s too large RSV value %d\n", name, rsv);
+ return -EINVAL;
+ }
+ if (rsv == AMUX_RSV3) {
+ dev_err(dev, "%s invalid RSV value %d\n", name, rsv);
+ return -EINVAL;
+ }
+ }
+
+ /* Optional decimation, if omitted we use the default */
+ ret = of_property_read_u32(np, "qcom,decimation", &dec);
+ if (!ret) {
+ ret = qcom_vadc_decimation_from_dt(dec);
+ if (ret < 0) {
+ dev_err(dev, "%s invalid decimation %d\n",
+ name, dec);
+ return ret;
+ }
+ ch->decimation = ret;
+ }
+
+ iio_chan->channel = chid;
+ iio_chan->address = hwchan->amux_channel;
+ iio_chan->datasheet_name = hwchan->datasheet_name;
+ iio_chan->type = hwchan->type;
+ /* All channels are raw or processed */
+ iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_PROCESSED);
+ iio_chan->indexed = 1;
+
+ dev_dbg(dev, "channel [PRESCALE/MUX: %02x AMUX: %02x] \"%s\" "
+ "ref voltage: %d, decimation %d "
+ "prescale %d/%d, scale function %d\n",
+ hwchan->pre_scale_mux, hwchan->amux_channel, ch->name,
+ ch->amux_ip_rsv, ch->decimation, hwchan->prescale.num,
+ hwchan->prescale.den, hwchan->scale_fn_type);
+
+ return 0;
+}
+
+static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc,
+ struct device_node *np)
+{
+ struct device_node *child;
+ struct pm8xxx_chan_info *ch;
+ int ret;
+ int i;
+
+ adc->nchans = of_get_available_child_count(np);
+ if (!adc->nchans) {
+ dev_err(adc->dev, "no channel children\n");
+ return -ENODEV;
+ }
+ dev_dbg(adc->dev, "found %d ADC channels\n", adc->nchans);
+
+ adc->iio_chans = devm_kcalloc(adc->dev, adc->nchans,
+ sizeof(*adc->iio_chans), GFP_KERNEL);
+ if (!adc->iio_chans)
+ return -ENOMEM;
+
+ adc->chans = devm_kcalloc(adc->dev, adc->nchans,
+ sizeof(*adc->chans), GFP_KERNEL);
+ if (!adc->chans)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_available_child_of_node(np, child) {
+ ch = &adc->chans[i];
+ ret = pm8xxx_xoadc_parse_channel(adc->dev, child,
+ adc->variant->channels,
+ &adc->iio_chans[i],
+ ch);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+ i++;
+ }
+
+ /* Check for required channels */
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_125V);
+ if (!ch) {
+ dev_err(adc->dev, "missing 1.25V reference channel\n");
+ return -ENODEV;
+ }
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_INTERNAL);
+ if (!ch) {
+ dev_err(adc->dev, "missing 0.625V reference channel\n");
+ return -ENODEV;
+ }
+ ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_MUXOFF);
+ if (!ch) {
+ dev_err(adc->dev, "missing MUXOFF reference channel\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int pm8xxx_xoadc_probe(struct platform_device *pdev)
+{
+ const struct xoadc_variant *variant;
+ struct pm8xxx_xoadc *adc;
+ struct iio_dev *indio_dev;
+ struct device_node *np = pdev->dev.of_node;
+ struct regmap *map;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ variant = of_device_get_match_data(dev);
+ if (!variant)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, indio_dev);
+
+ adc = iio_priv(indio_dev);
+ adc->dev = dev;
+ adc->variant = variant;
+ init_completion(&adc->complete);
+ mutex_init(&adc->lock);
+
+ ret = pm8xxx_xoadc_parse_channels(adc, np);
+ if (ret)
+ return ret;
+
+ map = dev_get_regmap(dev->parent, NULL);
+ if (!map) {
+ dev_err(dev, "parent regmap unavailable.\n");
+ return -ENXIO;
+ }
+ adc->map = map;
+
+ /* Bring up regulator */
+ adc->vref = devm_regulator_get(dev, "xoadc-ref");
+ if (IS_ERR(adc->vref)) {
+ dev_err(dev, "failed to get XOADC VREF regulator\n");
+ return PTR_ERR(adc->vref);
+ }
+ ret = regulator_enable(adc->vref);
+ if (ret) {
+ dev_err(dev, "failed to enable XOADC VREF regulator\n");
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
+ pm8xxx_eoc_irq, NULL, 0, variant->name, indio_dev);
+ if (ret) {
+ dev_err(dev, "unable to request IRQ\n");
+ goto out_disable_vref;
+ }
+
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = np;
+ indio_dev->name = variant->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &pm8xxx_xoadc_info;
+ indio_dev->channels = adc->iio_chans;
+ indio_dev->num_channels = adc->nchans;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto out_disable_vref;
+
+ ret = pm8xxx_calibrate_device(adc);
+ if (ret)
+ goto out_unreg_device;
+
+ dev_info(dev, "%s XOADC driver enabled\n", variant->name);
+
+ return 0;
+
+out_unreg_device:
+ iio_device_unregister(indio_dev);
+out_disable_vref:
+ regulator_disable(adc->vref);
+
+ return ret;
+}
+
+static int pm8xxx_xoadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct pm8xxx_xoadc *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ regulator_disable(adc->vref);
+
+ return 0;
+}
+
+static const struct xoadc_variant pm8018_variant = {
+ .name = "PM8018-XOADC",
+ .channels = pm8018_xoadc_channels,
+};
+
+static const struct xoadc_variant pm8038_variant = {
+ .name = "PM8038-XOADC",
+ .channels = pm8038_xoadc_channels,
+};
+
+static const struct xoadc_variant pm8058_variant = {
+ .name = "PM8058-XOADC",
+ .channels = pm8058_xoadc_channels,
+ .broken_ratiometric = true,
+ .prescaling = true,
+};
+
+static const struct xoadc_variant pm8921_variant = {
+ .name = "PM8921-XOADC",
+ .channels = pm8921_xoadc_channels,
+ .second_level_mux = true,
+};
+
+static const struct of_device_id pm8xxx_xoadc_id_table[] = {
+ {
+ .compatible = "qcom,pm8018-adc",
+ .data = &pm8018_variant,
+ },
+ {
+ .compatible = "qcom,pm8038-adc",
+ .data = &pm8038_variant,
+ },
+ {
+ .compatible = "qcom,pm8058-adc",
+ .data = &pm8058_variant,
+ },
+ {
+ .compatible = "qcom,pm8921-adc",
+ .data = &pm8921_variant,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_xoadc_id_table);
+
+static struct platform_driver pm8xxx_xoadc_driver = {
+ .driver = {
+ .name = "pm8xxx-adc",
+ .of_match_table = pm8xxx_xoadc_id_table,
+ },
+ .probe = pm8xxx_xoadc_probe,
+ .remove = pm8xxx_xoadc_remove,
+};
+module_platform_driver(pm8xxx_xoadc_driver);
+
+MODULE_DESCRIPTION("PM8xxx XOADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:pm8xxx-xoadc");
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
index 0a19761d656c..9e600bfd1765 100644
--- a/drivers/iio/adc/qcom-spmi-vadc.c
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -28,6 +28,8 @@
#include <dt-bindings/iio/qcom,spmi-vadc.h>
+#include "qcom-vadc-common.h"
+
/* VADC register and bit definitions */
#define VADC_REVISION2 0x1
#define VADC_REVISION2_SUPPORTED_VADC 1
@@ -75,84 +77,10 @@
#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 1800
-
-#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 PMI_CHG_SCALE_1 -138890
-#define PMI_CHG_SCALE_2 391750000000LL
-
#define VADC_CHAN_MIN VADC_USBIN
#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
/**
- * struct vadc_map_pt - Map the graph representation for ADC channel
- * @x: Represent the ADC digitized code.
- * @y: Represent the physical data which can be temperature, voltage,
- * resistance.
- */
-struct vadc_map_pt {
- s32 x;
- s32 y;
-};
-
-/*
- * 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.
@@ -162,9 +90,8 @@ struct vadc_prescale_ratio {
* start of conversion.
* @avg_samples: ability to provide single result from the ADC
* that is an average of multiple measurements.
- * @scale_fn: Represents the scaling function to convert voltage
+ * @scale_fn_type: Represents the scaling function to convert voltage
* physical units desired by the client for the channel.
- * Referenced from enum vadc_scale_fn_type.
*/
struct vadc_channel_prop {
unsigned int channel;
@@ -173,7 +100,7 @@ struct vadc_channel_prop {
unsigned int prescale;
unsigned int hw_settle_time;
unsigned int avg_samples;
- unsigned int scale_fn;
+ enum vadc_scale_fn_type scale_fn_type;
};
/**
@@ -204,35 +131,6 @@ struct vadc_priv {
struct mutex lock;
};
-/**
- * struct vadc_scale_fn - Scaling function prototype
- * @scale: Function pointer to one of the scaling functions
- * which takes the adc properties, channel properties,
- * and returns the physical result.
- */
-struct vadc_scale_fn {
- int (*scale)(struct vadc_priv *, const struct vadc_channel_prop *,
- u16, int *);
-};
-
-/**
- * enum vadc_scale_fn_type - Scaling function to convert ADC code to
- * physical scaled units for the channel.
- * SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
- * SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
- * Uses a mapping table with 100K pullup.
- * 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
- */
-enum vadc_scale_fn_type {
- SCALE_DEFAULT = 0,
- SCALE_THERM_100K_PULLUP,
- SCALE_PMIC_THERM,
- SCALE_XOTHERM,
- SCALE_PMI_CHG_TEMP,
-};
-
static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
{.num = 1, .den = 1},
{.num = 1, .den = 3},
@@ -244,44 +142,6 @@ static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
{.num = 1, .den = 10}
};
-/* Voltage to temperature */
-static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
- {1758, -40},
- {1742, -35},
- {1719, -30},
- {1691, -25},
- {1654, -20},
- {1608, -15},
- {1551, -10},
- {1483, -5},
- {1404, 0},
- {1315, 5},
- {1218, 10},
- {1114, 15},
- {1007, 20},
- {900, 25},
- {795, 30},
- {696, 35},
- {605, 40},
- {522, 45},
- {448, 50},
- {383, 55},
- {327, 60},
- {278, 65},
- {237, 70},
- {202, 75},
- {172, 80},
- {146, 85},
- {125, 90},
- {107, 95},
- {92, 100},
- {79, 105},
- {68, 110},
- {59, 115},
- {51, 120},
- {44, 125}
-};
-
static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
{
return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
@@ -553,159 +413,6 @@ err:
return ret;
}
-static int vadc_map_voltage_temp(const struct vadc_map_pt *pts,
- u32 tablesize, s32 input, s64 *output)
-{
- bool descending = 1;
- u32 i = 0;
-
- if (!pts)
- return -EINVAL;
-
- /* Check if table is descending or ascending */
- if (tablesize > 1) {
- if (pts[0].x < pts[1].x)
- descending = 0;
- }
-
- while (i < tablesize) {
- if ((descending) && (pts[i].x < input)) {
- /* table entry is less than measured*/
- /* value and table is descending, stop */
- break;
- } else if ((!descending) &&
- (pts[i].x > input)) {
- /* table entry is greater than measured*/
- /*value and table is ascending, stop */
- break;
- }
- i++;
- }
-
- if (i == 0) {
- *output = pts[0].y;
- } else if (i == tablesize) {
- *output = pts[tablesize - 1].y;
- } else {
- /* result is between search_index and search_index-1 */
- /* interpolate linearly */
- *output = (((s32)((pts[i].y - pts[i - 1].y) *
- (input - pts[i - 1].x)) /
- (pts[i].x - pts[i - 1].x)) +
- pts[i - 1].y);
- }
-
- return 0;
-}
-
-static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code,
- const struct vadc_channel_prop *prop,
- s64 *scale_voltage)
-{
- *scale_voltage = (adc_code -
- vadc->graph[prop->calibration].gnd);
- *scale_voltage *= vadc->graph[prop->calibration].dx;
- *scale_voltage = div64_s64(*scale_voltage,
- vadc->graph[prop->calibration].dy);
- if (prop->calibration == VADC_CALIB_ABSOLUTE)
- *scale_voltage +=
- vadc->graph[prop->calibration].dx;
-
- if (*scale_voltage < 0)
- *scale_voltage = 0;
-}
-
-static int vadc_scale_volt(struct vadc_priv *vadc,
- const struct vadc_channel_prop *prop, u16 adc_code,
- int *result_uv)
-{
- const struct vadc_prescale_ratio *prescale;
- s64 voltage = 0, result = 0;
-
- vadc_scale_calib(vadc, adc_code, prop, &voltage);
-
- prescale = &vadc_prescale_ratios[prop->prescale];
- voltage = voltage * prescale->den;
- result = div64_s64(voltage, prescale->num);
- *result_uv = result;
-
- return 0;
-}
-
-static int vadc_scale_therm(struct vadc_priv *vadc,
- const struct vadc_channel_prop *prop, u16 adc_code,
- int *result_mdec)
-{
- s64 voltage = 0, result = 0;
-
- vadc_scale_calib(vadc, adc_code, prop, &voltage);
-
- if (prop->calibration == VADC_CALIB_ABSOLUTE)
- voltage = div64_s64(voltage, 1000);
-
- vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
- ARRAY_SIZE(adcmap_100k_104ef_104fb),
- voltage, &result);
- result *= 1000;
- *result_mdec = result;
-
- return 0;
-}
-
-static int vadc_scale_die_temp(struct vadc_priv *vadc,
- const struct vadc_channel_prop *prop,
- u16 adc_code, int *result_mdec)
-{
- const struct vadc_prescale_ratio *prescale;
- s64 voltage = 0;
- u64 temp; /* Temporary variable for do_div */
-
- vadc_scale_calib(vadc, adc_code, prop, &voltage);
-
- if (voltage > 0) {
- prescale = &vadc_prescale_ratios[prop->prescale];
- temp = voltage * prescale->den;
- do_div(temp, prescale->num * 2);
- voltage = temp;
- } else {
- voltage = 0;
- }
-
- voltage -= KELVINMIL_CELSIUSMIL;
- *result_mdec = voltage;
-
- return 0;
-}
-
-static int vadc_scale_chg_temp(struct vadc_priv *vadc,
- const struct vadc_channel_prop *prop,
- u16 adc_code, int *result_mdec)
-{
- const struct vadc_prescale_ratio *prescale;
- s64 voltage = 0, result = 0;
-
- vadc_scale_calib(vadc, adc_code, prop, &voltage);
-
- prescale = &vadc_prescale_ratios[prop->prescale];
- voltage = voltage * prescale->den;
- voltage = div64_s64(voltage, prescale->num);
- voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
- voltage = (voltage + PMI_CHG_SCALE_2);
- result = div64_s64(voltage, 1000000);
- *result_mdec = result;
-
- return 0;
-}
-
-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;
@@ -742,14 +449,6 @@ static int vadc_avg_samples_from_dt(u32 value)
return __ffs64(value);
}
-static struct vadc_scale_fn scale_fn[] = {
- [SCALE_DEFAULT] = {vadc_scale_volt},
- [SCALE_THERM_100K_PULLUP] = {vadc_scale_therm},
- [SCALE_PMIC_THERM] = {vadc_scale_die_temp},
- [SCALE_XOTHERM] = {vadc_scale_therm},
- [SCALE_PMI_CHG_TEMP] = {vadc_scale_chg_temp},
-};
-
static int vadc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2,
long mask)
@@ -766,7 +465,13 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
if (ret)
break;
- scale_fn[prop->scale_fn].scale(vadc, prop, adc_code, val);
+ ret = qcom_vadc_scale(prop->scale_fn_type,
+ &vadc->graph[prop->calibration],
+ &vadc_prescale_ratios[prop->prescale],
+ (prop->calibration == VADC_CALIB_ABSOLUTE),
+ adc_code, val);
+ if (ret)
+ break;
return IIO_VAL_INT;
case IIO_CHAN_INFO_RAW:
@@ -809,7 +514,7 @@ struct vadc_channels {
unsigned int prescale_index;
enum iio_chan_type type;
long info_mask;
- unsigned int scale_fn;
+ enum vadc_scale_fn_type scale_fn_type;
};
#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \
@@ -818,7 +523,7 @@ struct vadc_channels {
.prescale_index = _pre, \
.type = _type, \
.info_mask = _mask, \
- .scale_fn = _scale \
+ .scale_fn_type = _scale \
}, \
#define VADC_NO_CHAN(_dname, _type, _mask, _pre) \
@@ -976,7 +681,7 @@ static int vadc_get_dt_channel_data(struct device *dev,
ret = of_property_read_u32(node, "qcom,decimation", &value);
if (!ret) {
- ret = vadc_decimation_from_dt(value);
+ ret = qcom_vadc_decimation_from_dt(value);
if (ret < 0) {
dev_err(dev, "%02x invalid decimation %d\n",
chan, value);
@@ -1068,7 +773,7 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
return ret;
}
- prop.scale_fn = vadc_chans[prop.channel].scale_fn;
+ prop.scale_fn_type = vadc_chans[prop.channel].scale_fn_type;
vadc->chan_props[index] = prop;
vadc_chan = &vadc_chans[prop.channel];
diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
new file mode 100644
index 000000000000..102fc51b10aa
--- /dev/null
+++ b/drivers/iio/adc/qcom-vadc-common.c
@@ -0,0 +1,230 @@
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/math64.h>
+#include <linux/log2.h>
+#include <linux/err.h>
+
+#include "qcom-vadc-common.h"
+
+/* Voltage to temperature */
+static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
+ {1758, -40},
+ {1742, -35},
+ {1719, -30},
+ {1691, -25},
+ {1654, -20},
+ {1608, -15},
+ {1551, -10},
+ {1483, -5},
+ {1404, 0},
+ {1315, 5},
+ {1218, 10},
+ {1114, 15},
+ {1007, 20},
+ {900, 25},
+ {795, 30},
+ {696, 35},
+ {605, 40},
+ {522, 45},
+ {448, 50},
+ {383, 55},
+ {327, 60},
+ {278, 65},
+ {237, 70},
+ {202, 75},
+ {172, 80},
+ {146, 85},
+ {125, 90},
+ {107, 95},
+ {92, 100},
+ {79, 105},
+ {68, 110},
+ {59, 115},
+ {51, 120},
+ {44, 125}
+};
+
+static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
+ u32 tablesize, s32 input, s64 *output)
+{
+ bool descending = 1;
+ u32 i = 0;
+
+ if (!pts)
+ return -EINVAL;
+
+ /* Check if table is descending or ascending */
+ if (tablesize > 1) {
+ if (pts[0].x < pts[1].x)
+ descending = 0;
+ }
+
+ while (i < tablesize) {
+ if ((descending) && (pts[i].x < input)) {
+ /* table entry is less than measured*/
+ /* value and table is descending, stop */
+ break;
+ } else if ((!descending) &&
+ (pts[i].x > input)) {
+ /* table entry is greater than measured*/
+ /*value and table is ascending, stop */
+ break;
+ }
+ i++;
+ }
+
+ if (i == 0) {
+ *output = pts[0].y;
+ } else if (i == tablesize) {
+ *output = pts[tablesize - 1].y;
+ } else {
+ /* result is between search_index and search_index-1 */
+ /* interpolate linearly */
+ *output = (((s32)((pts[i].y - pts[i - 1].y) *
+ (input - pts[i - 1].x)) /
+ (pts[i].x - pts[i - 1].x)) +
+ pts[i - 1].y);
+ }
+
+ return 0;
+}
+
+static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
+ u16 adc_code,
+ bool absolute,
+ s64 *scale_voltage)
+{
+ *scale_voltage = (adc_code - calib_graph->gnd);
+ *scale_voltage *= calib_graph->dx;
+ *scale_voltage = div64_s64(*scale_voltage, calib_graph->dy);
+ if (absolute)
+ *scale_voltage += calib_graph->dx;
+
+ if (*scale_voltage < 0)
+ *scale_voltage = 0;
+}
+
+static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
+ const struct vadc_prescale_ratio *prescale,
+ bool absolute, u16 adc_code,
+ int *result_uv)
+{
+ s64 voltage = 0, result = 0;
+
+ qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+ voltage = voltage * prescale->den;
+ result = div64_s64(voltage, prescale->num);
+ *result_uv = result;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
+ const struct vadc_prescale_ratio *prescale,
+ bool absolute, u16 adc_code,
+ int *result_mdec)
+{
+ s64 voltage = 0, result = 0;
+ int ret;
+
+ qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+ if (absolute)
+ voltage = div64_s64(voltage, 1000);
+
+ ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb),
+ voltage, &result);
+ if (ret)
+ return ret;
+
+ result *= 1000;
+ *result_mdec = result;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
+ const struct vadc_prescale_ratio *prescale,
+ bool absolute,
+ u16 adc_code, int *result_mdec)
+{
+ s64 voltage = 0;
+ u64 temp; /* Temporary variable for do_div */
+
+ qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+ if (voltage > 0) {
+ temp = voltage * prescale->den;
+ do_div(temp, prescale->num * 2);
+ voltage = temp;
+ } else {
+ voltage = 0;
+ }
+
+ voltage -= KELVINMIL_CELSIUSMIL;
+ *result_mdec = voltage;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
+ const struct vadc_prescale_ratio *prescale,
+ bool absolute,
+ u16 adc_code, int *result_mdec)
+{
+ s64 voltage = 0, result = 0;
+
+ qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+ voltage = voltage * prescale->den;
+ voltage = div64_s64(voltage, prescale->num);
+ voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
+ voltage = (voltage + PMI_CHG_SCALE_2);
+ result = div64_s64(voltage, 1000000);
+ *result_mdec = result;
+
+ 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,
+ bool absolute,
+ u16 adc_code, int *result)
+{
+ switch (scaletype) {
+ case SCALE_DEFAULT:
+ return qcom_vadc_scale_volt(calib_graph, prescale,
+ absolute, adc_code,
+ result);
+ case SCALE_THERM_100K_PULLUP:
+ case SCALE_XOTHERM:
+ return qcom_vadc_scale_therm(calib_graph, prescale,
+ absolute, adc_code,
+ result);
+ case SCALE_PMIC_THERM:
+ return qcom_vadc_scale_die_temp(calib_graph, prescale,
+ absolute, adc_code,
+ result);
+ case SCALE_PMI_CHG_TEMP:
+ return qcom_vadc_scale_chg_temp(calib_graph, prescale,
+ absolute, adc_code,
+ result);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(qcom_vadc_scale);
+
+int qcom_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);
+}
+EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);
diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h
new file mode 100644
index 000000000000..63c872a70adc
--- /dev/null
+++ b/drivers/iio/adc/qcom-vadc-common.h
@@ -0,0 +1,108 @@
+/*
+ * Code shared between the different Qualcomm PMIC voltage ADCs
+ */
+
+#ifndef QCOM_VADC_COMMON_H
+#define QCOM_VADC_COMMON_H
+
+#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 1800
+
+#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 PMI_CHG_SCALE_1 -138890
+#define PMI_CHG_SCALE_2 391750000000LL
+
+/**
+ * struct vadc_map_pt - Map the graph representation for ADC channel
+ * @x: Represent the ADC digitized code.
+ * @y: Represent the physical data which can be temperature, voltage,
+ * resistance.
+ */
+struct vadc_map_pt {
+ s32 x;
+ s32 y;
+};
+
+/*
+ * 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;
+};
+
+/**
+ * enum vadc_scale_fn_type - Scaling function to convert ADC code to
+ * physical scaled units for the channel.
+ * SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
+ * SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
+ * Uses a mapping table with 100K pullup.
+ * 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
+ */
+enum vadc_scale_fn_type {
+ SCALE_DEFAULT = 0,
+ SCALE_THERM_100K_PULLUP,
+ SCALE_PMIC_THERM,
+ SCALE_XOTHERM,
+ SCALE_PMI_CHG_TEMP,
+};
+
+int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
+ const struct vadc_linear_graph *calib_graph,
+ const struct vadc_prescale_ratio *prescale,
+ bool absolute,
+ 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/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index 85d701291654..ae6d3324f518 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -109,7 +109,7 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
{
- struct rockchip_saradc *info = (struct rockchip_saradc *)dev_id;
+ struct rockchip_saradc *info = dev_id;
/* Read value */
info->last_val = readl_relaxed(info->regs + SARADC_DATA);
diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c
new file mode 100644
index 000000000000..5dd61f6a57b9
--- /dev/null
+++ b/drivers/iio/adc/spear_adc.c
@@ -0,0 +1,395 @@
+/*
+ * ST SPEAr ADC driver
+ *
+ * Copyright 2012 Stefan Roese <sr@denx.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* SPEAR registers definitions */
+#define SPEAR600_ADC_SCAN_RATE_LO(x) ((x) & 0xFFFF)
+#define SPEAR600_ADC_SCAN_RATE_HI(x) (((x) >> 0x10) & 0xFFFF)
+#define SPEAR_ADC_CLK_LOW(x) (((x) & 0xf) << 0)
+#define SPEAR_ADC_CLK_HIGH(x) (((x) & 0xf) << 4)
+
+/* Bit definitions for SPEAR_ADC_STATUS */
+#define SPEAR_ADC_STATUS_START_CONVERSION BIT(0)
+#define SPEAR_ADC_STATUS_CHANNEL_NUM(x) ((x) << 1)
+#define SPEAR_ADC_STATUS_ADC_ENABLE BIT(4)
+#define SPEAR_ADC_STATUS_AVG_SAMPLE(x) ((x) << 5)
+#define SPEAR_ADC_STATUS_VREF_INTERNAL BIT(9)
+
+#define SPEAR_ADC_DATA_MASK 0x03ff
+#define SPEAR_ADC_DATA_BITS 10
+
+#define SPEAR_ADC_MOD_NAME "spear-adc"
+
+#define SPEAR_ADC_CHANNEL_NUM 8
+
+#define SPEAR_ADC_CLK_MIN 2500000
+#define SPEAR_ADC_CLK_MAX 20000000
+
+struct adc_regs_spear3xx {
+ u32 status;
+ u32 average;
+ u32 scan_rate;
+ u32 clk; /* Not avail for 1340 & 1310 */
+ u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
+ u32 ch_data[SPEAR_ADC_CHANNEL_NUM];
+};
+
+struct chan_data {
+ u32 lsb;
+ u32 msb;
+};
+
+struct adc_regs_spear6xx {
+ u32 status;
+ u32 pad[2];
+ u32 clk;
+ u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM];
+ struct chan_data ch_data[SPEAR_ADC_CHANNEL_NUM];
+ u32 scan_rate_lo;
+ u32 scan_rate_hi;
+ struct chan_data average;
+};
+
+struct spear_adc_state {
+ struct device_node *np;
+ struct adc_regs_spear3xx __iomem *adc_base_spear3xx;
+ struct adc_regs_spear6xx __iomem *adc_base_spear6xx;
+ struct clk *clk;
+ struct completion completion;
+ u32 current_clk;
+ u32 sampling_freq;
+ u32 avg_samples;
+ u32 vref_external;
+ u32 value;
+};
+
+/*
+ * Functions to access some SPEAr ADC register. Abstracted into
+ * static inline functions, because of different register offsets
+ * on different SoC variants (SPEAr300 vs SPEAr600 etc).
+ */
+static void spear_adc_set_status(struct spear_adc_state *st, u32 val)
+{
+ __raw_writel(val, &st->adc_base_spear6xx->status);
+}
+
+static void spear_adc_set_clk(struct spear_adc_state *st, u32 val)
+{
+ u32 clk_high, clk_low, count;
+ u32 apb_clk = clk_get_rate(st->clk);
+
+ count = DIV_ROUND_UP(apb_clk, val);
+ clk_low = count / 2;
+ clk_high = count - clk_low;
+ st->current_clk = apb_clk / count;
+
+ __raw_writel(SPEAR_ADC_CLK_LOW(clk_low) | SPEAR_ADC_CLK_HIGH(clk_high),
+ &st->adc_base_spear6xx->clk);
+}
+
+static void spear_adc_set_ctrl(struct spear_adc_state *st, int n,
+ u32 val)
+{
+ __raw_writel(val, &st->adc_base_spear6xx->ch_ctrl[n]);
+}
+
+static u32 spear_adc_get_average(struct spear_adc_state *st)
+{
+ if (of_device_is_compatible(st->np, "st,spear600-adc")) {
+ return __raw_readl(&st->adc_base_spear6xx->average.msb) &
+ SPEAR_ADC_DATA_MASK;
+ } else {
+ return __raw_readl(&st->adc_base_spear3xx->average) &
+ SPEAR_ADC_DATA_MASK;
+ }
+}
+
+static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate)
+{
+ if (of_device_is_compatible(st->np, "st,spear600-adc")) {
+ __raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate),
+ &st->adc_base_spear6xx->scan_rate_lo);
+ __raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate),
+ &st->adc_base_spear6xx->scan_rate_hi);
+ } else {
+ __raw_writel(rate, &st->adc_base_spear3xx->scan_rate);
+ }
+}
+
+static int spear_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct spear_adc_state *st = iio_priv(indio_dev);
+ u32 status;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+
+ status = SPEAR_ADC_STATUS_CHANNEL_NUM(chan->channel) |
+ SPEAR_ADC_STATUS_AVG_SAMPLE(st->avg_samples) |
+ SPEAR_ADC_STATUS_START_CONVERSION |
+ SPEAR_ADC_STATUS_ADC_ENABLE;
+ if (st->vref_external == 0)
+ status |= SPEAR_ADC_STATUS_VREF_INTERNAL;
+
+ spear_adc_set_status(st, status);
+ wait_for_completion(&st->completion); /* set by ISR */
+ *val = st->value;
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = st->vref_external;
+ *val2 = SPEAR_ADC_DATA_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->current_clk;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int spear_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct spear_adc_state *st = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+
+ if ((val < SPEAR_ADC_CLK_MIN) ||
+ (val > SPEAR_ADC_CLK_MAX) ||
+ (val2 != 0)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ spear_adc_set_clk(st, val);
+
+out:
+ mutex_unlock(&indio_dev->mlock);
+ return ret;
+}
+
+#define SPEAR_ADC_CHAN(idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+ .channel = idx, \
+}
+
+static const struct iio_chan_spec spear_adc_iio_channels[] = {
+ SPEAR_ADC_CHAN(0),
+ SPEAR_ADC_CHAN(1),
+ SPEAR_ADC_CHAN(2),
+ SPEAR_ADC_CHAN(3),
+ SPEAR_ADC_CHAN(4),
+ SPEAR_ADC_CHAN(5),
+ SPEAR_ADC_CHAN(6),
+ SPEAR_ADC_CHAN(7),
+};
+
+static irqreturn_t spear_adc_isr(int irq, void *dev_id)
+{
+ struct spear_adc_state *st = dev_id;
+
+ /* Read value to clear IRQ */
+ st->value = spear_adc_get_average(st);
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+}
+
+static int spear_adc_configure(struct spear_adc_state *st)
+{
+ int i;
+
+ /* Reset ADC core */
+ spear_adc_set_status(st, 0);
+ __raw_writel(0, &st->adc_base_spear6xx->clk);
+ for (i = 0; i < 8; i++)
+ spear_adc_set_ctrl(st, i, 0);
+ spear_adc_set_scanrate(st, 0);
+
+ spear_adc_set_clk(st, st->sampling_freq);
+
+ return 0;
+}
+
+static const struct iio_info spear_adc_info = {
+ .read_raw = &spear_adc_read_raw,
+ .write_raw = &spear_adc_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int spear_adc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct spear_adc_state *st;
+ struct resource *res;
+ struct iio_dev *indio_dev = NULL;
+ int ret = -ENODEV;
+ int irq;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(struct spear_adc_state));
+ if (!indio_dev) {
+ dev_err(dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ st->np = np;
+
+ /*
+ * SPEAr600 has a different register layout than other SPEAr SoC's
+ * (e.g. SPEAr3xx). Let's provide two register base addresses
+ * to support multi-arch kernels.
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ st->adc_base_spear6xx = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(st->adc_base_spear6xx))
+ return PTR_ERR(st->adc_base_spear6xx);
+
+ st->adc_base_spear3xx =
+ (struct adc_regs_spear3xx __iomem *)st->adc_base_spear6xx;
+
+ st->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(st->clk)) {
+ dev_err(dev, "failed getting clock\n");
+ return PTR_ERR(st->clk);
+ }
+
+ ret = clk_prepare_enable(st->clk);
+ if (ret) {
+ dev_err(dev, "failed enabling clock\n");
+ return ret;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_err(dev, "failed getting interrupt resource\n");
+ ret = -EINVAL;
+ goto errout2;
+ }
+
+ ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME,
+ st);
+ if (ret < 0) {
+ dev_err(dev, "failed requesting interrupt\n");
+ goto errout2;
+ }
+
+ if (of_property_read_u32(np, "sampling-frequency",
+ &st->sampling_freq)) {
+ dev_err(dev, "sampling-frequency missing in DT\n");
+ ret = -EINVAL;
+ goto errout2;
+ }
+
+ /*
+ * Optional avg_samples defaults to 0, resulting in single data
+ * conversion
+ */
+ of_property_read_u32(np, "average-samples", &st->avg_samples);
+
+ /*
+ * Optional vref_external defaults to 0, resulting in internal vref
+ * selection
+ */
+ of_property_read_u32(np, "vref-external", &st->vref_external);
+
+ spear_adc_configure(st);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&st->completion);
+
+ indio_dev->name = SPEAR_ADC_MOD_NAME;
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &spear_adc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = spear_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(spear_adc_iio_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto errout2;
+
+ dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq);
+
+ return 0;
+
+errout2:
+ clk_disable_unprepare(st->clk);
+ return ret;
+}
+
+static int spear_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct spear_adc_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ clk_disable_unprepare(st->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id spear_adc_dt_ids[] = {
+ { .compatible = "st,spear600-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, spear_adc_dt_ids);
+#endif
+
+static struct platform_driver spear_adc_driver = {
+ .probe = spear_adc_probe,
+ .remove = spear_adc_remove,
+ .driver = {
+ .name = SPEAR_ADC_MOD_NAME,
+ .of_match_table = of_match_ptr(spear_adc_dt_ids),
+ },
+};
+
+module_platform_driver(spear_adc_driver);
+
+MODULE_AUTHOR("Stefan Roese <sr@denx.de>");
+MODULE_DESCRIPTION("SPEAr ADC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 9b49a6addc2a..c28e7ff80e11 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -60,6 +60,8 @@
#define STM32F4_EOC BIT(1)
/* STM32F4_ADC_CR1 - bit fields */
+#define STM32F4_RES_SHIFT 24
+#define STM32F4_RES_MASK GENMASK(25, 24)
#define STM32F4_SCAN BIT(8)
#define STM32F4_EOCIE BIT(5)
@@ -141,6 +143,7 @@ struct stm32_adc_regs {
* @lock: spinlock
* @bufi: data buffer index
* @num_conv: expected number of scan conversions
+ * @res: data resolution (e.g. RES bitfield value)
* @trigger_polarity: external trigger polarity (e.g. exten)
* @dma_chan: dma channel
* @rx_buf: dma rx buffer cpu address
@@ -157,6 +160,7 @@ struct stm32_adc {
spinlock_t lock; /* interrupt lock */
unsigned int bufi;
unsigned int num_conv;
+ u32 res;
u32 trigger_polarity;
struct dma_chan *dma_chan;
u8 *rx_buf;
@@ -196,6 +200,11 @@ static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
{ IIO_VOLTAGE, 15, "in15" },
};
+static const unsigned int stm32f4_adc_resolutions[] = {
+ /* sorted values so the index matches RES[1:0] in STM32F4_ADC_CR1 */
+ 12, 10, 8, 6,
+};
+
/**
* stm32f4_sq - describe regular sequence registers
* - L: sequence len (register & bit field)
@@ -302,6 +311,14 @@ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
}
+static void stm32_adc_set_res(struct stm32_adc *adc)
+{
+ u32 val = stm32_adc_readl(adc, STM32F4_ADC_CR1);
+
+ val = (val & ~STM32F4_RES_MASK) | (adc->res << STM32F4_RES_SHIFT);
+ stm32_adc_writel(adc, STM32F4_ADC_CR1, val);
+}
+
/**
* stm32_adc_start_conv() - Start conversions for regular channels.
* @adc: stm32 adc instance
@@ -870,11 +887,37 @@ static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = {
{},
};
+static int stm32_adc_of_get_resolution(struct iio_dev *indio_dev)
+{
+ struct device_node *node = indio_dev->dev.of_node;
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ unsigned int i;
+ u32 res;
+
+ if (of_property_read_u32(node, "assigned-resolution-bits", &res))
+ res = stm32f4_adc_resolutions[0];
+
+ for (i = 0; i < ARRAY_SIZE(stm32f4_adc_resolutions); i++)
+ if (res == stm32f4_adc_resolutions[i])
+ break;
+ if (i >= ARRAY_SIZE(stm32f4_adc_resolutions)) {
+ dev_err(&indio_dev->dev, "Bad resolution: %u bits\n", res);
+ return -EINVAL;
+ }
+
+ dev_dbg(&indio_dev->dev, "Using %u bits resolution\n", res);
+ adc->res = i;
+
+ return 0;
+}
+
static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
struct iio_chan_spec *chan,
const struct stm32_adc_chan_spec *channel,
int scan_index)
{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+
chan->type = channel->type;
chan->channel = channel->channel;
chan->datasheet_name = channel->name;
@@ -883,7 +926,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
chan->scan_type.sign = 'u';
- chan->scan_type.realbits = 12;
+ chan->scan_type.realbits = stm32f4_adc_resolutions[adc->res];
chan->scan_type.storagebits = 16;
chan->ext_info = stm32_adc_ext_info;
}
@@ -1022,6 +1065,11 @@ static int stm32_adc_probe(struct platform_device *pdev)
return ret;
}
+ ret = stm32_adc_of_get_resolution(indio_dev);
+ if (ret < 0)
+ goto err_clk_disable;
+ stm32_adc_set_res(adc);
+
ret = stm32_adc_chan_of_init(indio_dev);
if (ret < 0)
goto err_clk_disable;
diff --git a/drivers/iio/adc/stx104.c b/drivers/iio/adc/stx104.c
index be2de48844bc..2da741d27540 100644
--- a/drivers/iio/adc/stx104.c
+++ b/drivers/iio/adc/stx104.c
@@ -49,7 +49,7 @@
static unsigned int base[max_num_isa_dev(STX104_EXTENT)];
static unsigned int num_stx104;
-module_param_array(base, uint, &num_stx104, 0);
+module_param_hw_array(base, uint, ioport, &num_stx104, 0);
MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
/**
@@ -318,6 +318,7 @@ static int stx104_probe(struct device *dev, unsigned int id)
}
indio_dev->name = dev_name(dev);
+ indio_dev->dev.parent = dev;
priv = iio_priv(indio_dev);
priv->base = base[id];
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
new file mode 100644
index 000000000000..b23527309088
--- /dev/null
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -0,0 +1,719 @@
+/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC
+ *
+ * Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ *
+ * The Allwinner SoCs all have an ADC that can also act as a touchscreen
+ * controller and a thermal sensor.
+ * The thermal sensor works only when the ADC acts as a touchscreen controller
+ * and is configured to throw an interrupt every fixed periods of time (let say
+ * every X seconds).
+ * One would be tempted to disable the IP on the hardware side rather than
+ * disabling interrupts to save some power but that resets the internal clock of
+ * the IP, resulting in having to wait X seconds every time we want to read the
+ * value of the thermal sensor.
+ * This is also the reason of using autosuspend in pm_runtime. If there was no
+ * autosuspend, the thermal sensor would need X seconds after every
+ * pm_runtime_get_sync to get a value from the ADC. The autosuspend allows the
+ * thermal sensor to be requested again in a certain time span before it gets
+ * shutdown for not being used.
+ */
+
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/thermal.h>
+#include <linux/delay.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/machine.h>
+#include <linux/mfd/sun4i-gpadc.h>
+
+static unsigned int sun4i_gpadc_chan_select(unsigned int chan)
+{
+ return SUN4I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
+}
+
+static unsigned int sun6i_gpadc_chan_select(unsigned int chan)
+{
+ return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
+}
+
+struct gpadc_data {
+ int temp_offset;
+ int temp_scale;
+ unsigned int tp_mode_en;
+ unsigned int tp_adc_select;
+ unsigned int (*adc_chan_select)(unsigned int chan);
+ unsigned int adc_chan_mask;
+};
+
+static const struct gpadc_data sun4i_gpadc_data = {
+ .temp_offset = -1932,
+ .temp_scale = 133,
+ .tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
+ .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
+ .adc_chan_select = &sun4i_gpadc_chan_select,
+ .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
+};
+
+static const struct gpadc_data sun5i_gpadc_data = {
+ .temp_offset = -1447,
+ .temp_scale = 100,
+ .tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
+ .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
+ .adc_chan_select = &sun4i_gpadc_chan_select,
+ .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
+};
+
+static const struct gpadc_data sun6i_gpadc_data = {
+ .temp_offset = -1623,
+ .temp_scale = 167,
+ .tp_mode_en = SUN6I_GPADC_CTRL1_TP_MODE_EN,
+ .tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
+ .adc_chan_select = &sun6i_gpadc_chan_select,
+ .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
+};
+
+static const struct gpadc_data sun8i_a33_gpadc_data = {
+ .temp_offset = -1662,
+ .temp_scale = 162,
+ .tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN,
+};
+
+struct sun4i_gpadc_iio {
+ struct iio_dev *indio_dev;
+ struct completion completion;
+ int temp_data;
+ u32 adc_data;
+ struct regmap *regmap;
+ unsigned int fifo_data_irq;
+ atomic_t ignore_fifo_data_irq;
+ unsigned int temp_data_irq;
+ atomic_t ignore_temp_data_irq;
+ const struct gpadc_data *data;
+ bool no_irq;
+ /* prevents concurrent reads of temperature and ADC */
+ struct mutex mutex;
+};
+
+#define SUN4I_GPADC_ADC_CHANNEL(_channel, _name) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = _channel, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = _name, \
+}
+
+static struct iio_map sun4i_gpadc_hwmon_maps[] = {
+ {
+ .adc_channel_label = "temp_adc",
+ .consumer_dev_name = "iio_hwmon.0",
+ },
+ { /* sentinel */ },
+};
+
+static const struct iio_chan_spec sun4i_gpadc_channels[] = {
+ SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"),
+ SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"),
+ SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"),
+ SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .datasheet_name = "temp_adc",
+ },
+};
+
+static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = {
+ SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"),
+ SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"),
+ SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"),
+ SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
+};
+
+static const struct iio_chan_spec sun8i_a33_gpadc_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .datasheet_name = "temp_adc",
+ },
+};
+
+static const struct regmap_config sun4i_gpadc_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+};
+
+static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel,
+ unsigned int irq)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+ int ret;
+ u32 reg;
+
+ pm_runtime_get_sync(indio_dev->dev.parent);
+
+ reinit_completion(&info->completion);
+
+ ret = regmap_write(info->regmap, SUN4I_GPADC_INT_FIFOC,
+ SUN4I_GPADC_INT_FIFOC_TP_FIFO_TRIG_LEVEL(1) |
+ SUN4I_GPADC_INT_FIFOC_TP_FIFO_FLUSH);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(info->regmap, SUN4I_GPADC_CTRL1, &reg);
+ if (ret)
+ return ret;
+
+ if (irq == info->fifo_data_irq) {
+ ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
+ info->data->tp_mode_en |
+ info->data->tp_adc_select |
+ info->data->adc_chan_select(channel));
+ /*
+ * When the IP changes channel, it needs a bit of time to get
+ * correct values.
+ */
+ if ((reg & info->data->adc_chan_mask) !=
+ info->data->adc_chan_select(channel))
+ mdelay(10);
+
+ } else {
+ /*
+ * The temperature sensor returns valid data only when the ADC
+ * operates in touchscreen mode.
+ */
+ ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
+ info->data->tp_mode_en);
+ }
+
+ if (ret)
+ return ret;
+
+ /*
+ * When the IP changes mode between ADC or touchscreen, it
+ * needs a bit of time to get correct values.
+ */
+ if ((reg & info->data->tp_adc_select) != info->data->tp_adc_select)
+ mdelay(100);
+
+ return 0;
+}
+
+static int sun4i_gpadc_read(struct iio_dev *indio_dev, int channel, int *val,
+ unsigned int irq)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&info->mutex);
+
+ ret = sun4i_prepare_for_irq(indio_dev, channel, irq);
+ if (ret)
+ goto err;
+
+ enable_irq(irq);
+
+ /*
+ * The temperature sensor throws an interruption periodically (currently
+ * set at periods of ~0.6s in sun4i_gpadc_runtime_resume). A 1s delay
+ * makes sure an interruption occurs in normal conditions. If it doesn't
+ * occur, then there is a timeout.
+ */
+ if (!wait_for_completion_timeout(&info->completion,
+ msecs_to_jiffies(1000))) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ if (irq == info->fifo_data_irq)
+ *val = info->adc_data;
+ else
+ *val = info->temp_data;
+
+ ret = 0;
+ pm_runtime_mark_last_busy(indio_dev->dev.parent);
+
+err:
+ pm_runtime_put_autosuspend(indio_dev->dev.parent);
+ mutex_unlock(&info->mutex);
+
+ return ret;
+}
+
+static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
+ int *val)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+
+ return sun4i_gpadc_read(indio_dev, channel, val, info->fifo_data_irq);
+}
+
+static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+
+ if (info->no_irq) {
+ pm_runtime_get_sync(indio_dev->dev.parent);
+
+ regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
+
+ pm_runtime_mark_last_busy(indio_dev->dev.parent);
+ pm_runtime_put_autosuspend(indio_dev->dev.parent);
+
+ return 0;
+ }
+
+ return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
+}
+
+static int sun4i_gpadc_temp_offset(struct iio_dev *indio_dev, int *val)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+
+ *val = info->data->temp_offset;
+
+ return 0;
+}
+
+static int sun4i_gpadc_temp_scale(struct iio_dev *indio_dev, int *val)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+
+ *val = info->data->temp_scale;
+
+ return 0;
+}
+
+static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OFFSET:
+ ret = sun4i_gpadc_temp_offset(indio_dev, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type == IIO_VOLTAGE)
+ ret = sun4i_gpadc_adc_read(indio_dev, chan->channel,
+ val);
+ else
+ ret = sun4i_gpadc_temp_read(indio_dev, val);
+
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_VOLTAGE) {
+ /* 3000mV / 4096 * raw */
+ *val = 0;
+ *val2 = 732421875;
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+
+ ret = sun4i_gpadc_temp_scale(indio_dev, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info sun4i_gpadc_iio_info = {
+ .read_raw = sun4i_gpadc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static irqreturn_t sun4i_gpadc_temp_data_irq_handler(int irq, void *dev_id)
+{
+ struct sun4i_gpadc_iio *info = dev_id;
+
+ if (atomic_read(&info->ignore_temp_data_irq))
+ goto out;
+
+ if (!regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, &info->temp_data))
+ complete(&info->completion);
+
+out:
+ disable_irq_nosync(info->temp_data_irq);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
+{
+ struct sun4i_gpadc_iio *info = dev_id;
+
+ if (atomic_read(&info->ignore_fifo_data_irq))
+ goto out;
+
+ if (!regmap_read(info->regmap, SUN4I_GPADC_DATA, &info->adc_data))
+ complete(&info->completion);
+
+out:
+ disable_irq_nosync(info->fifo_data_irq);
+ return IRQ_HANDLED;
+}
+
+static int sun4i_gpadc_runtime_suspend(struct device *dev)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
+
+ /* Disable the ADC on IP */
+ regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
+ /* Disable temperature sensor on IP */
+ regmap_write(info->regmap, SUN4I_GPADC_TPR, 0);
+
+ return 0;
+}
+
+static int sun4i_gpadc_runtime_resume(struct device *dev)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
+
+ /* clkin = 6MHz */
+ regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
+ SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
+ SUN4I_GPADC_CTRL0_FS_DIV(7) |
+ SUN4I_GPADC_CTRL0_T_ACQ(63));
+ regmap_write(info->regmap, SUN4I_GPADC_CTRL1, info->data->tp_mode_en);
+ regmap_write(info->regmap, SUN4I_GPADC_CTRL3,
+ SUN4I_GPADC_CTRL3_FILTER_EN |
+ SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
+ /* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s */
+ regmap_write(info->regmap, SUN4I_GPADC_TPR,
+ SUN4I_GPADC_TPR_TEMP_ENABLE |
+ SUN4I_GPADC_TPR_TEMP_PERIOD(800));
+
+ return 0;
+}
+
+static int sun4i_gpadc_get_temp(void *data, int *temp)
+{
+ struct sun4i_gpadc_iio *info = data;
+ int val, scale, offset;
+
+ if (sun4i_gpadc_temp_read(info->indio_dev, &val))
+ return -ETIMEDOUT;
+
+ sun4i_gpadc_temp_scale(info->indio_dev, &scale);
+ sun4i_gpadc_temp_offset(info->indio_dev, &offset);
+
+ *temp = (val + offset) * scale;
+
+ return 0;
+}
+
+static const struct thermal_zone_of_device_ops sun4i_ts_tz_ops = {
+ .get_temp = &sun4i_gpadc_get_temp,
+};
+
+static const struct dev_pm_ops sun4i_gpadc_pm_ops = {
+ .runtime_suspend = &sun4i_gpadc_runtime_suspend,
+ .runtime_resume = &sun4i_gpadc_runtime_resume,
+};
+
+static int sun4i_irq_init(struct platform_device *pdev, const char *name,
+ irq_handler_t handler, const char *devname,
+ unsigned int *irq, atomic_t *atomic)
+{
+ int ret;
+ struct sun4i_gpadc_dev *mfd_dev = dev_get_drvdata(pdev->dev.parent);
+ struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(&pdev->dev));
+
+ /*
+ * Once the interrupt is activated, the IP continuously performs
+ * conversions thus throws interrupts. The interrupt is activated right
+ * after being requested but we want to control when these interrupts
+ * occur thus we disable it right after being requested. However, an
+ * interrupt might occur between these two instructions and we have to
+ * make sure that does not happen, by using atomic flags. We set the
+ * flag before requesting the interrupt and unset it right after
+ * disabling the interrupt. When an interrupt occurs between these two
+ * instructions, reading the atomic flag will tell us to ignore the
+ * interrupt.
+ */
+ atomic_set(atomic, 1);
+
+ ret = platform_get_irq_byname(pdev, name);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "no %s interrupt registered\n", name);
+ return ret;
+ }
+
+ ret = regmap_irq_get_virq(mfd_dev->regmap_irqc, ret);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get virq for irq %s\n", name);
+ return ret;
+ }
+
+ *irq = ret;
+ ret = devm_request_any_context_irq(&pdev->dev, *irq, handler, 0,
+ devname, info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not request %s interrupt: %d\n",
+ name, ret);
+ return ret;
+ }
+
+ disable_irq(*irq);
+ atomic_set(atomic, 0);
+
+ return 0;
+}
+
+static const struct of_device_id sun4i_gpadc_of_id[] = {
+ {
+ .compatible = "allwinner,sun8i-a33-ths",
+ .data = &sun8i_a33_gpadc_data,
+ },
+ { /* sentinel */ }
+};
+
+static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
+ struct iio_dev *indio_dev)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+ const struct of_device_id *of_dev;
+ struct thermal_zone_device *tzd;
+ struct resource *mem;
+ void __iomem *base;
+ int ret;
+
+ of_dev = of_match_device(sun4i_gpadc_of_id, &pdev->dev);
+ if (!of_dev)
+ return -ENODEV;
+
+ info->no_irq = true;
+ info->data = (struct gpadc_data *)of_dev->data;
+ indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels);
+ indio_dev->channels = sun8i_a33_gpadc_channels;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &sun4i_gpadc_regmap_config);
+ if (IS_ERR(info->regmap)) {
+ ret = PTR_ERR(info->regmap);
+ dev_err(&pdev->dev, "failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ if (!IS_ENABLED(CONFIG_THERMAL_OF))
+ return 0;
+
+ tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, info,
+ &sun4i_ts_tz_ops);
+ if (IS_ERR(tzd))
+ dev_err(&pdev->dev, "could not register thermal sensor: %ld\n",
+ PTR_ERR(tzd));
+
+ return PTR_ERR_OR_ZERO(tzd);
+}
+
+static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
+ struct iio_dev *indio_dev)
+{
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+ struct sun4i_gpadc_dev *sun4i_gpadc_dev =
+ dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ info->no_irq = false;
+ info->regmap = sun4i_gpadc_dev->regmap;
+
+ indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels);
+ indio_dev->channels = sun4i_gpadc_channels;
+
+ info->data = (struct gpadc_data *)platform_get_device_id(pdev)->driver_data;
+
+ /*
+ * Since the controller needs to be in touchscreen mode for its thermal
+ * sensor to operate properly, and that switching between the two modes
+ * needs a delay, always registering in the thermal framework will
+ * significantly slow down the conversion rate of the ADCs.
+ *
+ * Therefore, instead of depending on THERMAL_OF in Kconfig, we only
+ * register the sensor if that option is enabled, eventually leaving
+ * that choice to the user.
+ */
+
+ if (IS_ENABLED(CONFIG_THERMAL_OF)) {
+ /*
+ * This driver is a child of an MFD which has a node in the DT
+ * but not its children, because of DT backward compatibility
+ * for A10, A13 and A31 SoCs. Therefore, the resulting devices
+ * of this driver do not have an of_node variable.
+ * However, its parent (the MFD driver) has an of_node variable
+ * and since devm_thermal_zone_of_sensor_register uses its first
+ * argument to match the phandle defined in the node of the
+ * thermal driver with the of_node of the device passed as first
+ * argument and the third argument to call ops from
+ * thermal_zone_of_device_ops, the solution is to use the parent
+ * device as first argument to match the phandle with its
+ * of_node, and the device from this driver as third argument to
+ * return the temperature.
+ */
+ struct thermal_zone_device *tzd;
+ tzd = devm_thermal_zone_of_sensor_register(pdev->dev.parent, 0,
+ info,
+ &sun4i_ts_tz_ops);
+ if (IS_ERR(tzd)) {
+ dev_err(&pdev->dev,
+ "could not register thermal sensor: %ld\n",
+ PTR_ERR(tzd));
+ return PTR_ERR(tzd);
+ }
+ } else {
+ indio_dev->num_channels =
+ ARRAY_SIZE(sun4i_gpadc_channels_no_temp);
+ indio_dev->channels = sun4i_gpadc_channels_no_temp;
+ }
+
+ if (IS_ENABLED(CONFIG_THERMAL_OF)) {
+ ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING",
+ sun4i_gpadc_temp_data_irq_handler,
+ "temp_data", &info->temp_data_irq,
+ &info->ignore_temp_data_irq);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
+ sun4i_gpadc_fifo_data_irq_handler, "fifo_data",
+ &info->fifo_data_irq, &info->ignore_fifo_data_irq);
+ if (ret < 0)
+ return ret;
+
+ if (IS_ENABLED(CONFIG_THERMAL_OF)) {
+ ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "failed to register iio map array\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int sun4i_gpadc_probe(struct platform_device *pdev)
+{
+ struct sun4i_gpadc_iio *info;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+ platform_set_drvdata(pdev, indio_dev);
+
+ mutex_init(&info->mutex);
+ info->indio_dev = indio_dev;
+ init_completion(&info->completion);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &sun4i_gpadc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (pdev->dev.of_node)
+ ret = sun4i_gpadc_probe_dt(pdev, indio_dev);
+ else
+ ret = sun4i_gpadc_probe_mfd(pdev, indio_dev);
+
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev,
+ SUN4I_GPADC_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_iio_device_register(&pdev->dev, indio_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "could not register the device\n");
+ goto err_map;
+ }
+
+ return 0;
+
+err_map:
+ if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
+ iio_map_array_unregister(indio_dev);
+
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static int sun4i_gpadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
+
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
+ iio_map_array_unregister(indio_dev);
+
+ return 0;
+}
+
+static const struct platform_device_id sun4i_gpadc_id[] = {
+ { "sun4i-a10-gpadc-iio", (kernel_ulong_t)&sun4i_gpadc_data },
+ { "sun5i-a13-gpadc-iio", (kernel_ulong_t)&sun5i_gpadc_data },
+ { "sun6i-a31-gpadc-iio", (kernel_ulong_t)&sun6i_gpadc_data },
+ { /* sentinel */ },
+};
+
+static struct platform_driver sun4i_gpadc_driver = {
+ .driver = {
+ .name = "sun4i-gpadc-iio",
+ .of_match_table = sun4i_gpadc_of_id,
+ .pm = &sun4i_gpadc_pm_ops,
+ },
+ .id_table = sun4i_gpadc_id,
+ .probe = sun4i_gpadc_probe,
+ .remove = sun4i_gpadc_remove,
+};
+
+module_platform_driver(sun4i_gpadc_driver);
+
+MODULE_DESCRIPTION("ADC driver for sunxi platforms");
+MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
index 422b314f5a3f..f76d979fb7e8 100644
--- a/drivers/iio/adc/ti-ads1015.c
+++ b/drivers/iio/adc/ti-ads1015.c
@@ -15,6 +15,7 @@
*/
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@@ -55,7 +56,7 @@
#define ADS1015_DEFAULT_DATA_RATE 4
#define ADS1015_DEFAULT_CHAN 0
-enum {
+enum chip_ids {
ADS1015,
ADS1115,
};
@@ -578,6 +579,7 @@ static int ads1015_probe(struct i2c_client *client,
struct iio_dev *indio_dev;
struct ads1015_data *data;
int ret;
+ enum chip_ids chip;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
@@ -593,7 +595,11 @@ static int ads1015_probe(struct i2c_client *client,
indio_dev->name = ADS1015_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
- switch (id->driver_data) {
+ if (client->dev.of_node)
+ chip = (enum chip_ids)of_device_get_match_data(&client->dev);
+ else
+ chip = id->driver_data;
+ switch (chip) {
case ADS1015:
indio_dev->channels = ads1015_channels;
indio_dev->num_channels = ARRAY_SIZE(ads1015_channels);
@@ -698,9 +704,23 @@ static const struct i2c_device_id ads1015_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ads1015_id);
+static const struct of_device_id ads1015_of_match[] = {
+ {
+ .compatible = "ti,ads1015",
+ .data = (void *)ADS1015
+ },
+ {
+ .compatible = "ti,ads1115",
+ .data = (void *)ADS1115
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ads1015_of_match);
+
static struct i2c_driver ads1015_driver = {
.driver = {
.name = ADS1015_DRV_NAME,
+ .of_match_table = ads1015_of_match,
.pm = &ads1015_pm_ops,
},
.probe = ads1015_probe,
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index 228a003adeed..01fc76f7d660 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -584,7 +584,7 @@ static int vf610_adc_read_data(struct vf610_adc *info)
static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
{
- struct iio_dev *indio_dev = (struct iio_dev *)dev_id;
+ struct iio_dev *indio_dev = dev_id;
struct vf610_adc *info = iio_priv(indio_dev);
int coco;