diff options
Diffstat (limited to 'drivers/iio/adc')
-rw-r--r-- | drivers/iio/adc/Kconfig | 20 | ||||
-rw-r--r-- | drivers/iio/adc/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/adc/ad7476.c | 20 | ||||
-rw-r--r-- | drivers/iio/adc/ad7768-1.c | 202 | ||||
-rw-r--r-- | drivers/iio/adc/exynos_adc.c | 19 | ||||
-rw-r--r-- | drivers/iio/adc/ingenic-adc.c | 364 | ||||
-rw-r--r-- | drivers/iio/adc/lpc32xx_adc.c | 15 | ||||
-rw-r--r-- | drivers/iio/adc/ti-ads124s08.c | 8 |
8 files changed, 623 insertions, 26 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 46ed6fa7e3f3..5debc67df70a 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -57,14 +57,17 @@ config AD7298 module will be called ad7298. config AD7476 - tristate "Analog Devices AD7476 and similar 1-channel ADCs driver" + tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD an TI" depends on SPI select IIO_BUFFER select IIO_TRIGGERED_BUFFER help - Say yes here to build support for Analog Devices AD7273, AD7274, AD7276, - AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, - AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC). + Say yes here to build support for the following SPI analog to + digital converters (ADCs): + Analog Devices: AD7273, AD7274, AD7276, AD7277, AD7278, AD7475, + AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, AD7495, AD7910, + AD7920. + Texas Instruments: ADS7866, ADS7867, ADS7868. To compile this driver as a module, choose M here: the module will be called ad7476. @@ -407,6 +410,15 @@ config INA2XX_ADC Say yes here to build support for TI INA2xx family of Power Monitors. This driver is mutually exclusive with the HWMON version. +config INGENIC_ADC + tristate "Ingenic JZ47xx SoCs ADC driver" + depends on MIPS || COMPILE_TEST + help + Say yes here to build support for the Ingenic JZ47xx SoCs ADC unit. + + This driver can also be built as a module. If so, the module will be + called ingenic_adc. + config IMX7D_ADC tristate "Freescale IMX7D ADC driver" depends on ARCH_MXC || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 79d6511f0af3..d50eb47da484 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_HI8435) += hi8435.o obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o +obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 0549686b9ef8..76747488044b 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -59,6 +59,9 @@ enum ad7476_supported_device_ids { ID_ADC081S, ID_ADC101S, ID_ADC121S, + ID_ADS7866, + ID_ADS7867, + ID_ADS7868, }; static irqreturn_t ad7476_trigger_handler(int irq, void *p) @@ -157,6 +160,8 @@ static int ad7476_read_raw(struct iio_dev *indio_dev, #define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \ BIT(IIO_CHAN_INFO_RAW)) #define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0) +#define ADS786X_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \ + BIT(IIO_CHAN_INFO_RAW)) static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { [ID_AD7091R] = { @@ -209,6 +214,18 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { .channel[0] = ADC081S_CHAN(12), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), }, + [ID_ADS7866] = { + .channel[0] = ADS786X_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_ADS7867] = { + .channel[0] = ADS786X_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_ADS7868] = { + .channel[0] = ADS786X_CHAN(8), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, }; static const struct iio_info ad7476_info = { @@ -314,6 +331,9 @@ static const struct spi_device_id ad7476_id[] = { {"adc081s", ID_ADC081S}, {"adc101s", ID_ADC101S}, {"adc121s", ID_ADC121S}, + {"ads7866", ID_ADS7866}, + {"ads7867", ID_ADS7867}, + {"ads7868", ID_ADS7868}, {} }; MODULE_DEVICE_TABLE(spi, ad7476_id); diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 78449e90b2f5..0d132708c429 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -9,6 +9,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> +#include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/regulator/consumer.h> @@ -59,6 +60,18 @@ #define AD7768_REG_DIG_DIAG_STATUS 0x30 #define AD7768_REG_MCLK_COUNTER 0x31 +/* AD7768_REG_POWER_CLOCK */ +#define AD7768_PWR_MCLK_DIV_MSK GENMASK(5, 4) +#define AD7768_PWR_MCLK_DIV(x) FIELD_PREP(AD7768_PWR_MCLK_DIV_MSK, x) +#define AD7768_PWR_PWRMODE_MSK GENMASK(1, 0) +#define AD7768_PWR_PWRMODE(x) FIELD_PREP(AD7768_PWR_PWRMODE_MSK, x) + +/* AD7768_REG_DIGITAL_FILTER */ +#define AD7768_DIG_FIL_FIL_MSK GENMASK(6, 4) +#define AD7768_DIG_FIL_FIL(x) FIELD_PREP(AD7768_DIG_FIL_FIL_MSK, x) +#define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0) +#define AD7768_DIG_FIL_DEC_RATE(x) FIELD_PREP(AD7768_DIG_FIL_DEC_MSK, x) + /* AD7768_REG_CONVERSION */ #define AD7768_CONV_MODE_MSK GENMASK(2, 0) #define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x) @@ -80,11 +93,51 @@ enum ad7768_pwrmode { AD7768_FAST_MODE = 3 }; +enum ad7768_mclk_div { + AD7768_MCLK_DIV_16, + AD7768_MCLK_DIV_8, + AD7768_MCLK_DIV_4, + AD7768_MCLK_DIV_2 +}; + +enum ad7768_dec_rate { + AD7768_DEC_RATE_32 = 0, + AD7768_DEC_RATE_64 = 1, + AD7768_DEC_RATE_128 = 2, + AD7768_DEC_RATE_256 = 3, + AD7768_DEC_RATE_512 = 4, + AD7768_DEC_RATE_1024 = 5, + AD7768_DEC_RATE_8 = 9, + AD7768_DEC_RATE_16 = 10 +}; + +struct ad7768_clk_configuration { + enum ad7768_mclk_div mclk_div; + enum ad7768_dec_rate dec_rate; + unsigned int clk_div; + enum ad7768_pwrmode pwrmode; +}; + +static const struct ad7768_clk_configuration ad7768_clk_config[] = { + { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE }, + { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE }, + { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE }, + { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE }, + { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_128, 256, AD7768_FAST_MODE }, + { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_128, 512, AD7768_MED_MODE }, + { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_256, 1024, AD7768_MED_MODE }, + { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_512, 2048, AD7768_MED_MODE }, + { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_1024, 4096, AD7768_MED_MODE }, + { AD7768_MCLK_DIV_8, AD7768_DEC_RATE_1024, 8192, AD7768_MED_MODE }, + { AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE }, +}; + static const struct iio_chan_spec ad7768_channels[] = { { .type = IIO_VOLTAGE, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), .indexed = 1, .channel = 0, .scan_index = 0, @@ -102,8 +155,12 @@ struct ad7768_state { struct spi_device *spi; struct regulator *vref; struct mutex lock; + struct clk *mclk; + unsigned int mclk_freq; + unsigned int samp_freq; struct completion completion; struct iio_trigger *trig; + struct gpio_desc *gpio_sync_in; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. @@ -210,6 +267,90 @@ err_unlock: return ret; } +static int ad7768_set_dig_fil(struct ad7768_state *st, + enum ad7768_dec_rate dec_rate) +{ + unsigned int mode; + int ret; + + if (dec_rate == AD7768_DEC_RATE_8 || dec_rate == AD7768_DEC_RATE_16) + mode = AD7768_DIG_FIL_FIL(dec_rate); + else + mode = AD7768_DIG_FIL_DEC_RATE(dec_rate); + + ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode); + if (ret < 0) + return ret; + + /* A sync-in pulse is required every time the filter dec rate changes */ + gpiod_set_value(st->gpio_sync_in, 1); + gpiod_set_value(st->gpio_sync_in, 0); + + return 0; +} + +static int ad7768_set_freq(struct ad7768_state *st, + unsigned int freq) +{ + unsigned int diff_new, diff_old, pwr_mode, i, idx; + int res, ret; + + diff_old = U32_MAX; + idx = 0; + + res = DIV_ROUND_CLOSEST(st->mclk_freq, freq); + + /* Find the closest match for the desired sampling frequency */ + for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) { + diff_new = abs(res - ad7768_clk_config[i].clk_div); + if (diff_new < diff_old) { + diff_old = diff_new; + idx = i; + } + } + + /* + * Set both the mclk_div and pwrmode with a single write to the + * POWER_CLOCK register + */ + pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) | + AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode); + ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, pwr_mode); + if (ret < 0) + return ret; + + ret = ad7768_set_dig_fil(st, ad7768_clk_config[idx].dec_rate); + if (ret < 0) + return ret; + + st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq, + ad7768_clk_config[idx].clk_div); + + return 0; +} + +static ssize_t ad7768_sampling_freq_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7768_state *st = iio_priv(indio_dev); + unsigned int freq; + int i, len = 0; + + for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) { + freq = DIV_ROUND_CLOSEST(st->mclk_freq, + ad7768_clk_config[i].clk_div); + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", freq); + } + + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ad7768_sampling_freq_avail); + static int ad7768_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) @@ -242,13 +383,43 @@ static int ad7768_read_raw(struct iio_dev *indio_dev, *val2 = chan->scan_type.realbits; return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->samp_freq; + + return IIO_VAL_INT; } return -EINVAL; } +static int ad7768_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + return ad7768_set_freq(st, val); + default: + return -EINVAL; + } +} + +static struct attribute *ad7768_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7768_group = { + .attrs = ad7768_attributes, +}; + static const struct iio_info ad7768_info = { + .attrs = &ad7768_group, .read_raw = &ad7768_read_raw, + .write_raw = &ad7768_write_raw, .debugfs_reg_access = &ad7768_reg_access, }; @@ -270,9 +441,13 @@ static int ad7768_setup(struct ad7768_state *st) if (ret) return ret; - /* Set power mode to fast */ - return ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, - AD7768_FAST_MODE); + st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_sync_in)) + return PTR_ERR(st->gpio_sync_in); + + /* Set the default sampling frequency to 32000 kSPS */ + return ad7768_set_freq(st, 32000); } static irqreturn_t ad7768_trigger_handler(int irq, void *p) @@ -356,6 +531,13 @@ static void ad7768_regulator_disable(void *data) regulator_disable(st->vref); } +static void ad7768_clk_disable(void *data) +{ + struct ad7768_state *st = data; + + clk_disable_unprepare(st->mclk); +} + static int ad7768_probe(struct spi_device *spi) { struct ad7768_state *st; @@ -383,6 +565,20 @@ static int ad7768_probe(struct spi_device *spi) if (ret) return ret; + st->mclk = devm_clk_get(&spi->dev, "mclk"); + if (IS_ERR(st->mclk)) + return PTR_ERR(st->mclk); + + ret = clk_prepare_enable(st->mclk); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad7768_clk_disable, st); + if (ret) + return ret; + + st->mclk_freq = clk_get_rate(st->mclk); + spi_set_drvdata(spi, indio_dev); mutex_init(&st->lock); diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index fa2d2b5767f3..1ca2c4d39f87 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -115,6 +115,7 @@ #define MAX_ADC_V2_CHANNELS 10 #define MAX_ADC_V1_CHANNELS 8 #define MAX_EXYNOS3250_ADC_CHANNELS 2 +#define MAX_EXYNOS4212_ADC_CHANNELS 4 #define MAX_S5PV210_ADC_CHANNELS 10 /* Bit definitions common for ADC_V1 and ADC_V2 */ @@ -271,6 +272,19 @@ static void exynos_adc_v1_start_conv(struct exynos_adc *info, writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); } +/* Exynos4212 and 4412 is like ADCv1 but with four channels only */ +static const struct exynos_adc_data exynos4212_adc_data = { + .num_channels = MAX_EXYNOS4212_ADC_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, + .clear_irq = exynos_adc_v1_clear_irq, + .start_conv = exynos_adc_v1_start_conv, +}; + static const struct exynos_adc_data exynos_adc_v1_data = { .num_channels = MAX_ADC_V1_CHANNELS, .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ @@ -493,6 +507,9 @@ static const struct of_device_id exynos_adc_match[] = { .compatible = "samsung,s5pv210-adc", .data = &exynos_adc_s5pv210_data, }, { + .compatible = "samsung,exynos4212-adc", + .data = &exynos4212_adc_data, + }, { .compatible = "samsung,exynos-adc-v1", .data = &exynos_adc_v1_data, }, { @@ -929,7 +946,7 @@ static int exynos_adc_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct exynos_adc *info = iio_priv(indio_dev); - if (IS_REACHABLE(CONFIG_INPUT)) { + if (IS_REACHABLE(CONFIG_INPUT) && info->input) { free_irq(info->tsirq, info); input_unregister_device(info->input); } diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c new file mode 100644 index 000000000000..6ee1673deb0d --- /dev/null +++ b/drivers/iio/adc/ingenic-adc.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ADC driver for the Ingenic JZ47xx SoCs + * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu> + * + * based on drivers/mfd/jz4740-adc.c + */ + +#include <dt-bindings/iio/adc/ingenic,adc.h> +#include <linux/clk.h> +#include <linux/iio/iio.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> + +#define JZ_ADC_REG_ENABLE 0x00 +#define JZ_ADC_REG_CFG 0x04 +#define JZ_ADC_REG_CTRL 0x08 +#define JZ_ADC_REG_STATUS 0x0c +#define JZ_ADC_REG_ADTCH 0x18 +#define JZ_ADC_REG_ADBDAT 0x1c +#define JZ_ADC_REG_ADSDAT 0x20 + +#define JZ_ADC_REG_CFG_BAT_MD BIT(4) + +#define JZ_ADC_AUX_VREF 3300 +#define JZ_ADC_AUX_VREF_BITS 12 +#define JZ_ADC_BATTERY_LOW_VREF 2500 +#define JZ_ADC_BATTERY_LOW_VREF_BITS 12 +#define JZ4725B_ADC_BATTERY_HIGH_VREF 7500 +#define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10 +#define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986) +#define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12 + +struct ingenic_adc_soc_data { + unsigned int battery_high_vref; + unsigned int battery_high_vref_bits; + const int *battery_raw_avail; + size_t battery_raw_avail_size; + const int *battery_scale_avail; + size_t battery_scale_avail_size; +}; + +struct ingenic_adc { + void __iomem *base; + struct clk *clk; + struct mutex lock; + const struct ingenic_adc_soc_data *soc_data; + bool low_vref_mode; +}; + +static void ingenic_adc_set_config(struct ingenic_adc *adc, + uint32_t mask, + uint32_t val) +{ + uint32_t cfg; + + clk_enable(adc->clk); + mutex_lock(&adc->lock); + + cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask; + cfg |= val; + writel(cfg, adc->base + JZ_ADC_REG_CFG); + + mutex_unlock(&adc->lock); + clk_disable(adc->clk); +} + +static void ingenic_adc_enable(struct ingenic_adc *adc, + int engine, + bool enabled) +{ + u8 val; + + mutex_lock(&adc->lock); + val = readb(adc->base + JZ_ADC_REG_ENABLE); + + if (enabled) + val |= BIT(engine); + else + val &= ~BIT(engine); + + writeb(val, adc->base + JZ_ADC_REG_ENABLE); + mutex_unlock(&adc->lock); +} + +static int ingenic_adc_capture(struct ingenic_adc *adc, + int engine) +{ + u8 val; + int ret; + + ingenic_adc_enable(adc, engine, true); + ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val, + !(val & BIT(engine)), 250, 1000); + if (ret) + ingenic_adc_enable(adc, engine, false); + + return ret; +} + +static int ingenic_adc_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long m) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + + switch (m) { + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case INGENIC_ADC_BATTERY: + if (val > JZ_ADC_BATTERY_LOW_VREF) { + ingenic_adc_set_config(adc, + JZ_ADC_REG_CFG_BAT_MD, + 0); + adc->low_vref_mode = false; + } else { + ingenic_adc_set_config(adc, + JZ_ADC_REG_CFG_BAT_MD, + JZ_ADC_REG_CFG_BAT_MD); + adc->low_vref_mode = true; + } + return 0; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const int jz4725b_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, +}; + +static const int jz4725b_adc_battery_scale_avail[] = { + JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, + JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, +}; + +static const int jz4740_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, +}; + +static const int jz4740_adc_battery_scale_avail[] = { + JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS, + JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, +}; + +static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = { + .battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF, + .battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, + .battery_raw_avail = jz4725b_adc_battery_raw_avail, + .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail), + .battery_scale_avail = jz4725b_adc_battery_scale_avail, + .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail), +}; + +static const struct ingenic_adc_soc_data jz4740_adc_soc_data = { + .battery_high_vref = JZ4740_ADC_BATTERY_HIGH_VREF, + .battery_high_vref_bits = JZ4740_ADC_BATTERY_HIGH_VREF_BITS, + .battery_raw_avail = jz4740_adc_battery_raw_avail, + .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail), + .battery_scale_avail = jz4740_adc_battery_scale_avail, + .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail), +}; + +static int ingenic_adc_read_avail(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + const int **vals, + int *type, + int *length, + long m) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + *type = IIO_VAL_INT; + *length = adc->soc_data->battery_raw_avail_size; + *vals = adc->soc_data->battery_raw_avail; + return IIO_AVAIL_RANGE; + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_FRACTIONAL_LOG2; + *length = adc->soc_data->battery_scale_avail_size; + *vals = adc->soc_data->battery_scale_avail; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + }; +} + +static int ingenic_adc_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + clk_enable(adc->clk); + ret = ingenic_adc_capture(adc, chan->channel); + if (ret) { + clk_disable(adc->clk); + return ret; + } + + switch (chan->channel) { + case INGENIC_ADC_AUX: + *val = readw(adc->base + JZ_ADC_REG_ADSDAT); + break; + case INGENIC_ADC_BATTERY: + *val = readw(adc->base + JZ_ADC_REG_ADBDAT); + break; + } + + clk_disable(adc->clk); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case INGENIC_ADC_AUX: + *val = JZ_ADC_AUX_VREF; + *val2 = JZ_ADC_AUX_VREF_BITS; + break; + case INGENIC_ADC_BATTERY: + if (adc->low_vref_mode) { + *val = JZ_ADC_BATTERY_LOW_VREF; + *val2 = JZ_ADC_BATTERY_LOW_VREF_BITS; + } else { + *val = adc->soc_data->battery_high_vref; + *val2 = adc->soc_data->battery_high_vref_bits; + } + break; + } + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static void ingenic_adc_clk_cleanup(void *data) +{ + clk_unprepare(data); +} + +static const struct iio_info ingenic_adc_info = { + .write_raw = ingenic_adc_write_raw, + .read_raw = ingenic_adc_read_raw, + .read_avail = ingenic_adc_read_avail, +}; + +static const struct iio_chan_spec ingenic_channels[] = { + { + .extend_name = "aux", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_AUX, + }, + { + .extend_name = "battery", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_BATTERY, + }, +}; + +static int ingenic_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *iio_dev; + struct ingenic_adc *adc; + struct resource *mem_base; + const struct ingenic_adc_soc_data *soc_data; + int ret; + + soc_data = device_get_match_data(dev); + if (!soc_data) + return -EINVAL; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!iio_dev) + return -ENOMEM; + + adc = iio_priv(iio_dev); + mutex_init(&adc->lock); + adc->soc_data = soc_data; + + mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + adc->base = devm_ioremap_resource(dev, mem_base); + if (IS_ERR(adc->base)) { + dev_err(dev, "Unable to ioremap mmio resource\n"); + return PTR_ERR(adc->base); + } + + adc->clk = devm_clk_get(dev, "adc"); + if (IS_ERR(adc->clk)) { + dev_err(dev, "Unable to get clock\n"); + return PTR_ERR(adc->clk); + } + + ret = clk_prepare_enable(adc->clk); + if (ret) { + dev_err(dev, "Failed to enable clock\n"); + return ret; + } + + /* Put hardware in a known passive state. */ + writeb(0x00, adc->base + JZ_ADC_REG_ENABLE); + writeb(0xff, adc->base + JZ_ADC_REG_CTRL); + clk_disable(adc->clk); + + ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk); + if (ret) { + dev_err(dev, "Unable to add action\n"); + return ret; + } + + iio_dev->dev.parent = dev; + iio_dev->name = "jz-adc"; + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->channels = ingenic_channels; + iio_dev->num_channels = ARRAY_SIZE(ingenic_channels); + iio_dev->info = &ingenic_adc_info; + + ret = devm_iio_device_register(dev, iio_dev); + if (ret) + dev_err(dev, "Unable to register IIO device\n"); + + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id ingenic_adc_of_match[] = { + { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, }, + { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, }, + { }, +}; +MODULE_DEVICE_TABLE(of, ingenic_adc_of_match); +#endif + +static struct platform_driver ingenic_adc_driver = { + .driver = { + .name = "ingenic-adc", + .of_match_table = of_match_ptr(ingenic_adc_of_match), + }, + .probe = ingenic_adc_probe, +}; +module_platform_driver(ingenic_adc_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c index 20b36690fa4f..e361c1532a75 100644 --- a/drivers/iio/adc/lpc32xx_adc.c +++ b/drivers/iio/adc/lpc32xx_adc.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * lpc32xx_adc.c - Support for ADC in LPC32XX * * 3-channel, 10-bit ADC * * Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index c2cf58908fc8..53f17e4f2f23 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -232,7 +232,7 @@ static int ads124s_read_raw(struct iio_dev *indio_dev, ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV); if (ret) { - dev_err(&priv->spi->dev, "Start converions failed\n"); + dev_err(&priv->spi->dev, "Start conversions failed\n"); goto out; } @@ -246,7 +246,7 @@ static int ads124s_read_raw(struct iio_dev *indio_dev, ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV); if (ret) { - dev_err(&priv->spi->dev, "Stop converions failed\n"); + dev_err(&priv->spi->dev, "Stop conversions failed\n"); goto out; } @@ -283,12 +283,12 @@ static irqreturn_t ads124s_trigger_handler(int irq, void *p) ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV); if (ret) - dev_err(&priv->spi->dev, "Start ADC converions failed\n"); + dev_err(&priv->spi->dev, "Start ADC conversions failed\n"); buffer[j] = ads124s_read(indio_dev, scan_index); ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV); if (ret) - dev_err(&priv->spi->dev, "Stop ADC converions failed\n"); + dev_err(&priv->spi->dev, "Stop ADC conversions failed\n"); j++; } |