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