diff options
Diffstat (limited to 'drivers/iio/adc')
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 = <c2497_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, ®val); + 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, ®val); - - 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, ®val); + 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, ®val); + 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, ®); + 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; |