Update Linux to v5.4.2
Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 4a75492..f0af3a4 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# ADC drivers
#
@@ -10,6 +11,17 @@
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
+config AD7124
+ tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver"
+ depends on SPI_MASTER
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7124-4 and AD7124-8
+ SPI analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7124.
+
config AD7266
tristate "Analog Devices AD7265/AD7266 ADC driver"
depends on SPI_MASTER
@@ -46,18 +58,48 @@
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.
+config AD7606
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config AD7606_IFACE_PARALLEL
+ tristate "Analog Devices AD7606 ADC driver with parallel interface support"
+ depends on HAS_IOMEM
+ select AD7606
+ help
+ Say yes here to build parallel interface support for Analog Devices:
+ ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7606_parallel.
+
+config AD7606_IFACE_SPI
+ tristate "Analog Devices AD7606 ADC driver with spi interface support"
+ depends on SPI
+ select AD7606
+ help
+ Say yes here to build spi interface support for Analog Devices:
+ ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7606_spi.
+
config AD7766
tristate "Analog Devices AD7766/AD7767 ADC driver"
depends on SPI_MASTER
@@ -70,6 +112,31 @@
To compile this driver as a module, choose M here: the module will be
called ad7766.
+config AD7768_1
+ tristate "Analog Devices AD7768-1 ADC driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD7768-1 SPI
+ simultaneously sampling sigma-delta analog to digital converter (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7768-1.
+
+config AD7780
+ tristate "Analog Devices AD7780 and similar ADCs driver"
+ depends on SPI
+ depends on GPIOLIB || COMPILE_TEST
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7170, AD7171,
+ AD7780 and AD7781 SPI analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7780.
+
config AD7791
tristate "Analog Devices AD7791 ADC driver"
depends on SPI
@@ -116,6 +183,16 @@
To compile this driver as a module, choose M here: the
module will be called ad7923.
+config AD7949
+ tristate "Analog Devices AD7949 and similar ADCs driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices
+ AD7949, AD7682, AD7689 8 Channel ADCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7949.
+
config AD799X
tristate "Analog Devices AD799x ADC driver"
depends on I2C
@@ -274,7 +351,7 @@
config EXYNOS_ADC
tristate "Exynos ADC driver support"
- depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
+ depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210 || (OF && COMPILE_TEST)
depends on HAS_IOMEM
help
Core support for the ADC block found in the Samsung EXYNOS series
@@ -326,7 +403,7 @@
This driver uses two GPIOs, one acts as the clock and controls the
channel selection and gain, the other one is used for the measurement
- data
+ data
Currently the raw value is read from the chip and delivered.
To get an actual weight one needs to subtract the
@@ -346,6 +423,15 @@
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
@@ -468,7 +554,7 @@
To compile this driver as a module, choose M here: the module will be
called max1363.
-config MAX9611
+config MAX9611
tristate "Maxim max9611/max9612 ADC driver"
depends on I2C
help
@@ -501,18 +587,28 @@
This driver can also be built as a module. If so, the module will be
called mcp3422.
+config MCP3911
+ tristate "Microchip Technology MCP3911 driver"
+ depends on SPI
+ help
+ Say yes here to build support for Microchip Technology's MCP3911
+ analog to digital converter.
+
+ This driver can also be built as a module. If so, the module will be
+ called mcp3911.
+
config MEDIATEK_MT6577_AUXADC
- tristate "MediaTek AUXADC driver"
- depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on HAS_IOMEM
- help
- Say yes here to enable support for MediaTek mt65xx AUXADC.
+ tristate "MediaTek AUXADC driver"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to enable support for MediaTek mt65xx AUXADC.
- The driver supports immediate mode operation to read from one of sixteen
- channels (external or internal).
+ The driver supports immediate mode operation to read from one of sixteen
+ channels (external or internal).
- This driver can also be built as a module. If so, the module will be
- called mt6577_auxadc.
+ This driver can also be built as a module. If so, the module will be
+ called mt6577_auxadc.
config MEN_Z188_ADC
tristate "MEN 16z188 ADC IP Core support"
@@ -545,6 +641,16 @@
To compile this driver as a module, choose M here: the
module will be called nau7802.
+config NPCM_ADC
+ tristate "Nuvoton NPCM ADC driver"
+ depends on ARCH_NPCM || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for Nuvoton NPCM ADC.
+
+ This driver can also be built as a module. If so, the module
+ will be called npcm_adc.
+
config PALMAS_GPADC
tristate "TI Palmas General Purpose ADC"
depends on MFD_PALMAS
@@ -596,6 +702,26 @@
To compile this driver as a module, choose M here: the module will
be called qcom-spmi-vadc.
+config QCOM_SPMI_ADC5
+ tristate "Qualcomm Technologies Inc. SPMI PMIC5 ADC"
+ depends on SPMI
+ select REGMAP_SPMI
+ select QCOM_VADC_COMMON
+ help
+ This is the IIO Voltage PMIC5 ADC driver for Qualcomm Technologies Inc.
+
+ The driver supports multiple channels read. The ADC is a 16-bit
+ sigma-delta ADC. The hardware supports calibrated results for
+ conversion requests and clients include reading voltage phone
+ power, on board system thermistors connected to the PMIC ADC,
+ PMIC die temperature, charger temperature, battery current, USB voltage
+ input, voltage signals connected to supported PMIC GPIO inputs. The
+ hardware supports internal pull-up for thermistors and can choose between
+ a 100k, 30k and 400k pull up using the ADC channels.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-spmi-adc5.
+
config RCAR_GYRO_ADC
tristate "Renesas R-Car GyroADC driver"
depends on ARCH_RCAR_GEN2 || COMPILE_TEST
@@ -696,7 +822,9 @@
depends on (ARCH_STM32 && OF) || COMPILE_TEST
select STM32_DFSDM_CORE
select REGMAP_MMIO
+ select IIO_BUFFER
select IIO_BUFFER_HW_CONSUMER
+ select IIO_TRIGGERED_BUFFER
help
Select this option to support ADCSigma delta modulator for
STMicroelectronics STM32 digital filter for sigma delta converter.
@@ -704,6 +832,13 @@
This driver can also be built as a module. If so, the module
will be called stm32-dfsdm-adc.
+config STMPE_ADC
+ tristate "STMicroelectronics STMPE ADC driver"
+ depends on OF && MFD_STMPE
+ help
+ Say yes here to build support for ST Microelectronics STMPE
+ built-in ADC block (stmpe811).
+
config STX104
tristate "Apex Embedded Systems STX104 driver"
depends on PC104 && X86
@@ -823,7 +958,7 @@
config TI_ADS1015
tristate "Texas Instruments ADS1015 ADC"
- depends on I2C && !SENSORS_ADS1015
+ depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
@@ -836,7 +971,7 @@
config TI_ADS7950
tristate "Texas Instruments ADS7950 ADC driver"
- depends on SPI
+ depends on SPI && GPIOLIB
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
@@ -847,6 +982,16 @@
To compile this driver as a module, choose M here: the
module will be called ti-ads7950.
+config TI_ADS8344
+ tristate "Texas Instruments ADS8344"
+ depends on SPI && OF
+ help
+ If you say yes here you get support for Texas Instruments ADS8344
+ ADC chips
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads8344.
+
config TI_ADS8688
tristate "Texas Instruments ADS8688"
depends on SPI && OF
@@ -857,6 +1002,16 @@
This driver can also be built as a module. If so, the module will be
called ti-ads8688.
+config TI_ADS124S08
+ tristate "Texas Instruments ADS124S08"
+ depends on SPI && OF
+ help
+ If you say yes here you get support for Texas Instruments ADS124S08
+ and ADS124S06 ADC chips
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads124s08.
+
config TI_AM335X_ADC
tristate "TI's AM335X ADC driver"
depends on MFD_TI_AM335X_TSCADC && HAS_DMA
@@ -930,7 +1085,6 @@
config XILINX_XADC
tristate "Xilinx XADC driver"
- depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
depends on HAS_IOMEM
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 03db7b5..ef9cc48 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -5,15 +5,22 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
+obj-$(CONFIG_AD7124) += ad7124.o
obj-$(CONFIG_AD7266) += ad7266.o
obj-$(CONFIG_AD7291) += ad7291.o
obj-$(CONFIG_AD7298) += ad7298.o
obj-$(CONFIG_AD7923) += ad7923.o
obj-$(CONFIG_AD7476) += ad7476.o
+obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o
+obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
+obj-$(CONFIG_AD7606) += ad7606.o
obj-$(CONFIG_AD7766) += ad7766.o
+obj-$(CONFIG_AD7768_1) += ad7768-1.o
+obj-$(CONFIG_AD7780) += ad7780.o
obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
+obj-$(CONFIG_AD7949) += ad7949.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
@@ -34,6 +41,7 @@
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
@@ -47,12 +55,15 @@
obj-$(CONFIG_MAX9611) += max9611.o
obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
+obj-$(CONFIG_MCP3911) += mcp3911.o
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
+obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
+obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
@@ -67,6 +78,7 @@
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
+obj-$(CONFIG_STMPE_ADC) += stmpe-adc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o
@@ -76,7 +88,9 @@
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
+obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
+obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
new file mode 100644
index 0000000..edc6f1c
--- /dev/null
+++ b/drivers/iio/adc/ad7124.c
@@ -0,0 +1,697 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AD7124 SPI ADC driver
+ *
+ * Copyright 2018 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+#include <linux/iio/sysfs.h>
+
+/* AD7124 registers */
+#define AD7124_COMMS 0x00
+#define AD7124_STATUS 0x00
+#define AD7124_ADC_CONTROL 0x01
+#define AD7124_DATA 0x02
+#define AD7124_IO_CONTROL_1 0x03
+#define AD7124_IO_CONTROL_2 0x04
+#define AD7124_ID 0x05
+#define AD7124_ERROR 0x06
+#define AD7124_ERROR_EN 0x07
+#define AD7124_MCLK_COUNT 0x08
+#define AD7124_CHANNEL(x) (0x09 + (x))
+#define AD7124_CONFIG(x) (0x19 + (x))
+#define AD7124_FILTER(x) (0x21 + (x))
+#define AD7124_OFFSET(x) (0x29 + (x))
+#define AD7124_GAIN(x) (0x31 + (x))
+
+/* AD7124_STATUS */
+#define AD7124_STATUS_POR_FLAG_MSK BIT(4)
+
+/* AD7124_ADC_CONTROL */
+#define AD7124_ADC_CTRL_PWR_MSK GENMASK(7, 6)
+#define AD7124_ADC_CTRL_PWR(x) FIELD_PREP(AD7124_ADC_CTRL_PWR_MSK, x)
+#define AD7124_ADC_CTRL_MODE_MSK GENMASK(5, 2)
+#define AD7124_ADC_CTRL_MODE(x) FIELD_PREP(AD7124_ADC_CTRL_MODE_MSK, x)
+
+/* AD7124_CHANNEL_X */
+#define AD7124_CHANNEL_EN_MSK BIT(15)
+#define AD7124_CHANNEL_EN(x) FIELD_PREP(AD7124_CHANNEL_EN_MSK, x)
+#define AD7124_CHANNEL_SETUP_MSK GENMASK(14, 12)
+#define AD7124_CHANNEL_SETUP(x) FIELD_PREP(AD7124_CHANNEL_SETUP_MSK, x)
+#define AD7124_CHANNEL_AINP_MSK GENMASK(9, 5)
+#define AD7124_CHANNEL_AINP(x) FIELD_PREP(AD7124_CHANNEL_AINP_MSK, x)
+#define AD7124_CHANNEL_AINM_MSK GENMASK(4, 0)
+#define AD7124_CHANNEL_AINM(x) FIELD_PREP(AD7124_CHANNEL_AINM_MSK, x)
+
+/* AD7124_CONFIG_X */
+#define AD7124_CONFIG_BIPOLAR_MSK BIT(11)
+#define AD7124_CONFIG_BIPOLAR(x) FIELD_PREP(AD7124_CONFIG_BIPOLAR_MSK, x)
+#define AD7124_CONFIG_REF_SEL_MSK GENMASK(4, 3)
+#define AD7124_CONFIG_REF_SEL(x) FIELD_PREP(AD7124_CONFIG_REF_SEL_MSK, x)
+#define AD7124_CONFIG_PGA_MSK GENMASK(2, 0)
+#define AD7124_CONFIG_PGA(x) FIELD_PREP(AD7124_CONFIG_PGA_MSK, x)
+#define AD7124_CONFIG_IN_BUFF_MSK GENMASK(7, 6)
+#define AD7124_CONFIG_IN_BUFF(x) FIELD_PREP(AD7124_CONFIG_IN_BUFF_MSK, x)
+
+/* AD7124_FILTER_X */
+#define AD7124_FILTER_FS_MSK GENMASK(10, 0)
+#define AD7124_FILTER_FS(x) FIELD_PREP(AD7124_FILTER_FS_MSK, x)
+
+enum ad7124_ids {
+ ID_AD7124_4,
+ ID_AD7124_8,
+};
+
+enum ad7124_ref_sel {
+ AD7124_REFIN1,
+ AD7124_REFIN2,
+ AD7124_INT_REF,
+ AD7124_AVDD_REF,
+};
+
+enum ad7124_power_mode {
+ AD7124_LOW_POWER,
+ AD7124_MID_POWER,
+ AD7124_FULL_POWER,
+};
+
+static const unsigned int ad7124_gain[8] = {
+ 1, 2, 4, 8, 16, 32, 64, 128
+};
+
+static const int ad7124_master_clk_freq_hz[3] = {
+ [AD7124_LOW_POWER] = 76800,
+ [AD7124_MID_POWER] = 153600,
+ [AD7124_FULL_POWER] = 614400,
+};
+
+static const char * const ad7124_ref_names[] = {
+ [AD7124_REFIN1] = "refin1",
+ [AD7124_REFIN2] = "refin2",
+ [AD7124_INT_REF] = "int",
+ [AD7124_AVDD_REF] = "avdd",
+};
+
+struct ad7124_chip_info {
+ unsigned int num_inputs;
+};
+
+struct ad7124_channel_config {
+ enum ad7124_ref_sel refsel;
+ bool bipolar;
+ bool buf_positive;
+ bool buf_negative;
+ unsigned int ain;
+ unsigned int vref_mv;
+ unsigned int pga_bits;
+ unsigned int odr;
+};
+
+struct ad7124_state {
+ const struct ad7124_chip_info *chip_info;
+ struct ad_sigma_delta sd;
+ struct ad7124_channel_config *channel_config;
+ struct regulator *vref[4];
+ struct clk *mclk;
+ unsigned int adc_control;
+ unsigned int num_channels;
+};
+
+static const struct iio_chan_spec ad7124_channel_template = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .differential = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .shift = 8,
+ .endianness = IIO_BE,
+ },
+};
+
+static struct ad7124_chip_info ad7124_chip_info_tbl[] = {
+ [ID_AD7124_4] = {
+ .num_inputs = 8,
+ },
+ [ID_AD7124_8] = {
+ .num_inputs = 16,
+ },
+};
+
+static int ad7124_find_closest_match(const int *array,
+ unsigned int size, int val)
+{
+ int i, idx;
+ unsigned int diff_new, diff_old;
+
+ diff_old = U32_MAX;
+ idx = 0;
+
+ for (i = 0; i < size; i++) {
+ diff_new = abs(val - array[i]);
+ if (diff_new < diff_old) {
+ diff_old = diff_new;
+ idx = i;
+ }
+ }
+
+ return idx;
+}
+
+static int ad7124_spi_write_mask(struct ad7124_state *st,
+ unsigned int addr,
+ unsigned long mask,
+ unsigned int val,
+ unsigned int bytes)
+{
+ unsigned int readval;
+ int ret;
+
+ ret = ad_sd_read_reg(&st->sd, addr, bytes, &readval);
+ if (ret < 0)
+ return ret;
+
+ readval &= ~mask;
+ readval |= val;
+
+ return ad_sd_write_reg(&st->sd, addr, bytes, readval);
+}
+
+static int ad7124_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
+
+ st->adc_control &= ~AD7124_ADC_CTRL_MODE_MSK;
+ st->adc_control |= AD7124_ADC_CTRL_MODE(mode);
+
+ return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
+}
+
+static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
+ unsigned int val;
+
+ val = st->channel_config[channel].ain | AD7124_CHANNEL_EN(1) |
+ AD7124_CHANNEL_SETUP(channel);
+
+ return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(channel), 2, val);
+}
+
+static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
+ .set_channel = ad7124_set_channel,
+ .set_mode = ad7124_set_mode,
+ .has_registers = true,
+ .addr_shift = 0,
+ .read_mask = BIT(6),
+ .data_reg = AD7124_DATA,
+};
+
+static int ad7124_set_channel_odr(struct ad7124_state *st,
+ unsigned int channel,
+ unsigned int odr)
+{
+ unsigned int fclk, odr_sel_bits;
+ int ret;
+
+ fclk = clk_get_rate(st->mclk);
+ /*
+ * FS[10:0] = fCLK / (fADC x 32) where:
+ * fADC is the output data rate
+ * fCLK is the master clock frequency
+ * FS[10:0] are the bits in the filter register
+ * FS[10:0] can have a value from 1 to 2047
+ */
+ odr_sel_bits = DIV_ROUND_CLOSEST(fclk, odr * 32);
+ if (odr_sel_bits < 1)
+ odr_sel_bits = 1;
+ else if (odr_sel_bits > 2047)
+ odr_sel_bits = 2047;
+
+ ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel),
+ AD7124_FILTER_FS_MSK,
+ AD7124_FILTER_FS(odr_sel_bits), 3);
+ if (ret < 0)
+ return ret;
+ /* fADC = fCLK / (FS[10:0] x 32) */
+ st->channel_config[channel].odr =
+ DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32);
+
+ return 0;
+}
+
+static int ad7124_set_channel_gain(struct ad7124_state *st,
+ unsigned int channel,
+ unsigned int gain)
+{
+ unsigned int res;
+ int ret;
+
+ res = ad7124_find_closest_match(ad7124_gain,
+ ARRAY_SIZE(ad7124_gain), gain);
+ ret = ad7124_spi_write_mask(st, AD7124_CONFIG(channel),
+ AD7124_CONFIG_PGA_MSK,
+ AD7124_CONFIG_PGA(res), 2);
+ if (ret < 0)
+ return ret;
+
+ st->channel_config[channel].pga_bits = res;
+
+ return 0;
+}
+
+static int ad7124_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ int idx, ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ if (ret < 0)
+ return ret;
+
+ /* After the conversion is performed, disable the channel */
+ ret = ad_sd_write_reg(&st->sd,
+ AD7124_CHANNEL(chan->address), 2,
+ st->channel_config[chan->address].ain |
+ AD7124_CHANNEL_EN(0));
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ idx = st->channel_config[chan->address].pga_bits;
+ *val = st->channel_config[chan->address].vref_mv;
+ if (st->channel_config[chan->address].bipolar)
+ *val2 = chan->scan_type.realbits - 1 + idx;
+ else
+ *val2 = chan->scan_type.realbits + idx;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ if (st->channel_config[chan->address].bipolar)
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ else
+ *val = 0;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->channel_config[chan->address].odr;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7124_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ unsigned int res, gain, full_scale, vref;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val2 != 0)
+ return -EINVAL;
+
+ return ad7124_set_channel_odr(st, chan->address, val);
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0)
+ return -EINVAL;
+
+ if (st->channel_config[chan->address].bipolar)
+ full_scale = 1 << (chan->scan_type.realbits - 1);
+ else
+ full_scale = 1 << chan->scan_type.realbits;
+
+ vref = st->channel_config[chan->address].vref_mv * 1000000LL;
+ res = DIV_ROUND_CLOSEST(vref, full_scale);
+ gain = DIV_ROUND_CLOSEST(res, val2);
+
+ return ad7124_set_channel_gain(st, chan->address, gain);
+ default:
+ return -EINVAL;
+ }
+}
+
+static IIO_CONST_ATTR(in_voltage_scale_available,
+ "0.000001164 0.000002328 0.000004656 0.000009313 0.000018626 0.000037252 0.000074505 0.000149011 0.000298023");
+
+static struct attribute *ad7124_attributes[] = {
+ &iio_const_attr_in_voltage_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7124_attrs_group = {
+ .attrs = ad7124_attributes,
+};
+
+static const struct iio_info ad7124_info = {
+ .read_raw = ad7124_read_raw,
+ .write_raw = ad7124_write_raw,
+ .validate_trigger = ad_sd_validate_trigger,
+ .attrs = &ad7124_attrs_group,
+};
+
+static int ad7124_soft_reset(struct ad7124_state *st)
+{
+ unsigned int readval, timeout;
+ int ret;
+
+ ret = ad_sd_reset(&st->sd, 64);
+ if (ret < 0)
+ return ret;
+
+ timeout = 100;
+ do {
+ ret = ad_sd_read_reg(&st->sd, AD7124_STATUS, 1, &readval);
+ if (ret < 0)
+ return ret;
+
+ if (!(readval & AD7124_STATUS_POR_FLAG_MSK))
+ return 0;
+
+ /* The AD7124 requires typically 2ms to power up and settle */
+ usleep_range(100, 2000);
+ } while (--timeout);
+
+ dev_err(&st->sd.spi->dev, "Soft reset failed\n");
+
+ return -EIO;
+}
+
+static int ad7124_init_channel_vref(struct ad7124_state *st,
+ unsigned int channel_number)
+{
+ unsigned int refsel = st->channel_config[channel_number].refsel;
+
+ switch (refsel) {
+ case AD7124_REFIN1:
+ case AD7124_REFIN2:
+ case AD7124_AVDD_REF:
+ if (IS_ERR(st->vref[refsel])) {
+ dev_err(&st->sd.spi->dev,
+ "Error, trying to use external voltage reference without a %s regulator.\n",
+ ad7124_ref_names[refsel]);
+ return PTR_ERR(st->vref[refsel]);
+ }
+ st->channel_config[channel_number].vref_mv =
+ regulator_get_voltage(st->vref[refsel]);
+ /* Conversion from uV to mV */
+ st->channel_config[channel_number].vref_mv /= 1000;
+ break;
+ case AD7124_INT_REF:
+ st->channel_config[channel_number].vref_mv = 2500;
+ break;
+ default:
+ dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
+ struct device_node *np)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ struct device_node *child;
+ struct iio_chan_spec *chan;
+ struct ad7124_channel_config *chan_config;
+ unsigned int ain[2], channel = 0, tmp;
+ int ret;
+
+ st->num_channels = of_get_available_child_count(np);
+ if (!st->num_channels) {
+ dev_err(indio_dev->dev.parent, "no channel children\n");
+ return -ENODEV;
+ }
+
+ chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
+ sizeof(*chan), GFP_KERNEL);
+ if (!chan)
+ return -ENOMEM;
+
+ chan_config = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
+ sizeof(*chan_config), GFP_KERNEL);
+ if (!chan_config)
+ return -ENOMEM;
+
+ indio_dev->channels = chan;
+ indio_dev->num_channels = st->num_channels;
+ st->channel_config = chan_config;
+
+ for_each_available_child_of_node(np, child) {
+ ret = of_property_read_u32(child, "reg", &channel);
+ if (ret)
+ goto err;
+
+ ret = of_property_read_u32_array(child, "diff-channels",
+ ain, 2);
+ if (ret)
+ goto err;
+
+ st->channel_config[channel].ain = AD7124_CHANNEL_AINP(ain[0]) |
+ AD7124_CHANNEL_AINM(ain[1]);
+ st->channel_config[channel].bipolar =
+ of_property_read_bool(child, "bipolar");
+
+ ret = of_property_read_u32(child, "adi,reference-select", &tmp);
+ if (ret)
+ st->channel_config[channel].refsel = AD7124_INT_REF;
+ else
+ st->channel_config[channel].refsel = tmp;
+
+ st->channel_config[channel].buf_positive =
+ of_property_read_bool(child, "adi,buffered-positive");
+ st->channel_config[channel].buf_negative =
+ of_property_read_bool(child, "adi,buffered-negative");
+
+ *chan = ad7124_channel_template;
+ chan->address = channel;
+ chan->scan_index = channel;
+ chan->channel = ain[0];
+ chan->channel2 = ain[1];
+
+ chan++;
+ }
+
+ return 0;
+err:
+ of_node_put(child);
+
+ return ret;
+}
+
+static int ad7124_setup(struct ad7124_state *st)
+{
+ unsigned int val, fclk, power_mode;
+ int i, ret, tmp;
+
+ fclk = clk_get_rate(st->mclk);
+ if (!fclk)
+ return -EINVAL;
+
+ /* The power mode changes the master clock frequency */
+ power_mode = ad7124_find_closest_match(ad7124_master_clk_freq_hz,
+ ARRAY_SIZE(ad7124_master_clk_freq_hz),
+ fclk);
+ if (fclk != ad7124_master_clk_freq_hz[power_mode]) {
+ ret = clk_set_rate(st->mclk, fclk);
+ if (ret)
+ return ret;
+ }
+
+ /* Set the power mode */
+ st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK;
+ st->adc_control |= AD7124_ADC_CTRL_PWR(power_mode);
+ ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < st->num_channels; i++) {
+ val = st->channel_config[i].ain | AD7124_CHANNEL_SETUP(i);
+ ret = ad_sd_write_reg(&st->sd, AD7124_CHANNEL(i), 2, val);
+ if (ret < 0)
+ return ret;
+
+ ret = ad7124_init_channel_vref(st, i);
+ if (ret < 0)
+ return ret;
+
+ tmp = (st->channel_config[i].buf_positive << 1) +
+ st->channel_config[i].buf_negative;
+
+ val = AD7124_CONFIG_BIPOLAR(st->channel_config[i].bipolar) |
+ AD7124_CONFIG_REF_SEL(st->channel_config[i].refsel) |
+ AD7124_CONFIG_IN_BUFF(tmp);
+ ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(i), 2, val);
+ if (ret < 0)
+ return ret;
+ /*
+ * 9.38 SPS is the minimum output data rate supported
+ * regardless of the selected power mode. Round it up to 10 and
+ * set all the enabled channels to this default value.
+ */
+ ret = ad7124_set_channel_odr(st, i, 10);
+ }
+
+ return ret;
+}
+
+static int ad7124_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id;
+ struct ad7124_state *st;
+ struct iio_dev *indio_dev;
+ int i, ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ id = spi_get_device_id(spi);
+ st->chip_info = &ad7124_chip_info_tbl[id->driver_data];
+
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7124_sigma_delta_info);
+
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad7124_info;
+
+ ret = ad7124_of_parse_channel_config(indio_dev, spi->dev.of_node);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(st->vref); i++) {
+ if (i == AD7124_INT_REF)
+ continue;
+
+ st->vref[i] = devm_regulator_get_optional(&spi->dev,
+ ad7124_ref_names[i]);
+ if (PTR_ERR(st->vref[i]) == -ENODEV)
+ continue;
+ else if (IS_ERR(st->vref[i]))
+ return PTR_ERR(st->vref[i]);
+
+ ret = regulator_enable(st->vref[i]);
+ if (ret)
+ return ret;
+ }
+
+ st->mclk = devm_clk_get(&spi->dev, "mclk");
+ if (IS_ERR(st->mclk)) {
+ ret = PTR_ERR(st->mclk);
+ goto error_regulator_disable;
+ }
+
+ ret = clk_prepare_enable(st->mclk);
+ if (ret < 0)
+ goto error_regulator_disable;
+
+ ret = ad7124_soft_reset(st);
+ if (ret < 0)
+ goto error_clk_disable_unprepare;
+
+ ret = ad7124_setup(st);
+ if (ret < 0)
+ goto error_clk_disable_unprepare;
+
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
+ if (ret < 0)
+ goto error_clk_disable_unprepare;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register iio device\n");
+ goto error_remove_trigger;
+ }
+
+ return 0;
+
+error_remove_trigger:
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+error_clk_disable_unprepare:
+ clk_disable_unprepare(st->mclk);
+error_regulator_disable:
+ for (i = ARRAY_SIZE(st->vref) - 1; i >= 0; i--) {
+ if (!IS_ERR_OR_NULL(st->vref[i]))
+ regulator_disable(st->vref[i]);
+ }
+
+ return ret;
+}
+
+static int ad7124_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7124_state *st = iio_priv(indio_dev);
+ int i;
+
+ iio_device_unregister(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+ clk_disable_unprepare(st->mclk);
+
+ for (i = ARRAY_SIZE(st->vref) - 1; i >= 0; i--) {
+ if (!IS_ERR_OR_NULL(st->vref[i]))
+ regulator_disable(st->vref[i]);
+ }
+
+ return 0;
+}
+
+static const struct spi_device_id ad7124_id_table[] = {
+ { "ad7124-4", ID_AD7124_4 },
+ { "ad7124-8", ID_AD7124_8 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7124_id_table);
+
+static const struct of_device_id ad7124_of_match[] = {
+ { .compatible = "adi,ad7124-4" },
+ { .compatible = "adi,ad7124-8" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7124_of_match);
+
+static struct spi_driver ad71124_driver = {
+ .driver = {
+ .name = "ad7124",
+ .of_match_table = ad7124_of_match,
+ },
+ .probe = ad7124_probe,
+ .remove = ad7124_remove,
+ .id_table = ad7124_id_table,
+};
+module_spi_driver(ad71124_driver);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7124 SPI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
index 605eb5e..c31b8ea 100644
--- a/drivers/iio/adc/ad7266.c
+++ b/drivers/iio/adc/ad7266.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7266/65 SPI ADC driver
*
* Copyright 2012 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c
index a862b5d..b2b137f 100644
--- a/drivers/iio/adc/ad7291.c
+++ b/drivers/iio/adc/ad7291.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD7291 8-Channel, I2C, 12-Bit SAR ADC with Temperature Sensor
*
* Copyright 2010-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/device.h>
diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c
index 2b20c6c..dc8d8c5 100644
--- a/drivers/iio/adc/ad7298.c
+++ b/drivers/iio/adc/ad7298.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7298 SPI ADC driver
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
@@ -385,6 +384,6 @@
};
module_spi_driver(ad7298_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7298 ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
index fbaae47..7674748 100644
--- a/drivers/iio/adc/ad7476.c
+++ b/drivers/iio/adc/ad7476.c
@@ -59,6 +59,9 @@
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 @@
#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 @@
.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 @@
{"adc081s", ID_ADC081S},
{"adc101s", ID_ADC101S},
{"adc121s", ID_ADC121S},
+ {"ads7866", ID_ADS7866},
+ {"ads7867", ID_ADS7867},
+ {"ads7868", ID_ADS7868},
{}
};
MODULE_DEVICE_TABLE(spi, ad7476_id);
@@ -328,6 +348,6 @@
};
module_spi_driver(ad7476_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
new file mode 100644
index 0000000..f5ba94c
--- /dev/null
+++ b/drivers/iio/adc/ad7606.c
@@ -0,0 +1,736 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7606 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/util_macros.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#include "ad7606.h"
+
+/*
+ * Scales are computed as 5000/32768 and 10000/32768 respectively,
+ * so that when applied to the raw values they provide mV values
+ */
+static const unsigned int ad7606_scale_avail[2] = {
+ 152588, 305176
+};
+
+
+static const unsigned int ad7616_sw_scale_avail[3] = {
+ 76293, 152588, 305176
+};
+
+static const unsigned int ad7606_oversampling_avail[7] = {
+ 1, 2, 4, 8, 16, 32, 64,
+};
+
+static const unsigned int ad7616_oversampling_avail[8] = {
+ 1, 2, 4, 8, 16, 32, 64, 128,
+};
+
+static int ad7606_reset(struct ad7606_state *st)
+{
+ if (st->gpio_reset) {
+ gpiod_set_value(st->gpio_reset, 1);
+ ndelay(100); /* t_reset >= 100ns */
+ gpiod_set_value(st->gpio_reset, 0);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int ad7606_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ if (readval) {
+ ret = st->bops->reg_read(st, reg);
+ if (ret < 0)
+ goto err_unlock;
+ *readval = ret;
+ ret = 0;
+ } else {
+ ret = st->bops->reg_write(st, reg, writeval);
+ }
+err_unlock:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int ad7606_read_samples(struct ad7606_state *st)
+{
+ unsigned int num = st->chip_info->num_channels;
+ u16 *data = st->data;
+ int ret;
+
+ /*
+ * The frstdata signal is set to high while and after reading the sample
+ * of the first channel and low for all other channels. This can be used
+ * to check that the incoming data is correctly aligned. During normal
+ * operation the data should never become unaligned, but some glitch or
+ * electrostatic discharge might cause an extra read or clock cycle.
+ * Monitoring the frstdata signal allows to recover from such failure
+ * situations.
+ */
+
+ if (st->gpio_frstdata) {
+ ret = st->bops->read_block(st->dev, 1, data);
+ if (ret)
+ return ret;
+
+ if (!gpiod_get_value(st->gpio_frstdata)) {
+ ad7606_reset(st);
+ return -EIO;
+ }
+
+ data++;
+ num--;
+ }
+
+ return st->bops->read_block(st->dev, num, data);
+}
+
+static irqreturn_t ad7606_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ ret = ad7606_read_samples(st);
+ if (ret == 0)
+ iio_push_to_buffers_with_timestamp(indio_dev, st->data,
+ iio_get_time_ns(indio_dev));
+
+ iio_trigger_notify_done(indio_dev->trig);
+ /* The rising edge of the CONVST signal starts a new conversion. */
+ gpiod_set_value(st->gpio_convst, 1);
+
+ mutex_unlock(&st->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ gpiod_set_value(st->gpio_convst, 1);
+ ret = wait_for_completion_timeout(&st->completion,
+ msecs_to_jiffies(1000));
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto error_ret;
+ }
+
+ ret = ad7606_read_samples(st);
+ if (ret == 0)
+ ret = st->data[ch];
+
+error_ret:
+ gpiod_set_value(st->gpio_convst, 0);
+
+ return ret;
+}
+
+static int ad7606_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret, ch = 0;
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7606_scan_direct(indio_dev, chan->address);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret < 0)
+ return ret;
+ *val = (short)ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (st->sw_mode_en)
+ ch = chan->address;
+ *val = 0;
+ *val2 = st->scale_avail[st->range[ch]];
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = st->oversampling;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static ssize_t ad7606_show_avail(char *buf, const unsigned int *vals,
+ unsigned int n, bool micros)
+{
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ micros ? "0.%06u " : "%u ", vals[i]);
+ }
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t in_voltage_scale_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_show_avail(buf, st->scale_avail, st->num_scales, true);
+}
+
+static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
+
+static int ad7606_write_scale_hw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ gpiod_set_value(st->gpio_range, val);
+
+ return 0;
+}
+
+static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ DECLARE_BITMAP(values, 3);
+
+ values[0] = val;
+
+ gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
+ st->gpio_os->info, values);
+
+ /* AD7616 requires a reset to update value */
+ if (st->chip_info->os_req_reset)
+ ad7606_reset(st);
+
+ return 0;
+}
+
+static int ad7606_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int i, ret, ch = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ mutex_lock(&st->lock);
+ i = find_closest(val2, st->scale_avail, st->num_scales);
+ if (st->sw_mode_en)
+ ch = chan->address;
+ ret = st->write_scale(indio_dev, ch, i);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ st->range[ch] = i;
+ mutex_unlock(&st->lock);
+
+ return 0;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ if (val2)
+ return -EINVAL;
+ i = find_closest(val, st->oversampling_avail,
+ st->num_os_ratios);
+ mutex_lock(&st->lock);
+ ret = st->write_os(indio_dev, i);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ st->oversampling = st->oversampling_avail[i];
+ mutex_unlock(&st->lock);
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t ad7606_oversampling_ratio_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_show_avail(buf, st->oversampling_avail,
+ st->num_os_ratios, false);
+}
+
+static IIO_DEVICE_ATTR(oversampling_ratio_available, 0444,
+ ad7606_oversampling_ratio_avail, NULL, 0);
+
+static struct attribute *ad7606_attributes_os_and_range[] = {
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ &iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_os_and_range = {
+ .attrs = ad7606_attributes_os_and_range,
+};
+
+static struct attribute *ad7606_attributes_os[] = {
+ &iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_os = {
+ .attrs = ad7606_attributes_os,
+};
+
+static struct attribute *ad7606_attributes_range[] = {
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_range = {
+ .attrs = ad7606_attributes_range,
+};
+
+static const struct iio_chan_spec ad7605_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ AD7605_CHANNEL(0),
+ AD7605_CHANNEL(1),
+ AD7605_CHANNEL(2),
+ AD7605_CHANNEL(3),
+};
+
+static const struct iio_chan_spec ad7606_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+ AD7606_CHANNEL(0),
+ AD7606_CHANNEL(1),
+ AD7606_CHANNEL(2),
+ AD7606_CHANNEL(3),
+ AD7606_CHANNEL(4),
+ AD7606_CHANNEL(5),
+ AD7606_CHANNEL(6),
+ AD7606_CHANNEL(7),
+};
+
+/*
+ * The current assumption that this driver makes for AD7616, is that it's
+ * working in Hardware Mode with Serial, Burst and Sequencer modes activated.
+ * To activate them, following pins must be pulled high:
+ * -SER/PAR
+ * -SEQEN
+ * And following pins must be pulled low:
+ * -WR/BURST
+ * -DB4/SER1W
+ */
+static const struct iio_chan_spec ad7616_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(16),
+ AD7606_CHANNEL(0),
+ AD7606_CHANNEL(1),
+ AD7606_CHANNEL(2),
+ AD7606_CHANNEL(3),
+ AD7606_CHANNEL(4),
+ AD7606_CHANNEL(5),
+ AD7606_CHANNEL(6),
+ AD7606_CHANNEL(7),
+ AD7606_CHANNEL(8),
+ AD7606_CHANNEL(9),
+ AD7606_CHANNEL(10),
+ AD7606_CHANNEL(11),
+ AD7606_CHANNEL(12),
+ AD7606_CHANNEL(13),
+ AD7606_CHANNEL(14),
+ AD7606_CHANNEL(15),
+};
+
+static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
+ /* More devices added in future */
+ [ID_AD7605_4] = {
+ .channels = ad7605_channels,
+ .num_channels = 5,
+ },
+ [ID_AD7606_8] = {
+ .channels = ad7606_channels,
+ .num_channels = 9,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7606_6] = {
+ .channels = ad7606_channels,
+ .num_channels = 7,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7606_4] = {
+ .channels = ad7606_channels,
+ .num_channels = 5,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7606B] = {
+ .channels = ad7606_channels,
+ .num_channels = 9,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7616] = {
+ .channels = ad7616_channels,
+ .num_channels = 17,
+ .oversampling_avail = ad7616_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
+ .os_req_reset = true,
+ .init_delay_ms = 15,
+ },
+};
+
+static int ad7606_request_gpios(struct ad7606_state *st)
+{
+ struct device *dev = st->dev;
+
+ st->gpio_convst = devm_gpiod_get(dev, "adi,conversion-start",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_convst))
+ return PTR_ERR(st->gpio_convst);
+
+ st->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_reset))
+ return PTR_ERR(st->gpio_reset);
+
+ st->gpio_range = devm_gpiod_get_optional(dev, "adi,range",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_range))
+ return PTR_ERR(st->gpio_range);
+
+ st->gpio_standby = devm_gpiod_get_optional(dev, "standby",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->gpio_standby))
+ return PTR_ERR(st->gpio_standby);
+
+ st->gpio_frstdata = devm_gpiod_get_optional(dev, "adi,first-data",
+ GPIOD_IN);
+ if (IS_ERR(st->gpio_frstdata))
+ return PTR_ERR(st->gpio_frstdata);
+
+ if (!st->chip_info->oversampling_num)
+ return 0;
+
+ st->gpio_os = devm_gpiod_get_array_optional(dev,
+ "adi,oversampling-ratio",
+ GPIOD_OUT_LOW);
+ return PTR_ERR_OR_ZERO(st->gpio_os);
+}
+
+/*
+ * The BUSY signal indicates when conversions are in progress, so when a rising
+ * edge of CONVST is applied, BUSY goes logic high and transitions low at the
+ * end of the entire conversion process. The falling edge of the BUSY signal
+ * triggers this interrupt.
+ */
+static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (iio_buffer_enabled(indio_dev)) {
+ gpiod_set_value(st->gpio_convst, 0);
+ iio_trigger_poll_chained(st->trig);
+ } else {
+ complete(&st->completion);
+ }
+
+ return IRQ_HANDLED;
+};
+
+static int ad7606_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (st->trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ad7606_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ iio_triggered_buffer_postenable(indio_dev);
+ gpiod_set_value(st->gpio_convst, 1);
+
+ return 0;
+}
+
+static int ad7606_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ gpiod_set_value(st->gpio_convst, 0);
+
+ return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops ad7606_buffer_ops = {
+ .postenable = &ad7606_buffer_postenable,
+ .predisable = &ad7606_buffer_predisable,
+};
+
+static const struct iio_info ad7606_info_no_os_or_range = {
+ .read_raw = &ad7606_read_raw,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_os_and_range = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .attrs = &ad7606_attribute_group_os_and_range,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_os_range_and_debug = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .debugfs_reg_access = &ad7606_reg_access,
+ .attrs = &ad7606_attribute_group_os_and_range,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_os = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .attrs = &ad7606_attribute_group_os,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_range = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .attrs = &ad7606_attribute_group_range,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_trigger_ops ad7606_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static void ad7606_regulator_disable(void *data)
+{
+ struct ad7606_state *st = data;
+
+ regulator_disable(st->reg);
+}
+
+int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
+ const char *name, unsigned int id,
+ const struct ad7606_bus_ops *bops)
+{
+ struct ad7606_state *st;
+ int ret;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ dev_set_drvdata(dev, indio_dev);
+
+ st->dev = dev;
+ mutex_init(&st->lock);
+ st->bops = bops;
+ st->base_address = base_address;
+ /* tied to logic low, analog input range is +/- 5V */
+ st->range[0] = 0;
+ st->oversampling = 1;
+ st->scale_avail = ad7606_scale_avail;
+ st->num_scales = ARRAY_SIZE(ad7606_scale_avail);
+
+ st->reg = devm_regulator_get(dev, "avcc");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(dev, "Failed to enable specified AVcc supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, ad7606_regulator_disable, st);
+ if (ret)
+ return ret;
+
+ st->chip_info = &ad7606_chip_info_tbl[id];
+
+ if (st->chip_info->oversampling_num) {
+ st->oversampling_avail = st->chip_info->oversampling_avail;
+ st->num_os_ratios = st->chip_info->oversampling_num;
+ }
+
+ ret = ad7606_request_gpios(st);
+ if (ret)
+ return ret;
+
+ indio_dev->dev.parent = dev;
+ if (st->gpio_os) {
+ if (st->gpio_range)
+ indio_dev->info = &ad7606_info_os_and_range;
+ else
+ indio_dev->info = &ad7606_info_os;
+ } else {
+ if (st->gpio_range)
+ indio_dev->info = &ad7606_info_range;
+ else
+ indio_dev->info = &ad7606_info_no_os_or_range;
+ }
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = name;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ init_completion(&st->completion);
+
+ ret = ad7606_reset(st);
+ if (ret)
+ dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
+
+ /* AD7616 requires al least 15ms to reconfigure after a reset */
+ if (st->chip_info->init_delay_ms) {
+ if (msleep_interruptible(st->chip_info->init_delay_ms))
+ return -ERESTARTSYS;
+ }
+
+ st->write_scale = ad7606_write_scale_hw;
+ st->write_os = ad7606_write_os_hw;
+
+ if (st->bops->sw_mode_config)
+ st->sw_mode_en = device_property_present(st->dev,
+ "adi,sw-mode");
+
+ if (st->sw_mode_en) {
+ /* Scale of 0.076293 is only available in sw mode */
+ st->scale_avail = ad7616_sw_scale_avail;
+ st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
+
+ /* After reset, in software mode, ±10 V is set by default */
+ memset32(st->range, 2, ARRAY_SIZE(st->range));
+ indio_dev->info = &ad7606_info_os_range_and_debug;
+
+ ret = st->bops->sw_mode_config(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name, indio_dev->id);
+ if (!st->trig)
+ return -ENOMEM;
+
+ st->trig->ops = &ad7606_trigger_ops;
+ st->trig->dev.parent = dev;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = devm_iio_trigger_register(dev, st->trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(st->trig);
+
+ ret = devm_request_threaded_irq(dev, irq,
+ NULL,
+ &ad7606_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad7606_trigger_handler,
+ &ad7606_buffer_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_GPL(ad7606_probe);
+
+#ifdef CONFIG_PM_SLEEP
+
+static int ad7606_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (st->gpio_standby) {
+ gpiod_set_value(st->gpio_range, 1);
+ gpiod_set_value(st->gpio_standby, 0);
+ }
+
+ return 0;
+}
+
+static int ad7606_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (st->gpio_standby) {
+ gpiod_set_value(st->gpio_range, st->range[0]);
+ gpiod_set_value(st->gpio_standby, 1);
+ ad7606_reset(st);
+ }
+
+ return 0;
+}
+
+SIMPLE_DEV_PM_OPS(ad7606_pm_ops, ad7606_suspend, ad7606_resume);
+EXPORT_SYMBOL_GPL(ad7606_pm_ops);
+
+#endif
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
new file mode 100644
index 0000000..9350ef1
--- /dev/null
+++ b/drivers/iio/adc/ad7606.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * AD7606 ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#ifndef IIO_ADC_AD7606_H_
+#define IIO_ADC_AD7606_H_
+
+#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = num, \
+ .address = num, \
+ .info_mask_separate = mask_sep, \
+ .info_mask_shared_by_type = mask_type, \
+ .info_mask_shared_by_all = mask_all, \
+ .scan_index = num, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+#define AD7605_CHANNEL(num) \
+ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
+ BIT(IIO_CHAN_INFO_SCALE), 0)
+
+#define AD7606_CHANNEL(num) \
+ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+
+#define AD7616_CHANNEL(num) \
+ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\
+ 0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+
+/**
+ * struct ad7606_chip_info - chip specific information
+ * @channels: channel specification
+ * @num_channels: number of channels
+ * @oversampling_avail pointer to the array which stores the available
+ * oversampling ratios.
+ * @oversampling_num number of elements stored in oversampling_avail array
+ * @os_req_reset some devices require a reset to update oversampling
+ * @init_delay_ms required delay in miliseconds for initialization
+ * after a restart
+ */
+struct ad7606_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ const unsigned int *oversampling_avail;
+ unsigned int oversampling_num;
+ bool os_req_reset;
+ unsigned long init_delay_ms;
+};
+
+/**
+ * struct ad7606_state - driver instance specific data
+ * @dev pointer to kernel device
+ * @chip_info entry in the table of chips that describes this device
+ * @reg regulator info for the the power supply of the device
+ * @bops bus operations (SPI or parallel)
+ * @range voltage range selection, selects which scale to apply
+ * @oversampling oversampling selection
+ * @base_address address from where to read data in parallel operation
+ * @sw_mode_en software mode enabled
+ * @scale_avail pointer to the array which stores the available scales
+ * @num_scales number of elements stored in the scale_avail array
+ * @oversampling_avail pointer to the array which stores the available
+ * oversampling ratios.
+ * @num_os_ratios number of elements stored in oversampling_avail array
+ * @write_scale pointer to the function which writes the scale
+ * @write_os pointer to the function which writes the os
+ * @lock protect sensor state from concurrent accesses to GPIOs
+ * @gpio_convst GPIO descriptor for conversion start signal (CONVST)
+ * @gpio_reset GPIO descriptor for device hard-reset
+ * @gpio_range GPIO descriptor for range selection
+ * @gpio_standby GPIO descriptor for stand-by signal (STBY),
+ * controls power-down mode of device
+ * @gpio_frstdata GPIO descriptor for reading from device when data
+ * is being read on the first channel
+ * @gpio_os GPIO descriptors to control oversampling on the device
+ * @complete completion to indicate end of conversion
+ * @trig The IIO trigger associated with the device.
+ * @data buffer for reading data from the device
+ * @d16 be16 buffer for reading data from the device
+ */
+struct ad7606_state {
+ struct device *dev;
+ const struct ad7606_chip_info *chip_info;
+ struct regulator *reg;
+ const struct ad7606_bus_ops *bops;
+ unsigned int range[16];
+ unsigned int oversampling;
+ void __iomem *base_address;
+ bool sw_mode_en;
+ const unsigned int *scale_avail;
+ unsigned int num_scales;
+ const unsigned int *oversampling_avail;
+ unsigned int num_os_ratios;
+ int (*write_scale)(struct iio_dev *indio_dev, int ch, int val);
+ int (*write_os)(struct iio_dev *indio_dev, int val);
+
+ struct mutex lock; /* protect sensor state */
+ struct gpio_desc *gpio_convst;
+ struct gpio_desc *gpio_reset;
+ struct gpio_desc *gpio_range;
+ struct gpio_desc *gpio_standby;
+ struct gpio_desc *gpio_frstdata;
+ struct gpio_descs *gpio_os;
+ struct iio_trigger *trig;
+ struct completion completion;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ * 16 * 16-bit samples + 64-bit timestamp
+ */
+ unsigned short data[20] ____cacheline_aligned;
+ __be16 d16[2];
+};
+
+/**
+ * struct ad7606_bus_ops - driver bus operations
+ * @read_block function pointer for reading blocks of data
+ * @sw_mode_config: pointer to a function which configured the device
+ * for software mode
+ * @reg_read function pointer for reading spi register
+ * @reg_write function pointer for writing spi register
+ * @write_mask function pointer for write spi register with mask
+ * @rd_wr_cmd pointer to the function which calculates the spi address
+ */
+struct ad7606_bus_ops {
+ /* more methods added in future? */
+ int (*read_block)(struct device *dev, int num, void *data);
+ int (*sw_mode_config)(struct iio_dev *indio_dev);
+ int (*reg_read)(struct ad7606_state *st, unsigned int addr);
+ int (*reg_write)(struct ad7606_state *st,
+ unsigned int addr,
+ unsigned int val);
+ int (*write_mask)(struct ad7606_state *st,
+ unsigned int addr,
+ unsigned long mask,
+ unsigned int val);
+ u16 (*rd_wr_cmd)(int addr, char isWriteOp);
+};
+
+int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
+ const char *name, unsigned int id,
+ const struct ad7606_bus_ops *bops);
+
+enum ad7606_supported_device_ids {
+ ID_AD7605_4,
+ ID_AD7606_8,
+ ID_AD7606_6,
+ ID_AD7606_4,
+ ID_AD7606B,
+ ID_AD7616,
+};
+
+#ifdef CONFIG_PM_SLEEP
+extern const struct dev_pm_ops ad7606_pm_ops;
+#define AD7606_PM_OPS (&ad7606_pm_ops)
+#else
+#define AD7606_PM_OPS NULL
+#endif
+
+#endif /* IIO_ADC_AD7606_H_ */
diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c
new file mode 100644
index 0000000..f732b3a
--- /dev/null
+++ b/drivers/iio/adc/ad7606_par.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7606 Parallel Interface ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <linux/iio/iio.h>
+#include "ad7606.h"
+
+static int ad7606_par16_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ insw((unsigned long)st->base_address, buf, count);
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_par16_bops = {
+ .read_block = ad7606_par16_read_block,
+};
+
+static int ad7606_par8_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ insb((unsigned long)st->base_address, buf, count * 2);
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_par8_bops = {
+ .read_block = ad7606_par8_read_block,
+};
+
+static int ad7606_par_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ struct resource *res;
+ void __iomem *addr;
+ resource_size_t remap_size;
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(addr))
+ return PTR_ERR(addr);
+
+ remap_size = resource_size(res);
+
+ return ad7606_probe(&pdev->dev, irq, addr,
+ id->name, id->driver_data,
+ remap_size > 1 ? &ad7606_par16_bops :
+ &ad7606_par8_bops);
+}
+
+static const struct platform_device_id ad7606_driver_ids[] = {
+ { .name = "ad7605-4", .driver_data = ID_AD7605_4, },
+ { .name = "ad7606-4", .driver_data = ID_AD7606_4, },
+ { .name = "ad7606-6", .driver_data = ID_AD7606_6, },
+ { .name = "ad7606-8", .driver_data = ID_AD7606_8, },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, ad7606_driver_ids);
+
+static const struct of_device_id ad7606_of_match[] = {
+ { .compatible = "adi,ad7605-4" },
+ { .compatible = "adi,ad7606-4" },
+ { .compatible = "adi,ad7606-6" },
+ { .compatible = "adi,ad7606-8" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7606_of_match);
+
+static struct platform_driver ad7606_driver = {
+ .probe = ad7606_par_probe,
+ .id_table = ad7606_driver_ids,
+ .driver = {
+ .name = "ad7606",
+ .pm = AD7606_PM_OPS,
+ .of_match_table = ad7606_of_match,
+ },
+};
+module_platform_driver(ad7606_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
new file mode 100644
index 0000000..29945ad
--- /dev/null
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7606 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include "ad7606.h"
+
+#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */
+
+#define AD7616_CONFIGURATION_REGISTER 0x02
+#define AD7616_OS_MASK GENMASK(4, 2)
+#define AD7616_BURST_MODE BIT(6)
+#define AD7616_SEQEN_MODE BIT(5)
+#define AD7616_RANGE_CH_A_ADDR_OFF 0x04
+#define AD7616_RANGE_CH_B_ADDR_OFF 0x06
+/*
+ * Range of channels from a group are stored in 2 registers.
+ * 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register.
+ * For channels from second group(8-15) the order is the same, only with
+ * an offset of 2 for register address.
+ */
+#define AD7616_RANGE_CH_ADDR(ch) ((ch) >> 2)
+/* The range of the channel is stored in 2 bits */
+#define AD7616_RANGE_CH_MSK(ch) (0b11 << (((ch) & 0b11) * 2))
+#define AD7616_RANGE_CH_MODE(ch, mode) ((mode) << ((((ch) & 0b11)) * 2))
+
+#define AD7606_CONFIGURATION_REGISTER 0x02
+#define AD7606_SINGLE_DOUT 0x00
+
+/*
+ * Range for AD7606B channels are stored in registers starting with address 0x3.
+ * Each register stores range for 2 channels(4 bits per channel).
+ */
+#define AD7606_RANGE_CH_MSK(ch) (GENMASK(3, 0) << (4 * ((ch) & 0x1)))
+#define AD7606_RANGE_CH_MODE(ch, mode) \
+ ((GENMASK(3, 0) & mode) << (4 * ((ch) & 0x1)))
+#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1))
+#define AD7606_OS_MODE 0x08
+
+static const struct iio_chan_spec ad7616_sw_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(16),
+ AD7616_CHANNEL(0),
+ AD7616_CHANNEL(1),
+ AD7616_CHANNEL(2),
+ AD7616_CHANNEL(3),
+ AD7616_CHANNEL(4),
+ AD7616_CHANNEL(5),
+ AD7616_CHANNEL(6),
+ AD7616_CHANNEL(7),
+ AD7616_CHANNEL(8),
+ AD7616_CHANNEL(9),
+ AD7616_CHANNEL(10),
+ AD7616_CHANNEL(11),
+ AD7616_CHANNEL(12),
+ AD7616_CHANNEL(13),
+ AD7616_CHANNEL(14),
+ AD7616_CHANNEL(15),
+};
+
+static const struct iio_chan_spec ad7606b_sw_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+ AD7616_CHANNEL(0),
+ AD7616_CHANNEL(1),
+ AD7616_CHANNEL(2),
+ AD7616_CHANNEL(3),
+ AD7616_CHANNEL(4),
+ AD7616_CHANNEL(5),
+ AD7616_CHANNEL(6),
+ AD7616_CHANNEL(7),
+};
+
+static const unsigned int ad7606B_oversampling_avail[9] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 256
+};
+
+static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp)
+{
+ /*
+ * The address of register consist of one w/r bit
+ * 6 bits of address followed by one reserved bit.
+ */
+ return ((addr & 0x7F) << 1) | ((isWriteOp & 0x1) << 7);
+}
+
+static u16 ad7606B_spi_rd_wr_cmd(int addr, char is_write_op)
+{
+ /*
+ * The address of register consists of one bit which
+ * specifies a read command placed in bit 6, followed by
+ * 6 bits of address.
+ */
+ return (addr & 0x3F) | (((~is_write_op) & 0x1) << 6);
+}
+
+static int ad7606_spi_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int i, ret;
+ unsigned short *data = buf;
+ __be16 *bdata = buf;
+
+ ret = spi_read(spi, buf, count * 2);
+ if (ret < 0) {
+ dev_err(&spi->dev, "SPI read error\n");
+ return ret;
+ }
+
+ for (i = 0; i < count; i++)
+ data[i] = be16_to_cpu(bdata[i]);
+
+ return 0;
+}
+
+static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->d16[0],
+ .len = 2,
+ .cs_change = 0,
+ }, {
+ .rx_buf = &st->d16[1],
+ .len = 2,
+ },
+ };
+ int ret;
+
+ st->d16[0] = cpu_to_be16(st->bops->rd_wr_cmd(addr, 0) << 8);
+
+ ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ return be16_to_cpu(st->d16[1]);
+}
+
+static int ad7606_spi_reg_write(struct ad7606_state *st,
+ unsigned int addr,
+ unsigned int val)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+
+ st->d16[0] = cpu_to_be16((st->bops->rd_wr_cmd(addr, 1) << 8) |
+ (val & 0x1FF));
+
+ return spi_write(spi, &st->d16[0], sizeof(st->d16[0]));
+}
+
+static int ad7606_spi_write_mask(struct ad7606_state *st,
+ unsigned int addr,
+ unsigned long mask,
+ unsigned int val)
+{
+ int readval;
+
+ readval = st->bops->reg_read(st, addr);
+ if (readval < 0)
+ return readval;
+
+ readval &= ~mask;
+ readval |= val;
+
+ return st->bops->reg_write(st, addr, readval);
+}
+
+static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned int ch_addr, mode, ch_index;
+
+
+ /*
+ * Ad7616 has 16 channels divided in group A and group B.
+ * The range of channels from A are stored in registers with address 4
+ * while channels from B are stored in register with address 6.
+ * The last bit from channels determines if it is from group A or B
+ * because the order of channels in iio is 0A, 0B, 1A, 1B...
+ */
+ ch_index = ch >> 1;
+
+ ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
+
+ if ((ch & 0x1) == 0) /* channel A */
+ ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
+ else /* channel B */
+ ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
+
+ /* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
+ mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
+ return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
+ mode);
+}
+
+static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER,
+ AD7616_OS_MASK, val << 2);
+}
+
+static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_spi_write_mask(st,
+ AD7606_RANGE_CH_ADDR(ch),
+ AD7606_RANGE_CH_MSK(ch),
+ AD7606_RANGE_CH_MODE(ch, val));
+}
+
+static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_spi_reg_write(st, AD7606_OS_MODE, val);
+}
+
+static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ /*
+ * Scale can be configured individually for each channel
+ * in software mode.
+ */
+ indio_dev->channels = ad7616_sw_channels;
+
+ st->write_scale = ad7616_write_scale_sw;
+ st->write_os = &ad7616_write_os_sw;
+
+ /* Activate Burst mode and SEQEN MODE */
+ return st->bops->write_mask(st,
+ AD7616_CONFIGURATION_REGISTER,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE);
+}
+
+static int ad7606B_sw_mode_config(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned long os[3] = {1};
+
+ /*
+ * Software mode is enabled when all three oversampling
+ * pins are set to high. If oversampling gpios are defined
+ * in the device tree, then they need to be set to high,
+ * otherwise, they must be hardwired to VDD
+ */
+ if (st->gpio_os) {
+ gpiod_set_array_value(ARRAY_SIZE(os),
+ st->gpio_os->desc, st->gpio_os->info, os);
+ }
+ /* OS of 128 and 256 are available only in software mode */
+ st->oversampling_avail = ad7606B_oversampling_avail;
+ st->num_os_ratios = ARRAY_SIZE(ad7606B_oversampling_avail);
+
+ st->write_scale = ad7606_write_scale_sw;
+ st->write_os = &ad7606_write_os_sw;
+
+ /* Configure device spi to output on a single channel */
+ st->bops->reg_write(st,
+ AD7606_CONFIGURATION_REGISTER,
+ AD7606_SINGLE_DOUT);
+
+ /*
+ * Scale can be configured individually for each channel
+ * in software mode.
+ */
+ indio_dev->channels = ad7606b_sw_channels;
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_spi_bops = {
+ .read_block = ad7606_spi_read_block,
+};
+
+static const struct ad7606_bus_ops ad7616_spi_bops = {
+ .read_block = ad7606_spi_read_block,
+ .reg_read = ad7606_spi_reg_read,
+ .reg_write = ad7606_spi_reg_write,
+ .write_mask = ad7606_spi_write_mask,
+ .rd_wr_cmd = ad7616_spi_rd_wr_cmd,
+ .sw_mode_config = ad7616_sw_mode_config,
+};
+
+static const struct ad7606_bus_ops ad7606B_spi_bops = {
+ .read_block = ad7606_spi_read_block,
+ .reg_read = ad7606_spi_reg_read,
+ .reg_write = ad7606_spi_reg_write,
+ .write_mask = ad7606_spi_write_mask,
+ .rd_wr_cmd = ad7606B_spi_rd_wr_cmd,
+ .sw_mode_config = ad7606B_sw_mode_config,
+};
+
+static int ad7606_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ const struct ad7606_bus_ops *bops;
+
+ switch (id->driver_data) {
+ case ID_AD7616:
+ bops = &ad7616_spi_bops;
+ break;
+ case ID_AD7606B:
+ bops = &ad7606B_spi_bops;
+ break;
+ default:
+ bops = &ad7606_spi_bops;
+ break;
+ }
+
+ return ad7606_probe(&spi->dev, spi->irq, NULL,
+ id->name, id->driver_data,
+ bops);
+}
+
+static const struct spi_device_id ad7606_id_table[] = {
+ { "ad7605-4", ID_AD7605_4 },
+ { "ad7606-4", ID_AD7606_4 },
+ { "ad7606-6", ID_AD7606_6 },
+ { "ad7606-8", ID_AD7606_8 },
+ { "ad7606b", ID_AD7606B },
+ { "ad7616", ID_AD7616 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7606_id_table);
+
+static const struct of_device_id ad7606_of_match[] = {
+ { .compatible = "adi,ad7605-4" },
+ { .compatible = "adi,ad7606-4" },
+ { .compatible = "adi,ad7606-6" },
+ { .compatible = "adi,ad7606-8" },
+ { .compatible = "adi,ad7606b" },
+ { .compatible = "adi,ad7616" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7606_of_match);
+
+static struct spi_driver ad7606_driver = {
+ .driver = {
+ .name = "ad7606",
+ .of_match_table = ad7606_of_match,
+ .pm = AD7606_PM_OPS,
+ },
+ .probe = ad7606_spi_probe,
+ .id_table = ad7606_id_table,
+};
+module_spi_driver(ad7606_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c
index 3ae14fc..bc388ea 100644
--- a/drivers/iio/adc/ad7766.c
+++ b/drivers/iio/adc/ad7766.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD7766/AD7767 SPI ADC driver
*
* Copyright 2016 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/clk.h>
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
new file mode 100644
index 0000000..0d13270
--- /dev/null
+++ b/drivers/iio/adc/ad7768-1.c
@@ -0,0 +1,655 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AD7768-1 SPI ADC driver
+ *
+ * Copyright 2017 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#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>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+/* AD7768 registers definition */
+#define AD7768_REG_CHIP_TYPE 0x3
+#define AD7768_REG_PROD_ID_L 0x4
+#define AD7768_REG_PROD_ID_H 0x5
+#define AD7768_REG_CHIP_GRADE 0x6
+#define AD7768_REG_SCRATCH_PAD 0x0A
+#define AD7768_REG_VENDOR_L 0x0C
+#define AD7768_REG_VENDOR_H 0x0D
+#define AD7768_REG_INTERFACE_FORMAT 0x14
+#define AD7768_REG_POWER_CLOCK 0x15
+#define AD7768_REG_ANALOG 0x16
+#define AD7768_REG_ANALOG2 0x17
+#define AD7768_REG_CONVERSION 0x18
+#define AD7768_REG_DIGITAL_FILTER 0x19
+#define AD7768_REG_SINC3_DEC_RATE_MSB 0x1A
+#define AD7768_REG_SINC3_DEC_RATE_LSB 0x1B
+#define AD7768_REG_DUTY_CYCLE_RATIO 0x1C
+#define AD7768_REG_SYNC_RESET 0x1D
+#define AD7768_REG_GPIO_CONTROL 0x1E
+#define AD7768_REG_GPIO_WRITE 0x1F
+#define AD7768_REG_GPIO_READ 0x20
+#define AD7768_REG_OFFSET_HI 0x21
+#define AD7768_REG_OFFSET_MID 0x22
+#define AD7768_REG_OFFSET_LO 0x23
+#define AD7768_REG_GAIN_HI 0x24
+#define AD7768_REG_GAIN_MID 0x25
+#define AD7768_REG_GAIN_LO 0x26
+#define AD7768_REG_SPI_DIAG_ENABLE 0x28
+#define AD7768_REG_ADC_DIAG_ENABLE 0x29
+#define AD7768_REG_DIG_DIAG_ENABLE 0x2A
+#define AD7768_REG_ADC_DATA 0x2C
+#define AD7768_REG_MASTER_STATUS 0x2D
+#define AD7768_REG_SPI_DIAG_STATUS 0x2E
+#define AD7768_REG_ADC_DIAG_STATUS 0x2F
+#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)
+
+#define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F))
+#define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F)
+
+enum ad7768_conv_mode {
+ AD7768_CONTINUOUS,
+ AD7768_ONE_SHOT,
+ AD7768_SINGLE,
+ AD7768_PERIODIC,
+ AD7768_STANDBY
+};
+
+enum ad7768_pwrmode {
+ AD7768_ECO_MODE = 0,
+ AD7768_MED_MODE = 2,
+ 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,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .shift = 8,
+ .endianness = IIO_BE,
+ },
+ },
+};
+
+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.
+ */
+ union {
+ __be32 d32;
+ u8 d8[2];
+ } data ____cacheline_aligned;
+};
+
+static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr,
+ unsigned int len)
+{
+ unsigned int shift;
+ int ret;
+
+ shift = 32 - (8 * len);
+ st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
+
+ ret = spi_write_then_read(st->spi, st->data.d8, 1,
+ &st->data.d32, len);
+ if (ret < 0)
+ return ret;
+
+ return (be32_to_cpu(st->data.d32) >> shift);
+}
+
+static int ad7768_spi_reg_write(struct ad7768_state *st,
+ unsigned int addr,
+ unsigned int val)
+{
+ st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
+ st->data.d8[1] = val & 0xFF;
+
+ return spi_write(st->spi, st->data.d8, 2);
+}
+
+static int ad7768_set_mode(struct ad7768_state *st,
+ enum ad7768_conv_mode mode)
+{
+ int regval;
+
+ regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1);
+ if (regval < 0)
+ return regval;
+
+ regval &= ~AD7768_CONV_MODE_MSK;
+ regval |= AD7768_CONV_MODE(mode);
+
+ return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval);
+}
+
+static int ad7768_scan_direct(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int readval, ret;
+
+ reinit_completion(&st->completion);
+
+ ret = ad7768_set_mode(st, AD7768_ONE_SHOT);
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_timeout(&st->completion,
+ msecs_to_jiffies(1000));
+ if (!ret)
+ return -ETIMEDOUT;
+
+ readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
+ if (readval < 0)
+ return readval;
+ /*
+ * Any SPI configuration of the AD7768-1 can only be
+ * performed in continuous conversion mode.
+ */
+ ret = ad7768_set_mode(st, AD7768_CONTINUOUS);
+ if (ret < 0)
+ return ret;
+
+ return readval;
+}
+
+static int ad7768_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ if (readval) {
+ ret = ad7768_spi_reg_read(st, reg, 1);
+ if (ret < 0)
+ goto err_unlock;
+ *readval = ret;
+ ret = 0;
+ } else {
+ ret = ad7768_spi_reg_write(st, reg, writeval);
+ }
+err_unlock:
+ mutex_unlock(&st->lock);
+
+ 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)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int scale_uv, ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7768_scan_direct(indio_dev);
+ if (ret >= 0)
+ *val = ret;
+
+ iio_device_release_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = regulator_get_voltage(st->vref);
+ if (scale_uv < 0)
+ return scale_uv;
+
+ *val = (scale_uv * 2) / 1000;
+ *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,
+};
+
+static int ad7768_setup(struct ad7768_state *st)
+{
+ int ret;
+
+ /*
+ * Two writes to the SPI_RESET[1:0] bits are required to initiate
+ * a software reset. The bits must first be set to 11, and then
+ * to 10. When the sequence is detected, the reset occurs.
+ * See the datasheet, page 70.
+ */
+ ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3);
+ if (ret)
+ return ret;
+
+ ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2);
+ if (ret)
+ return ret;
+
+ 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)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ ret = spi_read(st->spi, &st->data.d32, 3);
+ if (ret < 0)
+ goto err_unlock;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &st->data.d32,
+ iio_get_time_ns(indio_dev));
+
+ iio_trigger_notify_done(indio_dev->trig);
+err_unlock:
+ mutex_unlock(&st->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ad7768_interrupt(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ if (iio_buffer_enabled(indio_dev))
+ iio_trigger_poll(st->trig);
+ else
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+};
+
+static int ad7768_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ iio_triggered_buffer_postenable(indio_dev);
+ /*
+ * Write a 1 to the LSB of the INTERFACE_FORMAT register to enter
+ * continuous read mode. Subsequent data reads do not require an
+ * initial 8-bit write to query the ADC_DATA register.
+ */
+ return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT, 0x01);
+}
+
+static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ /*
+ * To exit continuous read mode, perform a single read of the ADC_DATA
+ * reg (0x2C), which allows further configuration of the device.
+ */
+ ret = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
+ if (ret < 0)
+ return ret;
+
+ return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
+ .postenable = &ad7768_buffer_postenable,
+ .predisable = &ad7768_buffer_predisable,
+};
+
+static const struct iio_trigger_ops ad7768_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static void ad7768_regulator_disable(void *data)
+{
+ struct ad7768_state *st = 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;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ st->vref = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(st->vref))
+ return PTR_ERR(st->vref);
+
+ ret = regulator_enable(st->vref);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified vref supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7768_regulator_disable, st);
+ 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);
+
+ indio_dev->channels = ad7768_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7768_channels);
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad7768_info;
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
+
+ ret = ad7768_setup(st);
+ if (ret < 0) {
+ dev_err(&spi->dev, "AD7768 setup failed\n");
+ return ret;
+ }
+
+ st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
+ indio_dev->name, indio_dev->id);
+ if (!st->trig)
+ return -ENOMEM;
+
+ st->trig->ops = &ad7768_trigger_ops;
+ st->trig->dev.parent = &spi->dev;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = devm_iio_trigger_register(&spi->dev, st->trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(st->trig);
+
+ init_completion(&st->completion);
+
+ ret = devm_request_irq(&spi->dev, spi->irq,
+ &ad7768_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ indio_dev->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad7768_trigger_handler,
+ &ad7768_buffer_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7768_id_table[] = {
+ { "ad7768-1", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7768_id_table);
+
+static const struct of_device_id ad7768_of_match[] = {
+ { .compatible = "adi,ad7768-1" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7768_of_match);
+
+static struct spi_driver ad7768_driver = {
+ .driver = {
+ .name = "ad7768-1",
+ .of_match_table = ad7768_of_match,
+ },
+ .probe = ad7768_probe,
+ .id_table = ad7768_id_table,
+};
+module_spi_driver(ad7768_driver);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7768-1 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c
new file mode 100644
index 0000000..217a5a5
--- /dev/null
+++ b/drivers/iio/adc/ad7780.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7170/AD7171 and AD7780/AD7781 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Copyright 2019 Renato Lui Geh
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/bits.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#define AD7780_RDY BIT(7)
+#define AD7780_FILTER BIT(6)
+#define AD7780_ERR BIT(5)
+#define AD7780_ID1 BIT(4)
+#define AD7780_ID0 BIT(3)
+#define AD7780_GAIN BIT(2)
+
+#define AD7170_ID 0
+#define AD7171_ID 1
+#define AD7780_ID 1
+#define AD7781_ID 0
+
+#define AD7780_ID_MASK (AD7780_ID0 | AD7780_ID1)
+
+#define AD7780_PATTERN_GOOD 1
+#define AD7780_PATTERN_MASK GENMASK(1, 0)
+
+#define AD7170_PATTERN_GOOD 5
+#define AD7170_PATTERN_MASK GENMASK(2, 0)
+
+#define AD7780_GAIN_MIDPOINT 64
+#define AD7780_FILTER_MIDPOINT 13350
+
+static const unsigned int ad778x_gain[2] = { 1, 128 };
+static const unsigned int ad778x_odr_avail[2] = { 10000, 16700 };
+
+struct ad7780_chip_info {
+ struct iio_chan_spec channel;
+ unsigned int pattern_mask;
+ unsigned int pattern;
+ bool is_ad778x;
+};
+
+struct ad7780_state {
+ const struct ad7780_chip_info *chip_info;
+ struct regulator *reg;
+ struct gpio_desc *powerdown_gpio;
+ struct gpio_desc *gain_gpio;
+ struct gpio_desc *filter_gpio;
+ unsigned int gain;
+ unsigned int odr;
+ unsigned int int_vref_mv;
+
+ struct ad_sigma_delta sd;
+};
+
+enum ad7780_supported_device_ids {
+ ID_AD7170,
+ ID_AD7171,
+ ID_AD7780,
+ ID_AD7781,
+};
+
+static struct ad7780_state *ad_sigma_delta_to_ad7780(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7780_state, sd);
+}
+
+static int ad7780_set_mode(struct ad_sigma_delta *sigma_delta,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta);
+ unsigned int val;
+
+ switch (mode) {
+ case AD_SD_MODE_SINGLE:
+ case AD_SD_MODE_CONTINUOUS:
+ val = 1;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+
+ gpiod_set_value(st->powerdown_gpio, val);
+
+ return 0;
+}
+
+static int ad7780_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad7780_state *st = iio_priv(indio_dev);
+ int voltage_uv;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ voltage_uv = regulator_get_voltage(st->reg);
+ if (voltage_uv < 0)
+ return voltage_uv;
+ voltage_uv /= 1000;
+ *val = voltage_uv * st->gain;
+ *val2 = chan->scan_type.realbits - 1;
+ st->int_vref_mv = voltage_uv;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->odr;
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7780_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long m)
+{
+ struct ad7780_state *st = iio_priv(indio_dev);
+ const struct ad7780_chip_info *chip_info = st->chip_info;
+ unsigned long long vref;
+ unsigned int full_scale, gain;
+
+ if (!chip_info->is_ad778x)
+ return -EINVAL;
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0)
+ return -EINVAL;
+
+ vref = st->int_vref_mv * 1000000LL;
+ full_scale = 1 << (chip_info->channel.scan_type.realbits - 1);
+ gain = DIV_ROUND_CLOSEST_ULL(vref, full_scale);
+ gain = DIV_ROUND_CLOSEST(gain, val2);
+ st->gain = gain;
+ if (gain < AD7780_GAIN_MIDPOINT)
+ gain = 0;
+ else
+ gain = 1;
+ gpiod_set_value(st->gain_gpio, gain);
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (1000*val + val2/1000 < AD7780_FILTER_MIDPOINT)
+ val = 0;
+ else
+ val = 1;
+ st->odr = ad778x_odr_avail[val];
+ gpiod_set_value(st->filter_gpio, val);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ad7780_postprocess_sample(struct ad_sigma_delta *sigma_delta,
+ unsigned int raw_sample)
+{
+ struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta);
+ const struct ad7780_chip_info *chip_info = st->chip_info;
+
+ if ((raw_sample & AD7780_ERR) ||
+ ((raw_sample & chip_info->pattern_mask) != chip_info->pattern))
+ return -EIO;
+
+ if (chip_info->is_ad778x) {
+ st->gain = ad778x_gain[raw_sample & AD7780_GAIN];
+ st->odr = ad778x_odr_avail[raw_sample & AD7780_FILTER];
+ }
+
+ return 0;
+}
+
+static const struct ad_sigma_delta_info ad7780_sigma_delta_info = {
+ .set_mode = ad7780_set_mode,
+ .postprocess_sample = ad7780_postprocess_sample,
+ .has_registers = false,
+};
+
+#define AD7780_CHANNEL(bits, wordsize) \
+ AD_SD_CHANNEL(1, 0, 0, bits, 32, (wordsize) - (bits))
+#define AD7170_CHANNEL(bits, wordsize) \
+ AD_SD_CHANNEL_NO_SAMP_FREQ(1, 0, 0, bits, 32, (wordsize) - (bits))
+
+static const struct ad7780_chip_info ad7780_chip_info_tbl[] = {
+ [ID_AD7170] = {
+ .channel = AD7170_CHANNEL(12, 24),
+ .pattern = AD7170_PATTERN_GOOD,
+ .pattern_mask = AD7170_PATTERN_MASK,
+ .is_ad778x = false,
+ },
+ [ID_AD7171] = {
+ .channel = AD7170_CHANNEL(16, 24),
+ .pattern = AD7170_PATTERN_GOOD,
+ .pattern_mask = AD7170_PATTERN_MASK,
+ .is_ad778x = false,
+ },
+ [ID_AD7780] = {
+ .channel = AD7780_CHANNEL(24, 32),
+ .pattern = AD7780_PATTERN_GOOD,
+ .pattern_mask = AD7780_PATTERN_MASK,
+ .is_ad778x = true,
+ },
+ [ID_AD7781] = {
+ .channel = AD7780_CHANNEL(20, 32),
+ .pattern = AD7780_PATTERN_GOOD,
+ .pattern_mask = AD7780_PATTERN_MASK,
+ .is_ad778x = true,
+ },
+};
+
+static const struct iio_info ad7780_info = {
+ .read_raw = ad7780_read_raw,
+ .write_raw = ad7780_write_raw,
+};
+
+static int ad7780_init_gpios(struct device *dev, struct ad7780_state *st)
+{
+ int ret;
+
+ st->powerdown_gpio = devm_gpiod_get_optional(dev,
+ "powerdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->powerdown_gpio)) {
+ ret = PTR_ERR(st->powerdown_gpio);
+ dev_err(dev, "Failed to request powerdown GPIO: %d\n", ret);
+ return ret;
+ }
+
+ if (!st->chip_info->is_ad778x)
+ return 0;
+
+
+ st->gain_gpio = devm_gpiod_get_optional(dev,
+ "adi,gain",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->gain_gpio)) {
+ ret = PTR_ERR(st->gain_gpio);
+ dev_err(dev, "Failed to request gain GPIO: %d\n", ret);
+ return ret;
+ }
+
+ st->filter_gpio = devm_gpiod_get_optional(dev,
+ "adi,filter",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->filter_gpio)) {
+ ret = PTR_ERR(st->filter_gpio);
+ dev_err(dev, "Failed to request filter GPIO: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ad7780_probe(struct spi_device *spi)
+{
+ struct ad7780_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->gain = 1;
+
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7780_sigma_delta_info);
+
+ st->chip_info =
+ &ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &st->chip_info->channel;
+ indio_dev->num_channels = 1;
+ indio_dev->info = &ad7780_info;
+
+ ret = ad7780_init_gpios(&spi->dev, st);
+ if (ret)
+ goto error_cleanup_buffer_and_trigger;
+
+ st->reg = devm_regulator_get(&spi->dev, "avdd");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified AVdd supply\n");
+ return ret;
+ }
+
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer_and_trigger;
+
+ return 0;
+
+error_cleanup_buffer_and_trigger:
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+error_disable_reg:
+ regulator_disable(st->reg);
+
+ return ret;
+}
+
+static int ad7780_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7780_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7780_id[] = {
+ {"ad7170", ID_AD7170},
+ {"ad7171", ID_AD7171},
+ {"ad7780", ID_AD7780},
+ {"ad7781", ID_AD7781},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7780_id);
+
+static struct spi_driver ad7780_driver = {
+ .driver = {
+ .name = "ad7780",
+ },
+ .probe = ad7780_probe,
+ .remove = ad7780_remove,
+ .id_table = ad7780_id,
+};
+module_spi_driver(ad7780_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7780 and similar ADCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c
index a9ff069..54025ea 100644
--- a/drivers/iio/adc/ad7791.c
+++ b/drivers/iio/adc/ad7791.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index d4bbe5b..bbc41ec 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7785/AD7792/AD7793/AD7794/AD7795 SPI ADC driver
*
* Copyright 2011-2012 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
@@ -822,6 +821,6 @@
};
module_spi_driver(ad7793_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c
index 205c0f1..6223043 100644
--- a/drivers/iio/adc/ad7887.c
+++ b/drivers/iio/adc/ad7887.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7887 SPI ADC driver
*
* Copyright 2010-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
@@ -362,6 +361,6 @@
};
module_spi_driver(ad7887_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7887 ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c
index ffb7e08..3212eb4 100644
--- a/drivers/iio/adc/ad7923.c
+++ b/drivers/iio/adc/ad7923.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7904/AD7914/AD7923/AD7924 SPI ADC driver
*
* Copyright 2011 Analog Devices Inc (from AD7923 Driver)
* Copyright 2012 CS Systemes d'Information
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
@@ -24,9 +23,9 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
-#define AD7923_WRITE_CR (1 << 11) /* write control register */
-#define AD7923_RANGE (1 << 1) /* range to REFin */
-#define AD7923_CODING (1 << 0) /* coding is straight binary */
+#define AD7923_WRITE_CR BIT(11) /* write control register */
+#define AD7923_RANGE BIT(1) /* range to REFin */
+#define AD7923_CODING BIT(0) /* coding is straight binary */
#define AD7923_PM_MODE_AS (1) /* auto shutdown */
#define AD7923_PM_MODE_FS (2) /* full shutdown */
#define AD7923_PM_MODE_OPS (3) /* normal operation */
@@ -40,16 +39,16 @@
#define AD7923_MAX_CHAN 4
-#define AD7923_PM_MODE_WRITE(mode) (mode << 4) /* write mode */
-#define AD7923_CHANNEL_WRITE(channel) (channel << 6) /* write channel */
-#define AD7923_SEQUENCE_WRITE(sequence) (((sequence & 1) << 3) \
- + ((sequence & 2) << 9))
+#define AD7923_PM_MODE_WRITE(mode) ((mode) << 4) /* write mode */
+#define AD7923_CHANNEL_WRITE(channel) ((channel) << 6) /* write channel */
+#define AD7923_SEQUENCE_WRITE(sequence) ((((sequence) & 1) << 3) \
+ + (((sequence) & 2) << 9))
/* write sequence fonction */
/* left shift for CR : bit 11 transmit in first */
#define AD7923_SHIFT_REGISTER 4
/* val = value, dec = left shift, bits = number of bits of the mask */
-#define EXTRACT(val, dec, bits) ((val >> dec) & ((1 << bits) - 1))
+#define EXTRACT(val, dec, bits) (((val) >> (dec)) & ((1 << (bits)) - 1))
struct ad7923_state {
struct spi_device *spi;
@@ -130,7 +129,7 @@
* ad7923_update_scan_mode() setup the spi transfer buffer for the new scan mask
**/
static int ad7923_update_scan_mode(struct iio_dev *indio_dev,
- const unsigned long *active_scan_mask)
+ const unsigned long *active_scan_mask)
{
struct ad7923_state *st = iio_priv(indio_dev);
int i, cmd, len;
@@ -181,7 +180,7 @@
goto done;
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
- iio_get_time_ns(indio_dev));
+ iio_get_time_ns(indio_dev));
done:
iio_trigger_notify_done(indio_dev->trig);
@@ -272,7 +271,7 @@
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
- if (indio_dev == NULL)
+ if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
@@ -314,7 +313,7 @@
return ret;
ret = iio_triggered_buffer_setup(indio_dev, NULL,
- &ad7923_trigger_handler, NULL);
+ &ad7923_trigger_handler, NULL);
if (ret)
goto error_disable_reg;
@@ -363,7 +362,7 @@
};
module_spi_driver(ad7923_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>");
MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c
new file mode 100644
index 0000000..ac0ffff
--- /dev/null
+++ b/drivers/iio/adc/ad7949.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0
+/* ad7949.c - Analog Devices ADC driver 14/16 bits 4/8 channels
+ *
+ * Copyright (C) 2018 CMC NV
+ *
+ * http://www.analog.com/media/en/technical-documentation/data-sheets/AD7949.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define AD7949_MASK_CHANNEL_SEL GENMASK(9, 7)
+#define AD7949_MASK_TOTAL GENMASK(13, 0)
+#define AD7949_OFFSET_CHANNEL_SEL 7
+#define AD7949_CFG_READ_BACK 0x1
+#define AD7949_CFG_REG_SIZE_BITS 14
+
+enum {
+ ID_AD7949 = 0,
+ ID_AD7682,
+ ID_AD7689,
+};
+
+struct ad7949_adc_spec {
+ u8 num_channels;
+ u8 resolution;
+};
+
+static const struct ad7949_adc_spec ad7949_adc_spec[] = {
+ [ID_AD7949] = { .num_channels = 8, .resolution = 14 },
+ [ID_AD7682] = { .num_channels = 4, .resolution = 16 },
+ [ID_AD7689] = { .num_channels = 8, .resolution = 16 },
+};
+
+/**
+ * struct ad7949_adc_chip - AD ADC chip
+ * @lock: protects write sequences
+ * @vref: regulator generating Vref
+ * @iio_dev: reference to iio structure
+ * @spi: reference to spi structure
+ * @resolution: resolution of the chip
+ * @cfg: copy of the configuration register
+ * @current_channel: current channel in use
+ * @buffer: buffer to send / receive data to / from device
+ */
+struct ad7949_adc_chip {
+ struct mutex lock;
+ struct regulator *vref;
+ struct iio_dev *indio_dev;
+ struct spi_device *spi;
+ u8 resolution;
+ u16 cfg;
+ unsigned int current_channel;
+ u32 buffer ____cacheline_aligned;
+};
+
+static bool ad7949_spi_cfg_is_read_back(struct ad7949_adc_chip *ad7949_adc)
+{
+ if (!(ad7949_adc->cfg & AD7949_CFG_READ_BACK))
+ return true;
+
+ return false;
+}
+
+static int ad7949_spi_bits_per_word(struct ad7949_adc_chip *ad7949_adc)
+{
+ int ret = ad7949_adc->resolution;
+
+ if (ad7949_spi_cfg_is_read_back(ad7949_adc))
+ ret += AD7949_CFG_REG_SIZE_BITS;
+
+ return ret;
+}
+
+static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val,
+ u16 mask)
+{
+ int ret;
+ int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc);
+ int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS;
+ struct spi_message msg;
+ struct spi_transfer tx[] = {
+ {
+ .tx_buf = &ad7949_adc->buffer,
+ .len = 4,
+ .bits_per_word = bits_per_word,
+ },
+ };
+
+ ad7949_adc->cfg = (val & mask) | (ad7949_adc->cfg & ~mask);
+ ad7949_adc->buffer = ad7949_adc->cfg << shift;
+ spi_message_init_with_transfers(&msg, tx, 1);
+ ret = spi_sync(ad7949_adc->spi, &msg);
+
+ /*
+ * This delay is to avoid a new request before the required time to
+ * send a new command to the device
+ */
+ udelay(2);
+ return ret;
+}
+
+static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val,
+ unsigned int channel)
+{
+ int ret;
+ int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc);
+ int mask = GENMASK(ad7949_adc->resolution, 0);
+ struct spi_message msg;
+ struct spi_transfer tx[] = {
+ {
+ .rx_buf = &ad7949_adc->buffer,
+ .len = 4,
+ .bits_per_word = bits_per_word,
+ },
+ };
+
+ ret = ad7949_spi_write_cfg(ad7949_adc,
+ channel << AD7949_OFFSET_CHANNEL_SEL,
+ AD7949_MASK_CHANNEL_SEL);
+ if (ret)
+ return ret;
+
+ ad7949_adc->buffer = 0;
+ spi_message_init_with_transfers(&msg, tx, 1);
+ ret = spi_sync(ad7949_adc->spi, &msg);
+ if (ret)
+ return ret;
+
+ /*
+ * This delay is to avoid a new request before the required time to
+ * send a new command to the device
+ */
+ udelay(2);
+
+ ad7949_adc->current_channel = channel;
+
+ if (ad7949_spi_cfg_is_read_back(ad7949_adc))
+ *val = (ad7949_adc->buffer >> AD7949_CFG_REG_SIZE_BITS) & mask;
+ else
+ *val = ad7949_adc->buffer & mask;
+
+ return 0;
+}
+
+#define AD7949_ADC_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec ad7949_adc_channels[] = {
+ AD7949_ADC_CHANNEL(0),
+ AD7949_ADC_CHANNEL(1),
+ AD7949_ADC_CHANNEL(2),
+ AD7949_ADC_CHANNEL(3),
+ AD7949_ADC_CHANNEL(4),
+ AD7949_ADC_CHANNEL(5),
+ AD7949_ADC_CHANNEL(6),
+ AD7949_ADC_CHANNEL(7),
+};
+
+static int ad7949_spi_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev);
+ int ret;
+
+ if (!val)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&ad7949_adc->lock);
+ ret = ad7949_spi_read_channel(ad7949_adc, val, chan->channel);
+ mutex_unlock(&ad7949_adc->lock);
+
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(ad7949_adc->vref);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 5000;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7949_spi_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (readval)
+ *readval = ad7949_adc->cfg;
+ else
+ ret = ad7949_spi_write_cfg(ad7949_adc,
+ writeval & AD7949_MASK_TOTAL, AD7949_MASK_TOTAL);
+
+ return ret;
+}
+
+static const struct iio_info ad7949_spi_info = {
+ .read_raw = ad7949_spi_read_raw,
+ .debugfs_reg_access = ad7949_spi_reg_access,
+};
+
+static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc)
+{
+ int ret;
+ int val;
+
+ /* Sequencer disabled, CFG readback disabled, IN0 as default channel */
+ ad7949_adc->current_channel = 0;
+ ret = ad7949_spi_write_cfg(ad7949_adc, 0x3C79, AD7949_MASK_TOTAL);
+
+ /*
+ * Do two dummy conversions to apply the first configuration setting.
+ * Required only after the start up of the device.
+ */
+ ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel);
+ ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel);
+
+ return ret;
+}
+
+static int ad7949_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ const struct ad7949_adc_spec *spec;
+ struct ad7949_adc_chip *ad7949_adc;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*ad7949_adc));
+ if (!indio_dev) {
+ dev_err(dev, "can not allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = dev->of_node;
+ indio_dev->info = &ad7949_spi_info;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad7949_adc_channels;
+ spi_set_drvdata(spi, indio_dev);
+
+ ad7949_adc = iio_priv(indio_dev);
+ ad7949_adc->indio_dev = indio_dev;
+ ad7949_adc->spi = spi;
+
+ spec = &ad7949_adc_spec[spi_get_device_id(spi)->driver_data];
+ indio_dev->num_channels = spec->num_channels;
+ ad7949_adc->resolution = spec->resolution;
+
+ ad7949_adc->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(ad7949_adc->vref)) {
+ dev_err(dev, "fail to request regulator\n");
+ return PTR_ERR(ad7949_adc->vref);
+ }
+
+ ret = regulator_enable(ad7949_adc->vref);
+ if (ret < 0) {
+ dev_err(dev, "fail to enable regulator\n");
+ return ret;
+ }
+
+ mutex_init(&ad7949_adc->lock);
+
+ ret = ad7949_spi_init(ad7949_adc);
+ if (ret) {
+ dev_err(dev, "enable to init this device: %d\n", ret);
+ goto err;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "fail to register iio device: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ mutex_destroy(&ad7949_adc->lock);
+ regulator_disable(ad7949_adc->vref);
+
+ return ret;
+}
+
+static int ad7949_spi_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ mutex_destroy(&ad7949_adc->lock);
+ regulator_disable(ad7949_adc->vref);
+
+ return 0;
+}
+
+static const struct of_device_id ad7949_spi_of_id[] = {
+ { .compatible = "adi,ad7949" },
+ { .compatible = "adi,ad7682" },
+ { .compatible = "adi,ad7689" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7949_spi_of_id);
+
+static const struct spi_device_id ad7949_spi_id[] = {
+ { "ad7949", ID_AD7949 },
+ { "ad7682", ID_AD7682 },
+ { "ad7689", ID_AD7689 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad7949_spi_id);
+
+static struct spi_driver ad7949_spi_driver = {
+ .driver = {
+ .name = "ad7949",
+ .of_match_table = ad7949_spi_of_id,
+ },
+ .probe = ad7949_spi_probe,
+ .remove = ad7949_spi_remove,
+ .id_table = ad7949_spi_id,
+};
+module_spi_driver(ad7949_spi_driver);
+
+MODULE_AUTHOR("Charles-Antoine Couret <charles-antoine.couret@essensium.com>");
+MODULE_DESCRIPTION("Analog Devices 14/16-bit 8-channel ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c
index e1da67d..f658012 100644
--- a/drivers/iio/adc/ad799x.c
+++ b/drivers/iio/adc/ad799x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* iio/adc/ad799x.c
* Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc.
@@ -11,15 +12,10 @@
* based on linux/drivers/acron/char/pcf8583.c
* Copyright (C) 2000 Russell King
*
- * 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.
- *
* ad799x.c
*
* Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997,
* ad7998 and similar chips.
- *
*/
#include <linux/interrupt.h>
@@ -814,10 +810,10 @@
ret = ad799x_write_config(st, st->chip_config->default_config);
if (ret < 0)
- goto error_disable_reg;
+ goto error_disable_vref;
ret = ad799x_read_config(st);
if (ret < 0)
- goto error_disable_reg;
+ goto error_disable_vref;
st->config = ret;
ret = iio_triggered_buffer_setup(indio_dev, NULL,
@@ -892,6 +888,6 @@
};
module_i2c_driver(ad799x_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD799x ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index fc95107..2640b75 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Support code for Analog Devices Sigma-Delta ADCs
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
@@ -62,7 +61,7 @@
struct spi_transfer t = {
.tx_buf = data,
.len = size + 1,
- .cs_change = sigma_delta->bus_locked,
+ .cs_change = sigma_delta->keep_cs_asserted,
};
struct spi_message m;
int ret;
@@ -121,6 +120,7 @@
if (sigma_delta->info->has_registers) {
data[0] = reg << sigma_delta->info->addr_shift;
data[0] |= sigma_delta->info->read_mask;
+ data[0] |= sigma_delta->comm;
spi_message_add_tail(&t[0], &m);
}
spi_message_add_tail(&t[1], &m);
@@ -217,6 +217,7 @@
spi_bus_lock(sigma_delta->spi->master);
sigma_delta->bus_locked = true;
+ sigma_delta->keep_cs_asserted = true;
reinit_completion(&sigma_delta->completion);
ret = ad_sigma_delta_set_mode(sigma_delta, mode);
@@ -234,9 +235,10 @@
ret = 0;
}
out:
+ sigma_delta->keep_cs_asserted = false;
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
sigma_delta->bus_locked = false;
spi_bus_unlock(sigma_delta->spi->master);
- ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
return ret;
}
@@ -278,6 +280,7 @@
{
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
unsigned int sample, raw_sample;
+ unsigned int data_reg;
int ret = 0;
if (iio_buffer_enabled(indio_dev))
@@ -288,6 +291,7 @@
spi_bus_lock(sigma_delta->spi->master);
sigma_delta->bus_locked = true;
+ sigma_delta->keep_cs_asserted = true;
reinit_completion(&sigma_delta->completion);
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
@@ -297,15 +301,17 @@
ret = wait_for_completion_interruptible_timeout(
&sigma_delta->completion, HZ);
- sigma_delta->bus_locked = false;
- spi_bus_unlock(sigma_delta->spi->master);
-
if (ret == 0)
ret = -EIO;
if (ret < 0)
goto out;
- ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_DATA,
+ if (sigma_delta->info->data_reg != 0)
+ data_reg = sigma_delta->info->data_reg;
+ else
+ data_reg = AD_SD_REG_DATA;
+
+ ret = ad_sd_read_reg(sigma_delta, data_reg,
DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8),
&raw_sample);
@@ -315,7 +321,10 @@
sigma_delta->irq_dis = true;
}
+ sigma_delta->keep_cs_asserted = false;
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+ sigma_delta->bus_locked = false;
+ spi_bus_unlock(sigma_delta->spi->master);
mutex_unlock(&indio_dev->mlock);
if (ret)
@@ -348,10 +357,12 @@
ret = ad_sigma_delta_set_channel(sigma_delta,
indio_dev->channels[channel].address);
if (ret)
- goto err_predisable;
+ return ret;
spi_bus_lock(sigma_delta->spi->master);
sigma_delta->bus_locked = true;
+ sigma_delta->keep_cs_asserted = true;
+
ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS);
if (ret)
goto err_unlock;
@@ -363,7 +374,6 @@
err_unlock:
spi_bus_unlock(sigma_delta->spi->master);
-err_predisable:
return ret;
}
@@ -380,6 +390,7 @@
sigma_delta->irq_dis = true;
}
+ sigma_delta->keep_cs_asserted = false;
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
sigma_delta->bus_locked = false;
@@ -392,6 +403,7 @@
struct iio_dev *indio_dev = pf->indio_dev;
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
unsigned int reg_size;
+ unsigned int data_reg;
uint8_t data[16];
int ret;
@@ -401,18 +413,23 @@
indio_dev->channels[0].scan_type.shift;
reg_size = DIV_ROUND_UP(reg_size, 8);
+ if (sigma_delta->info->data_reg != 0)
+ data_reg = sigma_delta->info->data_reg;
+ else
+ data_reg = AD_SD_REG_DATA;
+
switch (reg_size) {
case 4:
case 2:
case 1:
- ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
- reg_size, &data[0]);
+ ret = ad_sd_read_reg_raw(sigma_delta, data_reg, reg_size,
+ &data[0]);
break;
case 3:
/* We store 24 bit samples in a 32 bit word. Keep the upper
* byte set to zero. */
- ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
- reg_size, &data[1]);
+ ret = ad_sd_read_reg_raw(sigma_delta, data_reg, reg_size,
+ &data[1]);
break;
}
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index 9515ca1..d3fc39d 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aspeed AST2400/2500 ADC
*
* Copyright (C) 2017 Google, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
*/
#include <linux/clk.h>
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
index d5ea84c..a2837a0 100644
--- a/drivers/iio/adc/at91-sama5d2_adc.c
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Atmel ADC driver for SAMA5D2 devices and compatible.
*
* Copyright (C) 2015 Atmel,
* 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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>
@@ -1586,8 +1578,7 @@
static ssize_t at91_adc_get_fifo_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct iio_dev *indio_dev =
- platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(indio_dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", !!st->dma_st.dma_chan);
@@ -1596,8 +1587,7 @@
static ssize_t at91_adc_get_watermark(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct iio_dev *indio_dev =
- platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(indio_dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", st->dma_st.watermark);
@@ -1849,8 +1839,7 @@
static __maybe_unused int at91_adc_suspend(struct device *dev)
{
- struct iio_dev *indio_dev =
- platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(indio_dev);
/*
@@ -1870,8 +1859,7 @@
static __maybe_unused int at91_adc_resume(struct device *dev)
{
- struct iio_dev *indio_dev =
- platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(indio_dev);
int ret;
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index 75d2f73..abe9985 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the ADC present in the Atmel AT91 evaluation boards.
*
* Copyright 2011 Free Electrons
- *
- * Licensed under the GPLv2 or later.
*/
#include <linux/bitmap.h>
@@ -704,23 +703,29 @@
ret = wait_event_interruptible_timeout(st->wq_data_avail,
st->done,
msecs_to_jiffies(1000));
- if (ret == 0)
- ret = -ETIMEDOUT;
- if (ret < 0) {
- mutex_unlock(&st->lock);
- return ret;
- }
- *val = st->last_value;
-
+ /* Disable interrupts, regardless if adc conversion was
+ * successful or not
+ */
at91_adc_writel(st, AT91_ADC_CHDR,
AT91_ADC_CH(chan->channel));
at91_adc_writel(st, AT91_ADC_IDR, BIT(chan->channel));
- st->last_value = 0;
- st->done = false;
+ if (ret > 0) {
+ /* a valid conversion took place */
+ *val = st->last_value;
+ st->last_value = 0;
+ st->done = false;
+ ret = IIO_VAL_INT;
+ } else if (ret == 0) {
+ /* conversion timeout */
+ dev_err(&idev->dev, "ADC Channel %d timeout.\n",
+ chan->channel);
+ ret = -ETIMEDOUT;
+ }
+
mutex_unlock(&st->lock);
- return IIO_VAL_INT;
+ return ret;
case IIO_CHAN_INFO_SCALE:
*val = st->vref_mv;
@@ -1174,10 +1179,8 @@
idev->info = &at91_adc_info;
st->irq = platform_get_irq(pdev, 0);
- if (st->irq < 0) {
- dev_err(&pdev->dev, "No IRQ ID is designated\n");
+ if (st->irq < 0)
return -ENODEV;
- }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1354,7 +1357,7 @@
#ifdef CONFIG_PM_SLEEP
static int at91_adc_suspend(struct device *dev)
{
- struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *idev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(idev);
pinctrl_pm_select_sleep_state(dev);
@@ -1365,7 +1368,7 @@
static int at91_adc_resume(struct device *dev)
{
- struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *idev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(idev);
clk_prepare_enable(st->clk);
diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
index 5be7892..8805948 100644
--- a/drivers/iio/adc/axp20x_adc.c
+++ b/drivers/iio/adc/axp20x_adc.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* ADC driver for AXP20X and AXP22X PMICs
*
* Copyright (c) 2016 Free Electrons NextThing Co.
* Quentin Schulz <quentin.schulz@free-electrons.com>
- *
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License version 2 as published by the
- * Free Software Foundation.
*/
#include <linux/completion.h>
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
index 031d568..8ea2aed 100644
--- a/drivers/iio/adc/axp288_adc.c
+++ b/drivers/iio/adc/axp288_adc.c
@@ -1,21 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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/dmi.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
@@ -27,9 +19,23 @@
#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
+/*
+ * This mask enables all ADCs except for the battery temp-sensor (TS), that is
+ * left as-is to avoid breaking charging on devices without a temp-sensor.
+ */
+#define AXP288_ADC_EN_MASK 0xF0
+#define AXP288_ADC_TS_ENABLE 0x01
+
+#define AXP288_ADC_TS_BIAS_MASK GENMASK(5, 4)
+#define AXP288_ADC_TS_BIAS_20UA (0 << 4)
+#define AXP288_ADC_TS_BIAS_40UA (1 << 4)
+#define AXP288_ADC_TS_BIAS_60UA (2 << 4)
+#define AXP288_ADC_TS_BIAS_80UA (3 << 4)
+#define AXP288_ADC_TS_CURRENT_ON_OFF_MASK GENMASK(1, 0)
+#define AXP288_ADC_TS_CURRENT_OFF (0 << 0)
+#define AXP288_ADC_TS_CURRENT_ON_WHEN_CHARGING (1 << 0)
+#define AXP288_ADC_TS_CURRENT_ON_ONDEMAND (2 << 0)
+#define AXP288_ADC_TS_CURRENT_ON (3 << 0)
enum axp288_adc_id {
AXP288_ADC_TS,
@@ -44,6 +50,7 @@
struct axp288_adc_info {
int irq;
struct regmap *regmap;
+ bool ts_enabled;
};
static const struct iio_chan_spec axp288_adc_channels[] = {
@@ -115,21 +122,33 @@
return IIO_VAL_INT;
}
-static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
- unsigned long address)
+/*
+ * The current-source used for the battery temp-sensor (TS) is shared
+ * with the GPADC. For proper fuel-gauge and charger operation the TS
+ * current-source needs to be permanently on. But to read the GPADC we
+ * need to temporary switch the TS current-source to ondemand, so that
+ * the GPADC can use it, otherwise we will always read an all 0 value.
+ */
+static int axp288_adc_set_ts(struct axp288_adc_info *info,
+ unsigned int mode, unsigned long address)
{
int ret;
- /* channels other than GPADC do not need to switch TS pin */
+ /* No need to switch the current-source if the TS pin is disabled */
+ if (!info->ts_enabled)
+ return 0;
+
+ /* Channels other than GPADC do not need the current source */
if (address != AXP288_GP_ADC_H)
return 0;
- ret = regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_CURRENT_ON_OFF_MASK, mode);
if (ret)
return ret;
/* When switching to the GPADC pin give things some time to settle */
- if (mode == AXP288_ADC_TS_PIN_GPADC)
+ if (mode == AXP288_ADC_TS_CURRENT_ON_ONDEMAND)
usleep_range(6000, 10000);
return 0;
@@ -145,14 +164,14 @@
mutex_lock(&indio_dev->mlock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
+ if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON_ONDEMAND,
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,
+ if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON,
chan->address))
dev_err(&indio_dev->dev, "TS pin restore\n");
break;
@@ -164,13 +183,61 @@
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;
+/*
+ * We rely on the machine's firmware to correctly setup the TS pin bias current
+ * at boot. This lists systems with broken fw where we need to set it ourselves.
+ */
+static const struct dmi_system_id axp288_adc_ts_bias_override[] = {
+ {
+ /* Lenovo Ideapad 100S (11 inch) */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 100S-11IBY"),
+ },
+ .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA,
+ },
+ {}
+};
- return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
+static int axp288_adc_initialize(struct axp288_adc_info *info)
+{
+ const struct dmi_system_id *bias_override;
+ int ret, adc_enable_val;
+
+ bias_override = dmi_first_match(axp288_adc_ts_bias_override);
+ if (bias_override) {
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_BIAS_MASK,
+ (uintptr_t)bias_override->driver_data);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Determine if the TS pin is enabled and set the TS current-source
+ * accordingly.
+ */
+ ret = regmap_read(info->regmap, AXP20X_ADC_EN1, &adc_enable_val);
+ if (ret)
+ return ret;
+
+ if (adc_enable_val & AXP288_ADC_TS_ENABLE) {
+ info->ts_enabled = true;
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
+ AXP288_ADC_TS_CURRENT_ON);
+ } else {
+ info->ts_enabled = false;
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
+ AXP288_ADC_TS_CURRENT_OFF);
+ }
+ if (ret)
+ return ret;
+
+ /* Turn on the ADC for all channels except TS, leave TS as is */
+ return regmap_update_bits(info->regmap, AXP20X_ADC_EN1,
+ AXP288_ADC_EN_MASK, AXP288_ADC_EN_MASK);
}
static const struct iio_info axp288_adc_iio_info = {
@@ -190,17 +257,15 @@
info = iio_priv(indio_dev);
info->irq = platform_get_irq(pdev, 0);
- if (info->irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
+ if (info->irq < 0)
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);
+ ret = axp288_adc_initialize(info);
if (ret) {
dev_err(&pdev->dev, "unable to enable ADC device\n");
return ret;
diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c
index 7af59a4..646ebdc 100644
--- a/drivers/iio/adc/bcm_iproc_adc.c
+++ b/drivers/iio/adc/bcm_iproc_adc.c
@@ -1,17 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2016 Broadcom
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation (the "GPL").
- *
- * 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 version 2 (GPLv2) for more details.
- *
- * You should have received a copy of the GNU General Public License
- * version 2 (GPLv2) along with this source code.
*/
#include <linux/module.h>
@@ -551,11 +540,8 @@
}
adc_priv->irqno = platform_get_irq(pdev, 0);
- if (adc_priv->irqno <= 0) {
- dev_err(&pdev->dev, "platform_get_irq failed\n");
- ret = -ENODEV;
- return ret;
- }
+ if (adc_priv->irqno <= 0)
+ return -ENODEV;
ret = regmap_update_bits(adc_priv->regmap, IPROC_REGCTL2,
IPROC_ADC_AUXIN_SCAN_ENA, 0);
diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c
index 707d8b2..f93f1d9 100644
--- a/drivers/iio/adc/cc10001_adc.c
+++ b/drivers/iio/adc/cc10001_adc.c
@@ -1,10 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c
index 9ad6042..2d616ca 100644
--- a/drivers/iio/adc/cpcap-adc.c
+++ b/drivers/iio/adc/cpcap-adc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2017 Tony Lindgren <tony@atomide.com>
*
@@ -5,15 +6,6 @@
* earlier driver found in the Motorola Linux kernel:
*
* Copyright (C) 2009-2010 Motorola, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/delay.h>
diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c
index 0a5d9ce..ae8bcc3 100644
--- a/drivers/iio/adc/da9150-gpadc.c
+++ b/drivers/iio/adc/da9150-gpadc.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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>
@@ -341,10 +337,8 @@
init_completion(&gpadc->complete);
irq = platform_get_irq_byname(pdev, "GPADC");
- if (irq < 0) {
- dev_err(dev, "Failed to get IRQ: %d\n", irq);
+ if (irq < 0)
return irq;
- }
ret = devm_request_threaded_irq(dev, irq, NULL, da9150_gpadc_irq,
IRQF_ONESHOT, "GPADC", gpadc);
diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
index c64c667..5fa78c2 100644
--- a/drivers/iio/adc/dln2-adc.c
+++ b/drivers/iio/adc/dln2-adc.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the Diolan DLN-2 USB-ADC adapter
*
* Copyright (c) 2017 Jack Andersen
- *
- * 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.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c
index 4ebda8a..28f3d67 100644
--- a/drivers/iio/adc/envelope-detector.c
+++ b/drivers/iio/adc/envelope-detector.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Driver for an envelope detector using a DAC and a comparator
*
* Copyright (C) 2016 Axentia Technologies AB
*
* Author: Peter Rosin <peda@axentia.se>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
/*
@@ -360,11 +357,8 @@
}
env->comp_irq = platform_get_irq_byname(pdev, "comp");
- if (env->comp_irq < 0) {
- if (env->comp_irq != -EPROBE_DEFER)
- dev_err(dev, "failed to get compare interrupt\n");
+ if (env->comp_irq < 0)
return env->comp_irq;
- }
ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr,
0, "envelope-detector", env);
diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c
index 5036c39..5c97e8a 100644
--- a/drivers/iio/adc/ep93xx_adc.c
+++ b/drivers/iio/adc/ep93xx_adc.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for ADC module on the Cirrus Logic EP93xx series of SoCs
*
* Copyright (C) 2015 Alexander Sverdlin
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* The driver uses polling to get the conversion status. According to EP93xx
* datasheets, reading ADCResult register starts the conversion, but user is also
* responsible for ensuring that delay between adjacent conversion triggers is
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index f10443f..42a3ced 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* exynos_adc.c - Support for ADC in EXYNOS SoCs
*
* 8 ~ 10 channel, 10/12-bit ADC
*
* Copyright (C) 2013 Naveen Krishna Chatradhi <ch.naveen@samsung.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.
- *
- * 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>
@@ -115,6 +102,8 @@
#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 */
#define ADC_CON_EN_START (1u << 0)
@@ -270,6 +259,19 @@
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 */
@@ -282,6 +284,16 @@
.start_conv = exynos_adc_v1_start_conv,
};
+static const struct exynos_adc_data exynos_adc_s5pv210_data = {
+ .num_channels = MAX_S5PV210_ADC_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+
+ .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 void exynos_adc_s3c2416_start_conv(struct exynos_adc *info,
unsigned long addr)
{
@@ -479,6 +491,12 @@
.compatible = "samsung,s3c6410-adc",
.data = &exynos_adc_s3c64xx_data,
}, {
+ .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,
}, {
@@ -787,10 +805,8 @@
}
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
+ if (irq < 0)
return irq;
- }
info->irq = irq;
irq = platform_get_irq(pdev, 1);
@@ -915,7 +931,7 @@
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/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c
index 929c617..fa71489 100644
--- a/drivers/iio/adc/fsl-imx25-gcq.c
+++ b/drivers/iio/adc/fsl-imx25-gcq.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
*
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License version 2 as published by the
- * Free Software Foundation.
- *
* This is the driver for the imx25 GCQ (Generic Conversion Queue)
* connected to the imx25 ADC.
*/
@@ -343,7 +340,6 @@
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq <= 0) {
- dev_err(dev, "Failed to get IRQ\n");
ret = priv->irq;
if (!ret)
ret = -ENXIO;
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
index 6f6c9a3..8da45bf 100644
--- a/drivers/iio/adc/hi8435.c
+++ b/drivers/iio/adc/hi8435.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Holt Integrated Circuits HI-8435 threshold detector driver
*
* Copyright (C) 2015 Zodiac Inflight Innovations
* Copyright (C) 2015 Cogent Embedded, Inc.
- *
- * 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/delay.h>
@@ -460,6 +456,11 @@
return IRQ_HANDLED;
}
+static void hi8435_triggered_event_cleanup(void *data)
+{
+ iio_triggered_event_cleanup(data);
+}
+
static int hi8435_probe(struct spi_device *spi)
{
struct iio_dev *idev;
@@ -481,7 +482,7 @@
hi8435_writeb(priv, HI8435_CTRL_REG, 0);
} else {
udelay(5);
- gpiod_set_value(reset_gpio, 1);
+ gpiod_set_value_cansleep(reset_gpio, 1);
}
spi_set_drvdata(spi, idev);
@@ -517,27 +518,13 @@
if (ret)
return ret;
- ret = iio_device_register(idev);
- if (ret < 0) {
- dev_err(&spi->dev, "unable to register device\n");
- goto unregister_triggered_event;
- }
+ ret = devm_add_action_or_reset(&spi->dev,
+ hi8435_triggered_event_cleanup,
+ idev);
+ if (ret)
+ return ret;
- return 0;
-
-unregister_triggered_event:
- iio_triggered_event_cleanup(idev);
- return ret;
-}
-
-static int hi8435_remove(struct spi_device *spi)
-{
- struct iio_dev *idev = spi_get_drvdata(spi);
-
- iio_device_unregister(idev);
- iio_triggered_event_cleanup(idev);
-
- return 0;
+ return devm_iio_device_register(&spi->dev, idev);
}
static const struct of_device_id hi8435_dt_ids[] = {
@@ -558,7 +545,6 @@
.of_match_table = of_match_ptr(hi8435_dt_ids),
},
.probe = hi8435_probe,
- .remove = hi8435_remove,
.id_table = hi8435_id,
};
module_spi_driver(hi8435_driver);
diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c
index 36b59d8..62e6c8b 100644
--- a/drivers/iio/adc/hx711.c
+++ b/drivers/iio/adc/hx711.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HX711: analog to digital converter for weight sensor module
*
* Copyright (c) 2016 Andreas Klinger <ak@it-klinger.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.
*/
#include <linux/err.h>
#include <linux/kernel.h>
@@ -109,14 +100,14 @@
static int hx711_cycle(struct hx711_data *hx711_data)
{
- int val;
+ unsigned long flags;
/*
* if preempted for more then 60us while PD_SCK is high:
* hx711 is going in reset
* ==> measuring is false
*/
- preempt_disable();
+ local_irq_save(flags);
gpiod_set_value(hx711_data->gpiod_pd_sck, 1);
/*
@@ -126,7 +117,6 @@
*/
ndelay(hx711_data->data_ready_delay_ns);
- val = gpiod_get_value(hx711_data->gpiod_dout);
/*
* here we are not waiting for 0.2 us as suggested by the datasheet,
* because the oscilloscope showed in a test scenario
@@ -134,7 +124,7 @@
* and 0.56 us for PD_SCK low on TI Sitara with 800 MHz
*/
gpiod_set_value(hx711_data->gpiod_pd_sck, 0);
- preempt_enable();
+ local_irq_restore(flags);
/*
* make it a square wave for addressing cases with capacitance on
@@ -142,7 +132,8 @@
*/
ndelay(hx711_data->data_ready_delay_ns);
- return val;
+ /* sample as late as possible */
+ return gpiod_get_value(hx711_data->gpiod_dout);
}
static int hx711_read(struct hx711_data *hx711_data)
diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c
index ad6764f..2a2fbf7 100644
--- a/drivers/iio/adc/imx7d_adc.c
+++ b/drivers/iio/adc/imx7d_adc.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Freescale i.MX7D ADC driver
*
* Copyright (C) 2015 Freescale Semiconductor, Inc.
- *
- * 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/clk.h>
@@ -82,6 +78,7 @@
#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000
#define IMX7D_ADC_TIMEOUT msecs_to_jiffies(100)
+#define IMX7D_ADC_INPUT_CLK 24000000
enum imx7d_adc_clk_pre_div {
IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
@@ -104,8 +101,6 @@
enum imx7d_adc_average_num avg_num;
u32 core_time_unit; /* impact the sample rate */
-
- bool average_en;
};
struct imx7d_adc {
@@ -183,7 +178,6 @@
info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
info->adc_feature.core_time_unit = 1;
- info->adc_feature.average_en = true;
}
static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
@@ -244,9 +238,8 @@
/* the channel choose single conversion, and enable average mode */
cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
- IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE);
- if (info->adc_feature.average_en)
- cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN;
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE |
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN);
/*
* physical channel 0 chose logical channel A
@@ -276,13 +269,11 @@
static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
{
- /* input clock is always 24MHz */
- u32 input_clk = 24000000;
u32 analogue_core_clk;
u32 core_time_unit = info->adc_feature.core_time_unit;
u32 tmp;
- analogue_core_clk = input_clk / info->pre_div_num;
+ analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num;
tmp = (core_time_unit + 1) * 6;
return analogue_core_clk / tmp;
@@ -388,8 +379,9 @@
* timeout flags.
*/
if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
- pr_err("%s: ADC got conversion time out interrupt: 0x%08x\n",
- dev_name(info->dev), status);
+ dev_err(info->dev,
+ "ADC got conversion time out interrupt: 0x%08x\n",
+ status);
status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
}
@@ -433,136 +425,7 @@
writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
}
-static int imx7d_adc_probe(struct platform_device *pdev)
-{
- struct imx7d_adc *info;
- struct iio_dev *indio_dev;
- struct resource *mem;
- int irq;
- int ret;
-
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
- if (!indio_dev) {
- dev_err(&pdev->dev, "Failed allocating iio device\n");
- return -ENOMEM;
- }
-
- info = iio_priv(indio_dev);
- info->dev = &pdev->dev;
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- info->regs = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(info->regs)) {
- ret = PTR_ERR(info->regs);
- dev_err(&pdev->dev,
- "Failed to remap adc memory, err = %d\n", ret);
- return ret;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "No irq resource?\n");
- return irq;
- }
-
- info->clk = devm_clk_get(&pdev->dev, "adc");
- if (IS_ERR(info->clk)) {
- ret = PTR_ERR(info->clk);
- dev_err(&pdev->dev, "Failed getting clock, err = %d\n", ret);
- return ret;
- }
-
- info->vref = devm_regulator_get(&pdev->dev, "vref");
- if (IS_ERR(info->vref)) {
- ret = PTR_ERR(info->vref);
- dev_err(&pdev->dev,
- "Failed getting reference voltage, err = %d\n", ret);
- return ret;
- }
-
- ret = regulator_enable(info->vref);
- if (ret) {
- dev_err(&pdev->dev,
- "Can't enable adc reference top voltage, err = %d\n",
- ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, indio_dev);
-
- init_completion(&info->completion);
-
- indio_dev->name = dev_name(&pdev->dev);
- indio_dev->dev.parent = &pdev->dev;
- indio_dev->info = &imx7d_adc_iio_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = imx7d_adc_iio_channels;
- indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
-
- ret = clk_prepare_enable(info->clk);
- if (ret) {
- dev_err(&pdev->dev,
- "Could not prepare or enable the clock.\n");
- goto error_adc_clk_enable;
- }
-
- ret = devm_request_irq(info->dev, irq,
- imx7d_adc_isr, 0,
- dev_name(&pdev->dev), info);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed requesting irq, irq = %d\n", irq);
- goto error_iio_device_register;
- }
-
- imx7d_adc_feature_config(info);
- imx7d_adc_hw_init(info);
-
- ret = iio_device_register(indio_dev);
- if (ret) {
- imx7d_adc_power_down(info);
- dev_err(&pdev->dev, "Couldn't register the device.\n");
- goto error_iio_device_register;
- }
-
- return 0;
-
-error_iio_device_register:
- clk_disable_unprepare(info->clk);
-error_adc_clk_enable:
- regulator_disable(info->vref);
-
- return ret;
-}
-
-static int imx7d_adc_remove(struct platform_device *pdev)
-{
- struct iio_dev *indio_dev = platform_get_drvdata(pdev);
- struct imx7d_adc *info = iio_priv(indio_dev);
-
- iio_device_unregister(indio_dev);
-
- imx7d_adc_power_down(info);
-
- clk_disable_unprepare(info->clk);
- regulator_disable(info->vref);
-
- return 0;
-}
-
-static int __maybe_unused imx7d_adc_suspend(struct device *dev)
-{
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct imx7d_adc *info = iio_priv(indio_dev);
-
- imx7d_adc_power_down(info);
-
- clk_disable_unprepare(info->clk);
- regulator_disable(info->vref);
-
- return 0;
-}
-
-static int __maybe_unused imx7d_adc_resume(struct device *dev)
+static int imx7d_adc_enable(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct imx7d_adc *info = iio_priv(indio_dev);
@@ -589,11 +452,105 @@
return 0;
}
-static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_suspend, imx7d_adc_resume);
+static int imx7d_adc_disable(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ imx7d_adc_power_down(info);
+
+ clk_disable_unprepare(info->clk);
+ regulator_disable(info->vref);
+
+ return 0;
+}
+
+static void __imx7d_adc_disable(void *data)
+{
+ imx7d_adc_disable(data);
+}
+
+static int imx7d_adc_probe(struct platform_device *pdev)
+{
+ struct imx7d_adc *info;
+ struct iio_dev *indio_dev;
+ struct device *dev = &pdev->dev;
+ int irq;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "Failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+ info->dev = dev;
+
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ info->clk = devm_clk_get(dev, "adc");
+ if (IS_ERR(info->clk)) {
+ ret = PTR_ERR(info->clk);
+ dev_err(dev, "Failed getting clock, err = %d\n", ret);
+ return ret;
+ }
+
+ info->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(info->vref)) {
+ ret = PTR_ERR(info->vref);
+ dev_err(dev,
+ "Failed getting reference voltage, err = %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&info->completion);
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &imx7d_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = imx7d_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
+
+ ret = devm_request_irq(dev, irq, imx7d_adc_isr, 0, dev_name(dev), info);
+ if (ret < 0) {
+ dev_err(dev, "Failed requesting irq, irq = %d\n", irq);
+ return ret;
+ }
+
+ imx7d_adc_feature_config(info);
+
+ ret = imx7d_adc_enable(&indio_dev->dev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, __imx7d_adc_disable,
+ &indio_dev->dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_disable, imx7d_adc_enable);
static struct platform_driver imx7d_adc_driver = {
.probe = imx7d_adc_probe,
- .remove = imx7d_adc_remove,
.driver = {
.name = "imx7d_adc",
.of_match_table = imx7d_adc_match,
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
index d123962..bdd7cba 100644
--- a/drivers/iio/adc/ina2xx-adc.c
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -250,6 +250,7 @@
*val2 = chip->shunt_resistor_uohm;
return IIO_VAL_FRACTIONAL;
}
+ return -EINVAL;
case IIO_CHAN_INFO_HARDWAREGAIN:
switch (chan->address) {
@@ -262,6 +263,7 @@
*val = chip->range_vbus == 32 ? 1 : 2;
return IIO_VAL_INT;
}
+ return -EINVAL;
}
return -EINVAL;
diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c
new file mode 100644
index 0000000..e234970
--- /dev/null
+++ b/drivers/iio/adc/ingenic-adc.c
@@ -0,0 +1,416 @@
+// 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/kernel.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_ADCLK 0x28
+
+#define JZ_ADC_REG_CFG_BAT_MD BIT(4)
+#define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0
+#define JZ_ADC_REG_ADCLK_CLKDIV10US_LSB 16
+
+#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;
+
+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;
+ int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc);
+};
+
+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 int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
+{
+ struct clk *parent_clk;
+ unsigned long parent_rate, rate;
+ unsigned int div_main, div_10us;
+
+ parent_clk = clk_get_parent(adc->clk);
+ if (!parent_clk) {
+ dev_err(dev, "ADC clock has no parent\n");
+ return -ENODEV;
+ }
+ parent_rate = clk_get_rate(parent_clk);
+
+ /*
+ * The JZ4725B ADC works at 500 kHz to 8 MHz.
+ * We pick the highest rate possible.
+ * In practice we typically get 6 MHz, half of the 12 MHz EXT clock.
+ */
+ div_main = DIV_ROUND_UP(parent_rate, 8000000);
+ div_main = clamp(div_main, 1u, 64u);
+ rate = parent_rate / div_main;
+ if (rate < 500000 || rate > 8000000) {
+ dev_err(dev, "No valid divider for ADC main clock\n");
+ return -EINVAL;
+ }
+
+ /* We also need a divider that produces a 10us clock. */
+ div_10us = DIV_ROUND_UP(rate, 100000);
+
+ writel(((div_10us - 1) << JZ_ADC_REG_ADCLK_CLKDIV10US_LSB) |
+ (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
+ adc->base + JZ_ADC_REG_ADCLK);
+
+ return 0;
+}
+
+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),
+ .init_clk_div = jz4725b_adc_init_clk_div,
+};
+
+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),
+ .init_clk_div = NULL, /* no ADCLK register on JZ4740 */
+};
+
+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))
+ 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;
+ }
+
+ /* Set clock dividers. */
+ if (soc_data->init_clk_div) {
+ ret = soc_data->init_clk_div(dev, adc);
+ if (ret) {
+ clk_disable_unprepare(adc->clk);
+ 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/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c
index 3bc4df9..c1fc1b6 100644
--- a/drivers/iio/adc/lp8788_adc.c
+++ b/drivers/iio/adc/lp8788_adc.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TI LP8788 MFD - ADC driver
*
* Copyright 2012 Texas Instruments
*
* Author: Milo(Woogyom) Kim <milo.kim@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/delay.h>
diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c
index 041dc4a..e400a95 100644
--- a/drivers/iio/adc/lpc18xx_adc.c
+++ b/drivers/iio/adc/lpc18xx_adc.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO ADC driver for NXP LPC18xx ADC
*
* Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* UNSUPPORTED hardware features:
* - Hardware triggers
* - Burst mode
diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c
index 20b3669..b896f7f 100644
--- a/drivers/iio/adc/lpc32xx_adc.c
+++ b/drivers/iio/adc/lpc32xx_adc.c
@@ -1,39 +1,21 @@
+// 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/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/completion.h>
-#include <linux/of.h>
-
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
+#include <linux/regulator/consumer.h>
/*
* LPC32XX registers definitions
@@ -65,6 +47,7 @@
void __iomem *adc_base;
struct clk *clk;
struct completion completion;
+ struct regulator *vref;
u32 value;
};
@@ -77,7 +60,9 @@
{
struct lpc32xx_adc_state *st = iio_priv(indio_dev);
int ret;
- if (mask == IIO_CHAN_INFO_RAW) {
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
mutex_lock(&indio_dev->mlock);
ret = clk_prepare_enable(st->clk);
if (ret) {
@@ -97,22 +82,36 @@
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT;
- }
- return -EINVAL;
+ case IIO_CHAN_INFO_SCALE:
+ *val = regulator_get_voltage(st->vref) / 1000;
+ *val2 = 10;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
}
static const struct iio_info lpc32xx_adc_iio_info = {
.read_raw = &lpc32xx_read_raw,
};
-#define LPC32XX_ADC_CHANNEL(_index) { \
+#define LPC32XX_ADC_CHANNEL_BASE(_index) \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _index, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.address = LPC32XXAD_IN * _index, \
- .scan_index = _index, \
+ .scan_index = _index,
+
+#define LPC32XX_ADC_CHANNEL(_index) { \
+ LPC32XX_ADC_CHANNEL_BASE(_index) \
+}
+
+#define LPC32XX_ADC_SCALE_CHANNEL(_index) { \
+ LPC32XX_ADC_CHANNEL_BASE(_index) \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
}
static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
@@ -121,6 +120,12 @@
LPC32XX_ADC_CHANNEL(2),
};
+static const struct iio_chan_spec lpc32xx_adc_iio_scale_channels[] = {
+ LPC32XX_ADC_SCALE_CHANNEL(0),
+ LPC32XX_ADC_SCALE_CHANNEL(1),
+ LPC32XX_ADC_SCALE_CHANNEL(2),
+};
+
static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id)
{
struct lpc32xx_adc_state *st = dev_id;
@@ -167,10 +172,8 @@
}
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- dev_err(&pdev->dev, "failed getting interrupt resource\n");
+ if (irq <= 0)
return -ENXIO;
- }
retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
LPC32XXAD_NAME, st);
@@ -179,6 +182,15 @@
return retval;
}
+ st->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(st->vref)) {
+ iodev->channels = lpc32xx_adc_iio_channels;
+ dev_info(&pdev->dev,
+ "Missing vref regulator: No scaling available\n");
+ } else {
+ iodev->channels = lpc32xx_adc_iio_scale_channels;
+ }
+
platform_set_drvdata(pdev, iodev);
init_completion(&st->completion);
@@ -187,7 +199,6 @@
iodev->dev.parent = &pdev->dev;
iodev->info = &lpc32xx_adc_iio_info;
iodev->modes = INDIO_DIRECT_MODE;
- iodev->channels = lpc32xx_adc_iio_channels;
iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels);
retval = devm_iio_device_register(&pdev->dev, iodev);
diff --git a/drivers/iio/adc/ltc2471.c b/drivers/iio/adc/ltc2471.c
index b88102b..55fab61 100644
--- a/drivers/iio/adc/ltc2471.c
+++ b/drivers/iio/adc/ltc2471.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Linear Technology LTC2471 and LTC2473 voltage monitors
* The LTC2473 is identical to the 2471, but reports a differential signal.
*
* Copyright (C) 2017 Topic Embedded Products
* Author: Mike Looijmans <mike.looijmans@topic.nl>
- *
- * License: GPLv2
*/
#include <linux/err.h>
diff --git a/drivers/iio/adc/ltc2485.c b/drivers/iio/adc/ltc2485.c
index b24c140..c418466 100644
--- a/drivers/iio/adc/ltc2485.c
+++ b/drivers/iio/adc/ltc2485.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ltc2485.c - Driver for Linear Technology LTC2485 ADC
*
* Copyright (C) 2016 Alison Schofield <amsfield22@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* Datasheet: http://cds.linear.com/docs/en/datasheet/2485fd.pdf
*/
diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c
index f1f7cdf..4704060 100644
--- a/drivers/iio/adc/ltc2497.c
+++ b/drivers/iio/adc/ltc2497.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ltc2497.c - Driver for Analog Devices/Linear Technology LTC2497 ADC
*
* Copyright (C) 2017 Analog Devices Inc.
*
- * Licensed under the GPL-2.
- *
* Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf
*/
diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c
index 311c1a8..2148834 100644
--- a/drivers/iio/adc/max1027.c
+++ b/drivers/iio/adc/max1027.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* iio/adc/max1027.c
* Copyright (C) 2014 Philippe Reynes
@@ -6,10 +7,6 @@
* Copyright 2011 Analog Devices Inc (from AD7923 Driver)
* Copyright 2012 CS Systemes d'Information
*
- * 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.
- *
* max1027.c
*
* Partial support for max1027 and similar chips.
@@ -430,8 +427,9 @@
return -ENOMEM;
}
- ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
- &max1027_trigger_handler, NULL);
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &max1027_trigger_handler, NULL);
if (ret < 0) {
dev_err(&indio_dev->dev, "Failed to setup buffer\n");
return ret;
@@ -442,7 +440,7 @@
if (st->trig == NULL) {
ret = -ENOMEM;
dev_err(&indio_dev->dev, "Failed to allocate iio trigger\n");
- goto fail_trigger_alloc;
+ return ret;
}
st->trig->ops = &max1027_trigger_ops;
@@ -457,7 +455,7 @@
spi->dev.driver->name, st->trig);
if (ret < 0) {
dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n");
- goto fail_dev_register;
+ return ret;
}
/* Disable averaging */
@@ -465,34 +463,10 @@
ret = spi_write(st->spi, &st->reg, 1);
if (ret < 0) {
dev_err(&indio_dev->dev, "Failed to configure averaging register\n");
- goto fail_dev_register;
+ return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(&indio_dev->dev, "Failed to register iio device\n");
- goto fail_dev_register;
- }
-
- return 0;
-
-fail_dev_register:
-fail_trigger_alloc:
- iio_triggered_buffer_cleanup(indio_dev);
-
- return ret;
-}
-
-static int max1027_remove(struct spi_device *spi)
-{
- struct iio_dev *indio_dev = spi_get_drvdata(spi);
-
- pr_debug("%s: remove(spi = 0x%p)\n", __func__, spi);
-
- iio_device_unregister(indio_dev);
- iio_triggered_buffer_cleanup(indio_dev);
-
- return 0;
+ return devm_iio_device_register(&spi->dev, indio_dev);
}
static struct spi_driver max1027_driver = {
@@ -501,7 +475,6 @@
.of_match_table = of_match_ptr(max1027_adc_dt_ids),
},
.probe = max1027_probe,
- .remove = max1027_remove,
.id_table = max1027_id,
};
module_spi_driver(max1027_driver);
diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c
index af59ab2..3440539 100644
--- a/drivers/iio/adc/max11100.c
+++ b/drivers/iio/adc/max11100.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* iio/adc/max11100.c
* Maxim max11100 ADC Driver with IIO interface
*
* Copyright (C) 2016-17 Renesas Electronics Corporation
* Copyright (C) 2016-17 Jacopo Mondi
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/kernel.h>
diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c
index 49db9e9..3b6f3b9 100644
--- a/drivers/iio/adc/max1118.c
+++ b/drivers/iio/adc/max1118.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs driver
*
* Copyright (c) 2017 Akinobu Mita <akinobu.mita@gmail.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1117-MAX1119.pdf
*
* SPI interface connections
diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c
index a8d35ae..5c2cc61 100644
--- a/drivers/iio/adc/max1363.c
+++ b/drivers/iio/adc/max1363.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* iio/adc/max1363.c
* Copyright (C) 2008-2010 Jonathan Cameron
@@ -9,10 +10,6 @@
* Copyright (C) 2000 Russell King
*
* Driver for max1363 and similar chips.
- *
- * 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/interrupt.h>
diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c
index 0538ff8..da073d7 100644
--- a/drivers/iio/adc/max9611.c
+++ b/drivers/iio/adc/max9611.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* iio/adc/max9611.c
*
@@ -5,10 +6,6 @@
* 12-bit ADC interface.
*
* Copyright (C) 2017 Jacopo Mondi
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
/*
@@ -86,7 +83,7 @@
#define MAX9611_TEMP_MAX_POS 0x7f80
#define MAX9611_TEMP_MAX_NEG 0xff80
#define MAX9611_TEMP_MIN_NEG 0xd980
-#define MAX9611_TEMP_MASK GENMASK(7, 15)
+#define MAX9611_TEMP_MASK GENMASK(15, 7)
#define MAX9611_TEMP_SHIFT 0x07
#define MAX9611_TEMP_RAW(_r) ((_r) >> MAX9611_TEMP_SHIFT)
#define MAX9611_TEMP_SCALE_NUM 1000000
@@ -289,7 +286,7 @@
return ret;
if (*adc_raw > 0) {
- *csa_gain = gain_selectors[i];
+ *csa_gain = (enum max9611_csa_gain)gain_selectors[i];
return 0;
}
}
@@ -483,7 +480,7 @@
if (ret)
return ret;
- regval = ret & MAX9611_TEMP_MASK;
+ regval &= MAX9611_TEMP_MASK;
if ((regval > MAX9611_TEMP_MAX_POS &&
regval < MAX9611_TEMP_MIN_NEG) ||
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index a04856d..38bf100 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
* Copyright (C) 2014 Rose Technology
@@ -34,10 +35,6 @@
* http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08
* http://ww1.microchip.com/downloads/en/DeviceDoc/21700E.pdf mcp3301
* http://ww1.microchip.com/downloads/en/DeviceDoc/21950D.pdf mcp3550/1/3
- *
- * 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/err.h>
diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c
index 819f260..ea24d7c 100644
--- a/drivers/iio/adc/mcp3422.c
+++ b/drivers/iio/adc/mcp3422.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* mcp3422.c - driver for the Microchip mcp3421/2/3/4/5/6/7/8 chip family
*
@@ -10,11 +11,6 @@
*
* This driver exports the value of analog input voltage to sysfs, the
* voltage unit is nV.
- *
- * 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/err.h>
diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
new file mode 100644
index 0000000..dd52f08
--- /dev/null
+++ b/drivers/iio/adc/mcp3911.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Microchip MCP3911, Two-channel Analog Front End
+ *
+ * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
+ * Copyright (C) 2018 Kent Gustavsson <kent@minoris.se>
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define MCP3911_REG_CHANNEL0 0x00
+#define MCP3911_REG_CHANNEL1 0x03
+#define MCP3911_REG_MOD 0x06
+#define MCP3911_REG_PHASE 0x07
+#define MCP3911_REG_GAIN 0x09
+
+#define MCP3911_REG_STATUSCOM 0x0a
+#define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4)
+#define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3)
+#define MCP3911_STATUSCOM_EN_OFFCAL BIT(2)
+#define MCP3911_STATUSCOM_EN_GAINCAL BIT(1)
+
+#define MCP3911_REG_CONFIG 0x0c
+#define MCP3911_CONFIG_CLKEXT BIT(1)
+#define MCP3911_CONFIG_VREFEXT BIT(2)
+
+#define MCP3911_REG_OFFCAL_CH0 0x0e
+#define MCP3911_REG_GAINCAL_CH0 0x11
+#define MCP3911_REG_OFFCAL_CH1 0x14
+#define MCP3911_REG_GAINCAL_CH1 0x17
+#define MCP3911_REG_VREFCAL 0x1a
+
+#define MCP3911_CHANNEL(x) (MCP3911_REG_CHANNEL0 + x * 3)
+#define MCP3911_OFFCAL(x) (MCP3911_REG_OFFCAL_CH0 + x * 6)
+
+/* Internal voltage reference in uV */
+#define MCP3911_INT_VREF_UV 1200000
+
+#define MCP3911_REG_READ(reg, id) ((((reg) << 1) | ((id) << 5) | (1 << 0)) & 0xff)
+#define MCP3911_REG_WRITE(reg, id) ((((reg) << 1) | ((id) << 5) | (0 << 0)) & 0xff)
+
+#define MCP3911_NUM_CHANNELS 2
+
+struct mcp3911 {
+ struct spi_device *spi;
+ struct mutex lock;
+ struct regulator *vref;
+ struct clk *clki;
+ u32 dev_addr;
+};
+
+static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len)
+{
+ int ret;
+
+ reg = MCP3911_REG_READ(reg, adc->dev_addr);
+ ret = spi_write_then_read(adc->spi, ®, 1, val, len);
+ if (ret < 0)
+ return ret;
+
+ be32_to_cpus(val);
+ *val >>= ((4 - len) * 8);
+ dev_dbg(&adc->spi->dev, "reading 0x%x from register 0x%x\n", *val,
+ reg >> 1);
+ return ret;
+}
+
+static int mcp3911_write(struct mcp3911 *adc, u8 reg, u32 val, u8 len)
+{
+ dev_dbg(&adc->spi->dev, "writing 0x%x to register 0x%x\n", val, reg);
+
+ val <<= (3 - len) * 8;
+ cpu_to_be32s(&val);
+ val |= MCP3911_REG_WRITE(reg, adc->dev_addr);
+
+ return spi_write(adc->spi, &val, len + 1);
+}
+
+static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask,
+ u32 val, u8 len)
+{
+ u32 tmp;
+ int ret;
+
+ ret = mcp3911_read(adc, reg, &tmp, len);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ val |= tmp & ~mask;
+ return mcp3911_write(adc, reg, val, len);
+}
+
+static int mcp3911_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct mcp3911 *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&adc->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = mcp3911_read(adc,
+ MCP3911_CHANNEL(channel->channel), val, 3);
+ if (ret)
+ goto out;
+
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_OFFSET:
+ ret = mcp3911_read(adc,
+ MCP3911_OFFCAL(channel->channel), val, 3);
+ if (ret)
+ goto out;
+
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ if (adc->vref) {
+ ret = regulator_get_voltage(adc->vref);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "failed to get vref voltage: %d\n",
+ ret);
+ goto out;
+ }
+
+ *val = ret / 1000;
+ } else {
+ *val = MCP3911_INT_VREF_UV;
+ }
+
+ *val2 = 24;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+ }
+
+out:
+ mutex_unlock(&adc->lock);
+ return ret;
+}
+
+static int mcp3911_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int val,
+ int val2, long mask)
+{
+ struct mcp3911 *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&adc->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_OFFSET:
+ if (val2 != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Write offset */
+ ret = mcp3911_write(adc, MCP3911_OFFCAL(channel->channel), val,
+ 3);
+ if (ret)
+ goto out;
+
+ /* Enable offset*/
+ ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM,
+ MCP3911_STATUSCOM_EN_OFFCAL,
+ MCP3911_STATUSCOM_EN_OFFCAL, 2);
+ break;
+ }
+
+out:
+ mutex_unlock(&adc->lock);
+ return ret;
+}
+
+#define MCP3911_CHAN(idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = idx, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec mcp3911_channels[] = {
+ MCP3911_CHAN(0),
+ MCP3911_CHAN(1),
+};
+
+static const struct iio_info mcp3911_info = {
+ .read_raw = mcp3911_read_raw,
+ .write_raw = mcp3911_write_raw,
+};
+
+static int mcp3911_config(struct mcp3911 *adc, struct device_node *of_node)
+{
+ u32 configreg;
+ int ret;
+
+ of_property_read_u32(of_node, "device-addr", &adc->dev_addr);
+ if (adc->dev_addr > 3) {
+ dev_err(&adc->spi->dev,
+ "invalid device address (%i). Must be in range 0-3.\n",
+ adc->dev_addr);
+ return -EINVAL;
+ }
+ dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr);
+
+ ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2);
+ if (ret)
+ return ret;
+
+ if (adc->vref) {
+ dev_dbg(&adc->spi->dev, "use external voltage reference\n");
+ configreg |= MCP3911_CONFIG_VREFEXT;
+ } else {
+ dev_dbg(&adc->spi->dev,
+ "use internal voltage reference (1.2V)\n");
+ configreg &= ~MCP3911_CONFIG_VREFEXT;
+ }
+
+ if (adc->clki) {
+ dev_dbg(&adc->spi->dev, "use external clock as clocksource\n");
+ configreg |= MCP3911_CONFIG_CLKEXT;
+ } else {
+ dev_dbg(&adc->spi->dev,
+ "use crystal oscillator as clocksource\n");
+ configreg &= ~MCP3911_CONFIG_CLKEXT;
+ }
+
+ return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2);
+}
+
+static int mcp3911_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct mcp3911 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+
+ adc->vref = devm_regulator_get_optional(&adc->spi->dev, "vref");
+ if (IS_ERR(adc->vref)) {
+ if (PTR_ERR(adc->vref) == -ENODEV) {
+ adc->vref = NULL;
+ } else {
+ dev_err(&adc->spi->dev,
+ "failed to get regulator (%ld)\n",
+ PTR_ERR(adc->vref));
+ return PTR_ERR(adc->vref);
+ }
+
+ } else {
+ ret = regulator_enable(adc->vref);
+ if (ret)
+ return ret;
+ }
+
+ adc->clki = devm_clk_get(&adc->spi->dev, NULL);
+ if (IS_ERR(adc->clki)) {
+ if (PTR_ERR(adc->clki) == -ENOENT) {
+ adc->clki = NULL;
+ } else {
+ dev_err(&adc->spi->dev,
+ "failed to get adc clk (%ld)\n",
+ PTR_ERR(adc->clki));
+ ret = PTR_ERR(adc->clki);
+ goto reg_disable;
+ }
+ } else {
+ ret = clk_prepare_enable(adc->clki);
+ if (ret < 0) {
+ dev_err(&adc->spi->dev,
+ "Failed to enable clki: %d\n", ret);
+ goto reg_disable;
+ }
+ }
+
+ ret = mcp3911_config(adc, spi->dev.of_node);
+ if (ret)
+ goto clk_disable;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->dev.of_node = spi->dev.of_node;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mcp3911_info;
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->channels = mcp3911_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mcp3911_channels);
+
+ mutex_init(&adc->lock);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto clk_disable;
+
+ return ret;
+
+clk_disable:
+ clk_disable_unprepare(adc->clki);
+reg_disable:
+ if (adc->vref)
+ regulator_disable(adc->vref);
+
+ return ret;
+}
+
+static int mcp3911_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct mcp3911 *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ clk_disable_unprepare(adc->clki);
+ if (adc->vref)
+ regulator_disable(adc->vref);
+
+ return 0;
+}
+
+static const struct of_device_id mcp3911_dt_ids[] = {
+ { .compatible = "microchip,mcp3911" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp3911_dt_ids);
+
+static const struct spi_device_id mcp3911_id[] = {
+ { "mcp3911", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mcp3911_id);
+
+static struct spi_driver mcp3911_driver = {
+ .driver = {
+ .name = "mcp3911",
+ .of_match_table = mcp3911_dt_ids,
+ },
+ .probe = mcp3911_probe,
+ .remove = mcp3911_remove,
+ .id_table = mcp3911_id,
+};
+module_spi_driver(mcp3911_driver);
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_AUTHOR("Kent Gustavsson <kent@minoris.se>");
+MODULE_DESCRIPTION("Microchip Technology MCP3911");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c
index c802617..3b2fbb7 100644
--- a/drivers/iio/adc/men_z188_adc.c
+++ b/drivers/iio/adc/men_z188_adc.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MEN 16z188 Analog to Digial Converter
*
* Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
* Author: Johannes Thumshirn <johannes.thumshirn@men.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; version 2 of the License.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c
index da2d16d..7b27306 100644
--- a/drivers/iio/adc/meson_saradc.c
+++ b/drivers/iio/adc/meson_saradc.c
@@ -1,14 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Amlogic Meson Successive Approximation Register (SAR) A/D Converter
*
* Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/bitfield.h>
@@ -18,6 +12,7 @@
#include <linux/io.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -25,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/mfd/syscon.h>
#define MESON_SAR_ADC_REG0 0x00
#define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31)
@@ -148,7 +144,6 @@
#define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26)
#define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16)
#define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15)
- #define MESON_SAR_ADC_DELTA_10_TS_C_SHIFT 11
#define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11)
#define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10)
#define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0)
@@ -166,6 +161,17 @@
#define MESON_SAR_ADC_MAX_FIFO_SIZE 32
#define MESON_SAR_ADC_TIMEOUT 100 /* ms */
+#define MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL 6
+#define MESON_SAR_ADC_TEMP_OFFSET 27
+
+/* temperature sensor calibration information in eFuse */
+#define MESON_SAR_ADC_EFUSE_BYTES 4
+#define MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL GENMASK(6, 0)
+#define MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED BIT(7)
+
+#define MESON_HHI_DPLL_TOP_0 0x318
+#define MESON_HHI_DPLL_TOP_0_TSC_BIT4 BIT(9)
+
/* for use with IIO_VAL_INT_PLUS_MICRO */
#define MILLION 1000000
@@ -173,18 +179,28 @@
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _chan, \
+ .address = _chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \
BIT(IIO_CHAN_INFO_CALIBSCALE), \
.datasheet_name = "SAR_ADC_CH"#_chan, \
}
-/*
- * TODO: the hardware supports IIO_TEMP for channel 6 as well which is
- * currently not supported by this driver.
- */
+#define MESON_SAR_ADC_TEMP_CHAN(_chan) { \
+ .type = IIO_TEMP, \
+ .channel = _chan, \
+ .address = MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE), \
+ .datasheet_name = "TEMP_SENSOR", \
+}
+
static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
MESON_SAR_ADC_CHAN(0),
MESON_SAR_ADC_CHAN(1),
@@ -197,6 +213,19 @@
IIO_CHAN_SOFT_TIMESTAMP(8),
};
+static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = {
+ MESON_SAR_ADC_CHAN(0),
+ MESON_SAR_ADC_CHAN(1),
+ MESON_SAR_ADC_CHAN(2),
+ MESON_SAR_ADC_CHAN(3),
+ MESON_SAR_ADC_CHAN(4),
+ MESON_SAR_ADC_CHAN(5),
+ MESON_SAR_ADC_CHAN(6),
+ MESON_SAR_ADC_CHAN(7),
+ MESON_SAR_ADC_TEMP_CHAN(8),
+ IIO_CHAN_SOFT_TIMESTAMP(9),
+};
+
enum meson_sar_adc_avg_mode {
NO_AVERAGING = 0x0,
MEAN_AVERAGING = 0x1,
@@ -225,6 +254,9 @@
u32 bandgap_reg;
unsigned int resolution;
const struct regmap_config *regmap_config;
+ u8 temperature_trimming_bits;
+ unsigned int temperature_multiplier;
+ unsigned int temperature_divider;
};
struct meson_sar_adc_data {
@@ -235,7 +267,7 @@
struct meson_sar_adc_priv {
struct regmap *regmap;
struct regulator *vref;
- const struct meson_sar_adc_data *data;
+ const struct meson_sar_adc_param *param;
struct clk *clkin;
struct clk *core_clk;
struct clk *adc_sel_clk;
@@ -246,6 +278,10 @@
struct completion done;
int calibbias;
int calibscale;
+ struct regmap *tsc_regmap;
+ bool temperature_sensor_calibrated;
+ u8 temperature_sensor_coefficient;
+ u16 temperature_sensor_adc_val;
};
static const struct regmap_config meson_sar_adc_regmap_config_gxbb = {
@@ -280,7 +316,7 @@
/* use val_calib = scale * val_raw + offset calibration function */
tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias;
- return clamp(tmp, 0, (1 << priv->data->param->resolution) - 1);
+ return clamp(tmp, 0, (1 << priv->param->resolution) - 1);
}
static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
@@ -324,15 +360,15 @@
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val);
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval);
- if (fifo_chan != chan->channel) {
+ if (fifo_chan != chan->address) {
dev_err(&indio_dev->dev,
- "ADC FIFO entry belongs to channel %d instead of %d\n",
- fifo_chan, chan->channel);
+ "ADC FIFO entry belongs to channel %d instead of %lu\n",
+ fifo_chan, chan->address);
return -EINVAL;
}
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval);
- fifo_val &= GENMASK(priv->data->param->resolution - 1, 0);
+ fifo_val &= GENMASK(priv->param->resolution - 1, 0);
*val = meson_sar_adc_calib_val(indio_dev, fifo_val);
return 0;
@@ -344,16 +380,16 @@
enum meson_sar_adc_num_samples samples)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
- int val, channel = chan->channel;
+ int val, address = chan->address;
- val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(channel);
+ val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
- MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(channel),
+ MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(address),
val);
- val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(channel);
+ val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
- MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(channel), val);
+ MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(address), val);
}
static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
@@ -373,25 +409,32 @@
/* map channel index 0 to the channel which we want to read */
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0),
- chan->channel);
+ chan->address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval);
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
- chan->channel);
+ chan->address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
regval);
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
- chan->channel);
+ chan->address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
regval);
- if (chan->channel == 6)
- regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
- MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0);
+ if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL) {
+ if (chan->type == IIO_TEMP)
+ regval = MESON_SAR_ADC_DELTA_10_TEMP_SEL;
+ else
+ regval = 0;
+
+ regmap_update_bits(priv->regmap,
+ MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TEMP_SEL, regval);
+ }
}
static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
@@ -451,7 +494,7 @@
mutex_lock(&indio_dev->mlock);
- if (priv->data->param->has_bl30_integration) {
+ if (priv->param->has_bl30_integration) {
/* prevent BL30 from using the SAR ADC while we are using it */
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
MESON_SAR_ADC_DELAY_KERNEL_BUSY,
@@ -479,7 +522,7 @@
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
- if (priv->data->param->has_bl30_integration)
+ if (priv->param->has_bl30_integration)
/* allow BL30 to use the SAR ADC again */
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0);
@@ -506,8 +549,12 @@
enum meson_sar_adc_num_samples avg_samples,
int *val)
{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
int ret;
+ if (chan->type == IIO_TEMP && !priv->temperature_sensor_calibrated)
+ return -ENOTSUPP;
+
ret = meson_sar_adc_lock(indio_dev);
if (ret)
return ret;
@@ -527,8 +574,8 @@
if (ret) {
dev_warn(indio_dev->dev.parent,
- "failed to read sample for channel %d: %d\n",
- chan->channel, ret);
+ "failed to read sample for channel %lu: %d\n",
+ chan->address, ret);
return ret;
}
@@ -555,16 +602,30 @@
break;
case IIO_CHAN_INFO_SCALE:
- ret = regulator_get_voltage(priv->vref);
- if (ret < 0) {
- dev_err(indio_dev->dev.parent,
- "failed to get vref voltage: %d\n", ret);
- return ret;
- }
+ if (chan->type == IIO_VOLTAGE) {
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "failed to get vref voltage: %d\n",
+ ret);
+ return ret;
+ }
- *val = ret / 1000;
- *val2 = priv->data->param->resolution;
- return IIO_VAL_FRACTIONAL_LOG2;
+ *val = ret / 1000;
+ *val2 = priv->param->resolution;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ } else if (chan->type == IIO_TEMP) {
+ /* SoC specific multiplier and divider */
+ *val = priv->param->temperature_multiplier;
+ *val2 = priv->param->temperature_divider;
+
+ /* celsius to millicelsius */
+ *val *= 1000;
+
+ return IIO_VAL_FRACTIONAL;
+ } else {
+ return -EINVAL;
+ }
case IIO_CHAN_INFO_CALIBBIAS:
*val = priv->calibbias;
@@ -575,6 +636,13 @@
*val2 = priv->calibscale % MILLION;
return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = DIV_ROUND_CLOSEST(MESON_SAR_ADC_TEMP_OFFSET *
+ priv->param->temperature_divider,
+ priv->param->temperature_multiplier);
+ *val -= priv->temperature_sensor_adc_val;
+ return IIO_VAL_INT;
+
default:
return -EINVAL;
}
@@ -587,8 +655,11 @@
struct clk_init_data init;
const char *clk_parents[1];
- init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%pOF#adc_div",
- indio_dev->dev.of_node);
+ init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_div",
+ dev_name(indio_dev->dev.parent));
+ if (!init.name)
+ return -ENOMEM;
+
init.flags = 0;
init.ops = &clk_divider_ops;
clk_parents[0] = __clk_get_name(priv->clkin);
@@ -606,8 +677,11 @@
if (WARN_ON(IS_ERR(priv->adc_div_clk)))
return PTR_ERR(priv->adc_div_clk);
- init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%pOF#adc_en",
- indio_dev->dev.of_node);
+ init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_en",
+ dev_name(indio_dev->dev.parent));
+ if (!init.name)
+ return -ENOMEM;
+
init.flags = CLK_SET_RATE_PARENT;
init.ops = &clk_gate_ops;
clk_parents[0] = __clk_get_name(priv->adc_div_clk);
@@ -625,6 +699,74 @@
return 0;
}
+static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ u8 *buf, trimming_bits, trimming_mask, upper_adc_val;
+ struct nvmem_cell *temperature_calib;
+ size_t read_len;
+ int ret;
+
+ temperature_calib = devm_nvmem_cell_get(&indio_dev->dev,
+ "temperature_calib");
+ if (IS_ERR(temperature_calib)) {
+ ret = PTR_ERR(temperature_calib);
+
+ /*
+ * leave the temperature sensor disabled if no calibration data
+ * was passed via nvmem-cells.
+ */
+ if (ret == -ENODEV)
+ return 0;
+
+ if (ret != -EPROBE_DEFER)
+ dev_err(indio_dev->dev.parent,
+ "failed to get temperature_calib cell\n");
+
+ return ret;
+ }
+
+ priv->tsc_regmap =
+ syscon_regmap_lookup_by_phandle(indio_dev->dev.parent->of_node,
+ "amlogic,hhi-sysctrl");
+ if (IS_ERR(priv->tsc_regmap)) {
+ dev_err(indio_dev->dev.parent,
+ "failed to get amlogic,hhi-sysctrl regmap\n");
+ return PTR_ERR(priv->tsc_regmap);
+ }
+
+ read_len = MESON_SAR_ADC_EFUSE_BYTES;
+ buf = nvmem_cell_read(temperature_calib, &read_len);
+ if (IS_ERR(buf)) {
+ dev_err(indio_dev->dev.parent,
+ "failed to read temperature_calib cell\n");
+ return PTR_ERR(buf);
+ } else if (read_len != MESON_SAR_ADC_EFUSE_BYTES) {
+ kfree(buf);
+ dev_err(indio_dev->dev.parent,
+ "invalid read size of temperature_calib cell\n");
+ return -EINVAL;
+ }
+
+ trimming_bits = priv->param->temperature_trimming_bits;
+ trimming_mask = BIT(trimming_bits) - 1;
+
+ priv->temperature_sensor_calibrated =
+ buf[3] & MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED;
+ priv->temperature_sensor_coefficient = buf[2] & trimming_mask;
+
+ upper_adc_val = FIELD_GET(MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL,
+ buf[3]);
+
+ priv->temperature_sensor_adc_val = buf[2];
+ priv->temperature_sensor_adc_val |= upper_adc_val << BITS_PER_BYTE;
+ priv->temperature_sensor_adc_val >>= trimming_bits;
+
+ kfree(buf);
+
+ return 0;
+}
+
static int meson_sar_adc_init(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
@@ -636,7 +778,7 @@
*/
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
- if (priv->data->param->has_bl30_integration) {
+ if (priv->param->has_bl30_integration) {
/*
* leave sampling delay and the input clocks as configured by
* BL30 to make sure BL30 gets the values it expects when
@@ -649,10 +791,12 @@
meson_sar_adc_stop_sample_engine(indio_dev);
- /* update the channel 6 MUX to select the temperature sensor */
+ /*
+ * disable this bit as seems to be only relevant for Meson6 (based
+ * on the vendor driver), which we don't support at the moment.
+ */
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
- MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL,
- MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL);
+ MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL, 0);
/* disable all channels by default */
regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0);
@@ -709,6 +853,45 @@
regval |= MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW;
regmap_write(priv->regmap, MESON_SAR_ADC_AUX_SW, regval);
+ if (priv->temperature_sensor_calibrated) {
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE1,
+ MESON_SAR_ADC_DELTA_10_TS_REVE1);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE0,
+ MESON_SAR_ADC_DELTA_10_TS_REVE0);
+
+ /*
+ * set bits [3:0] of the TSC (temperature sensor coefficient)
+ * to get the correct values when reading the temperature.
+ */
+ regval = FIELD_PREP(MESON_SAR_ADC_DELTA_10_TS_C_MASK,
+ priv->temperature_sensor_coefficient);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_C_MASK, regval);
+
+ if (priv->param->temperature_trimming_bits == 5) {
+ if (priv->temperature_sensor_coefficient & BIT(4))
+ regval = MESON_HHI_DPLL_TOP_0_TSC_BIT4;
+ else
+ regval = 0;
+
+ /*
+ * bit [4] (the 5th bit when starting to count at 1)
+ * of the TSC is located in the HHI register area.
+ */
+ regmap_update_bits(priv->tsc_regmap,
+ MESON_HHI_DPLL_TOP_0,
+ MESON_HHI_DPLL_TOP_0_TSC_BIT4,
+ regval);
+ }
+ } else {
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE1, 0);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE0, 0);
+ }
+
ret = clk_set_parent(priv->adc_sel_clk, priv->clkin);
if (ret) {
dev_err(indio_dev->dev.parent,
@@ -716,7 +899,7 @@
return ret;
}
- ret = clk_set_rate(priv->adc_clk, priv->data->param->clock_rate);
+ ret = clk_set_rate(priv->adc_clk, priv->param->clock_rate);
if (ret) {
dev_err(indio_dev->dev.parent,
"failed to set adc clock rate\n");
@@ -729,7 +912,7 @@
static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
- const struct meson_sar_adc_param *param = priv->data->param;
+ const struct meson_sar_adc_param *param = priv->param;
u32 enable_mask;
if (param->bandgap_reg == MESON_SAR_ADC_REG11)
@@ -849,13 +1032,13 @@
int ret, nominal0, nominal1, value0, value1;
/* use points 25% and 75% for calibration */
- nominal0 = (1 << priv->data->param->resolution) / 4;
- nominal1 = (1 << priv->data->param->resolution) * 3 / 4;
+ nominal0 = (1 << priv->param->resolution) / 4;
+ nominal1 = (1 << priv->param->resolution) * 3 / 4;
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
usleep_range(10, 20);
ret = meson_sar_adc_get_sample(indio_dev,
- &meson_sar_adc_iio_channels[7],
+ &indio_dev->channels[7],
MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
if (ret < 0)
goto out;
@@ -863,7 +1046,7 @@
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4);
usleep_range(10, 20);
ret = meson_sar_adc_get_sample(indio_dev,
- &meson_sar_adc_iio_channels[7],
+ &indio_dev->channels[7],
MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
if (ret < 0)
goto out;
@@ -894,6 +1077,20 @@
.bandgap_reg = MESON_SAR_ADC_DELTA_10,
.regmap_config = &meson_sar_adc_regmap_config_meson8,
.resolution = 10,
+ .temperature_trimming_bits = 4,
+ .temperature_multiplier = 18 * 10000,
+ .temperature_divider = 1024 * 10 * 85,
+};
+
+static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = {
+ .has_bl30_integration = false,
+ .clock_rate = 1150000,
+ .bandgap_reg = MESON_SAR_ADC_DELTA_10,
+ .regmap_config = &meson_sar_adc_regmap_config_meson8,
+ .resolution = 10,
+ .temperature_trimming_bits = 5,
+ .temperature_multiplier = 10,
+ .temperature_divider = 32,
};
static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = {
@@ -918,12 +1115,12 @@
};
static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = {
- .param = &meson_sar_adc_meson8_param,
+ .param = &meson_sar_adc_meson8b_param,
.name = "meson-meson8b-saradc",
};
static const struct meson_sar_adc_data meson_sar_adc_meson8m2_data = {
- .param = &meson_sar_adc_meson8_param,
+ .param = &meson_sar_adc_meson8b_param,
.name = "meson-meson8m2-saradc",
};
@@ -947,6 +1144,11 @@
.name = "meson-axg-saradc",
};
+static const struct meson_sar_adc_data meson_sar_adc_g12a_data = {
+ .param = &meson_sar_adc_gxl_param,
+ .name = "meson-g12a-saradc",
+};
+
static const struct of_device_id meson_sar_adc_of_match[] = {
{
.compatible = "amlogic,meson8-saradc",
@@ -972,6 +1174,9 @@
}, {
.compatible = "amlogic,meson-axg-saradc",
.data = &meson_sar_adc_axg_data,
+ }, {
+ .compatible = "amlogic,meson-g12a-saradc",
+ .data = &meson_sar_adc_g12a_data,
},
{},
};
@@ -979,11 +1184,11 @@
static int meson_sar_adc_probe(struct platform_device *pdev)
{
+ const struct meson_sar_adc_data *match_data;
struct meson_sar_adc_priv *priv;
struct iio_dev *indio_dev;
struct resource *res;
void __iomem *base;
- const struct of_device_id *match;
int irq, ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
@@ -995,28 +1200,30 @@
priv = iio_priv(indio_dev);
init_completion(&priv->done);
- match = of_match_device(meson_sar_adc_of_match, &pdev->dev);
- if (!match) {
- dev_err(&pdev->dev, "failed to match device\n");
+ match_data = of_device_get_match_data(&pdev->dev);
+ if (!match_data) {
+ dev_err(&pdev->dev, "failed to get match data\n");
return -ENODEV;
}
- priv->data = match->data;
+ priv->param = match_data->param;
- indio_dev->name = priv->data->name;
+ indio_dev->name = match_data->name;
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &meson_sar_adc_iio_info;
- indio_dev->channels = meson_sar_adc_iio_channels;
- indio_dev->num_channels = ARRAY_SIZE(meson_sar_adc_iio_channels);
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ priv->param->regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
if (!irq)
return -EINVAL;
@@ -1026,11 +1233,6 @@
if (ret)
return ret;
- priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- priv->data->param->regmap_config);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
-
priv->clkin = devm_clk_get(&pdev->dev, "clkin");
if (IS_ERR(priv->clkin)) {
dev_err(&pdev->dev, "failed to get clkin\n");
@@ -1078,6 +1280,22 @@
priv->calibscale = MILLION;
+ if (priv->param->temperature_trimming_bits) {
+ ret = meson_sar_adc_temp_sensor_init(indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->temperature_sensor_calibrated) {
+ indio_dev->channels = meson_sar_adc_and_temp_iio_channels;
+ indio_dev->num_channels =
+ ARRAY_SIZE(meson_sar_adc_and_temp_iio_channels);
+ } else {
+ indio_dev->channels = meson_sar_adc_iio_channels;
+ indio_dev->num_channels =
+ ARRAY_SIZE(meson_sar_adc_iio_channels);
+ }
+
ret = meson_sar_adc_init(indio_dev);
if (ret)
goto err;
diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c
index 95d76ab..7bbb64c 100644
--- a/drivers/iio/adc/mt6577_auxadc.c
+++ b/drivers/iio/adc/mt6577_auxadc.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016 MediaTek Inc.
* Author: Zhiyong Tao <zhiyong.tao@mediatek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 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/clk.h>
@@ -42,10 +34,26 @@
#define MT6577_AUXADC_POWER_READY_MS 1
#define MT6577_AUXADC_SAMPLE_READY_US 25
+struct mtk_auxadc_compatible {
+ bool sample_data_cali;
+ bool check_global_idle;
+};
+
struct mt6577_auxadc_device {
void __iomem *reg_base;
struct clk *adc_clk;
struct mutex lock;
+ const struct mtk_auxadc_compatible *dev_comp;
+};
+
+static const struct mtk_auxadc_compatible mt8173_compat = {
+ .sample_data_cali = false,
+ .check_global_idle = true,
+};
+
+static const struct mtk_auxadc_compatible mt6765_compat = {
+ .sample_data_cali = true,
+ .check_global_idle = false,
};
#define MT6577_AUXADC_CHANNEL(idx) { \
@@ -74,6 +82,11 @@
MT6577_AUXADC_CHANNEL(15),
};
+static int mt_auxadc_get_cali_data(int rawdata, bool enable_cali)
+{
+ return rawdata;
+}
+
static inline void mt6577_auxadc_mod_reg(void __iomem *reg,
u32 or_mask, u32 and_mask)
{
@@ -120,15 +133,17 @@
/* we must delay here for hardware sample channel data */
udelay(MT6577_AUXADC_SAMPLE_READY_US);
- /* check MTK_AUXADC_CON2 if auxadc is idle */
- ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, val,
- ((val & MT6577_AUXADC_STA) == 0),
- MT6577_AUXADC_SLEEP_US,
- MT6577_AUXADC_TIMEOUT_US);
- if (ret < 0) {
- dev_err(indio_dev->dev.parent,
- "wait for auxadc idle time out\n");
- goto err_timeout;
+ if (adc_dev->dev_comp->check_global_idle) {
+ /* check MTK_AUXADC_CON2 if auxadc is idle */
+ ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2,
+ val, ((val & MT6577_AUXADC_STA) == 0),
+ MT6577_AUXADC_SLEEP_US,
+ MT6577_AUXADC_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "wait for auxadc idle time out\n");
+ goto err_timeout;
+ }
}
/* read channel and make sure ready bit == 1 */
@@ -163,6 +178,8 @@
int *val2,
long info)
{
+ struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
+
switch (info) {
case IIO_CHAN_INFO_PROCESSED:
*val = mt6577_auxadc_read(indio_dev, chan);
@@ -172,6 +189,8 @@
chan->channel);
return *val;
}
+ if (adc_dev->dev_comp->sample_data_cali)
+ *val = mt_auxadc_get_cali_data(*val, true);
return IIO_VAL_INT;
default:
@@ -304,10 +323,11 @@
mt6577_auxadc_resume);
static const struct of_device_id mt6577_auxadc_of_match[] = {
- { .compatible = "mediatek,mt2701-auxadc", },
- { .compatible = "mediatek,mt2712-auxadc", },
- { .compatible = "mediatek,mt7622-auxadc", },
- { .compatible = "mediatek,mt8173-auxadc", },
+ { .compatible = "mediatek,mt2701-auxadc", .data = &mt8173_compat},
+ { .compatible = "mediatek,mt2712-auxadc", .data = &mt8173_compat},
+ { .compatible = "mediatek,mt7622-auxadc", .data = &mt8173_compat},
+ { .compatible = "mediatek,mt8173-auxadc", .data = &mt8173_compat},
+ { .compatible = "mediatek,mt6765-auxadc", .data = &mt6765_compat},
{ }
};
MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match);
diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
index c627513..9d2f74c 100644
--- a/drivers/iio/adc/mxs-lradc-adc.c
+++ b/drivers/iio/adc/mxs-lradc-adc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Freescale MXS LRADC ADC driver
*
@@ -7,16 +8,6 @@
* Authors:
* Marek Vasut <marex@denx.de>
* Ksenija Stanojevic <ksenija.stanojevic@gmail.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.
- *
- * 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/completion.h>
@@ -465,6 +456,8 @@
trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name,
iio->id);
+ if (!trig)
+ return -ENOMEM;
trig->dev.parent = adc->dev;
iio_trigger_set_drvdata(trig, iio);
diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c
index 8997e74..5725791 100644
--- a/drivers/iio/adc/nau7802.c
+++ b/drivers/iio/adc/nau7802.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the Nuvoton NAU7802 ADC
*
* Copyright 2013 Free Electrons
- *
- * Licensed under the GPLv2 or later.
*/
#include <linux/delay.h>
diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c
new file mode 100644
index 0000000..910f358
--- /dev/null
+++ b/drivers/iio/adc/npcm_adc.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Nuvoton Technology corporation.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+struct npcm_adc {
+ bool int_status;
+ u32 adc_sample_hz;
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *adc_clk;
+ wait_queue_head_t wq;
+ struct regulator *vref;
+ struct regmap *rst_regmap;
+};
+
+/* NPCM7xx reset module */
+#define NPCM7XX_IPSRST1_OFFSET 0x020
+#define NPCM7XX_IPSRST1_ADC_RST BIT(27)
+
+/* ADC registers */
+#define NPCM_ADCCON 0x00
+#define NPCM_ADCDATA 0x04
+
+/* ADCCON Register Bits */
+#define NPCM_ADCCON_ADC_INT_EN BIT(21)
+#define NPCM_ADCCON_REFSEL BIT(19)
+#define NPCM_ADCCON_ADC_INT_ST BIT(18)
+#define NPCM_ADCCON_ADC_EN BIT(17)
+#define NPCM_ADCCON_ADC_RST BIT(16)
+#define NPCM_ADCCON_ADC_CONV BIT(13)
+
+#define NPCM_ADCCON_CH_MASK GENMASK(27, 24)
+#define NPCM_ADCCON_CH(x) ((x) << 24)
+#define NPCM_ADCCON_DIV_SHIFT 1
+#define NPCM_ADCCON_DIV_MASK GENMASK(8, 1)
+#define NPCM_ADC_DATA_MASK(x) ((x) & GENMASK(9, 0))
+
+#define NPCM_ADC_ENABLE (NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN)
+
+/* ADC General Definition */
+#define NPCM_RESOLUTION_BITS 10
+#define NPCM_INT_VREF_MV 2000
+
+#define NPCM_ADC_CHAN(ch) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = ch, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec npcm_adc_iio_channels[] = {
+ NPCM_ADC_CHAN(0),
+ NPCM_ADC_CHAN(1),
+ NPCM_ADC_CHAN(2),
+ NPCM_ADC_CHAN(3),
+ NPCM_ADC_CHAN(4),
+ NPCM_ADC_CHAN(5),
+ NPCM_ADC_CHAN(6),
+ NPCM_ADC_CHAN(7),
+};
+
+static irqreturn_t npcm_adc_isr(int irq, void *data)
+{
+ u32 regtemp;
+ struct iio_dev *indio_dev = data;
+ struct npcm_adc *info = iio_priv(indio_dev);
+
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ if (regtemp & NPCM_ADCCON_ADC_INT_ST) {
+ iowrite32(regtemp, info->regs + NPCM_ADCCON);
+ wake_up_interruptible(&info->wq);
+ info->int_status = true;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel)
+{
+ int ret;
+ u32 regtemp;
+
+ /* Select ADC channel */
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ regtemp &= ~NPCM_ADCCON_CH_MASK;
+ info->int_status = false;
+ iowrite32(regtemp | NPCM_ADCCON_CH(channel) |
+ NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
+
+ ret = wait_event_interruptible_timeout(info->wq, info->int_status,
+ msecs_to_jiffies(10));
+ if (ret == 0) {
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) {
+ /* if conversion failed - reset ADC module */
+ regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
+ NPCM7XX_IPSRST1_ADC_RST);
+ msleep(100);
+ regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
+ 0x0);
+ msleep(100);
+
+ /* Enable ADC and start conversion module */
+ iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV,
+ info->regs + NPCM_ADCCON);
+ dev_err(info->dev, "RESET ADC Complete\n");
+ }
+ return -ETIMEDOUT;
+ }
+ if (ret < 0)
+ return ret;
+
+ *val = NPCM_ADC_DATA_MASK(ioread32(info->regs + NPCM_ADCDATA));
+
+ return 0;
+}
+
+static int npcm_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret;
+ int vref_uv;
+ struct npcm_adc *info = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = npcm_adc_read(info, val, chan->channel);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret) {
+ dev_err(info->dev, "NPCM ADC read failed\n");
+ return ret;
+ }
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (!IS_ERR(info->vref)) {
+ vref_uv = regulator_get_voltage(info->vref);
+ *val = vref_uv / 1000;
+ } else {
+ *val = NPCM_INT_VREF_MV;
+ }
+ *val2 = NPCM_RESOLUTION_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = info->adc_sample_hz;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct iio_info npcm_adc_iio_info = {
+ .read_raw = &npcm_adc_read_raw,
+};
+
+static const struct of_device_id npcm_adc_match[] = {
+ { .compatible = "nuvoton,npcm750-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, npcm_adc_match);
+
+static int npcm_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ int irq;
+ u32 div;
+ u32 reg_con;
+ struct resource *res;
+ struct npcm_adc *info;
+ struct iio_dev *indio_dev;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+ info = iio_priv(indio_dev);
+
+ info->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ info->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ info->adc_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->adc_clk)) {
+ dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n");
+ return PTR_ERR(info->adc_clk);
+ }
+
+ /* calculate ADC clock sample rate */
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ div = reg_con & NPCM_ADCCON_DIV_MASK;
+ div = div >> NPCM_ADCCON_DIV_SHIFT;
+ info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
+
+ if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) {
+ info->rst_regmap = syscon_regmap_lookup_by_compatible
+ ("nuvoton,npcm750-rst");
+ if (IS_ERR(info->rst_regmap)) {
+ dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n");
+ ret = PTR_ERR(info->rst_regmap);
+ goto err_disable_clk;
+ }
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ ret = -EINVAL;
+ goto err_disable_clk;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0,
+ "NPCM_ADC", indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "failed requesting interrupt\n");
+ goto err_disable_clk;
+ }
+
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ info->vref = devm_regulator_get_optional(&pdev->dev, "vref");
+ if (!IS_ERR(info->vref)) {
+ ret = regulator_enable(info->vref);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't enable ADC reference voltage\n");
+ goto err_disable_clk;
+ }
+
+ iowrite32(reg_con & ~NPCM_ADCCON_REFSEL,
+ info->regs + NPCM_ADCCON);
+ } else {
+ /*
+ * Any error which is not ENODEV indicates the regulator
+ * has been specified and so is a failure case.
+ */
+ if (PTR_ERR(info->vref) != -ENODEV) {
+ ret = PTR_ERR(info->vref);
+ goto err_disable_clk;
+ }
+
+ /* Use internal reference */
+ iowrite32(reg_con | NPCM_ADCCON_REFSEL,
+ info->regs + NPCM_ADCCON);
+ }
+
+ init_waitqueue_head(&info->wq);
+
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ reg_con |= NPCM_ADC_ENABLE;
+
+ /* Enable the ADC Module */
+ iowrite32(reg_con, info->regs + NPCM_ADCCON);
+
+ /* Start ADC conversion */
+ iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
+
+ platform_set_drvdata(pdev, indio_dev);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &npcm_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = npcm_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ goto err_iio_register;
+ }
+
+ pr_info("NPCM ADC driver probed\n");
+
+ return 0;
+
+err_iio_register:
+ iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
+ if (!IS_ERR(info->vref))
+ regulator_disable(info->vref);
+err_disable_clk:
+ clk_disable_unprepare(info->adc_clk);
+
+ return ret;
+}
+
+static int npcm_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct npcm_adc *info = iio_priv(indio_dev);
+ u32 regtemp;
+
+ iio_device_unregister(indio_dev);
+
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
+ if (!IS_ERR(info->vref))
+ regulator_disable(info->vref);
+ clk_disable_unprepare(info->adc_clk);
+
+ return 0;
+}
+
+static struct platform_driver npcm_adc_driver = {
+ .probe = npcm_adc_probe,
+ .remove = npcm_adc_remove,
+ .driver = {
+ .name = "npcm_adc",
+ .of_match_table = npcm_adc_match,
+ },
+};
+
+module_platform_driver(npcm_adc_driver);
+
+MODULE_DESCRIPTION("Nuvoton NPCM ADC Driver");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c
index 69b9aff..46e595e 100644
--- a/drivers/iio/adc/palmas_gpadc.c
+++ b/drivers/iio/adc/palmas_gpadc.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* palmas-adc.c -- TI PALMAS GPADC.
*
* Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
*
* Author: Pradeep Goudagunta <pgoudagunta@nvidia.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 version 2.
*/
#include <linux/module.h>
diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
index b093ecd..c599ffa 100644
--- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c
+++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Qualcomm PM8xxx PMIC XOADC driver
*
@@ -423,18 +424,14 @@
static struct pm8xxx_chan_info *
pm8xxx_get_channel(struct pm8xxx_xoadc *adc, u8 chan)
{
- struct pm8xxx_chan_info *ch;
int i;
for (i = 0; i < adc->nchans; i++) {
- ch = &adc->chans[i];
+ struct pm8xxx_chan_info *ch = &adc->chans[i];
if (ch->hwchan->amux_channel == chan)
- break;
+ return ch;
}
- if (i == adc->nchans)
- return NULL;
-
- return ch;
+ return NULL;
}
static int pm8xxx_read_channel_rsv(struct pm8xxx_xoadc *adc,
@@ -708,8 +705,8 @@
* mux.
*/
if (iiospec->args_count != 2) {
- dev_err(&indio_dev->dev, "wrong number of arguments for %s need 2 got %d\n",
- iiospec->np->name,
+ dev_err(&indio_dev->dev, "wrong number of arguments for %pOFn need 2 got %d\n",
+ iiospec->np,
iiospec->args_count);
return -EINVAL;
}
diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
new file mode 100644
index 0000000..21fdcde
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -0,0 +1,798 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+#include "qcom-vadc-common.h"
+
+#define ADC5_USR_REVISION1 0x0
+#define ADC5_USR_STATUS1 0x8
+#define ADC5_USR_STATUS1_REQ_STS BIT(1)
+#define ADC5_USR_STATUS1_EOC BIT(0)
+#define ADC5_USR_STATUS1_REQ_STS_EOC_MASK 0x3
+
+#define ADC5_USR_STATUS2 0x9
+#define ADC5_USR_STATUS2_CONV_SEQ_MASK 0x70
+#define ADC5_USR_STATUS2_CONV_SEQ_MASK_SHIFT 0x5
+
+#define ADC5_USR_IBAT_MEAS 0xf
+#define ADC5_USR_IBAT_MEAS_SUPPORTED BIT(0)
+
+#define ADC5_USR_DIG_PARAM 0x42
+#define ADC5_USR_DIG_PARAM_CAL_VAL BIT(6)
+#define ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT 6
+#define ADC5_USR_DIG_PARAM_CAL_SEL 0x30
+#define ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT 4
+#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL 0xc
+#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT 2
+
+#define ADC5_USR_FAST_AVG_CTL 0x43
+#define ADC5_USR_FAST_AVG_CTL_EN BIT(7)
+#define ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK 0x7
+
+#define ADC5_USR_CH_SEL_CTL 0x44
+
+#define ADC5_USR_DELAY_CTL 0x45
+#define ADC5_USR_HW_SETTLE_DELAY_MASK 0xf
+
+#define ADC5_USR_EN_CTL1 0x46
+#define ADC5_USR_EN_CTL1_ADC_EN BIT(7)
+
+#define ADC5_USR_CONV_REQ 0x47
+#define ADC5_USR_CONV_REQ_REQ BIT(7)
+
+#define ADC5_USR_DATA0 0x50
+
+#define ADC5_USR_DATA1 0x51
+
+#define ADC5_USR_IBAT_DATA0 0x52
+
+#define ADC5_USR_IBAT_DATA1 0x53
+
+/*
+ * Conversion time varies based on the decimation, clock rate, fast average
+ * samples and measurements queued across different VADC peripherals.
+ * Set the timeout to a max of 100ms.
+ */
+#define ADC5_CONV_TIME_MIN_US 263
+#define ADC5_CONV_TIME_MAX_US 264
+#define ADC5_CONV_TIME_RETRY 400
+#define ADC5_CONV_TIMEOUT msecs_to_jiffies(100)
+
+/* Digital version >= 5.3 supports hw_settle_2 */
+#define ADC5_HW_SETTLE_DIFF_MINOR 3
+#define ADC5_HW_SETTLE_DIFF_MAJOR 5
+
+enum adc5_cal_method {
+ ADC5_NO_CAL = 0,
+ ADC5_RATIOMETRIC_CAL,
+ ADC5_ABSOLUTE_CAL
+};
+
+enum adc5_cal_val {
+ ADC5_TIMER_CAL = 0,
+ ADC5_NEW_CAL
+};
+
+/**
+ * struct adc5_channel_prop - ADC channel property.
+ * @channel: channel number, refer to the channel list.
+ * @cal_method: calibration method.
+ * @cal_val: calibration value
+ * @decimation: sampling rate supported for the channel.
+ * @prescale: channel scaling performed on the input signal.
+ * @hw_settle_time: the time between AMUX being configured and the
+ * start of conversion.
+ * @avg_samples: ability to provide single result from the ADC
+ * that is an average of multiple measurements.
+ * @scale_fn_type: Represents the scaling function to convert voltage
+ * physical units desired by the client for the channel.
+ * @datasheet_name: Channel name used in device tree.
+ */
+struct adc5_channel_prop {
+ unsigned int channel;
+ enum adc5_cal_method cal_method;
+ enum adc5_cal_val cal_val;
+ unsigned int decimation;
+ unsigned int prescale;
+ unsigned int hw_settle_time;
+ unsigned int avg_samples;
+ enum vadc_scale_fn_type scale_fn_type;
+ const char *datasheet_name;
+};
+
+/**
+ * struct adc5_chip - ADC private structure.
+ * @regmap: SPMI ADC5 peripheral register map field.
+ * @dev: SPMI ADC5 device.
+ * @base: base address for the ADC peripheral.
+ * @nchannels: number of ADC channels.
+ * @chan_props: array of ADC channel properties.
+ * @iio_chans: array of IIO channels specification.
+ * @poll_eoc: use polling instead of interrupt.
+ * @complete: ADC result notification after interrupt is received.
+ * @lock: ADC lock for access to the peripheral.
+ * @data: software configuration data.
+ */
+struct adc5_chip {
+ struct regmap *regmap;
+ struct device *dev;
+ u16 base;
+ unsigned int nchannels;
+ struct adc5_channel_prop *chan_props;
+ struct iio_chan_spec *iio_chans;
+ bool poll_eoc;
+ struct completion complete;
+ struct mutex lock;
+ const struct adc5_data *data;
+};
+
+static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
+ {.num = 1, .den = 1},
+ {.num = 1, .den = 3},
+ {.num = 1, .den = 4},
+ {.num = 1, .den = 6},
+ {.num = 1, .den = 20},
+ {.num = 1, .den = 8},
+ {.num = 10, .den = 81},
+ {.num = 1, .den = 10},
+ {.num = 1, .den = 16}
+};
+
+static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
+{
+ return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
+}
+
+static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
+{
+ return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
+}
+
+static int adc5_prescaling_from_dt(u32 num, u32 den)
+{
+ unsigned int pre;
+
+ for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
+ if (adc5_prescale_ratios[pre].num == num &&
+ adc5_prescale_ratios[pre].den == den)
+ break;
+
+ if (pre == ARRAY_SIZE(adc5_prescale_ratios))
+ return -EINVAL;
+
+ return pre;
+}
+
+static int adc5_hw_settle_time_from_dt(u32 value,
+ const unsigned int *hw_settle)
+{
+ unsigned int i;
+
+ for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
+ if (value == hw_settle[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int adc5_avg_samples_from_dt(u32 value)
+{
+ if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
+ return -EINVAL;
+
+ return __ffs(value);
+}
+
+static int adc5_decimation_from_dt(u32 value,
+ const unsigned int *decimation)
+{
+ unsigned int i;
+
+ for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
+ if (value == decimation[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
+{
+ int ret;
+ u8 rslt_lsb, rslt_msb;
+
+ ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, sizeof(rslt_lsb));
+ if (ret)
+ return ret;
+
+ ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb));
+ if (ret)
+ return ret;
+
+ *data = (rslt_msb << 8) | rslt_lsb;
+
+ if (*data == ADC5_USR_DATA_CHECK) {
+ pr_err("Invalid data:0x%x\n", *data);
+ return -EINVAL;
+ }
+
+ pr_debug("voltage raw code:0x%x\n", *data);
+
+ return 0;
+}
+
+static int adc5_poll_wait_eoc(struct adc5_chip *adc)
+{
+ unsigned int count, retry = ADC5_CONV_TIME_RETRY;
+ u8 status1;
+ int ret;
+
+ for (count = 0; count < retry; count++) {
+ ret = adc5_read(adc, ADC5_USR_STATUS1, &status1,
+ sizeof(status1));
+ if (ret)
+ return ret;
+
+ status1 &= ADC5_USR_STATUS1_REQ_STS_EOC_MASK;
+ if (status1 == ADC5_USR_STATUS1_EOC)
+ return 0;
+
+ usleep_range(ADC5_CONV_TIME_MIN_US, ADC5_CONV_TIME_MAX_US);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static void adc5_update_dig_param(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop, u8 *data)
+{
+ /* Update calibration value */
+ *data &= ~ADC5_USR_DIG_PARAM_CAL_VAL;
+ *data |= (prop->cal_val << ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT);
+
+ /* Update calibration select */
+ *data &= ~ADC5_USR_DIG_PARAM_CAL_SEL;
+ *data |= (prop->cal_method << ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT);
+
+ /* Update decimation ratio select */
+ *data &= ~ADC5_USR_DIG_PARAM_DEC_RATIO_SEL;
+ *data |= (prop->decimation << ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT);
+}
+
+static int adc5_configure(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop)
+{
+ int ret;
+ u8 buf[6];
+
+ /* Read registers 0x42 through 0x46 */
+ ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ /* Digital param selection */
+ adc5_update_dig_param(adc, prop, &buf[0]);
+
+ /* Update fast average sample value */
+ buf[1] &= (u8) ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
+ buf[1] |= prop->avg_samples;
+
+ /* Select ADC channel */
+ buf[2] = prop->channel;
+
+ /* Select HW settle delay for channel */
+ buf[3] &= (u8) ~ADC5_USR_HW_SETTLE_DELAY_MASK;
+ buf[3] |= prop->hw_settle_time;
+
+ /* Select ADC enable */
+ buf[4] |= ADC5_USR_EN_CTL1_ADC_EN;
+
+ /* Select CONV request */
+ buf[5] |= ADC5_USR_CONV_REQ_REQ;
+
+ if (!adc->poll_eoc)
+ reinit_completion(&adc->complete);
+
+ return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+}
+
+static int adc5_do_conversion(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop,
+ struct iio_chan_spec const *chan,
+ u16 *data_volt, u16 *data_cur)
+{
+ int ret;
+
+ mutex_lock(&adc->lock);
+
+ ret = adc5_configure(adc, prop);
+ if (ret) {
+ pr_err("ADC configure failed with %d\n", ret);
+ goto unlock;
+ }
+
+ if (adc->poll_eoc) {
+ ret = adc5_poll_wait_eoc(adc);
+ if (ret < 0) {
+ pr_err("EOC bit not set\n");
+ goto unlock;
+ }
+ } else {
+ ret = wait_for_completion_timeout(&adc->complete,
+ ADC5_CONV_TIMEOUT);
+ if (!ret) {
+ pr_debug("Did not get completion timeout.\n");
+ ret = adc5_poll_wait_eoc(adc);
+ if (ret < 0) {
+ pr_err("EOC bit not set\n");
+ goto unlock;
+ }
+ }
+ }
+
+ ret = adc5_read_voltage_data(adc, data_volt);
+unlock:
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+static irqreturn_t adc5_isr(int irq, void *dev_id)
+{
+ struct adc5_chip *adc = dev_id;
+
+ complete(&adc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int adc5_of_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ int i;
+
+ for (i = 0; i < adc->nchannels; i++)
+ if (adc->chan_props[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+static int adc5_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ struct adc5_channel_prop *prop;
+ u16 adc_code_volt, adc_code_cur;
+ int ret;
+
+ prop = &adc->chan_props[chan->address];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = adc5_do_conversion(adc, prop, chan,
+ &adc_code_volt, &adc_code_cur);
+ if (ret)
+ return ret;
+
+ ret = qcom_adc5_hw_scale(prop->scale_fn_type,
+ &adc5_prescale_ratios[prop->prescale],
+ adc->data,
+ adc_code_volt, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct iio_info adc5_info = {
+ .read_raw = adc5_read_raw,
+ .of_xlate = adc5_of_xlate,
+};
+
+struct adc5_channels {
+ const char *datasheet_name;
+ unsigned int prescale_index;
+ enum iio_chan_type type;
+ long info_mask;
+ enum vadc_scale_fn_type scale_fn_type;
+};
+
+/* In these definitions, _pre refers to an index into adc5_prescale_ratios. */
+#define ADC5_CHAN(_dname, _type, _mask, _pre, _scale) \
+ { \
+ .datasheet_name = _dname, \
+ .prescale_index = _pre, \
+ .type = _type, \
+ .info_mask = _mask, \
+ .scale_fn_type = _scale, \
+ }, \
+
+#define ADC5_CHAN_TEMP(_dname, _pre, _scale) \
+ ADC5_CHAN(_dname, IIO_TEMP, \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ _pre, _scale) \
+
+#define ADC5_CHAN_VOLT(_dname, _pre, _scale) \
+ ADC5_CHAN(_dname, IIO_VOLTAGE, \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ _pre, _scale) \
+
+static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
+ [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
+ SCALE_HW_CALIB_PMIC_THERM)
+ [ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 8,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 0,
+ SCALE_HW_CALIB_PM5_CHG_TEMP)
+ /* Charger prescales SBUx and MID_CHG to fit within 1.8V upper unit */
+ [ADC5_SBUx] = ADC5_CHAN_VOLT("chg_sbux", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_MID_CHG_DIV6] = ADC5_CHAN_VOLT("chg_mid_chg", 3,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 0,
+ SCALE_HW_CALIB_XOTHERM)
+ [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 0,
+ SCALE_HW_CALIB_PM5_SMB_TEMP)
+};
+
+static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
+ [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
+ SCALE_HW_CALIB_PMIC_THERM)
+ [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+};
+
+static int adc5_get_dt_channel_data(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop,
+ struct device_node *node,
+ const struct adc5_data *data)
+{
+ const char *name = node->name, *channel_name;
+ u32 chan, value, varr[2];
+ int ret;
+ struct device *dev = adc->dev;
+
+ ret = of_property_read_u32(node, "reg", &chan);
+ if (ret) {
+ dev_err(dev, "invalid channel number %s\n", name);
+ return ret;
+ }
+
+ if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA ||
+ !data->adc_chans[chan].datasheet_name) {
+ dev_err(dev, "%s invalid channel number %d\n", name, chan);
+ return -EINVAL;
+ }
+
+ /* the channel has DT description */
+ prop->channel = chan;
+
+ channel_name = of_get_property(node,
+ "label", NULL) ? : node->name;
+ if (!channel_name) {
+ pr_err("Invalid channel name\n");
+ return -EINVAL;
+ }
+ prop->datasheet_name = channel_name;
+
+ ret = of_property_read_u32(node, "qcom,decimation", &value);
+ if (!ret) {
+ ret = adc5_decimation_from_dt(value, data->decimation);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid decimation %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->decimation = ret;
+ } else {
+ prop->decimation = ADC5_DECIMATION_DEFAULT;
+ }
+
+ ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
+ if (!ret) {
+ ret = adc5_prescaling_from_dt(varr[0], varr[1]);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
+ chan, varr[0], varr[1]);
+ return ret;
+ }
+ prop->prescale = ret;
+ } else {
+ prop->prescale =
+ adc->data->adc_chans[prop->channel].prescale_index;
+ }
+
+ ret = of_property_read_u32(node, "qcom,hw-settle-time", &value);
+ if (!ret) {
+ u8 dig_version[2];
+
+ ret = adc5_read(adc, ADC5_USR_REVISION1, dig_version,
+ sizeof(dig_version));
+ if (ret < 0) {
+ dev_err(dev, "Invalid dig version read %d\n", ret);
+ return ret;
+ }
+
+ pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0],
+ dig_version[1]);
+ /* Digital controller >= 5.3 have hw_settle_2 option */
+ if (dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
+ dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR)
+ ret = adc5_hw_settle_time_from_dt(value,
+ data->hw_settle_2);
+ else
+ ret = adc5_hw_settle_time_from_dt(value,
+ data->hw_settle_1);
+
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid hw-settle-time %d us\n",
+ chan, value);
+ return ret;
+ }
+ prop->hw_settle_time = ret;
+ } else {
+ prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
+ }
+
+ ret = of_property_read_u32(node, "qcom,avg-samples", &value);
+ if (!ret) {
+ ret = adc5_avg_samples_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid avg-samples %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->avg_samples = ret;
+ } else {
+ prop->avg_samples = VADC_DEF_AVG_SAMPLES;
+ }
+
+ if (of_property_read_bool(node, "qcom,ratiometric"))
+ prop->cal_method = ADC5_RATIOMETRIC_CAL;
+ else
+ prop->cal_method = ADC5_ABSOLUTE_CAL;
+
+ /*
+ * Default to using timer calibration. Using a fresh calibration value
+ * for every conversion will increase the overall time for a request.
+ */
+ prop->cal_val = ADC5_TIMER_CAL;
+
+ dev_dbg(dev, "%02x name %s\n", chan, name);
+
+ return 0;
+}
+
+static const struct adc5_data adc5_data_pmic = {
+ .full_scale_code_volt = 0x70e4,
+ .full_scale_code_cur = 0x2710,
+ .adc_chans = adc5_chans_pmic,
+ .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
+ {250, 420, 840},
+ .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 800, 900, 1, 2, 4, 6, 8, 10},
+ .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 1, 2, 4, 8, 16, 32, 64, 128},
+};
+
+static const struct adc5_data adc5_data_pmic_rev2 = {
+ .full_scale_code_volt = 0x4000,
+ .full_scale_code_cur = 0x1800,
+ .adc_chans = adc5_chans_rev2,
+ .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
+ {256, 512, 1024},
+ .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {0, 100, 200, 300, 400, 500, 600, 700,
+ 800, 900, 1, 2, 4, 6, 8, 10},
+ .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 1, 2, 4, 8, 16, 32, 64, 128},
+};
+
+static const struct of_device_id adc5_match_table[] = {
+ {
+ .compatible = "qcom,spmi-adc5",
+ .data = &adc5_data_pmic,
+ },
+ {
+ .compatible = "qcom,spmi-adc-rev2",
+ .data = &adc5_data_pmic_rev2,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adc5_match_table);
+
+static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
+{
+ const struct adc5_channels *adc_chan;
+ struct iio_chan_spec *iio_chan;
+ struct adc5_channel_prop prop, *chan_props;
+ struct device_node *child;
+ unsigned int index = 0;
+ const struct of_device_id *id;
+ const struct adc5_data *data;
+ int ret;
+
+ adc->nchannels = of_get_available_child_count(node);
+ if (!adc->nchannels)
+ return -EINVAL;
+
+ adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels,
+ sizeof(*adc->iio_chans), GFP_KERNEL);
+ if (!adc->iio_chans)
+ return -ENOMEM;
+
+ adc->chan_props = devm_kcalloc(adc->dev, adc->nchannels,
+ sizeof(*adc->chan_props), GFP_KERNEL);
+ if (!adc->chan_props)
+ return -ENOMEM;
+
+ chan_props = adc->chan_props;
+ iio_chan = adc->iio_chans;
+ id = of_match_node(adc5_match_table, node);
+ if (id)
+ data = id->data;
+ else
+ data = &adc5_data_pmic;
+ adc->data = data;
+
+ for_each_available_child_of_node(node, child) {
+ ret = adc5_get_dt_channel_data(adc, &prop, child, data);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+
+ prop.scale_fn_type =
+ data->adc_chans[prop.channel].scale_fn_type;
+ *chan_props = prop;
+ adc_chan = &data->adc_chans[prop.channel];
+
+ iio_chan->channel = prop.channel;
+ iio_chan->datasheet_name = prop.datasheet_name;
+ iio_chan->extend_name = prop.datasheet_name;
+ iio_chan->info_mask_separate = adc_chan->info_mask;
+ iio_chan->type = adc_chan->type;
+ iio_chan->address = index;
+ iio_chan++;
+ chan_props++;
+ index++;
+ }
+
+ return 0;
+}
+
+static int adc5_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct adc5_chip *adc;
+ struct regmap *regmap;
+ int ret, irq_eoc;
+ u32 reg;
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ ret = of_property_read_u32(node, "reg", ®);
+ if (ret < 0)
+ return ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->regmap = regmap;
+ adc->dev = dev;
+ adc->base = reg;
+ init_completion(&adc->complete);
+ mutex_init(&adc->lock);
+
+ ret = adc5_get_dt_data(adc, node);
+ if (ret) {
+ pr_err("adc get dt data failed\n");
+ return ret;
+ }
+
+ irq_eoc = platform_get_irq(pdev, 0);
+ if (irq_eoc < 0) {
+ if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
+ return irq_eoc;
+ adc->poll_eoc = true;
+ } else {
+ ret = devm_request_irq(dev, irq_eoc, adc5_isr, 0,
+ "pm-adc5", adc);
+ if (ret)
+ return ret;
+ }
+
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = node;
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &adc5_info;
+ indio_dev->channels = adc->iio_chans;
+ indio_dev->num_channels = adc->nchannels;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct platform_driver adc5_driver = {
+ .driver = {
+ .name = "qcom-spmi-adc5.c",
+ .of_match_table = adc5_match_table,
+ },
+ .probe = adc5_probe,
+};
+module_platform_driver(adc5_driver);
+
+MODULE_ALIAS("platform:qcom-spmi-adc5");
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c
index 3f062cd..46858ed 100644
--- a/drivers/iio/adc/qcom-spmi-iadc.c
+++ b/drivers/iio/adc/qcom-spmi-iadc.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 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>
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
index 3680e0d..203ad59 100644
--- a/drivers/iio/adc/qcom-spmi-vadc.c
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2016, 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>
diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
index fe3d782..dcd7fb5 100644
--- a/drivers/iio/adc/qcom-vadc-common.c
+++ b/drivers/iio/adc/qcom-vadc-common.c
@@ -47,8 +47,79 @@
{44, 125}
};
+/*
+ * Voltage to temperature table for 100k pull up for NTCG104EF104 with
+ * 1.875V reference.
+ */
+static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
+ { 1831, -40000 },
+ { 1814, -35000 },
+ { 1791, -30000 },
+ { 1761, -25000 },
+ { 1723, -20000 },
+ { 1675, -15000 },
+ { 1616, -10000 },
+ { 1545, -5000 },
+ { 1463, 0 },
+ { 1370, 5000 },
+ { 1268, 10000 },
+ { 1160, 15000 },
+ { 1049, 20000 },
+ { 937, 25000 },
+ { 828, 30000 },
+ { 726, 35000 },
+ { 630, 40000 },
+ { 544, 45000 },
+ { 467, 50000 },
+ { 399, 55000 },
+ { 340, 60000 },
+ { 290, 65000 },
+ { 247, 70000 },
+ { 209, 75000 },
+ { 179, 80000 },
+ { 153, 85000 },
+ { 130, 90000 },
+ { 112, 95000 },
+ { 96, 100000 },
+ { 82, 105000 },
+ { 71, 110000 },
+ { 62, 115000 },
+ { 53, 120000 },
+ { 46, 125000 },
+};
+
+static int qcom_vadc_scale_hw_calib_volt(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_uv);
+static int qcom_vadc_scale_hw_calib_therm(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_smb_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_chg5_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_calib_die_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+
+static struct qcom_adc5_scale_type scale_adc5_fn[] = {
+ [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
+ [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
+ [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
+ [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
+ [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
+ [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
+};
+
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
- u32 tablesize, s32 input, s64 *output)
+ u32 tablesize, s32 input, int *output)
{
bool descending = 1;
u32 i = 0;
@@ -128,7 +199,7 @@
bool absolute, u16 adc_code,
int *result_mdec)
{
- s64 voltage = 0, result = 0;
+ s64 voltage = 0;
int ret;
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
@@ -138,12 +209,11 @@
ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
- voltage, &result);
+ voltage, result_mdec);
if (ret)
return ret;
- result *= 1000;
- *result_mdec = result;
+ *result_mdec *= 1000;
return 0;
}
@@ -191,6 +261,99 @@
return 0;
}
+static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ unsigned int factor)
+{
+ s64 voltage, temp, adc_vdd_ref_mv = 1875;
+
+ /*
+ * The normal data range is between 0V to 1.875V. On cases where
+ * we read low voltage values, the ADC code can go beyond the
+ * range and the scale result is incorrect so we clamp the values
+ * for the cases where the code represents a value below 0V
+ */
+ if (adc_code > VADC5_MAX_CODE)
+ adc_code = 0;
+
+ /* (ADC code * vref_vadc (1.875V)) / full_scale_code */
+ voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
+ voltage = div64_s64(voltage, data->full_scale_code_volt);
+ if (voltage > 0) {
+ voltage *= prescale->den;
+ temp = prescale->num * factor;
+ voltage = div64_s64(voltage, temp);
+ } else {
+ voltage = 0;
+ }
+
+ return (int) voltage;
+}
+
+static int qcom_vadc_scale_hw_calib_volt(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_uv)
+{
+ *result_uv = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 1);
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_calib_therm(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ int voltage;
+
+ voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 1000);
+
+ /* Map voltage to temperature from look-up table */
+ return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+ voltage, result_mdec);
+}
+
+static int qcom_vadc_scale_hw_calib_die_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 2);
+ *result_mdec -= KELVINMIL_CELSIUSMIL;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_smb_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100,
+ prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR);
+ *result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_chg5_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 4);
+ *result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec;
+
+ return 0;
+}
+
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
@@ -221,6 +384,22 @@
}
EXPORT_SYMBOL(qcom_vadc_scale);
+int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result)
+{
+ if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
+ scaletype < SCALE_HW_CALIB_INVALID)) {
+ pr_err("Invalid scale type %d\n", scaletype);
+ return -EINVAL;
+ }
+
+ return scale_adc5_fn[scaletype].scale_fn(prescale, data,
+ adc_code, result);
+}
+EXPORT_SYMBOL(qcom_adc5_hw_scale);
+
int qcom_vadc_decimation_from_dt(u32 value)
{
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h
index 1d5354f..bbb1fa0 100644
--- a/drivers/iio/adc/qcom-vadc-common.h
+++ b/drivers/iio/adc/qcom-vadc-common.h
@@ -25,15 +25,31 @@
#define VADC_DECIMATION_MIN 512
#define VADC_DECIMATION_MAX 4096
+#define ADC5_DEF_VBAT_PRESCALING 1 /* 1:3 */
+#define ADC5_DECIMATION_SHORT 250
+#define ADC5_DECIMATION_MEDIUM 420
+#define ADC5_DECIMATION_LONG 840
+/* Default decimation - 1024 for rev2, 840 for pmic5 */
+#define ADC5_DECIMATION_DEFAULT 2
+#define ADC5_DECIMATION_SAMPLES_MAX 3
#define VADC_HW_SETTLE_DELAY_MAX 10000
+#define VADC_HW_SETTLE_SAMPLES_MAX 16
#define VADC_AVG_SAMPLES_MAX 512
+#define ADC5_AVG_SAMPLES_MAX 16
#define KELVINMIL_CELSIUSMIL 273150
+#define PMIC5_CHG_TEMP_SCALE_FACTOR 377500
+#define PMIC5_SMB_TEMP_CONSTANT 419400
+#define PMIC5_SMB_TEMP_SCALE_FACTOR 356
#define PMI_CHG_SCALE_1 -138890
#define PMI_CHG_SCALE_2 391750000000LL
+#define VADC5_MAX_CODE 0x7fff
+#define ADC5_FULL_SCALE_CODE 0x70e4
+#define ADC5_USR_DATA_CHECK 0x8000
+
/**
* struct vadc_map_pt - Map the graph representation for ADC channel
* @x: Represent the ADC digitized code.
@@ -89,6 +105,18 @@
* SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
* SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
* SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
+ * SCALE_HW_CALIB_DEFAULT: Default scaling to convert raw adc code to
+ * voltage (uV) with hardware applied offset/slope values to adc code.
+ * SCALE_HW_CALIB_THERM_100K_PULLUP: Returns temperature in millidegC using
+ * lookup table. The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_XOTHERM: Returns XO thermistor voltage in millidegC using
+ * 100k pullup. The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
+ * The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5
+ * charger temperature.
+ * SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5
+ * SMB1390 temperature.
*/
enum vadc_scale_fn_type {
SCALE_DEFAULT = 0,
@@ -96,6 +124,22 @@
SCALE_PMIC_THERM,
SCALE_XOTHERM,
SCALE_PMI_CHG_TEMP,
+ SCALE_HW_CALIB_DEFAULT,
+ SCALE_HW_CALIB_THERM_100K_PULLUP,
+ SCALE_HW_CALIB_XOTHERM,
+ SCALE_HW_CALIB_PMIC_THERM,
+ SCALE_HW_CALIB_PM5_CHG_TEMP,
+ SCALE_HW_CALIB_PM5_SMB_TEMP,
+ SCALE_HW_CALIB_INVALID,
+};
+
+struct adc5_data {
+ const u32 full_scale_code_volt;
+ const u32 full_scale_code_cur;
+ const struct adc5_channels *adc_chans;
+ unsigned int *decimation;
+ unsigned int *hw_settle_1;
+ unsigned int *hw_settle_2;
};
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
@@ -104,6 +148,16 @@
bool absolute,
u16 adc_code, int *result_mdec);
+struct qcom_adc5_scale_type {
+ int (*scale_fn)(const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data, u16 adc_code, int *result);
+};
+
+int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+
int qcom_vadc_decimation_from_dt(u32 value);
#endif /* QCOM_VADC_COMMON_H */
diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
index dcb5017..c37f201 100644
--- a/drivers/iio/adc/rcar-gyroadc.c
+++ b/drivers/iio/adc/rcar-gyroadc.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Renesas R-Car GyroADC driver
*
* Copyright 2016 Marek Vasut <marek.vasut@gmail.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.
- *
- * 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>
@@ -343,8 +334,8 @@
for_each_child_of_node(np, child) {
of_id = of_match_node(rcar_gyroadc_child_match, child);
if (!of_id) {
- dev_err(dev, "Ignoring unsupported ADC \"%s\".",
- child->name);
+ dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".",
+ child);
continue;
}
@@ -381,17 +372,17 @@
ret = of_property_read_u32(child, "reg", ®);
if (ret) {
dev_err(dev,
- "Failed to get child reg property of ADC \"%s\".\n",
- child->name);
+ "Failed to get child reg property of ADC \"%pOFn\".\n",
+ child);
return ret;
}
/* Channel number is too high. */
if (reg >= num_channels) {
dev_err(dev,
- "Only %i channels supported with %s, but reg = <%i>.\n",
- num_channels, child->name, reg);
- return ret;
+ "Only %i channels supported with %pOFn, but reg = <%i>.\n",
+ num_channels, child, reg);
+ return -EINVAL;
}
}
@@ -400,7 +391,7 @@
dev_err(dev,
"Channel %i uses different ADC mode than the rest.\n",
reg);
- return ret;
+ return -EINVAL;
}
/* Channel is valid, grab the regulator. */
@@ -494,10 +485,8 @@
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
- if (!indio_dev) {
- dev_err(dev, "Failed to allocate IIO device.\n");
+ if (!indio_dev)
return -ENOMEM;
- }
priv = iio_priv(indio_dev);
priv->dev = dev;
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index 1f98566..582ba04 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Rockchip Successive Approximation Register (SAR) A/D Converter
* Copyright (C) 2014 ROCKCHIP, Inc.
- *
- * 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.
*/
#include <linux/module.h>
@@ -253,10 +244,8 @@
init_completion(&info->completion);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
0, dev_name(&pdev->dev), info);
diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c
index 2b60efe..a6c0465 100644
--- a/drivers/iio/adc/sc27xx_adc.c
+++ b/drivers/iio/adc/sc27xx_adc.c
@@ -3,12 +3,13 @@
#include <linux/hwspinlock.h>
#include <linux/iio/iio.h>
-#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/slab.h>
/* PMIC global registers definition */
#define SC27XX_MODULE_EN 0xc08
@@ -44,12 +45,19 @@
/* Bits definitions for SC27XX_ADC_INT_CLR registers */
#define SC27XX_ADC_IRQ_CLR BIT(0)
+/* Bits definitions for SC27XX_ADC_INT_RAW registers */
+#define SC27XX_ADC_IRQ_RAW BIT(0)
+
/* Mask definition for SC27XX_ADC_DATA register */
#define SC27XX_ADC_DATA_MASK GENMASK(11, 0)
/* Timeout (ms) for the trylock of hardware spinlocks */
#define SC27XX_ADC_HWLOCK_TIMEOUT 5000
+/* Timeout (us) for ADC data conversion according to ADC datasheet */
+#define SC27XX_ADC_RDY_TIMEOUT 1000000
+#define SC27XX_ADC_POLL_RAW_STATUS 500
+
/* Maximum ADC channel number */
#define SC27XX_ADC_CHANNEL_MAX 32
@@ -67,10 +75,8 @@
* subsystems which will access the unique ADC controller.
*/
struct hwspinlock *hwlock;
- struct completion completion;
int channel_scale[SC27XX_ADC_CHANNEL_MAX];
u32 base;
- int value;
int irq;
};
@@ -87,16 +93,73 @@
* should use the small-scale graph, and if more than 1.2v, we should use the
* big-scale graph.
*/
-static const struct sc27xx_adc_linear_graph big_scale_graph = {
+static struct sc27xx_adc_linear_graph big_scale_graph = {
4200, 3310,
3600, 2832,
};
-static const struct sc27xx_adc_linear_graph small_scale_graph = {
+static struct sc27xx_adc_linear_graph small_scale_graph = {
1000, 3413,
100, 341,
};
+static const struct sc27xx_adc_linear_graph big_scale_graph_calib = {
+ 4200, 856,
+ 3600, 733,
+};
+
+static const struct sc27xx_adc_linear_graph small_scale_graph_calib = {
+ 1000, 833,
+ 100, 80,
+};
+
+static int sc27xx_adc_get_calib_data(u32 calib_data, int calib_adc)
+{
+ return ((calib_data & 0xff) + calib_adc - 128) * 4;
+}
+
+static int sc27xx_adc_scale_calibration(struct sc27xx_adc_data *data,
+ bool big_scale)
+{
+ const struct sc27xx_adc_linear_graph *calib_graph;
+ struct sc27xx_adc_linear_graph *graph;
+ struct nvmem_cell *cell;
+ const char *cell_name;
+ u32 calib_data = 0;
+ void *buf;
+ size_t len;
+
+ if (big_scale) {
+ calib_graph = &big_scale_graph_calib;
+ graph = &big_scale_graph;
+ cell_name = "big_scale_calib";
+ } else {
+ calib_graph = &small_scale_graph_calib;
+ graph = &small_scale_graph;
+ cell_name = "small_scale_calib";
+ }
+
+ cell = nvmem_cell_get(data->dev, cell_name);
+ if (IS_ERR(cell))
+ return PTR_ERR(cell);
+
+ buf = nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ memcpy(&calib_data, buf, min(len, sizeof(u32)));
+
+ /* Only need to calibrate the adc values in the linear graph. */
+ graph->adc0 = sc27xx_adc_get_calib_data(calib_data, calib_graph->adc0);
+ graph->adc1 = sc27xx_adc_get_calib_data(calib_data >> 8,
+ calib_graph->adc1);
+
+ kfree(buf);
+ return 0;
+}
+
static int sc27xx_adc_get_ratio(int channel, int scale)
{
switch (channel) {
@@ -126,9 +189,7 @@
int scale, int *val)
{
int ret;
- u32 tmp;
-
- reinit_completion(&data->completion);
+ u32 tmp, value, status;
ret = hwspin_lock_timeout_raw(data->hwlock, SC27XX_ADC_HWLOCK_TIMEOUT);
if (ret) {
@@ -141,6 +202,11 @@
if (ret)
goto unlock_adc;
+ ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR,
+ SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR);
+ if (ret)
+ goto disable_adc;
+
/* Configure the channel id and scale */
tmp = (scale << SC27XX_ADC_SCALE_SHIFT) & SC27XX_ADC_SCALE_MASK;
tmp |= channel & SC27XX_ADC_CHN_ID_MASK;
@@ -164,7 +230,21 @@
if (ret)
goto disable_adc;
- wait_for_completion(&data->completion);
+ ret = regmap_read_poll_timeout(data->regmap,
+ data->base + SC27XX_ADC_INT_RAW,
+ status, (status & SC27XX_ADC_IRQ_RAW),
+ SC27XX_ADC_POLL_RAW_STATUS,
+ SC27XX_ADC_RDY_TIMEOUT);
+ if (ret) {
+ dev_err(data->dev, "read adc timeout, status = 0x%x\n", status);
+ goto disable_adc;
+ }
+
+ ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA, &value);
+ if (ret)
+ goto disable_adc;
+
+ value &= SC27XX_ADC_DATA_MASK;
disable_adc:
regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
@@ -173,32 +253,11 @@
hwspin_unlock_raw(data->hwlock);
if (!ret)
- *val = data->value;
+ *val = value;
return ret;
}
-static irqreturn_t sc27xx_adc_isr(int irq, void *dev_id)
-{
- struct sc27xx_adc_data *data = dev_id;
- int ret;
-
- ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR,
- SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR);
- if (ret)
- return IRQ_RETVAL(ret);
-
- ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA,
- &data->value);
- if (ret)
- return IRQ_RETVAL(ret);
-
- data->value &= SC27XX_ADC_DATA_MASK;
- complete(&data->completion);
-
- return IRQ_HANDLED;
-}
-
static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data,
int channel, int scale,
u32 *div_numerator, u32 *div_denominator)
@@ -209,7 +268,7 @@
*div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK;
}
-static int sc27xx_adc_to_volt(const struct sc27xx_adc_linear_graph *graph,
+static int sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph,
int raw_adc)
{
int tmp;
@@ -273,6 +332,17 @@
int ret, tmp;
switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = sc27xx_adc_read(data, chan->channel, scale, &tmp);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret)
+ return ret;
+
+ *val = tmp;
+ return IIO_VAL_INT;
+
case IIO_CHAN_INFO_PROCESSED:
mutex_lock(&indio_dev->mlock);
ret = sc27xx_adc_read_processed(data, chan->channel, scale,
@@ -315,48 +385,47 @@
.write_raw = &sc27xx_adc_write_raw,
};
-#define SC27XX_ADC_CHANNEL(index) { \
+#define SC27XX_ADC_CHANNEL(index, mask) { \
.type = IIO_VOLTAGE, \
.channel = index, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
- BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate = mask | BIT(IIO_CHAN_INFO_SCALE), \
.datasheet_name = "CH##index", \
.indexed = 1, \
}
static const struct iio_chan_spec sc27xx_channels[] = {
- SC27XX_ADC_CHANNEL(0),
- SC27XX_ADC_CHANNEL(1),
- SC27XX_ADC_CHANNEL(2),
- SC27XX_ADC_CHANNEL(3),
- SC27XX_ADC_CHANNEL(4),
- SC27XX_ADC_CHANNEL(5),
- SC27XX_ADC_CHANNEL(6),
- SC27XX_ADC_CHANNEL(7),
- SC27XX_ADC_CHANNEL(8),
- SC27XX_ADC_CHANNEL(9),
- SC27XX_ADC_CHANNEL(10),
- SC27XX_ADC_CHANNEL(11),
- SC27XX_ADC_CHANNEL(12),
- SC27XX_ADC_CHANNEL(13),
- SC27XX_ADC_CHANNEL(14),
- SC27XX_ADC_CHANNEL(15),
- SC27XX_ADC_CHANNEL(16),
- SC27XX_ADC_CHANNEL(17),
- SC27XX_ADC_CHANNEL(18),
- SC27XX_ADC_CHANNEL(19),
- SC27XX_ADC_CHANNEL(20),
- SC27XX_ADC_CHANNEL(21),
- SC27XX_ADC_CHANNEL(22),
- SC27XX_ADC_CHANNEL(23),
- SC27XX_ADC_CHANNEL(24),
- SC27XX_ADC_CHANNEL(25),
- SC27XX_ADC_CHANNEL(26),
- SC27XX_ADC_CHANNEL(27),
- SC27XX_ADC_CHANNEL(28),
- SC27XX_ADC_CHANNEL(29),
- SC27XX_ADC_CHANNEL(30),
- SC27XX_ADC_CHANNEL(31),
+ SC27XX_ADC_CHANNEL(0, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(1, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(2, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(3, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(4, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(5, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(6, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(7, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(8, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(9, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(10, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(11, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(12, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(13, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(14, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(15, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(16, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(17, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(18, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(19, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(20, BIT(IIO_CHAN_INFO_RAW)),
+ SC27XX_ADC_CHANNEL(21, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(22, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(23, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(24, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(25, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(26, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(27, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(28, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(29, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(30, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(31, BIT(IIO_CHAN_INFO_PROCESSED)),
};
static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
@@ -375,8 +444,12 @@
if (ret)
goto disable_adc;
- ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN,
- SC27XX_ADC_IRQ_EN, SC27XX_ADC_IRQ_EN);
+ /* ADC channel scales' calibration from nvmem device */
+ ret = sc27xx_adc_scale_calibration(data, true);
+ if (ret)
+ goto disable_clk;
+
+ ret = sc27xx_adc_scale_calibration(data, false);
if (ret)
goto disable_clk;
@@ -396,9 +469,6 @@
{
struct sc27xx_adc_data *data = _data;
- regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN,
- SC27XX_ADC_IRQ_EN, 0);
-
/* Disable ADC work clock and controller clock */
regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN,
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0);
@@ -416,88 +486,76 @@
static int sc27xx_adc_probe(struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
struct sc27xx_adc_data *sc27xx_data;
struct iio_dev *indio_dev;
int ret;
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*sc27xx_data));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*sc27xx_data));
if (!indio_dev)
return -ENOMEM;
sc27xx_data = iio_priv(indio_dev);
- sc27xx_data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ sc27xx_data->regmap = dev_get_regmap(dev->parent, NULL);
if (!sc27xx_data->regmap) {
- dev_err(&pdev->dev, "failed to get ADC regmap\n");
+ dev_err(dev, "failed to get ADC regmap\n");
return -ENODEV;
}
ret = of_property_read_u32(np, "reg", &sc27xx_data->base);
if (ret) {
- dev_err(&pdev->dev, "failed to get ADC base address\n");
+ dev_err(dev, "failed to get ADC base address\n");
return ret;
}
sc27xx_data->irq = platform_get_irq(pdev, 0);
- if (sc27xx_data->irq < 0) {
- dev_err(&pdev->dev, "failed to get ADC irq number\n");
+ if (sc27xx_data->irq < 0)
return sc27xx_data->irq;
- }
ret = of_hwspin_lock_get_id(np, 0);
if (ret < 0) {
- dev_err(&pdev->dev, "failed to get hwspinlock id\n");
+ dev_err(dev, "failed to get hwspinlock id\n");
return ret;
}
sc27xx_data->hwlock = hwspin_lock_request_specific(ret);
if (!sc27xx_data->hwlock) {
- dev_err(&pdev->dev, "failed to request hwspinlock\n");
+ dev_err(dev, "failed to request hwspinlock\n");
return -ENXIO;
}
- ret = devm_add_action(&pdev->dev, sc27xx_adc_free_hwlock,
+ ret = devm_add_action_or_reset(dev, sc27xx_adc_free_hwlock,
sc27xx_data->hwlock);
if (ret) {
- sc27xx_adc_free_hwlock(sc27xx_data->hwlock);
- dev_err(&pdev->dev, "failed to add hwspinlock action\n");
+ dev_err(dev, "failed to add hwspinlock action\n");
return ret;
}
- init_completion(&sc27xx_data->completion);
- sc27xx_data->dev = &pdev->dev;
+ sc27xx_data->dev = dev;
ret = sc27xx_adc_enable(sc27xx_data);
if (ret) {
- dev_err(&pdev->dev, "failed to enable ADC module\n");
+ dev_err(dev, "failed to enable ADC module\n");
return ret;
}
- ret = devm_add_action(&pdev->dev, sc27xx_adc_disable, sc27xx_data);
+ ret = devm_add_action_or_reset(dev, sc27xx_adc_disable, sc27xx_data);
if (ret) {
- sc27xx_adc_disable(sc27xx_data);
- dev_err(&pdev->dev, "failed to add ADC disable action\n");
+ dev_err(dev, "failed to add ADC disable action\n");
return ret;
}
- ret = devm_request_threaded_irq(&pdev->dev, sc27xx_data->irq, NULL,
- sc27xx_adc_isr, IRQF_ONESHOT,
- pdev->name, sc27xx_data);
- if (ret) {
- dev_err(&pdev->dev, "failed to request ADC irq\n");
- return ret;
- }
-
- indio_dev->dev.parent = &pdev->dev;
- indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = dev;
+ indio_dev->name = dev_name(dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &sc27xx_info;
indio_dev->channels = sc27xx_channels;
indio_dev->num_channels = ARRAY_SIZE(sc27xx_channels);
- ret = devm_iio_device_register(&pdev->dev, indio_dev);
+ ret = devm_iio_device_register(dev, indio_dev);
if (ret)
- dev_err(&pdev->dev, "could not register iio (ADC)");
+ dev_err(dev, "could not register iio (ADC)");
return ret;
}
diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c
index b1da2c4..592b97c 100644
--- a/drivers/iio/adc/spear_adc.c
+++ b/drivers/iio/adc/spear_adc.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ST SPEAr ADC driver
*
* Copyright 2012 Stefan Roese <sr@denx.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -302,7 +301,6 @@
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
- dev_err(dev, "failed getting interrupt resource\n");
ret = -EINVAL;
goto errout2;
}
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index ca432e7..93a096a 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -14,52 +14,47 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdesc.h>
#include <linux/irqdomain.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include "stm32-adc-core.h"
-/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
-#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
-#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+#define STM32_ADC_CORE_SLEEP_DELAY_MS 2000
-/* STM32F4_ADC_CSR - bit fields */
-#define STM32F4_EOC3 BIT(17)
-#define STM32F4_EOC2 BIT(9)
-#define STM32F4_EOC1 BIT(1)
+/* SYSCFG registers */
+#define STM32MP1_SYSCFG_PMCSETR 0x04
+#define STM32MP1_SYSCFG_PMCCLRR 0x44
-/* STM32F4_ADC_CCR - bit fields */
-#define STM32F4_ADC_ADCPRE_SHIFT 16
-#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
+/* SYSCFG bit fields */
+#define STM32MP1_SYSCFG_ANASWVDD_MASK BIT(9)
-/* STM32H7 - common registers for all ADC instances */
-#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
-#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
-
-/* STM32H7_ADC_CSR - bit fields */
-#define STM32H7_EOC_SLV BIT(18)
-#define STM32H7_EOC_MST BIT(2)
-
-/* STM32H7_ADC_CCR - bit fields */
-#define STM32H7_PRESC_SHIFT 18
-#define STM32H7_PRESC_MASK GENMASK(21, 18)
-#define STM32H7_CKMODE_SHIFT 16
-#define STM32H7_CKMODE_MASK GENMASK(17, 16)
+/* SYSCFG capability flags */
+#define HAS_VBOOSTER BIT(0)
+#define HAS_ANASWVDD BIT(1)
/**
* stm32_adc_common_regs - stm32 common registers, compatible dependent data
* @csr: common status register offset
+ * @ccr: common control register offset
* @eoc1: adc1 end of conversion flag in @csr
* @eoc2: adc2 end of conversion flag in @csr
* @eoc3: adc3 end of conversion flag in @csr
+ * @ier: interrupt enable register offset for each adc
+ * @eocie_msk: end of conversion interrupt enable mask in @ier
*/
struct stm32_adc_common_regs {
u32 csr;
+ u32 ccr;
u32 eoc1_msk;
u32 eoc2_msk;
u32 eoc3_msk;
+ u32 ier;
+ u32 eocie_msk;
};
struct stm32_adc_priv;
@@ -69,11 +64,13 @@
* @regs: common registers for all instances
* @clk_sel: clock selection routine
* @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
+ * @has_syscfg: SYSCFG capability flags
*/
struct stm32_adc_priv_cfg {
const struct stm32_adc_common_regs *regs;
int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
u32 max_clk_rate_hz;
+ unsigned int has_syscfg;
};
/**
@@ -82,18 +79,32 @@
* @domain: irq domain reference
* @aclk: clock reference for the analog circuitry
* @bclk: bus clock common for all ADCs, depends on part used
+ * @booster: booster supply reference
+ * @vdd: vdd supply reference
+ * @vdda: vdda analog supply reference
* @vref: regulator reference
+ * @vdd_uv: vdd supply voltage (microvolts)
+ * @vdda_uv: vdda supply voltage (microvolts)
* @cfg: compatible configuration data
* @common: common data for all ADC instances
+ * @ccr_bak: backup CCR in low power mode
+ * @syscfg: reference to syscon, system control registers
*/
struct stm32_adc_priv {
int irq[STM32_ADC_MAX_ADCS];
struct irq_domain *domain;
struct clk *aclk;
struct clk *bclk;
+ struct regulator *booster;
+ struct regulator *vdd;
+ struct regulator *vdda;
struct regulator *vref;
+ int vdd_uv;
+ int vdda_uv;
const struct stm32_adc_priv_cfg *cfg;
struct stm32_adc_common common;
+ u32 ccr_bak;
+ struct regmap *syscfg;
};
static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
@@ -265,18 +276,38 @@
/* STM32F4 common registers definitions */
static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
.csr = STM32F4_ADC_CSR,
+ .ccr = STM32F4_ADC_CCR,
.eoc1_msk = STM32F4_EOC1,
.eoc2_msk = STM32F4_EOC2,
.eoc3_msk = STM32F4_EOC3,
+ .ier = STM32F4_ADC_CR1,
+ .eocie_msk = STM32F4_EOCIE,
};
/* STM32H7 common registers definitions */
static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
.csr = STM32H7_ADC_CSR,
+ .ccr = STM32H7_ADC_CCR,
.eoc1_msk = STM32H7_EOC_MST,
.eoc2_msk = STM32H7_EOC_SLV,
+ .ier = STM32H7_ADC_IER,
+ .eocie_msk = STM32H7_EOCIE,
};
+static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = {
+ 0, STM32_ADC_OFFSET, STM32_ADC_OFFSET * 2,
+};
+
+static unsigned int stm32_adc_eoc_enabled(struct stm32_adc_priv *priv,
+ unsigned int adc)
+{
+ u32 ier, offset = stm32_adc_offset[adc];
+
+ ier = readl_relaxed(priv->common.base + offset + priv->cfg->regs->ier);
+
+ return ier & priv->cfg->regs->eocie_msk;
+}
+
/* ADC common interrupt for all instances */
static void stm32_adc_irq_handler(struct irq_desc *desc)
{
@@ -287,13 +318,28 @@
chained_irq_enter(chip, desc);
status = readl_relaxed(priv->common.base + priv->cfg->regs->csr);
- if (status & priv->cfg->regs->eoc1_msk)
+ /*
+ * End of conversion may be handled by using IRQ or DMA. There may be a
+ * race here when two conversions complete at the same time on several
+ * ADCs. EOC may be read 'set' for several ADCs, with:
+ * - an ADC configured to use DMA (EOC triggers the DMA request, and
+ * is then automatically cleared by DR read in hardware)
+ * - an ADC configured to use IRQs (EOCIE bit is set. The handler must
+ * be called in this case)
+ * So both EOC status bit in CSR and EOCIE control bit must be checked
+ * before invoking the interrupt handler (e.g. call ISR only for
+ * IRQ-enabled ADCs).
+ */
+ if (status & priv->cfg->regs->eoc1_msk &&
+ stm32_adc_eoc_enabled(priv, 0))
generic_handle_irq(irq_find_mapping(priv->domain, 0));
- if (status & priv->cfg->regs->eoc2_msk)
+ if (status & priv->cfg->regs->eoc2_msk &&
+ stm32_adc_eoc_enabled(priv, 1))
generic_handle_irq(irq_find_mapping(priv->domain, 1));
- if (status & priv->cfg->regs->eoc3_msk)
+ if (status & priv->cfg->regs->eoc3_msk &&
+ stm32_adc_eoc_enabled(priv, 2))
generic_handle_irq(irq_find_mapping(priv->domain, 2));
chained_irq_exit(chip, desc);
@@ -338,7 +384,6 @@
*/
if (i && priv->irq[i] == -ENXIO)
continue;
- dev_err(&pdev->dev, "failed to get irq\n");
return priv->irq[i];
}
@@ -379,6 +424,230 @@
}
}
+static int stm32_adc_core_switches_supply_en(struct stm32_adc_priv *priv,
+ struct device *dev)
+{
+ int ret;
+
+ /*
+ * On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog
+ * switches (via PCSEL) which have reduced performances when their
+ * supply is below 2.7V (vdda by default):
+ * - Voltage booster can be used, to get full ADC performances
+ * (increases power consumption).
+ * - Vdd can be used to supply them, if above 2.7V (STM32MP1 only).
+ *
+ * Recommended settings for ANASWVDD and EN_BOOSTER:
+ * - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1)
+ * - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1
+ * - vdda >= 2.7V: ANASWVDD = 0, EN_BOOSTER = 0 (default)
+ */
+ if (priv->vdda_uv < 2700000) {
+ if (priv->syscfg && priv->vdd_uv > 2700000) {
+ ret = regulator_enable(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(priv->syscfg,
+ STM32MP1_SYSCFG_PMCSETR,
+ STM32MP1_SYSCFG_ANASWVDD_MASK);
+ if (ret < 0) {
+ regulator_disable(priv->vdd);
+ dev_err(dev, "vdd select failed, %d\n", ret);
+ return ret;
+ }
+ dev_dbg(dev, "analog switches supplied by vdd\n");
+
+ return 0;
+ }
+
+ if (priv->booster) {
+ /*
+ * This is optional, as this is a trade-off between
+ * analog performance and power consumption.
+ */
+ ret = regulator_enable(priv->booster);
+ if (ret < 0) {
+ dev_err(dev, "booster enable failed %d\n", ret);
+ return ret;
+ }
+ dev_dbg(dev, "analog switches supplied by booster\n");
+
+ return 0;
+ }
+ }
+
+ /* Fallback using vdda (default), nothing to do */
+ dev_dbg(dev, "analog switches supplied by vdda (%d uV)\n",
+ priv->vdda_uv);
+
+ return 0;
+}
+
+static void stm32_adc_core_switches_supply_dis(struct stm32_adc_priv *priv)
+{
+ if (priv->vdda_uv < 2700000) {
+ if (priv->syscfg && priv->vdd_uv > 2700000) {
+ regmap_write(priv->syscfg, STM32MP1_SYSCFG_PMCCLRR,
+ STM32MP1_SYSCFG_ANASWVDD_MASK);
+ regulator_disable(priv->vdd);
+ return;
+ }
+ if (priv->booster)
+ regulator_disable(priv->booster);
+ }
+}
+
+static int stm32_adc_core_hw_start(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+ int ret;
+
+ ret = regulator_enable(priv->vdda);
+ if (ret < 0) {
+ dev_err(dev, "vdda enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vdda);
+ if (ret < 0) {
+ dev_err(dev, "vdda get voltage failed, %d\n", ret);
+ goto err_vdda_disable;
+ }
+ priv->vdda_uv = ret;
+
+ ret = stm32_adc_core_switches_supply_en(priv, dev);
+ if (ret < 0)
+ goto err_vdda_disable;
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "vref enable failed\n");
+ goto err_switches_dis;
+ }
+
+ if (priv->bclk) {
+ ret = clk_prepare_enable(priv->bclk);
+ if (ret < 0) {
+ dev_err(dev, "bus clk enable failed\n");
+ goto err_regulator_disable;
+ }
+ }
+
+ if (priv->aclk) {
+ ret = clk_prepare_enable(priv->aclk);
+ if (ret < 0) {
+ dev_err(dev, "adc clk enable failed\n");
+ goto err_bclk_disable;
+ }
+ }
+
+ writel_relaxed(priv->ccr_bak, priv->common.base + priv->cfg->regs->ccr);
+
+ return 0;
+
+err_bclk_disable:
+ if (priv->bclk)
+ clk_disable_unprepare(priv->bclk);
+err_regulator_disable:
+ regulator_disable(priv->vref);
+err_switches_dis:
+ stm32_adc_core_switches_supply_dis(priv);
+err_vdda_disable:
+ regulator_disable(priv->vdda);
+
+ return ret;
+}
+
+static void stm32_adc_core_hw_stop(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+ /* Backup CCR that may be lost (depends on power state to achieve) */
+ priv->ccr_bak = readl_relaxed(priv->common.base + priv->cfg->regs->ccr);
+ if (priv->aclk)
+ clk_disable_unprepare(priv->aclk);
+ if (priv->bclk)
+ clk_disable_unprepare(priv->bclk);
+ regulator_disable(priv->vref);
+ stm32_adc_core_switches_supply_dis(priv);
+ regulator_disable(priv->vdda);
+}
+
+static int stm32_adc_core_switches_probe(struct device *dev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ /* Analog switches supply can be controlled by syscfg (optional) */
+ priv->syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(priv->syscfg)) {
+ ret = PTR_ERR(priv->syscfg);
+ if (ret != -ENODEV) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Can't probe syscfg: %d\n", ret);
+ return ret;
+ }
+ priv->syscfg = NULL;
+ }
+
+ /* Booster can be used to supply analog switches (optional) */
+ if (priv->cfg->has_syscfg & HAS_VBOOSTER &&
+ of_property_read_bool(np, "booster-supply")) {
+ priv->booster = devm_regulator_get_optional(dev, "booster");
+ if (IS_ERR(priv->booster)) {
+ ret = PTR_ERR(priv->booster);
+ if (ret != -ENODEV) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "can't get booster %d\n",
+ ret);
+ return ret;
+ }
+ priv->booster = NULL;
+ }
+ }
+
+ /* Vdd can be used to supply analog switches (optional) */
+ if (priv->cfg->has_syscfg & HAS_ANASWVDD &&
+ of_property_read_bool(np, "vdd-supply")) {
+ priv->vdd = devm_regulator_get_optional(dev, "vdd");
+ if (IS_ERR(priv->vdd)) {
+ ret = PTR_ERR(priv->vdd);
+ if (ret != -ENODEV) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "can't get vdd %d\n", ret);
+ return ret;
+ }
+ priv->vdd = NULL;
+ }
+ }
+
+ if (priv->vdd) {
+ ret = regulator_enable(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd get voltage failed %d\n", ret);
+ regulator_disable(priv->vdd);
+ return ret;
+ }
+ priv->vdd_uv = ret;
+
+ regulator_disable(priv->vdd);
+ }
+
+ return 0;
+}
+
static int stm32_adc_probe(struct platform_device *pdev)
{
struct stm32_adc_priv *priv;
@@ -393,6 +662,7 @@
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ platform_set_drvdata(pdev, &priv->common);
priv->cfg = (const struct stm32_adc_priv_cfg *)
of_match_device(dev->driver->of_match_table, dev)->data;
@@ -403,6 +673,14 @@
return PTR_ERR(priv->common.base);
priv->common.phys_base = res->start;
+ priv->vdda = devm_regulator_get(&pdev->dev, "vdda");
+ if (IS_ERR(priv->vdda)) {
+ ret = PTR_ERR(priv->vdda);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "vdda get failed, %d\n", ret);
+ return ret;
+ }
+
priv->vref = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(priv->vref)) {
ret = PTR_ERR(priv->vref);
@@ -410,67 +688,55 @@
return ret;
}
- ret = regulator_enable(priv->vref);
- if (ret < 0) {
- dev_err(&pdev->dev, "vref enable failed\n");
- return ret;
- }
-
- ret = regulator_get_voltage(priv->vref);
- if (ret < 0) {
- dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
- goto err_regulator_disable;
- }
- priv->common.vref_mv = ret / 1000;
- dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
-
priv->aclk = devm_clk_get(&pdev->dev, "adc");
if (IS_ERR(priv->aclk)) {
ret = PTR_ERR(priv->aclk);
- if (ret == -ENOENT) {
- priv->aclk = NULL;
- } else {
+ if (ret != -ENOENT) {
dev_err(&pdev->dev, "Can't get 'adc' clock\n");
- goto err_regulator_disable;
+ return ret;
}
- }
-
- if (priv->aclk) {
- ret = clk_prepare_enable(priv->aclk);
- if (ret < 0) {
- dev_err(&pdev->dev, "adc clk enable failed\n");
- goto err_regulator_disable;
- }
+ priv->aclk = NULL;
}
priv->bclk = devm_clk_get(&pdev->dev, "bus");
if (IS_ERR(priv->bclk)) {
ret = PTR_ERR(priv->bclk);
- if (ret == -ENOENT) {
- priv->bclk = NULL;
- } else {
+ if (ret != -ENOENT) {
dev_err(&pdev->dev, "Can't get 'bus' clock\n");
- goto err_aclk_disable;
+ return ret;
}
+ priv->bclk = NULL;
}
- if (priv->bclk) {
- ret = clk_prepare_enable(priv->bclk);
- if (ret < 0) {
- dev_err(&pdev->dev, "adc clk enable failed\n");
- goto err_aclk_disable;
- }
+ ret = stm32_adc_core_switches_probe(dev, priv);
+ if (ret)
+ return ret;
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_ADC_CORE_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ret = stm32_adc_core_hw_start(dev);
+ if (ret)
+ goto err_pm_stop;
+
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+ goto err_hw_stop;
}
+ priv->common.vref_mv = ret / 1000;
+ dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
ret = priv->cfg->clk_sel(pdev, priv);
if (ret < 0)
- goto err_bclk_disable;
+ goto err_hw_stop;
ret = stm32_adc_irq_probe(pdev, priv);
if (ret < 0)
- goto err_bclk_disable;
-
- platform_set_drvdata(pdev, &priv->common);
+ goto err_hw_stop;
ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
if (ret < 0) {
@@ -478,21 +744,19 @@
goto err_irq_remove;
}
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
return 0;
err_irq_remove:
stm32_adc_irq_remove(pdev, priv);
-
-err_bclk_disable:
- if (priv->bclk)
- clk_disable_unprepare(priv->bclk);
-
-err_aclk_disable:
- if (priv->aclk)
- clk_disable_unprepare(priv->aclk);
-
-err_regulator_disable:
- regulator_disable(priv->vref);
+err_hw_stop:
+ stm32_adc_core_hw_stop(dev);
+err_pm_stop:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
return ret;
}
@@ -502,17 +766,39 @@
struct stm32_adc_common *common = platform_get_drvdata(pdev);
struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+ pm_runtime_get_sync(&pdev->dev);
of_platform_depopulate(&pdev->dev);
stm32_adc_irq_remove(pdev, priv);
- if (priv->bclk)
- clk_disable_unprepare(priv->bclk);
- if (priv->aclk)
- clk_disable_unprepare(priv->aclk);
- regulator_disable(priv->vref);
+ stm32_adc_core_hw_stop(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
return 0;
}
+#if defined(CONFIG_PM)
+static int stm32_adc_core_runtime_suspend(struct device *dev)
+{
+ stm32_adc_core_hw_stop(dev);
+
+ return 0;
+}
+
+static int stm32_adc_core_runtime_resume(struct device *dev)
+{
+ return stm32_adc_core_hw_start(dev);
+}
+#endif
+
+static const struct dev_pm_ops stm32_adc_core_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(stm32_adc_core_runtime_suspend,
+ stm32_adc_core_runtime_resume,
+ NULL)
+};
+
static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
.regs = &stm32f4_adc_common_regs,
.clk_sel = stm32f4_adc_clk_sel,
@@ -523,12 +809,14 @@
.regs = &stm32h7_adc_common_regs,
.clk_sel = stm32h7_adc_clk_sel,
.max_clk_rate_hz = 36000000,
+ .has_syscfg = HAS_VBOOSTER,
};
static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
.regs = &stm32h7_adc_common_regs,
.clk_sel = stm32h7_adc_clk_sel,
.max_clk_rate_hz = 40000000,
+ .has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD,
};
static const struct of_device_id stm32_adc_of_match[] = {
@@ -552,6 +840,7 @@
.driver = {
.name = "stm32-adc-core",
.of_match_table = stm32_adc_of_match,
+ .pm = &stm32_adc_core_pm_ops,
},
};
module_platform_driver(stm32_adc_driver);
diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
index 8af507b..2579d51 100644
--- a/drivers/iio/adc/stm32-adc-core.h
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -25,8 +25,145 @@
* --------------------------------------------------------
*/
#define STM32_ADC_MAX_ADCS 3
+#define STM32_ADC_OFFSET 0x100
#define STM32_ADCX_COMN_OFFSET 0x300
+/* STM32F4 - Registers for each ADC instance */
+#define STM32F4_ADC_SR 0x00
+#define STM32F4_ADC_CR1 0x04
+#define STM32F4_ADC_CR2 0x08
+#define STM32F4_ADC_SMPR1 0x0C
+#define STM32F4_ADC_SMPR2 0x10
+#define STM32F4_ADC_HTR 0x24
+#define STM32F4_ADC_LTR 0x28
+#define STM32F4_ADC_SQR1 0x2C
+#define STM32F4_ADC_SQR2 0x30
+#define STM32F4_ADC_SQR3 0x34
+#define STM32F4_ADC_JSQR 0x38
+#define STM32F4_ADC_JDR1 0x3C
+#define STM32F4_ADC_JDR2 0x40
+#define STM32F4_ADC_JDR3 0x44
+#define STM32F4_ADC_JDR4 0x48
+#define STM32F4_ADC_DR 0x4C
+
+/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
+#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+
+/* STM32F4_ADC_SR - bit fields */
+#define STM32F4_STRT BIT(4)
+#define STM32F4_EOC BIT(1)
+
+/* STM32F4_ADC_CR1 - bit fields */
+#define STM32F4_RES_SHIFT 24
+#define STM32F4_RES_MASK GENMASK(25, 24)
+#define STM32F4_SCAN BIT(8)
+#define STM32F4_EOCIE BIT(5)
+
+/* STM32F4_ADC_CR2 - bit fields */
+#define STM32F4_SWSTART BIT(30)
+#define STM32F4_EXTEN_SHIFT 28
+#define STM32F4_EXTEN_MASK GENMASK(29, 28)
+#define STM32F4_EXTSEL_SHIFT 24
+#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
+#define STM32F4_EOCS BIT(10)
+#define STM32F4_DDS BIT(9)
+#define STM32F4_DMA BIT(8)
+#define STM32F4_ADON BIT(0)
+
+/* STM32F4_ADC_CSR - bit fields */
+#define STM32F4_EOC3 BIT(17)
+#define STM32F4_EOC2 BIT(9)
+#define STM32F4_EOC1 BIT(1)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_ADCPRE_SHIFT 16
+#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
+
+/* STM32H7 - Registers for each ADC instance */
+#define STM32H7_ADC_ISR 0x00
+#define STM32H7_ADC_IER 0x04
+#define STM32H7_ADC_CR 0x08
+#define STM32H7_ADC_CFGR 0x0C
+#define STM32H7_ADC_SMPR1 0x14
+#define STM32H7_ADC_SMPR2 0x18
+#define STM32H7_ADC_PCSEL 0x1C
+#define STM32H7_ADC_SQR1 0x30
+#define STM32H7_ADC_SQR2 0x34
+#define STM32H7_ADC_SQR3 0x38
+#define STM32H7_ADC_SQR4 0x3C
+#define STM32H7_ADC_DR 0x40
+#define STM32H7_ADC_DIFSEL 0xC0
+#define STM32H7_ADC_CALFACT 0xC4
+#define STM32H7_ADC_CALFACT2 0xC8
+
+/* STM32H7 - common registers for all ADC instances */
+#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
+
+/* STM32H7_ADC_ISR - bit fields */
+#define STM32MP1_VREGREADY BIT(12)
+#define STM32H7_EOC BIT(2)
+#define STM32H7_ADRDY BIT(0)
+
+/* STM32H7_ADC_IER - bit fields */
+#define STM32H7_EOCIE STM32H7_EOC
+
+/* STM32H7_ADC_CR - bit fields */
+#define STM32H7_ADCAL BIT(31)
+#define STM32H7_ADCALDIF BIT(30)
+#define STM32H7_DEEPPWD BIT(29)
+#define STM32H7_ADVREGEN BIT(28)
+#define STM32H7_LINCALRDYW6 BIT(27)
+#define STM32H7_LINCALRDYW5 BIT(26)
+#define STM32H7_LINCALRDYW4 BIT(25)
+#define STM32H7_LINCALRDYW3 BIT(24)
+#define STM32H7_LINCALRDYW2 BIT(23)
+#define STM32H7_LINCALRDYW1 BIT(22)
+#define STM32H7_ADCALLIN BIT(16)
+#define STM32H7_BOOST BIT(8)
+#define STM32H7_ADSTP BIT(4)
+#define STM32H7_ADSTART BIT(2)
+#define STM32H7_ADDIS BIT(1)
+#define STM32H7_ADEN BIT(0)
+
+/* STM32H7_ADC_CFGR bit fields */
+#define STM32H7_EXTEN_SHIFT 10
+#define STM32H7_EXTEN_MASK GENMASK(11, 10)
+#define STM32H7_EXTSEL_SHIFT 5
+#define STM32H7_EXTSEL_MASK GENMASK(9, 5)
+#define STM32H7_RES_SHIFT 2
+#define STM32H7_RES_MASK GENMASK(4, 2)
+#define STM32H7_DMNGT_SHIFT 0
+#define STM32H7_DMNGT_MASK GENMASK(1, 0)
+
+enum stm32h7_adc_dmngt {
+ STM32H7_DMNGT_DR_ONLY, /* Regular data in DR only */
+ STM32H7_DMNGT_DMA_ONESHOT, /* DMA one shot mode */
+ STM32H7_DMNGT_DFSDM, /* DFSDM mode */
+ STM32H7_DMNGT_DMA_CIRC, /* DMA circular mode */
+};
+
+/* STM32H7_ADC_CALFACT - bit fields */
+#define STM32H7_CALFACT_D_SHIFT 16
+#define STM32H7_CALFACT_D_MASK GENMASK(26, 16)
+#define STM32H7_CALFACT_S_SHIFT 0
+#define STM32H7_CALFACT_S_MASK GENMASK(10, 0)
+
+/* STM32H7_ADC_CALFACT2 - bit fields */
+#define STM32H7_LINCALFACT_SHIFT 0
+#define STM32H7_LINCALFACT_MASK GENMASK(29, 0)
+
+/* STM32H7_ADC_CSR - bit fields */
+#define STM32H7_EOC_SLV BIT(18)
+#define STM32H7_EOC_MST BIT(2)
+
+/* STM32H7_ADC_CCR - bit fields */
+#define STM32H7_PRESC_SHIFT 18
+#define STM32H7_PRESC_MASK GENMASK(21, 18)
+#define STM32H7_CKMODE_SHIFT 16
+#define STM32H7_CKMODE_MASK GENMASK(17, 16)
+
/**
* struct stm32_adc_common - stm32 ADC driver common data (for all instances)
* @base: control registers base cpu addr
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 3784118..73aee59 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -22,120 +22,12 @@
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include "stm32-adc-core.h"
-/* STM32F4 - Registers for each ADC instance */
-#define STM32F4_ADC_SR 0x00
-#define STM32F4_ADC_CR1 0x04
-#define STM32F4_ADC_CR2 0x08
-#define STM32F4_ADC_SMPR1 0x0C
-#define STM32F4_ADC_SMPR2 0x10
-#define STM32F4_ADC_HTR 0x24
-#define STM32F4_ADC_LTR 0x28
-#define STM32F4_ADC_SQR1 0x2C
-#define STM32F4_ADC_SQR2 0x30
-#define STM32F4_ADC_SQR3 0x34
-#define STM32F4_ADC_JSQR 0x38
-#define STM32F4_ADC_JDR1 0x3C
-#define STM32F4_ADC_JDR2 0x40
-#define STM32F4_ADC_JDR3 0x44
-#define STM32F4_ADC_JDR4 0x48
-#define STM32F4_ADC_DR 0x4C
-
-/* STM32F4_ADC_SR - bit fields */
-#define STM32F4_STRT BIT(4)
-#define STM32F4_EOC BIT(1)
-
-/* STM32F4_ADC_CR1 - bit fields */
-#define STM32F4_RES_SHIFT 24
-#define STM32F4_RES_MASK GENMASK(25, 24)
-#define STM32F4_SCAN BIT(8)
-#define STM32F4_EOCIE BIT(5)
-
-/* STM32F4_ADC_CR2 - bit fields */
-#define STM32F4_SWSTART BIT(30)
-#define STM32F4_EXTEN_SHIFT 28
-#define STM32F4_EXTEN_MASK GENMASK(29, 28)
-#define STM32F4_EXTSEL_SHIFT 24
-#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
-#define STM32F4_EOCS BIT(10)
-#define STM32F4_DDS BIT(9)
-#define STM32F4_DMA BIT(8)
-#define STM32F4_ADON BIT(0)
-
-/* STM32H7 - Registers for each ADC instance */
-#define STM32H7_ADC_ISR 0x00
-#define STM32H7_ADC_IER 0x04
-#define STM32H7_ADC_CR 0x08
-#define STM32H7_ADC_CFGR 0x0C
-#define STM32H7_ADC_SMPR1 0x14
-#define STM32H7_ADC_SMPR2 0x18
-#define STM32H7_ADC_PCSEL 0x1C
-#define STM32H7_ADC_SQR1 0x30
-#define STM32H7_ADC_SQR2 0x34
-#define STM32H7_ADC_SQR3 0x38
-#define STM32H7_ADC_SQR4 0x3C
-#define STM32H7_ADC_DR 0x40
-#define STM32H7_ADC_DIFSEL 0xC0
-#define STM32H7_ADC_CALFACT 0xC4
-#define STM32H7_ADC_CALFACT2 0xC8
-
-/* STM32H7_ADC_ISR - bit fields */
-#define STM32MP1_VREGREADY BIT(12)
-#define STM32H7_EOC BIT(2)
-#define STM32H7_ADRDY BIT(0)
-
-/* STM32H7_ADC_IER - bit fields */
-#define STM32H7_EOCIE STM32H7_EOC
-
-/* STM32H7_ADC_CR - bit fields */
-#define STM32H7_ADCAL BIT(31)
-#define STM32H7_ADCALDIF BIT(30)
-#define STM32H7_DEEPPWD BIT(29)
-#define STM32H7_ADVREGEN BIT(28)
-#define STM32H7_LINCALRDYW6 BIT(27)
-#define STM32H7_LINCALRDYW5 BIT(26)
-#define STM32H7_LINCALRDYW4 BIT(25)
-#define STM32H7_LINCALRDYW3 BIT(24)
-#define STM32H7_LINCALRDYW2 BIT(23)
-#define STM32H7_LINCALRDYW1 BIT(22)
-#define STM32H7_ADCALLIN BIT(16)
-#define STM32H7_BOOST BIT(8)
-#define STM32H7_ADSTP BIT(4)
-#define STM32H7_ADSTART BIT(2)
-#define STM32H7_ADDIS BIT(1)
-#define STM32H7_ADEN BIT(0)
-
-/* STM32H7_ADC_CFGR bit fields */
-#define STM32H7_EXTEN_SHIFT 10
-#define STM32H7_EXTEN_MASK GENMASK(11, 10)
-#define STM32H7_EXTSEL_SHIFT 5
-#define STM32H7_EXTSEL_MASK GENMASK(9, 5)
-#define STM32H7_RES_SHIFT 2
-#define STM32H7_RES_MASK GENMASK(4, 2)
-#define STM32H7_DMNGT_SHIFT 0
-#define STM32H7_DMNGT_MASK GENMASK(1, 0)
-
-enum stm32h7_adc_dmngt {
- STM32H7_DMNGT_DR_ONLY, /* Regular data in DR only */
- STM32H7_DMNGT_DMA_ONESHOT, /* DMA one shot mode */
- STM32H7_DMNGT_DFSDM, /* DFSDM mode */
- STM32H7_DMNGT_DMA_CIRC, /* DMA circular mode */
-};
-
-/* STM32H7_ADC_CALFACT - bit fields */
-#define STM32H7_CALFACT_D_SHIFT 16
-#define STM32H7_CALFACT_D_MASK GENMASK(26, 16)
-#define STM32H7_CALFACT_S_SHIFT 0
-#define STM32H7_CALFACT_S_MASK GENMASK(10, 0)
-
-/* STM32H7_ADC_CALFACT2 - bit fields */
-#define STM32H7_LINCALFACT_SHIFT 0
-#define STM32H7_LINCALFACT_MASK GENMASK(29, 0)
-
/* Number of linear calibration shadow registers / LINCALRDYW control bits */
#define STM32H7_LINCALFACT_NUM 6
@@ -148,6 +40,7 @@
#define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */
#define STM32_ADC_TIMEOUT_US 100000
#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+#define STM32_ADC_HW_STOP_DELAY_MS 100
#define STM32_DMA_BUFFER_SIZE PAGE_SIZE
@@ -199,11 +92,13 @@
* @calfact_s: Calibration offset for single ended channels
* @calfact_d: Calibration offset in differential
* @lincalfact: Linearity calibration factor
+ * @calibrated: Indicates calibration status
*/
struct stm32_adc_calib {
u32 calfact_s;
u32 calfact_d;
u32 lincalfact[STM32H7_LINCALFACT_NUM];
+ bool calibrated;
};
/**
@@ -251,7 +146,6 @@
* @trigs: external trigger sources
* @clk_required: clock is required
* @has_vregready: vregready status flag presence
- * @selfcalib: optional routine for self-calibration
* @prepare: optional prepare routine (power-up, enable)
* @start_conv: routine to start conversions
* @stop_conv: routine to stop conversions
@@ -264,7 +158,6 @@
struct stm32_adc_trig_info *trigs;
bool clk_required;
bool has_vregready;
- int (*selfcalib)(struct stm32_adc *);
int (*prepare)(struct stm32_adc *);
void (*start_conv)(struct stm32_adc *, bool dma);
void (*stop_conv)(struct stm32_adc *);
@@ -623,6 +516,47 @@
stm32_adc_writel(adc, res->reg, val);
}
+static int stm32_adc_hw_stop(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+
+ if (adc->cfg->unprepare)
+ adc->cfg->unprepare(adc);
+
+ if (adc->clk)
+ clk_disable_unprepare(adc->clk);
+
+ return 0;
+}
+
+static int stm32_adc_hw_start(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+ int ret;
+
+ if (adc->clk) {
+ ret = clk_prepare_enable(adc->clk);
+ if (ret)
+ return ret;
+ }
+
+ stm32_adc_set_res(adc);
+
+ if (adc->cfg->prepare) {
+ ret = adc->cfg->prepare(adc);
+ if (ret)
+ goto err_clk_dis;
+ }
+
+ return 0;
+
+err_clk_dis:
+ if (adc->clk)
+ clk_disable_unprepare(adc->clk);
+
+ return ret;
+}
+
/**
* stm32f4_adc_start_conv() - Start conversions for regular channels.
* @adc: stm32 adc instance
@@ -777,6 +711,7 @@
/**
* stm32h7_adc_read_selfcalib() - read calibration shadow regs, save result
* @adc: stm32 adc instance
+ * Note: Must be called once ADC is enabled, so LINCALRDYW[1..6] are writable
*/
static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc)
{
@@ -784,11 +719,6 @@
int i, ret;
u32 lincalrdyw_mask, val;
- /* Enable adc so LINCALRDYW1..6 bits are writable */
- ret = stm32h7_adc_enable(adc);
- if (ret)
- return ret;
-
/* Read linearity calibration */
lincalrdyw_mask = STM32H7_LINCALRDYW6;
for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
@@ -801,7 +731,7 @@
100, STM32_ADC_TIMEOUT_US);
if (ret) {
dev_err(&indio_dev->dev, "Failed to read calfact\n");
- goto disable;
+ return ret;
}
val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2);
@@ -817,11 +747,9 @@
adc->cal.calfact_s >>= STM32H7_CALFACT_S_SHIFT;
adc->cal.calfact_d = (val & STM32H7_CALFACT_D_MASK);
adc->cal.calfact_d >>= STM32H7_CALFACT_D_SHIFT;
+ adc->cal.calibrated = true;
-disable:
- stm32h7_adc_disable(adc);
-
- return ret;
+ return 0;
}
/**
@@ -898,9 +826,9 @@
#define STM32H7_ADC_CALIB_TIMEOUT_US 100000
/**
- * stm32h7_adc_selfcalib() - Procedure to calibrate ADC (from power down)
+ * stm32h7_adc_selfcalib() - Procedure to calibrate ADC
* @adc: stm32 adc instance
- * Exit from power down, calibrate ADC, then return to power down.
+ * Note: Must be called once ADC is out of power down.
*/
static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
{
@@ -908,9 +836,8 @@
int ret;
u32 val;
- ret = stm32h7_adc_exit_pwr_down(adc);
- if (ret)
- return ret;
+ if (adc->cal.calibrated)
+ return true;
/*
* Select calibration mode:
@@ -927,7 +854,7 @@
STM32H7_ADC_CALIB_TIMEOUT_US);
if (ret) {
dev_err(&indio_dev->dev, "calibration failed\n");
- goto pwr_dwn;
+ goto out;
}
/*
@@ -944,18 +871,13 @@
STM32H7_ADC_CALIB_TIMEOUT_US);
if (ret) {
dev_err(&indio_dev->dev, "calibration failed\n");
- goto pwr_dwn;
+ goto out;
}
+out:
stm32_adc_clr_bits(adc, STM32H7_ADC_CR,
STM32H7_ADCALDIF | STM32H7_ADCALLIN);
- /* Read calibration result for future reference */
- ret = stm32h7_adc_read_selfcalib(adc);
-
-pwr_dwn:
- stm32h7_adc_enter_pwr_down(adc);
-
return ret;
}
@@ -972,19 +894,28 @@
*/
static int stm32h7_adc_prepare(struct stm32_adc *adc)
{
- int ret;
+ int calib, ret;
ret = stm32h7_adc_exit_pwr_down(adc);
if (ret)
return ret;
+ ret = stm32h7_adc_selfcalib(adc);
+ if (ret < 0)
+ goto pwr_dwn;
+ calib = ret;
+
stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
ret = stm32h7_adc_enable(adc);
if (ret)
goto pwr_dwn;
- ret = stm32h7_adc_restore_selfcalib(adc);
+ /* Either restore or read calibration result for future reference */
+ if (calib)
+ ret = stm32h7_adc_restore_selfcalib(adc);
+ else
+ ret = stm32h7_adc_read_selfcalib(adc);
if (ret)
goto disable;
@@ -1174,6 +1105,7 @@
int *res)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
const struct stm32_adc_regspec *regs = adc->cfg->regs;
long timeout;
u32 val;
@@ -1183,10 +1115,10 @@
adc->bufi = 0;
- if (adc->cfg->prepare) {
- ret = adc->cfg->prepare(adc);
- if (ret)
- return ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
}
/* Apply sampling time settings */
@@ -1224,8 +1156,8 @@
stm32_adc_conv_irq_disable(adc);
- if (adc->cfg->unprepare)
- adc->cfg->unprepare(adc);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return ret;
}
@@ -1333,15 +1265,22 @@
const unsigned long *scan_mask)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
int ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
+
adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength);
ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
- if (ret)
- return ret;
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
- return 0;
+ return ret;
}
static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
@@ -1371,12 +1310,23 @@
unsigned *readval)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
if (!readval)
stm32_adc_writel(adc, reg, writeval);
else
*readval = stm32_adc_readl(adc, reg);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
return 0;
}
@@ -1449,7 +1399,7 @@
cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
if (ret) {
- dmaengine_terminate_all(adc->dma_chan);
+ dmaengine_terminate_sync(adc->dma_chan);
return ret;
}
@@ -1459,21 +1409,22 @@
return 0;
}
-static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
+static int __stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
int ret;
- if (adc->cfg->prepare) {
- ret = adc->cfg->prepare(adc);
- if (ret)
- return ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
}
ret = stm32_adc_set_trig(indio_dev, indio_dev->trig);
if (ret) {
dev_err(&indio_dev->dev, "Can't set trigger\n");
- goto err_unprepare;
+ goto err_pm_put;
}
ret = stm32_adc_dma_start(indio_dev);
@@ -1482,10 +1433,6 @@
goto err_clr_trig;
}
- ret = iio_triggered_buffer_postenable(indio_dev);
- if (ret < 0)
- goto err_stop_dma;
-
/* Reset adc buffer index */
adc->bufi = 0;
@@ -1496,39 +1443,58 @@
return 0;
-err_stop_dma:
- if (adc->dma_chan)
- dmaengine_terminate_all(adc->dma_chan);
err_clr_trig:
stm32_adc_set_trig(indio_dev, NULL);
-err_unprepare:
- if (adc->cfg->unprepare)
- adc->cfg->unprepare(adc);
+err_pm_put:
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return ret;
}
-static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
+static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = __stm32_adc_buffer_postenable(indio_dev);
+ if (ret < 0)
+ iio_triggered_buffer_predisable(indio_dev);
+
+ return ret;
+}
+
+static void __stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
{
struct stm32_adc *adc = iio_priv(indio_dev);
- int ret;
+ struct device *dev = indio_dev->dev.parent;
adc->cfg->stop_conv(adc);
if (!adc->dma_chan)
stm32_adc_conv_irq_disable(adc);
- ret = iio_triggered_buffer_predisable(indio_dev);
- if (ret < 0)
- dev_err(&indio_dev->dev, "predisable failed\n");
-
if (adc->dma_chan)
- dmaengine_terminate_all(adc->dma_chan);
+ dmaengine_terminate_sync(adc->dma_chan);
if (stm32_adc_set_trig(indio_dev, NULL))
dev_err(&indio_dev->dev, "Can't clear trigger\n");
- if (adc->cfg->unprepare)
- adc->cfg->unprepare(adc);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+}
+
+static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ __stm32_adc_buffer_predisable(indio_dev);
+
+ ret = iio_triggered_buffer_predisable(indio_dev);
+ if (ret < 0)
+ dev_err(&indio_dev->dev, "predisable failed\n");
return ret;
}
@@ -1844,10 +1810,8 @@
}
adc->irq = platform_get_irq(pdev, 0);
- if (adc->irq < 0) {
- dev_err(&pdev->dev, "failed to get irq\n");
+ if (adc->irq < 0)
return adc->irq;
- }
ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
0, pdev->name, adc);
@@ -1867,32 +1831,17 @@
}
}
- if (adc->clk) {
- ret = clk_prepare_enable(adc->clk);
- if (ret < 0) {
- dev_err(&pdev->dev, "clk enable failed\n");
- return ret;
- }
- }
-
ret = stm32_adc_of_get_resolution(indio_dev);
if (ret < 0)
- goto err_clk_disable;
- stm32_adc_set_res(adc);
-
- if (adc->cfg->selfcalib) {
- ret = adc->cfg->selfcalib(adc);
- if (ret)
- goto err_clk_disable;
- }
+ return ret;
ret = stm32_adc_chan_of_init(indio_dev);
if (ret < 0)
- goto err_clk_disable;
+ return ret;
ret = stm32_adc_dma_request(indio_dev);
if (ret < 0)
- goto err_clk_disable;
+ return ret;
ret = iio_triggered_buffer_setup(indio_dev,
&iio_pollfunc_store_time,
@@ -1903,15 +1852,35 @@
goto err_dma_disable;
}
+ /* Get stm32-adc-core PM online */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_ADC_HW_STOP_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ret = stm32_adc_hw_start(dev);
+ if (ret)
+ goto err_buffer_cleanup;
+
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&pdev->dev, "iio dev register failed\n");
- goto err_buffer_cleanup;
+ goto err_hw_stop;
}
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
return 0;
+err_hw_stop:
+ stm32_adc_hw_stop(dev);
+
err_buffer_cleanup:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
iio_triggered_buffer_cleanup(indio_dev);
err_dma_disable:
@@ -1921,9 +1890,6 @@
adc->rx_buf, adc->rx_dma_buf);
dma_release_channel(adc->dma_chan);
}
-err_clk_disable:
- if (adc->clk)
- clk_disable_unprepare(adc->clk);
return ret;
}
@@ -1933,7 +1899,12 @@
struct stm32_adc *adc = platform_get_drvdata(pdev);
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ pm_runtime_get_sync(&pdev->dev);
iio_device_unregister(indio_dev);
+ stm32_adc_hw_stop(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
iio_triggered_buffer_cleanup(indio_dev);
if (adc->dma_chan) {
dma_free_coherent(adc->dma_chan->device->dev,
@@ -1941,12 +1912,62 @@
adc->rx_buf, adc->rx_dma_buf);
dma_release_channel(adc->dma_chan);
}
- if (adc->clk)
- clk_disable_unprepare(adc->clk);
return 0;
}
+#if defined(CONFIG_PM_SLEEP)
+static int stm32_adc_suspend(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ if (iio_buffer_enabled(indio_dev))
+ __stm32_adc_buffer_predisable(indio_dev);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int stm32_adc_resume(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret < 0)
+ return ret;
+
+ if (!iio_buffer_enabled(indio_dev))
+ return 0;
+
+ ret = stm32_adc_update_scan_mode(indio_dev,
+ indio_dev->active_scan_mask);
+ if (ret < 0)
+ return ret;
+
+ return __stm32_adc_buffer_postenable(indio_dev);
+}
+#endif
+
+#if defined(CONFIG_PM)
+static int stm32_adc_runtime_suspend(struct device *dev)
+{
+ return stm32_adc_hw_stop(dev);
+}
+
+static int stm32_adc_runtime_resume(struct device *dev)
+{
+ return stm32_adc_hw_start(dev);
+}
+#endif
+
+static const struct dev_pm_ops stm32_adc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_adc_suspend, stm32_adc_resume)
+ SET_RUNTIME_PM_OPS(stm32_adc_runtime_suspend, stm32_adc_runtime_resume,
+ NULL)
+};
+
static const struct stm32_adc_cfg stm32f4_adc_cfg = {
.regs = &stm32f4_adc_regspec,
.adc_info = &stm32f4_adc_info,
@@ -1961,7 +1982,6 @@
.regs = &stm32h7_adc_regspec,
.adc_info = &stm32h7_adc_info,
.trigs = stm32h7_adc_trigs,
- .selfcalib = stm32h7_adc_selfcalib,
.start_conv = stm32h7_adc_start_conv,
.stop_conv = stm32h7_adc_stop_conv,
.prepare = stm32h7_adc_prepare,
@@ -1974,7 +1994,6 @@
.adc_info = &stm32h7_adc_info,
.trigs = stm32h7_adc_trigs,
.has_vregready = true,
- .selfcalib = stm32h7_adc_selfcalib,
.start_conv = stm32h7_adc_start_conv,
.stop_conv = stm32h7_adc_stop_conv,
.prepare = stm32h7_adc_prepare,
@@ -1996,6 +2015,7 @@
.driver = {
.name = "stm32-adc",
.of_match_table = stm32_adc_of_match,
+ .pm = &stm32_adc_pm_ops,
},
};
module_platform_driver(stm32_adc_driver);
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index fcd4a1c..e493242 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -12,6 +12,11 @@
#include <linux/iio/buffer.h>
#include <linux/iio/hw-consumer.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/timer/stm32-lptim-trigger.h>
+#include <linux/iio/timer/stm32-timer-trigger.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -34,9 +39,21 @@
#define DFSDM_MAX_INT_OVERSAMPLING 256
#define DFSDM_MAX_FL_OVERSAMPLING 1024
-/* Max sample resolutions */
-#define DFSDM_MAX_RES BIT(31)
-#define DFSDM_DATA_RES BIT(23)
+/* Limit filter output resolution to 31 bits. (i.e. sample range is +/-2^30) */
+#define DFSDM_DATA_MAX BIT(30)
+/*
+ * Data are output as two's complement data in a 24 bit field.
+ * Data from filters are in the range +/-2^(n-1)
+ * 2^(n-1) maximum positive value cannot be coded in 2's complement n bits
+ * An extra bit is required to avoid wrap-around of the binary code for 2^(n-1)
+ * So, the resolution of samples from filter is actually limited to 23 bits
+ */
+#define DFSDM_DATA_RES 24
+
+/* Filter configuration */
+#define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \
+ DFSDM_CR1_RSYNC_MASK | DFSDM_CR1_JSYNC_MASK | \
+ DFSDM_CR1_JSCAN_MASK)
enum sd_converter_type {
DFSDM_AUDIO,
@@ -54,6 +71,8 @@
struct stm32_dfsdm *dfsdm;
const struct stm32_dfsdm_dev_data *dev_data;
unsigned int fl_id;
+ unsigned int nconv;
+ unsigned long smask;
/* ADC specific */
unsigned int oversamp;
@@ -114,14 +133,70 @@
return -EINVAL;
}
-static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
- unsigned int fast, unsigned int oversamp)
+/**
+ * struct stm32_dfsdm_trig_info - DFSDM trigger info
+ * @name: name of the trigger, corresponding to its source
+ * @jextsel: trigger signal selection
+ */
+struct stm32_dfsdm_trig_info {
+ const char *name;
+ unsigned int jextsel;
+};
+
+/* hardware injected trigger enable, edge selection */
+enum stm32_dfsdm_jexten {
+ STM32_DFSDM_JEXTEN_DISABLED,
+ STM32_DFSDM_JEXTEN_RISING_EDGE,
+ STM32_DFSDM_JEXTEN_FALLING_EDGE,
+ STM32_DFSDM_EXTEN_BOTH_EDGES,
+};
+
+static const struct stm32_dfsdm_trig_info stm32_dfsdm_trigs[] = {
+ { TIM1_TRGO, 0 },
+ { TIM1_TRGO2, 1 },
+ { TIM8_TRGO, 2 },
+ { TIM8_TRGO2, 3 },
+ { TIM3_TRGO, 4 },
+ { TIM4_TRGO, 5 },
+ { TIM16_OC1, 6 },
+ { TIM6_TRGO, 7 },
+ { TIM7_TRGO, 8 },
+ { LPTIM1_OUT, 26 },
+ { LPTIM2_OUT, 27 },
+ { LPTIM3_OUT, 28 },
+ {},
+};
+
+static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ int i;
+
+ /* lookup triggers registered by stm32 timer trigger driver */
+ for (i = 0; stm32_dfsdm_trigs[i].name; i++) {
+ /**
+ * Checking both stm32 timer trigger type and trig name
+ * should be safe against arbitrary trigger names.
+ */
+ if ((is_stm32_timer_trigger(trig) ||
+ is_stm32_lptim_trigger(trig)) &&
+ !strcmp(stm32_dfsdm_trigs[i].name, trig->name)) {
+ return stm32_dfsdm_trigs[i].jextsel;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl,
+ unsigned int fast, unsigned int oversamp)
{
unsigned int i, d, fosr, iosr;
- u64 res;
- s64 delta;
+ u64 res, max;
+ int bits, shift;
unsigned int m = 1; /* multiplication factor */
unsigned int p = fl->ford; /* filter order (ford) */
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fast];
pr_debug("%s: Requested oversampling: %d\n", __func__, oversamp);
/*
@@ -140,11 +215,8 @@
/*
* Look for filter and integrator oversampling ratios which allows
- * to reach 24 bits data output resolution.
- * Leave as soon as if exact resolution if reached.
- * Otherwise the higher resolution below 32 bits is kept.
+ * to maximize data output resolution.
*/
- fl->res = 0;
for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
if (fast)
@@ -169,50 +241,128 @@
res = fosr;
for (i = p - 1; i > 0; i--) {
res = res * (u64)fosr;
- if (res > DFSDM_MAX_RES)
+ if (res > DFSDM_DATA_MAX)
break;
}
- if (res > DFSDM_MAX_RES)
+ if (res > DFSDM_DATA_MAX)
continue;
+
res = res * (u64)m * (u64)iosr;
- if (res > DFSDM_MAX_RES)
+ if (res > DFSDM_DATA_MAX)
continue;
- delta = res - DFSDM_DATA_RES;
+ if (res >= flo->res) {
+ flo->res = res;
+ flo->fosr = fosr;
+ flo->iosr = iosr;
- if (res >= fl->res) {
- fl->res = res;
- fl->fosr = fosr;
- fl->iosr = iosr;
- fl->fast = fast;
- pr_debug("%s: fosr = %d, iosr = %d\n",
- __func__, fl->fosr, fl->iosr);
+ bits = fls(flo->res);
+ /* 8 LBSs in data register contain chan info */
+ max = flo->res << 8;
+
+ /* if resolution is not a power of two */
+ if (flo->res > BIT(bits - 1))
+ bits++;
+ else
+ max--;
+
+ shift = DFSDM_DATA_RES - bits;
+ /*
+ * Compute right/left shift
+ * Right shift is performed by hardware
+ * when transferring samples to data register.
+ * Left shift is done by software on buffer
+ */
+ if (shift > 0) {
+ /* Resolution is lower than 24 bits */
+ flo->rshift = 0;
+ flo->lshift = shift;
+ } else {
+ /*
+ * If resolution is 24 bits or more,
+ * max positive value may be ambiguous
+ * (equal to max negative value as sign
+ * bit is dropped).
+ * Reduce resolution to 23 bits (rshift)
+ * to keep the sign on bit 23 and treat
+ * saturation before rescaling on 24
+ * bits (lshift).
+ */
+ flo->rshift = 1 - shift;
+ flo->lshift = 1;
+ max >>= flo->rshift;
+ }
+ flo->max = (s32)max;
+
+ pr_debug("%s: fast %d, fosr %d, iosr %d, res 0x%llx/%d bits, rshift %d, lshift %d\n",
+ __func__, fast, flo->fosr, flo->iosr,
+ flo->res, bits, flo->rshift,
+ flo->lshift);
}
-
- if (!delta)
- return 0;
}
}
- if (!fl->res)
+ if (!flo->res)
return -EINVAL;
return 0;
}
-static int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm,
- unsigned int ch_id)
+static int stm32_dfsdm_compute_all_osrs(struct iio_dev *indio_dev,
+ unsigned int oversamp)
{
- return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
- DFSDM_CHCFGR1_CHEN_MASK,
- DFSDM_CHCFGR1_CHEN(1));
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+ int ret0, ret1;
+
+ memset(&fl->flo[0], 0, sizeof(fl->flo[0]));
+ memset(&fl->flo[1], 0, sizeof(fl->flo[1]));
+
+ ret0 = stm32_dfsdm_compute_osrs(fl, 0, oversamp);
+ ret1 = stm32_dfsdm_compute_osrs(fl, 1, oversamp);
+ if (ret0 < 0 && ret1 < 0) {
+ dev_err(&indio_dev->dev,
+ "Filter parameters not found: errors %d/%d\n",
+ ret0, ret1);
+ return -EINVAL;
+ }
+
+ return 0;
}
-static void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm,
- unsigned int ch_id)
+static int stm32_dfsdm_start_channel(struct stm32_dfsdm_adc *adc)
{
- regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
- DFSDM_CHCFGR1_CHEN_MASK, DFSDM_CHCFGR1_CHEN(0));
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+ int ret;
+
+ for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
+ DFSDM_CHCFGR1_CHEN_MASK,
+ DFSDM_CHCFGR1_CHEN(1));
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void stm32_dfsdm_stop_channel(struct stm32_dfsdm_adc *adc)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+
+ for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
+ DFSDM_CHCFGR1_CHEN_MASK,
+ DFSDM_CHCFGR1_CHEN(0));
+ }
}
static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
@@ -237,9 +387,11 @@
DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
}
-static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm,
- unsigned int fl_id)
+static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
{
+ struct stm32_dfsdm *dfsdm = adc->dfsdm;
int ret;
/* Enable filter */
@@ -248,7 +400,11 @@
if (ret < 0)
return ret;
- /* Start conversion */
+ /* Nothing more to do for injected (scan mode/triggered) conversions */
+ if (adc->nconv > 1 || trig)
+ return 0;
+
+ /* Software start (single or continuous) regular conversion */
return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
DFSDM_CR1_RSWSTART_MASK,
DFSDM_CR1_RSWSTART(1));
@@ -262,22 +418,101 @@
DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
}
-static int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm,
- unsigned int fl_id, unsigned int ch_id)
+static int stm32_dfsdm_filter_set_trig(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
{
- struct regmap *regmap = dfsdm->regmap;
- struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ u32 jextsel = 0, jexten = STM32_DFSDM_JEXTEN_DISABLED;
+ int ret;
+
+ if (trig) {
+ ret = stm32_dfsdm_get_jextsel(indio_dev, trig);
+ if (ret < 0)
+ return ret;
+
+ /* set trigger source and polarity (default to rising edge) */
+ jextsel = ret;
+ jexten = STM32_DFSDM_JEXTEN_RISING_EDGE;
+ }
+
+ ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_JEXTSEL_MASK | DFSDM_CR1_JEXTEN_MASK,
+ DFSDM_CR1_JEXTSEL(jextsel) |
+ DFSDM_CR1_JEXTEN(jexten));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int stm32_dfsdm_channels_configure(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[0];
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+ int ret;
+
+ fl->fast = 0;
+
+ /*
+ * In continuous mode, use fast mode configuration,
+ * if it provides a better resolution.
+ */
+ if (adc->nconv == 1 && !trig &&
+ (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE)) {
+ if (fl->flo[1].res >= fl->flo[0].res) {
+ fl->fast = 1;
+ flo = &fl->flo[1];
+ }
+ }
+
+ if (!flo->res)
+ return -EINVAL;
+
+ for_each_set_bit(bit, &adc->smask,
+ sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+
+ ret = regmap_update_bits(regmap,
+ DFSDM_CHCFGR2(chan->channel),
+ DFSDM_CHCFGR2_DTRBS_MASK,
+ DFSDM_CHCFGR2_DTRBS(flo->rshift));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
+ u32 cr1;
+ const struct iio_chan_spec *chan;
+ unsigned int bit, jchg = 0;
int ret;
/* Average integrator oversampling */
ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
- DFSDM_FCR_IOSR(fl->iosr - 1));
+ DFSDM_FCR_IOSR(flo->iosr - 1));
if (ret)
return ret;
/* Filter order and Oversampling */
ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
- DFSDM_FCR_FOSR(fl->fosr - 1));
+ DFSDM_FCR_FOSR(flo->fosr - 1));
if (ret)
return ret;
@@ -286,15 +521,74 @@
if (ret)
return ret;
- /* No scan mode supported for the moment */
- ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
- DFSDM_CR1_RCH(ch_id));
+ ret = stm32_dfsdm_filter_set_trig(adc, fl_id, trig);
if (ret)
return ret;
- return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
- DFSDM_CR1_RSYNC_MASK,
- DFSDM_CR1_RSYNC(fl->sync_mode));
+ ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_FAST_MASK,
+ DFSDM_CR1_FAST(fl->fast));
+ if (ret)
+ return ret;
+
+ /*
+ * DFSDM modes configuration W.R.T audio/iio type modes
+ * ----------------------------------------------------------------
+ * Modes | regular | regular | injected | injected |
+ * | | continuous | | + scan |
+ * --------------|---------|--------------|----------|------------|
+ * single conv | x | | | |
+ * (1 chan) | | | | |
+ * --------------|---------|--------------|----------|------------|
+ * 1 Audio chan | | sample freq | | |
+ * | | or sync_mode | | |
+ * --------------|---------|--------------|----------|------------|
+ * 1 IIO chan | | sample freq | trigger | |
+ * | | or sync_mode | | |
+ * --------------|---------|--------------|----------|------------|
+ * 2+ IIO chans | | | | trigger or |
+ * | | | | sync_mode |
+ * ----------------------------------------------------------------
+ */
+ if (adc->nconv == 1 && !trig) {
+ bit = __ffs(adc->smask);
+ chan = indio_dev->channels + bit;
+
+ /* Use regular conversion for single channel without trigger */
+ cr1 = DFSDM_CR1_RCH(chan->channel);
+
+ /* Continuous conversions triggered by SPI clk in buffer mode */
+ if (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE)
+ cr1 |= DFSDM_CR1_RCONT(1);
+
+ cr1 |= DFSDM_CR1_RSYNC(fl->sync_mode);
+ } else {
+ /* Use injected conversion for multiple channels */
+ for_each_set_bit(bit, &adc->smask,
+ sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ jchg |= BIT(chan->channel);
+ }
+ ret = regmap_write(regmap, DFSDM_JCHGR(fl_id), jchg);
+ if (ret < 0)
+ return ret;
+
+ /* Use scan mode for multiple channels */
+ cr1 = DFSDM_CR1_JSCAN((adc->nconv > 1) ? 1 : 0);
+
+ /*
+ * Continuous conversions not supported in injected mode,
+ * either use:
+ * - conversions in sync with filter 0
+ * - triggered conversions
+ */
+ if (!fl->sync_mode && !trig)
+ return -EINVAL;
+ cr1 |= DFSDM_CR1_JSYNC(fl->sync_mode);
+ }
+
+ return regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_CFG_MASK,
+ cr1);
}
static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
@@ -378,13 +672,36 @@
return snprintf(buf, PAGE_SIZE, "%d\n", adc->spi_freq);
}
+static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev,
+ unsigned int sample_freq,
+ unsigned int spi_freq)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ unsigned int oversamp;
+ int ret;
+
+ oversamp = DIV_ROUND_CLOSEST(spi_freq, sample_freq);
+ if (spi_freq % sample_freq)
+ dev_dbg(&indio_dev->dev,
+ "Rate not accurate. requested (%u), actual (%u)\n",
+ sample_freq, spi_freq / oversamp);
+
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, oversamp);
+ if (ret < 0)
+ return ret;
+
+ adc->sample_freq = spi_freq / oversamp;
+ adc->oversamp = oversamp;
+
+ return 0;
+}
+
static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
uintptr_t priv,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel];
unsigned int sample_freq = adc->sample_freq;
unsigned int spi_freq;
@@ -403,17 +720,9 @@
return -EINVAL;
if (sample_freq) {
- if (spi_freq % sample_freq)
- dev_warn(&indio_dev->dev,
- "Sampling rate not accurate (%d)\n",
- spi_freq / (spi_freq / sample_freq));
-
- ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
- if (ret < 0) {
- dev_err(&indio_dev->dev,
- "No filter parameters that match!\n");
+ ret = dfsdm_adc_set_samp_freq(indio_dev, sample_freq, spi_freq);
+ if (ret < 0)
return ret;
- }
}
adc->spi_freq = spi_freq;
@@ -421,72 +730,48 @@
}
static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
- const struct iio_chan_spec *chan,
- bool dma)
+ struct iio_trigger *trig)
{
struct regmap *regmap = adc->dfsdm->regmap;
int ret;
- unsigned int dma_en = 0, cont_en = 0;
- ret = stm32_dfsdm_start_channel(adc->dfsdm, chan->channel);
+ ret = stm32_dfsdm_channels_configure(adc, adc->fl_id, trig);
if (ret < 0)
return ret;
- ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id,
- chan->channel);
+ ret = stm32_dfsdm_start_channel(adc);
+ if (ret < 0)
+ return ret;
+
+ ret = stm32_dfsdm_filter_configure(adc, adc->fl_id, trig);
if (ret < 0)
goto stop_channels;
- if (dma) {
- /* Enable DMA transfer*/
- dma_en = DFSDM_CR1_RDMAEN(1);
- /* Enable conversion triggered by SPI clock*/
- cont_en = DFSDM_CR1_RCONT(1);
- }
- /* Enable DMA transfer*/
- ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RDMAEN_MASK, dma_en);
+ ret = stm32_dfsdm_start_filter(adc, adc->fl_id, trig);
if (ret < 0)
- goto stop_channels;
-
- /* Enable conversion triggered by SPI clock*/
- ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RCONT_MASK, cont_en);
- if (ret < 0)
- goto stop_channels;
-
- ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
- if (ret < 0)
- goto stop_channels;
+ goto filter_unconfigure;
return 0;
+filter_unconfigure:
+ regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_CFG_MASK, 0);
stop_channels:
- regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RDMAEN_MASK, 0);
-
- regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RCONT_MASK, 0);
- stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);
+ stm32_dfsdm_stop_channel(adc);
return ret;
}
-static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc,
- const struct iio_chan_spec *chan)
+static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
{
struct regmap *regmap = adc->dfsdm->regmap;
stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
- /* Clean conversion options */
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RDMAEN_MASK, 0);
+ DFSDM_CR1_CFG_MASK, 0);
- regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RCONT_MASK, 0);
-
- stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);
+ stm32_dfsdm_stop_channel(adc);
}
static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
@@ -494,6 +779,7 @@
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
+ unsigned int rx_buf_sz = DFSDM_DMA_BUFFER_SIZE;
/*
* DMA cyclic transfers are used, buffer is split into two periods.
@@ -502,7 +788,7 @@
* - one buffer (period) driver pushed to ASoC side.
*/
watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
- adc->buf_sz = watermark * 2;
+ adc->buf_sz = min(rx_buf_sz, watermark * 2 * adc->nconv);
return 0;
}
@@ -532,13 +818,67 @@
return 0;
}
-static void stm32_dfsdm_audio_dma_buffer_done(void *data)
+static inline void stm32_dfsdm_process_data(struct stm32_dfsdm_adc *adc,
+ s32 *buffer)
+{
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
+ unsigned int i = adc->nconv;
+ s32 *ptr = buffer;
+
+ while (i--) {
+ /* Mask 8 LSB that contains the channel ID */
+ *ptr &= 0xFFFFFF00;
+ /* Convert 2^(n-1) sample to 2^(n-1)-1 to avoid wrap-around */
+ if (*ptr > flo->max)
+ *ptr -= 1;
+ /*
+ * Samples from filter are retrieved with 23 bits resolution
+ * or less. Shift left to align MSB on 24 bits.
+ */
+ *ptr <<= flo->lshift;
+
+ ptr++;
+ }
+}
+
+static irqreturn_t stm32_dfsdm_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int available = stm32_dfsdm_adc_dma_residue(adc);
+
+ while (available >= indio_dev->scan_bytes) {
+ s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi];
+
+ stm32_dfsdm_process_data(adc, buffer);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ pf->timestamp);
+ available -= indio_dev->scan_bytes;
+ adc->bufi += indio_dev->scan_bytes;
+ if (adc->bufi >= adc->buf_sz)
+ adc->bufi = 0;
+ }
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void stm32_dfsdm_dma_buffer_done(void *data)
{
struct iio_dev *indio_dev = data;
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int available = stm32_dfsdm_adc_dma_residue(adc);
size_t old_pos;
+ if (indio_dev->currentmode & INDIO_BUFFER_TRIGGERED) {
+ iio_trigger_poll_chained(indio_dev->trig);
+ return;
+ }
+
/*
* FIXME: In Kernel interface does not support cyclic DMA buffer,and
* offers only an interface to push data samples per samples.
@@ -553,10 +893,10 @@
old_pos = adc->bufi;
while (available >= indio_dev->scan_bytes) {
- u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi];
+ s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi];
- /* Mask 8 LSB that contains the channel ID */
- *buffer = (*buffer & 0xFFFFFF00) << 8;
+ stm32_dfsdm_process_data(adc, buffer);
+
available -= indio_dev->scan_bytes;
adc->bufi += indio_dev->scan_bytes;
if (adc->bufi >= adc->buf_sz) {
@@ -566,6 +906,9 @@
adc->bufi = 0;
old_pos = 0;
}
+ /* regular iio buffer without trigger */
+ if (adc->dev_data->type == DFSDM_IIO)
+ iio_push_to_buffers(indio_dev, buffer);
}
if (adc->cb)
adc->cb(&adc->rx_buf[old_pos], adc->bufi - old_pos,
@@ -575,6 +918,15 @@
static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ /*
+ * The DFSDM supports half-word transfers. However, for 16 bits record,
+ * 4 bytes buswidth is kept, to avoid losing samples LSBs when left
+ * shift is required.
+ */
+ struct dma_slave_config config = {
+ .src_addr = (dma_addr_t)adc->dfsdm->phys_base,
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ };
struct dma_async_tx_descriptor *desc;
dma_cookie_t cookie;
int ret;
@@ -585,6 +937,14 @@
dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
adc->buf_sz, adc->buf_sz / 2);
+ if (adc->nconv == 1 && !indio_dev->trig)
+ config.src_addr += DFSDM_RDATAR(adc->fl_id);
+ else
+ config.src_addr += DFSDM_JDATAR(adc->fl_id);
+ ret = dmaengine_slave_config(adc->dma_chan, &config);
+ if (ret)
+ return ret;
+
/* Prepare a DMA cyclic transaction */
desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
adc->dma_buf,
@@ -594,71 +954,154 @@
if (!desc)
return -EBUSY;
- desc->callback = stm32_dfsdm_audio_dma_buffer_done;
+ desc->callback = stm32_dfsdm_dma_buffer_done;
desc->callback_param = indio_dev;
cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
- if (ret) {
- dmaengine_terminate_all(adc->dma_chan);
- return ret;
- }
+ if (ret)
+ goto err_stop_dma;
/* Issue pending DMA requests */
dma_async_issue_pending(adc->dma_chan);
+ if (adc->nconv == 1 && !indio_dev->trig) {
+ /* Enable regular DMA transfer*/
+ ret = regmap_update_bits(adc->dfsdm->regmap,
+ DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_RDMAEN_MASK,
+ DFSDM_CR1_RDMAEN_MASK);
+ } else {
+ /* Enable injected DMA transfer*/
+ ret = regmap_update_bits(adc->dfsdm->regmap,
+ DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_JDMAEN_MASK,
+ DFSDM_CR1_JDMAEN_MASK);
+ }
+
+ if (ret < 0)
+ goto err_stop_dma;
+
+ return 0;
+
+err_stop_dma:
+ dmaengine_terminate_all(adc->dma_chan);
+
+ return ret;
+}
+
+static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ if (!adc->dma_chan)
+ return;
+
+ regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_RDMAEN_MASK | DFSDM_CR1_JDMAEN_MASK, 0);
+ dmaengine_terminate_all(adc->dma_chan);
+}
+
+static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ adc->nconv = bitmap_weight(scan_mask, indio_dev->masklength);
+ adc->smask = *scan_mask;
+
+ dev_dbg(&indio_dev->dev, "nconv=%d mask=%lx\n", adc->nconv, *scan_mask);
+
return 0;
}
-static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+static int __stm32_dfsdm_postenable(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- const struct iio_chan_spec *chan = &indio_dev->channels[0];
int ret;
/* Reset adc buffer index */
adc->bufi = 0;
+ if (adc->hwc) {
+ ret = iio_hw_consumer_enable(adc->hwc);
+ if (ret < 0)
+ return ret;
+ }
+
ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
if (ret < 0)
- return ret;
+ goto err_stop_hwc;
- ret = stm32_dfsdm_start_conv(adc, chan, true);
+ ret = stm32_dfsdm_adc_dma_start(indio_dev);
if (ret) {
- dev_err(&indio_dev->dev, "Can't start conversion\n");
+ dev_err(&indio_dev->dev, "Can't start DMA\n");
goto stop_dfsdm;
}
- if (adc->dma_chan) {
- ret = stm32_dfsdm_adc_dma_start(indio_dev);
- if (ret) {
- dev_err(&indio_dev->dev, "Can't start DMA\n");
- goto err_stop_conv;
- }
+ ret = stm32_dfsdm_start_conv(adc, indio_dev->trig);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't start conversion\n");
+ goto err_stop_dma;
}
return 0;
-err_stop_conv:
- stm32_dfsdm_stop_conv(adc, chan);
+err_stop_dma:
+ stm32_dfsdm_adc_dma_stop(indio_dev);
stop_dfsdm:
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+err_stop_hwc:
+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);
return ret;
}
-static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = __stm32_dfsdm_postenable(indio_dev);
+ if (ret < 0)
+ goto err_predisable;
+
+ return 0;
+
+err_predisable:
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ iio_triggered_buffer_predisable(indio_dev);
+
+ return ret;
+}
+
+static void __stm32_dfsdm_predisable(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- const struct iio_chan_spec *chan = &indio_dev->channels[0];
- if (adc->dma_chan)
- dmaengine_terminate_all(adc->dma_chan);
+ stm32_dfsdm_stop_conv(adc);
- stm32_dfsdm_stop_conv(adc, chan);
+ stm32_dfsdm_adc_dma_stop(indio_dev);
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);
+}
+
+static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+{
+ __stm32_dfsdm_predisable(indio_dev);
+
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ iio_triggered_buffer_predisable(indio_dev);
+
return 0;
}
@@ -736,7 +1179,9 @@
if (ret < 0)
goto stop_dfsdm;
- ret = stm32_dfsdm_start_conv(adc, chan, false);
+ adc->nconv = 1;
+ adc->smask = BIT(chan->scan_index);
+ ret = stm32_dfsdm_start_conv(adc, NULL);
if (ret < 0) {
regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
@@ -757,7 +1202,7 @@
else
ret = IIO_VAL_INT;
- stm32_dfsdm_stop_conv(adc, chan);
+ stm32_dfsdm_stop_conv(adc);
stop_dfsdm:
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
@@ -770,23 +1215,29 @@
int val, int val2, long mask)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel];
unsigned int spi_freq;
int ret = -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- ret = stm32_dfsdm_set_osrs(fl, 0, val);
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, val);
if (!ret)
adc->oversamp = val;
-
+ iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
if (!val)
return -EINVAL;
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
switch (ch->src) {
case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL:
spi_freq = adc->dfsdm->spi_master_freq;
@@ -799,20 +1250,9 @@
spi_freq = adc->spi_freq;
}
- if (spi_freq % val)
- dev_warn(&indio_dev->dev,
- "Sampling rate not accurate (%d)\n",
- spi_freq / (spi_freq / val));
-
- ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / val));
- if (ret < 0) {
- dev_err(&indio_dev->dev,
- "Not able to find parameter that match!\n");
- return ret;
- }
- adc->sample_freq = val;
-
- return 0;
+ ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
}
return -EINVAL;
@@ -827,11 +1267,15 @@
switch (mask) {
case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
ret = iio_hw_consumer_enable(adc->hwc);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: IIO enable failed (channel %d)\n",
__func__, chan->channel);
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
@@ -840,8 +1284,10 @@
dev_err(&indio_dev->dev,
"%s: Conversion failed (channel %d)\n",
__func__, chan->channel);
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
+ iio_device_release_direct_mode(indio_dev);
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
@@ -858,15 +1304,25 @@
return -EINVAL;
}
+static int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ return stm32_dfsdm_get_jextsel(indio_dev, trig) < 0 ? -EINVAL : 0;
+}
+
static const struct iio_info stm32_dfsdm_info_audio = {
.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
.read_raw = stm32_dfsdm_read_raw,
.write_raw = stm32_dfsdm_write_raw,
+ .update_scan_mode = stm32_dfsdm_update_scan_mode,
};
static const struct iio_info stm32_dfsdm_info_adc = {
+ .hwfifo_set_watermark = stm32_dfsdm_set_watermark,
.read_raw = stm32_dfsdm_read_raw,
.write_raw = stm32_dfsdm_write_raw,
+ .update_scan_mode = stm32_dfsdm_update_scan_mode,
+ .validate_trigger = stm32_dfsdm_validate_trigger,
};
static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
@@ -926,12 +1382,6 @@
static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- struct dma_slave_config config = {
- .src_addr = (dma_addr_t)adc->dfsdm->phys_base +
- DFSDM_RDATAR(adc->fl_id),
- .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
- };
- int ret;
adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
if (!adc->dma_chan)
@@ -941,23 +1391,14 @@
DFSDM_DMA_BUFFER_SIZE,
&adc->dma_buf, GFP_KERNEL);
if (!adc->rx_buf) {
- ret = -ENOMEM;
- goto err_release;
+ dma_release_channel(adc->dma_chan);
+ return -ENOMEM;
}
- ret = dmaengine_slave_config(adc->dma_chan, &config);
- if (ret)
- goto err_free;
+ indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
+ indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
return 0;
-
-err_free:
- dma_free_coherent(adc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
- adc->rx_buf, adc->dma_buf);
-err_release:
- dma_release_channel(adc->dma_chan);
-
- return ret;
}
static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
@@ -978,14 +1419,15 @@
* IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
*/
ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
- ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+ ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
if (adc->dev_data->type == DFSDM_AUDIO) {
- ch->scan_type.sign = 's';
ch->ext_info = dfsdm_adc_audio_ext_info;
} else {
- ch->scan_type.sign = 'u';
+ ch->scan_type.shift = 8;
}
+ ch->scan_type.sign = 's';
ch->scan_type.realbits = 24;
ch->scan_type.storagebits = 32;
@@ -1000,9 +1442,6 @@
struct stm32_dfsdm_channel *d_ch;
int ret;
- indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
- indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
-
ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
if (!ch)
return -ENOMEM;
@@ -1034,8 +1473,7 @@
int ret, chan_idx;
adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING;
- ret = stm32_dfsdm_set_osrs(&adc->dfsdm->fl_list[adc->fl_id], 0,
- adc->oversamp);
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp);
if (ret < 0)
return ret;
@@ -1070,6 +1508,25 @@
init_completion(&adc->completion);
+ /* Optionally request DMA */
+ if (stm32_dfsdm_dma_request(indio_dev)) {
+ dev_dbg(&indio_dev->dev, "No DMA support\n");
+ return 0;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ &stm32_dfsdm_adc_trigger_handler,
+ &stm32_dfsdm_buffer_setup_ops);
+ if (ret) {
+ stm32_dfsdm_dma_release(indio_dev);
+ dev_err(&indio_dev->dev, "buffer setup failed\n");
+ return ret;
+ }
+
+ /* lptimer/timer hardware triggers */
+ indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;
+
return 0;
}
@@ -1117,7 +1574,7 @@
iio->dev.parent = dev;
iio->dev.of_node = np;
- iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ iio->modes = INDIO_DIRECT_MODE;
platform_set_drvdata(pdev, adc);
@@ -1144,6 +1601,9 @@
* So IRQ associated to filter instance 0 is dedicated to the Filter 0.
*/
irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
0, pdev->name, adc);
if (ret < 0) {
@@ -1203,10 +1663,48 @@
return 0;
}
+static int __maybe_unused stm32_dfsdm_adc_suspend(struct device *dev)
+{
+ struct stm32_dfsdm_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ if (iio_buffer_enabled(indio_dev))
+ __stm32_dfsdm_predisable(indio_dev);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_dfsdm_adc_resume(struct device *dev)
+{
+ struct stm32_dfsdm_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ const struct iio_chan_spec *chan;
+ struct stm32_dfsdm_channel *ch;
+ int i, ret;
+
+ /* restore channels configuration */
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ chan = indio_dev->channels + i;
+ ch = &adc->dfsdm->ch_list[chan->channel];
+ ret = stm32_dfsdm_chan_configure(adc->dfsdm, ch);
+ if (ret)
+ return ret;
+ }
+
+ if (iio_buffer_enabled(indio_dev))
+ __stm32_dfsdm_postenable(indio_dev);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stm32_dfsdm_adc_pm_ops,
+ stm32_dfsdm_adc_suspend, stm32_dfsdm_adc_resume);
+
static struct platform_driver stm32_dfsdm_adc_driver = {
.driver = {
.name = "stm32-dfsdm-adc",
.of_match_table = stm32_dfsdm_adc_match,
+ .pm = &stm32_dfsdm_adc_pm_ops,
},
.probe = stm32_dfsdm_adc_probe,
.remove = stm32_dfsdm_adc_remove,
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
index bf089f5..26e2011 100644
--- a/drivers/iio/adc/stm32-dfsdm-core.c
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -12,6 +12,8 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -90,6 +92,36 @@
struct clk *aclk; /* audio clock */
};
+static inline struct dfsdm_priv *to_stm32_dfsdm_priv(struct stm32_dfsdm *dfsdm)
+{
+ return container_of(dfsdm, struct dfsdm_priv, dfsdm);
+}
+
+static int stm32_dfsdm_clk_prepare_enable(struct stm32_dfsdm *dfsdm)
+{
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret || !priv->aclk)
+ return ret;
+
+ ret = clk_prepare_enable(priv->aclk);
+ if (ret)
+ clk_disable_unprepare(priv->clk);
+
+ return ret;
+}
+
+static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm)
+{
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+
+ if (priv->aclk)
+ clk_disable_unprepare(priv->aclk);
+ clk_disable_unprepare(priv->clk);
+}
+
/**
* stm32_dfsdm_start_dfsdm - start global dfsdm interface.
*
@@ -98,24 +130,17 @@
*/
int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
{
- struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
struct device *dev = &priv->pdev->dev;
unsigned int clk_div = priv->spi_clk_out_div, clk_src;
int ret;
if (atomic_inc_return(&priv->n_active_ch) == 1) {
- ret = clk_prepare_enable(priv->clk);
+ ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- dev_err(dev, "Failed to start clock\n");
+ pm_runtime_put_noidle(dev);
goto error_ret;
}
- if (priv->aclk) {
- ret = clk_prepare_enable(priv->aclk);
- if (ret < 0) {
- dev_err(dev, "Failed to start audio clock\n");
- goto disable_clk;
- }
- }
/* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */
clk_src = priv->aclk ? 1 : 0;
@@ -123,21 +148,21 @@
DFSDM_CHCFGR1_CKOUTSRC_MASK,
DFSDM_CHCFGR1_CKOUTSRC(clk_src));
if (ret < 0)
- goto disable_aclk;
+ goto pm_put;
/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
DFSDM_CHCFGR1_CKOUTDIV_MASK,
DFSDM_CHCFGR1_CKOUTDIV(clk_div));
if (ret < 0)
- goto disable_aclk;
+ goto pm_put;
/* Global enable of DFSDM interface */
ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
DFSDM_CHCFGR1_DFSDMEN_MASK,
DFSDM_CHCFGR1_DFSDMEN(1));
if (ret < 0)
- goto disable_aclk;
+ goto pm_put;
}
dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
@@ -145,11 +170,8 @@
return 0;
-disable_aclk:
- clk_disable_unprepare(priv->aclk);
-disable_clk:
- clk_disable_unprepare(priv->clk);
-
+pm_put:
+ pm_runtime_put_sync(dev);
error_ret:
atomic_dec(&priv->n_active_ch);
@@ -165,7 +187,7 @@
*/
int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
{
- struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
int ret;
if (atomic_dec_and_test(&priv->n_active_ch)) {
@@ -183,9 +205,7 @@
if (ret < 0)
return ret;
- clk_disable_unprepare(priv->clk);
- if (priv->aclk)
- clk_disable_unprepare(priv->aclk);
+ pm_runtime_put_sync(&priv->pdev->dev);
}
dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
atomic_read(&priv->n_active_ch));
@@ -199,7 +219,7 @@
{
struct device_node *node = pdev->dev.of_node;
struct resource *res;
- unsigned long clk_freq;
+ unsigned long clk_freq, divider;
unsigned int spi_freq, rem;
int ret;
@@ -213,6 +233,8 @@
}
priv->dfsdm.phys_base = res->start;
priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->dfsdm.base))
+ return PTR_ERR(priv->dfsdm.base);
/*
* "dfsdm" clock is mandatory for DFSDM peripheral clocking.
@@ -222,8 +244,10 @@
*/
priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
if (IS_ERR(priv->clk)) {
- dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
- return -EINVAL;
+ ret = PTR_ERR(priv->clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get clock (%d)\n", ret);
+ return ret;
}
priv->aclk = devm_clk_get(&pdev->dev, "audio");
@@ -243,13 +267,20 @@
return 0;
}
- priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
- if (!priv->spi_clk_out_div) {
- /* spi_clk_out_div == 0 means ckout is OFF */
+ divider = div_u64_rem(clk_freq, spi_freq, &rem);
+ /* Round up divider when ckout isn't precise, not to exceed spi_freq */
+ if (rem)
+ divider++;
+
+ /* programmable divider is in range of [2:256] */
+ if (divider < 2 || divider > 256) {
dev_err(&pdev->dev, "spi-max-frequency not achievable\n");
return -EINVAL;
}
- priv->dfsdm.spi_master_freq = spi_freq;
+
+ /* SPI clock output divider is: divider = CKOUTDIV + 1 */
+ priv->spi_clk_out_div = divider - 1;
+ priv->dfsdm.spi_master_freq = clk_freq / (priv->spi_clk_out_div + 1);
if (rem) {
dev_warn(&pdev->dev, "SPI clock not accurate\n");
@@ -318,14 +349,111 @@
platform_set_drvdata(pdev, dfsdm);
- return devm_of_platform_populate(&pdev->dev);
+ ret = stm32_dfsdm_clk_prepare_enable(dfsdm);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to start clock\n");
+ return ret;
+ }
+
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (ret)
+ goto pm_put;
+
+ pm_runtime_put(&pdev->dev);
+
+ return 0;
+
+pm_put:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return ret;
}
+static int stm32_dfsdm_core_remove(struct platform_device *pdev)
+{
+ struct stm32_dfsdm *dfsdm = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ of_platform_depopulate(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_dfsdm_core_suspend(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ /* Balance devm_regmap_init_mmio_clk() clk_prepare() */
+ clk_unprepare(priv->clk);
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int __maybe_unused stm32_dfsdm_core_resume(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare(priv->clk);
+ if (ret)
+ return ret;
+
+ return pm_runtime_force_resume(dev);
+}
+
+static int __maybe_unused stm32_dfsdm_core_runtime_suspend(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_dfsdm_core_runtime_resume(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+
+ return stm32_dfsdm_clk_prepare_enable(dfsdm);
+}
+
+static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend,
+ stm32_dfsdm_core_resume)
+ SET_RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend,
+ stm32_dfsdm_core_runtime_resume,
+ NULL)
+};
+
static struct platform_driver stm32_dfsdm_driver = {
.probe = stm32_dfsdm_probe,
+ .remove = stm32_dfsdm_core_remove,
.driver = {
.name = "stm32-dfsdm",
.of_match_table = stm32_dfsdm_of_match,
+ .pm = &stm32_dfsdm_core_pm_ops,
},
};
diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
index 8708394..5dbdae4 100644
--- a/drivers/iio/adc/stm32-dfsdm.h
+++ b/drivers/iio/adc/stm32-dfsdm.h
@@ -243,19 +243,33 @@
};
/**
- * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * struct stm32_dfsdm_filter_osr - DFSDM filter settings linked to oversampling
* @iosr: integrator oversampling
* @fosr: filter oversampling
- * @ford: filter order
+ * @rshift: output sample right shift (hardware shift)
+ * @lshift: output sample left shift (software shift)
* @res: output sample resolution
+ * @max: output sample maximum positive value
+ */
+struct stm32_dfsdm_filter_osr {
+ unsigned int iosr;
+ unsigned int fosr;
+ unsigned int rshift;
+ unsigned int lshift;
+ u64 res;
+ s32 max;
+};
+
+/**
+ * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * @ford: filter order
+ * @flo: filter oversampling data table indexed by fast mode flag
* @sync_mode: filter synchronized with filter 0
* @fast: filter fast mode
*/
struct stm32_dfsdm_filter {
- unsigned int iosr;
- unsigned int fosr;
enum stm32_dfsdm_sinc_order ford;
- u64 res;
+ struct stm32_dfsdm_filter_osr flo[2];
unsigned int sync_mode;
unsigned int fast;
};
diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c
new file mode 100644
index 0000000..bd72727
--- /dev/null
+++ b/drivers/iio/adc/stmpe-adc.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STMicroelectronics STMPE811 IIO ADC Driver
+ *
+ * 4 channel, 10/12-bit ADC
+ *
+ * Copyright (C) 2013-2018 Toradex AG <stefan.agner@toradex.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/stmpe.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+
+#define STMPE_REG_INT_STA 0x0B
+#define STMPE_REG_ADC_INT_EN 0x0E
+#define STMPE_REG_ADC_INT_STA 0x0F
+
+#define STMPE_REG_ADC_CTRL1 0x20
+#define STMPE_REG_ADC_CTRL2 0x21
+#define STMPE_REG_ADC_CAPT 0x22
+#define STMPE_REG_ADC_DATA_CH(channel) (0x30 + 2 * (channel))
+
+#define STMPE_REG_TEMP_CTRL 0x60
+#define STMPE_TEMP_CTRL_ENABLE BIT(0)
+#define STMPE_TEMP_CTRL_ACQ BIT(1)
+#define STMPE_TEMP_CTRL_THRES_EN BIT(3)
+#define STMPE_START_ONE_TEMP_CONV (STMPE_TEMP_CTRL_ENABLE | \
+ STMPE_TEMP_CTRL_ACQ | \
+ STMPE_TEMP_CTRL_THRES_EN)
+#define STMPE_REG_TEMP_DATA 0x61
+#define STMPE_REG_TEMP_TH 0x63
+#define STMPE_ADC_LAST_NR 7
+#define STMPE_TEMP_CHANNEL (STMPE_ADC_LAST_NR + 1)
+
+#define STMPE_ADC_CH(channel) ((1 << (channel)) & 0xff)
+
+#define STMPE_ADC_TIMEOUT msecs_to_jiffies(1000)
+
+struct stmpe_adc {
+ struct stmpe *stmpe;
+ struct clk *clk;
+ struct device *dev;
+ struct mutex lock;
+
+ /* We are allocating plus one for the temperature channel */
+ struct iio_chan_spec stmpe_adc_iio_channels[STMPE_ADC_LAST_NR + 2];
+
+ struct completion completion;
+
+ u8 channel;
+ u32 value;
+};
+
+static int stmpe_read_voltage(struct stmpe_adc *info,
+ struct iio_chan_spec const *chan, int *val)
+{
+ long ret;
+
+ mutex_lock(&info->lock);
+
+ reinit_completion(&info->completion);
+
+ info->channel = (u8)chan->channel;
+
+ if (info->channel > STMPE_ADC_LAST_NR) {
+ mutex_unlock(&info->lock);
+ return -EINVAL;
+ }
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_CAPT,
+ STMPE_ADC_CH(info->channel));
+
+ ret = wait_for_completion_timeout(&info->completion, STMPE_ADC_TIMEOUT);
+
+ if (ret <= 0) {
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA,
+ STMPE_ADC_CH(info->channel));
+ mutex_unlock(&info->lock);
+ return -ETIMEDOUT;
+ }
+
+ *val = info->value;
+
+ mutex_unlock(&info->lock);
+
+ return 0;
+}
+
+static int stmpe_read_temp(struct stmpe_adc *info,
+ struct iio_chan_spec const *chan, int *val)
+{
+ long ret;
+
+ mutex_lock(&info->lock);
+
+ reinit_completion(&info->completion);
+
+ info->channel = (u8)chan->channel;
+
+ if (info->channel != STMPE_TEMP_CHANNEL) {
+ mutex_unlock(&info->lock);
+ return -EINVAL;
+ }
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_TEMP_CTRL,
+ STMPE_START_ONE_TEMP_CONV);
+
+ ret = wait_for_completion_timeout(&info->completion, STMPE_ADC_TIMEOUT);
+
+ if (ret <= 0) {
+ mutex_unlock(&info->lock);
+ return -ETIMEDOUT;
+ }
+
+ /*
+ * absolute temp = +V3.3 * value /7.51 [K]
+ * scale to [milli °C]
+ */
+ *val = ((449960l * info->value) / 1024l) - 273150;
+
+ mutex_unlock(&info->lock);
+
+ return 0;
+}
+
+static int stmpe_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct stmpe_adc *info = iio_priv(indio_dev);
+ long ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ ret = stmpe_read_voltage(info, chan, val);
+ break;
+
+ case IIO_TEMP:
+ ret = stmpe_read_temp(info, chan, val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = 3300;
+ *val2 = info->stmpe->mod_12b ? 12 : 10;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static irqreturn_t stmpe_adc_isr(int irq, void *dev_id)
+{
+ struct stmpe_adc *info = (struct stmpe_adc *)dev_id;
+ u16 data;
+
+ if (info->channel <= STMPE_ADC_LAST_NR) {
+ int int_sta;
+
+ int_sta = stmpe_reg_read(info->stmpe, STMPE_REG_ADC_INT_STA);
+
+ /* Is the interrupt relevant */
+ if (!(int_sta & STMPE_ADC_CH(info->channel)))
+ return IRQ_NONE;
+
+ /* Read value */
+ stmpe_block_read(info->stmpe,
+ STMPE_REG_ADC_DATA_CH(info->channel), 2, (u8 *) &data);
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA, int_sta);
+ } else if (info->channel == STMPE_TEMP_CHANNEL) {
+ /* Read value */
+ stmpe_block_read(info->stmpe, STMPE_REG_TEMP_DATA, 2,
+ (u8 *) &data);
+ } else {
+ return IRQ_NONE;
+ }
+
+ info->value = (u32) be16_to_cpu(data);
+ complete(&info->completion);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info stmpe_adc_iio_info = {
+ .read_raw = &stmpe_read_raw,
+};
+
+static void stmpe_adc_voltage_chan(struct iio_chan_spec *ics, int chan)
+{
+ ics->type = IIO_VOLTAGE;
+ ics->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ ics->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ ics->indexed = 1;
+ ics->channel = chan;
+}
+
+static void stmpe_adc_temp_chan(struct iio_chan_spec *ics, int chan)
+{
+ ics->type = IIO_TEMP;
+ ics->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED);
+ ics->indexed = 1;
+ ics->channel = chan;
+}
+
+static int stmpe_adc_init_hw(struct stmpe_adc *adc)
+{
+ int ret;
+ struct stmpe *stmpe = adc->stmpe;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_ADC);
+ if (ret) {
+ dev_err(stmpe->dev, "Could not enable clock for ADC\n");
+ return ret;
+ }
+
+ ret = stmpe811_adc_common_init(stmpe);
+ if (ret) {
+ stmpe_disable(stmpe, STMPE_BLOCK_ADC);
+ return ret;
+ }
+
+ /* use temp irq for each conversion completion */
+ stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH, 0);
+ stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH + 1, 0);
+
+ return 0;
+}
+
+static int stmpe_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct stmpe_adc *info;
+ struct device_node *np;
+ u32 norequest_mask = 0;
+ int irq_temp, irq_adc;
+ int num_chan = 0;
+ int i = 0;
+ int ret;
+
+ irq_adc = platform_get_irq_byname(pdev, "STMPE_ADC");
+ if (irq_adc < 0)
+ return irq_adc;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct stmpe_adc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+ mutex_init(&info->lock);
+
+ init_completion(&info->completion);
+ ret = devm_request_threaded_irq(&pdev->dev, irq_adc, NULL,
+ stmpe_adc_isr, IRQF_ONESHOT,
+ "stmpe-adc", info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
+ irq_adc);
+ return ret;
+ }
+
+ irq_temp = platform_get_irq_byname(pdev, "STMPE_TEMP_SENS");
+ if (irq_temp >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev, irq_temp, NULL,
+ stmpe_adc_isr, IRQF_ONESHOT,
+ "stmpe-adc", info);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "failed requesting irq for"
+ " temp sensor, irq = %d\n", irq_temp);
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &stmpe_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ info->stmpe = dev_get_drvdata(pdev->dev.parent);
+
+ np = pdev->dev.of_node;
+
+ if (!np)
+ dev_err(&pdev->dev, "no device tree node found\n");
+
+ of_property_read_u32(np, "st,norequest-mask", &norequest_mask);
+
+ for_each_clear_bit(i, (unsigned long *) &norequest_mask,
+ (STMPE_ADC_LAST_NR + 1)) {
+ stmpe_adc_voltage_chan(&info->stmpe_adc_iio_channels[num_chan], i);
+ num_chan++;
+ }
+ stmpe_adc_temp_chan(&info->stmpe_adc_iio_channels[num_chan], i);
+ num_chan++;
+ indio_dev->channels = info->stmpe_adc_iio_channels;
+ indio_dev->num_channels = num_chan;
+
+ ret = stmpe_adc_init_hw(info);
+ if (ret)
+ return ret;
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_EN,
+ ~(norequest_mask & 0xFF));
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA,
+ ~(norequest_mask & 0xFF));
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static int __maybe_unused stmpe_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct stmpe_adc *info = iio_priv(indio_dev);
+
+ stmpe_adc_init_hw(info);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stmpe_adc_pm_ops, NULL, stmpe_adc_resume);
+
+static struct platform_driver stmpe_adc_driver = {
+ .probe = stmpe_adc_probe,
+ .driver = {
+ .name = "stmpe-adc",
+ .pm = &stmpe_adc_pm_ops,
+ },
+};
+module_platform_driver(stmpe_adc_driver);
+
+static const struct of_device_id stmpe_adc_ids[] = {
+ { .compatible = "st,stmpe-adc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, stmpe_adc_ids);
+
+MODULE_AUTHOR("Stefan Agner <stefan.agner@toradex.com>");
+MODULE_DESCRIPTION("STMPEXXX ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stmpe-adc");
diff --git a/drivers/iio/adc/stx104.c b/drivers/iio/adc/stx104.c
index 0662ca1..f87bbc7 100644
--- a/drivers/iio/adc/stx104.c
+++ b/drivers/iio/adc/stx104.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO driver for the Apex Embedded Systems STX104
* Copyright (C) 2016 William Breathitt Gray
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/bitops.h>
#include <linux/device.h>
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index 04d7147..176e1cb 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC
*
* Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.com>
*
- * This program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License version 2 as published by the
- * Free Software Foundation.
- *
* The Allwinner SoCs all have an ADC that can also act as a touchscreen
* controller and a thermal sensor.
* The thermal sensor works only when the ADC acts as a touchscreen controller
@@ -463,10 +460,8 @@
atomic_set(atomic, 1);
ret = platform_get_irq_byname(pdev, name);
- if (ret < 0) {
- dev_err(&pdev->dev, "no %s interrupt registered\n", name);
+ if (ret < 0)
return ret;
- }
ret = regmap_irq_get_virq(mfd_dev->regmap_irqc, ret);
if (ret < 0) {
diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
index 405e377..0235863 100644
--- a/drivers/iio/adc/ti-adc081c.c
+++ b/drivers/iio/adc/ti-adc081c.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TI ADC081C/ADC101C/ADC121C 8/10/12-bit ADC driver
*
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2016 Intel
*
- * 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.
- *
* Datasheets:
* http://www.ti.com/lit/ds/symlink/adc081c021.pdf
* http://www.ti.com/lit/ds/symlink/adc101c021.pdf
diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c
index 188dae7..6ea39f4 100644
--- a/drivers/iio/adc/ti-adc0832.c
+++ b/drivers/iio/adc/ti-adc0832.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADC0831/ADC0832/ADC0834/ADC0838 8-bit ADC driver
*
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Datasheet: http://www.ti.com/lit/ds/symlink/adc0832-n.pdf
*/
diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
index 2550464..bdedf45 100644
--- a/drivers/iio/adc/ti-adc084s021.c
+++ b/drivers/iio/adc/ti-adc084s021.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Copyright (C) 2017 Axis Communications AB
*
* Driver for Texas Instruments' ADC084S021 ADC chip.
* Datasheets can be found here:
* http://www.ti.com/lit/ds/symlink/adc084s021.pdf
- *
- * 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/err.h>
diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c
index 841203e..de9aaeb 100644
--- a/drivers/iio/adc/ti-adc108s102.c
+++ b/drivers/iio/adc/ti-adc108s102.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TI ADC108S102 SPI ADC driver
*
* Copyright (c) 2013-2015 Intel Corporation.
* Copyright (c) 2017 Siemens AG
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
* This IIO device driver is designed to work with the following
* analog to digital converters from Texas Instruments:
* ADC108S102
diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c
index 703d68a..68a9dcb 100644
--- a/drivers/iio/adc/ti-adc12138.c
+++ b/drivers/iio/adc/ti-adc12138.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADC12130/ADC12132/ADC12138 12-bit plus sign ADC driver
*
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Datasheet: http://www.ti.com/lit/ds/symlink/adc12138.pdf
*/
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
index 7cf39b3..1e5a936 100644
--- a/drivers/iio/adc/ti-adc128s052.c
+++ b/drivers/iio/adc/ti-adc128s052.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com>
*
@@ -6,16 +7,14 @@
* http://www.ti.com/lit/ds/symlink/adc128s052.pdf
* http://www.ti.com/lit/ds/symlink/adc122s021.pdf
* http://www.ti.com/lit/ds/symlink/adc124s021.pdf
- *
- * 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/acpi.h>
#include <linux/err.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
struct adc128_configuration {
@@ -135,10 +134,15 @@
static int adc128_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
+ unsigned int config;
struct adc128 *adc;
- int config = spi_get_device_id(spi)->driver_data;
int ret;
+ if (dev_fwnode(&spi->dev))
+ config = (unsigned long) device_get_match_data(&spi->dev);
+ else
+ config = spi_get_device_id(spi)->driver_data;
+
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
if (!indio_dev)
return -ENOMEM;
@@ -186,23 +190,40 @@
static const struct of_device_id adc128_of_match[] = {
{ .compatible = "ti,adc128s052", },
{ .compatible = "ti,adc122s021", },
+ { .compatible = "ti,adc122s051", },
+ { .compatible = "ti,adc122s101", },
{ .compatible = "ti,adc124s021", },
+ { .compatible = "ti,adc124s051", },
+ { .compatible = "ti,adc124s101", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, adc128_of_match);
static const struct spi_device_id adc128_id[] = {
- { "adc128s052", 0}, /* index into adc128_config */
- { "adc122s021", 1},
- { "adc124s021", 2},
+ { "adc128s052", 0 }, /* index into adc128_config */
+ { "adc122s021", 1 },
+ { "adc122s051", 1 },
+ { "adc122s101", 1 },
+ { "adc124s021", 2 },
+ { "adc124s051", 2 },
+ { "adc124s101", 2 },
{ }
};
MODULE_DEVICE_TABLE(spi, adc128_id);
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id adc128_acpi_match[] = {
+ { "AANT1280", 2 }, /* ADC124S021 compatible ACPI ID */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, adc128_acpi_match);
+#endif
+
static struct spi_driver adc128_driver = {
.driver = {
.name = "adc128s052",
.of_match_table = of_match_ptr(adc128_of_match),
+ .acpi_match_table = ACPI_PTR(adc128_acpi_match),
},
.probe = adc128_probe,
.remove = adc128_remove,
diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
index 6a114dc..a550b13 100644
--- a/drivers/iio/adc/ti-ads1015.c
+++ b/drivers/iio/adc/ti-ads1015.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADS1015 - Texas Instruments Analog-to-Digital Converter
*
* Copyright (c) 2016, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for ADS1015 ADC 7-bit I2C slave address:
* * 0x48 - ADDR connected to Ground
* * 0x49 - ADDR connected to Vdd
diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c
new file mode 100644
index 0000000..552c2be
--- /dev/null
+++ b/drivers/iio/adc/ti-ads124s08.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0
+/* TI ADS124S0X chip family driver
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/sysfs.h>
+
+/* Commands */
+#define ADS124S08_CMD_NOP 0x00
+#define ADS124S08_CMD_WAKEUP 0x02
+#define ADS124S08_CMD_PWRDWN 0x04
+#define ADS124S08_CMD_RESET 0x06
+#define ADS124S08_CMD_START 0x08
+#define ADS124S08_CMD_STOP 0x0a
+#define ADS124S08_CMD_SYOCAL 0x16
+#define ADS124S08_CMD_SYGCAL 0x17
+#define ADS124S08_CMD_SFOCAL 0x19
+#define ADS124S08_CMD_RDATA 0x12
+#define ADS124S08_CMD_RREG 0x20
+#define ADS124S08_CMD_WREG 0x40
+
+/* Registers */
+#define ADS124S08_ID_REG 0x00
+#define ADS124S08_STATUS 0x01
+#define ADS124S08_INPUT_MUX 0x02
+#define ADS124S08_PGA 0x03
+#define ADS124S08_DATA_RATE 0x04
+#define ADS124S08_REF 0x05
+#define ADS124S08_IDACMAG 0x06
+#define ADS124S08_IDACMUX 0x07
+#define ADS124S08_VBIAS 0x08
+#define ADS124S08_SYS 0x09
+#define ADS124S08_OFCAL0 0x0a
+#define ADS124S08_OFCAL1 0x0b
+#define ADS124S08_OFCAL2 0x0c
+#define ADS124S08_FSCAL0 0x0d
+#define ADS124S08_FSCAL1 0x0e
+#define ADS124S08_FSCAL2 0x0f
+#define ADS124S08_GPIODAT 0x10
+#define ADS124S08_GPIOCON 0x11
+
+/* ADS124S0x common channels */
+#define ADS124S08_AIN0 0x00
+#define ADS124S08_AIN1 0x01
+#define ADS124S08_AIN2 0x02
+#define ADS124S08_AIN3 0x03
+#define ADS124S08_AIN4 0x04
+#define ADS124S08_AIN5 0x05
+#define ADS124S08_AINCOM 0x0c
+/* ADS124S08 only channels */
+#define ADS124S08_AIN6 0x06
+#define ADS124S08_AIN7 0x07
+#define ADS124S08_AIN8 0x08
+#define ADS124S08_AIN9 0x09
+#define ADS124S08_AIN10 0x0a
+#define ADS124S08_AIN11 0x0b
+#define ADS124S08_MAX_CHANNELS 12
+
+#define ADS124S08_POS_MUX_SHIFT 0x04
+#define ADS124S08_INT_REF 0x09
+
+#define ADS124S08_START_REG_MASK 0x1f
+#define ADS124S08_NUM_BYTES_MASK 0x1f
+
+#define ADS124S08_START_CONV 0x01
+#define ADS124S08_STOP_CONV 0x00
+
+enum ads124s_id {
+ ADS124S08_ID,
+ ADS124S06_ID,
+};
+
+struct ads124s_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+struct ads124s_private {
+ const struct ads124s_chip_info *chip_info;
+ struct gpio_desc *reset_gpio;
+ struct spi_device *spi;
+ struct mutex lock;
+ u8 data[5] ____cacheline_aligned;
+};
+
+#define ADS124S08_CHAN(index) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ }, \
+}
+
+static const struct iio_chan_spec ads124s06_channels[] = {
+ ADS124S08_CHAN(0),
+ ADS124S08_CHAN(1),
+ ADS124S08_CHAN(2),
+ ADS124S08_CHAN(3),
+ ADS124S08_CHAN(4),
+ ADS124S08_CHAN(5),
+};
+
+static const struct iio_chan_spec ads124s08_channels[] = {
+ ADS124S08_CHAN(0),
+ ADS124S08_CHAN(1),
+ ADS124S08_CHAN(2),
+ ADS124S08_CHAN(3),
+ ADS124S08_CHAN(4),
+ ADS124S08_CHAN(5),
+ ADS124S08_CHAN(6),
+ ADS124S08_CHAN(7),
+ ADS124S08_CHAN(8),
+ ADS124S08_CHAN(9),
+ ADS124S08_CHAN(10),
+ ADS124S08_CHAN(11),
+};
+
+static const struct ads124s_chip_info ads124s_chip_info_tbl[] = {
+ [ADS124S08_ID] = {
+ .channels = ads124s08_channels,
+ .num_channels = ARRAY_SIZE(ads124s08_channels),
+ },
+ [ADS124S06_ID] = {
+ .channels = ads124s06_channels,
+ .num_channels = ARRAY_SIZE(ads124s06_channels),
+ },
+};
+
+static int ads124s_write_cmd(struct iio_dev *indio_dev, u8 command)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+
+ priv->data[0] = command;
+
+ return spi_write(priv->spi, &priv->data[0], 1);
+}
+
+static int ads124s_write_reg(struct iio_dev *indio_dev, u8 reg, u8 data)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+
+ priv->data[0] = ADS124S08_CMD_WREG | reg;
+ priv->data[1] = 0x0;
+ priv->data[2] = data;
+
+ return spi_write(priv->spi, &priv->data[0], 3);
+}
+
+static int ads124s_reset(struct iio_dev *indio_dev)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+
+ if (priv->reset_gpio) {
+ gpiod_set_value(priv->reset_gpio, 0);
+ udelay(200);
+ gpiod_set_value(priv->reset_gpio, 1);
+ } else {
+ return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET);
+ }
+
+ return 0;
+};
+
+static int ads124s_read(struct iio_dev *indio_dev, unsigned int chan)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+ int ret;
+ u32 tmp;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &priv->data[0],
+ .len = 4,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &priv->data[1],
+ .rx_buf = &priv->data[1],
+ .len = 4,
+ },
+ };
+
+ priv->data[0] = ADS124S08_CMD_RDATA;
+ memset(&priv->data[1], ADS124S08_CMD_NOP, sizeof(priv->data) - 1);
+
+ ret = spi_sync_transfer(priv->spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ tmp = priv->data[2] << 16 | priv->data[3] << 8 | priv->data[4];
+
+ return tmp;
+}
+
+static int ads124s_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&priv->lock);
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX,
+ chan->channel);
+ if (ret) {
+ dev_err(&priv->spi->dev, "Set ADC CH failed\n");
+ goto out;
+ }
+
+ ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
+ if (ret) {
+ dev_err(&priv->spi->dev, "Start conversions failed\n");
+ goto out;
+ }
+
+ ret = ads124s_read(indio_dev, chan->channel);
+ if (ret < 0) {
+ dev_err(&priv->spi->dev, "Read ADC failed\n");
+ goto out;
+ }
+
+ *val = ret;
+
+ ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
+ if (ret) {
+ dev_err(&priv->spi->dev, "Stop conversions failed\n");
+ goto out;
+ }
+
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static const struct iio_info ads124s_info = {
+ .read_raw = &ads124s_read_raw,
+};
+
+static irqreturn_t ads124s_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads124s_private *priv = iio_priv(indio_dev);
+ u32 buffer[ADS124S08_MAX_CHANNELS + sizeof(s64)/sizeof(u16)];
+ int scan_index, j = 0;
+ int ret;
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX,
+ scan_index);
+ if (ret)
+ dev_err(&priv->spi->dev, "Set ADC CH failed\n");
+
+ ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
+ if (ret)
+ 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 conversions failed\n");
+
+ j++;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ pf->timestamp);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ads124s_probe(struct spi_device *spi)
+{
+ struct ads124s_private *ads124s_priv;
+ struct iio_dev *indio_dev;
+ const struct spi_device_id *spi_id = spi_get_device_id(spi);
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ads124s_priv));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ ads124s_priv = iio_priv(indio_dev);
+
+ ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ads124s_priv->reset_gpio))
+ dev_info(&spi->dev, "Reset GPIO not defined\n");
+
+ ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data];
+
+ spi_set_drvdata(spi, indio_dev);
+
+ ads124s_priv->spi = spi;
+
+ indio_dev->name = spi_id->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->dev.of_node = spi->dev.of_node;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ads124s_priv->chip_info->channels;
+ indio_dev->num_channels = ads124s_priv->chip_info->num_channels;
+ indio_dev->info = &ads124s_info;
+
+ mutex_init(&ads124s_priv->lock);
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ ads124s_trigger_handler, NULL);
+ if (ret) {
+ dev_err(&spi->dev, "iio triggered buffer setup failed\n");
+ return ret;
+ }
+
+ ads124s_reset(indio_dev);
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ads124s_id[] = {
+ { "ads124s06", ADS124S06_ID },
+ { "ads124s08", ADS124S08_ID },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ads124s_id);
+
+static const struct of_device_id ads124s_of_table[] = {
+ { .compatible = "ti,ads124s06" },
+ { .compatible = "ti,ads124s08" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ads124s_of_table);
+
+static struct spi_driver ads124s_driver = {
+ .driver = {
+ .name = "ads124s08",
+ .of_match_table = ads124s_of_table,
+ },
+ .probe = ads124s_probe,
+ .id_table = ads124s_id,
+};
+module_spi_driver(ads124s_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmuprhy@ti.com>");
+MODULE_DESCRIPTION("TI TI_ADS12S0X ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c
index a5bd594..2e66e4d 100644
--- a/drivers/iio/adc/ti-ads7950.c
+++ b/drivers/iio/adc/ti-ads7950.c
@@ -17,6 +17,7 @@
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -36,12 +37,15 @@
*/
#define TI_ADS7950_VA_MV_ACPI_DEFAULT 5000
+#define TI_ADS7950_CR_GPIO BIT(14)
#define TI_ADS7950_CR_MANUAL BIT(12)
#define TI_ADS7950_CR_WRITE BIT(11)
#define TI_ADS7950_CR_CHAN(ch) ((ch) << 7)
#define TI_ADS7950_CR_RANGE_5V BIT(6)
+#define TI_ADS7950_CR_GPIO_DATA BIT(4)
#define TI_ADS7950_MAX_CHAN 16
+#define TI_ADS7950_NUM_GPIOS 4
#define TI_ADS7950_TIMESTAMP_SIZE (sizeof(int64_t) / sizeof(__be16))
@@ -49,27 +53,63 @@
#define TI_ADS7950_EXTRACT(val, dec, bits) \
(((val) >> (dec)) & ((1 << (bits)) - 1))
+#define TI_ADS7950_MAN_CMD(cmd) (TI_ADS7950_CR_MANUAL | (cmd))
+#define TI_ADS7950_GPIO_CMD(cmd) (TI_ADS7950_CR_GPIO | (cmd))
+
+/* Manual mode configuration */
+#define TI_ADS7950_MAN_CMD_SETTINGS(st) \
+ (TI_ADS7950_MAN_CMD(TI_ADS7950_CR_WRITE | st->cmd_settings_bitmask))
+/* GPIO mode configuration */
+#define TI_ADS7950_GPIO_CMD_SETTINGS(st) \
+ (TI_ADS7950_GPIO_CMD(st->gpio_cmd_settings_bitmask))
+
struct ti_ads7950_state {
struct spi_device *spi;
- struct spi_transfer ring_xfer[TI_ADS7950_MAX_CHAN + 2];
+ struct spi_transfer ring_xfer;
struct spi_transfer scan_single_xfer[3];
struct spi_message ring_msg;
struct spi_message scan_single_msg;
+ /* Lock to protect the spi xfer buffers */
+ struct mutex slock;
+ struct gpio_chip chip;
+
struct regulator *reg;
unsigned int vref_mv;
- unsigned int settings;
+ /*
+ * Bitmask of lower 7 bits used for configuration
+ * These bits only can be written when TI_ADS7950_CR_WRITE
+ * is set, otherwise it retains its original state.
+ * [0-3] GPIO signal
+ * [4] Set following frame to return GPIO signal values
+ * [5] Powers down device
+ * [6] Sets Vref range1(2.5v) or range2(5v)
+ *
+ * Bits present on Manual/Auto1/Auto2 commands
+ */
+ unsigned int cmd_settings_bitmask;
+
+ /*
+ * Bitmask of GPIO command
+ * [0-3] GPIO direction
+ * [4-6] Different GPIO alarm mode configurations
+ * [7] GPIO 2 as device range input
+ * [8] GPIO 3 as device power down input
+ * [9] Reset all registers
+ * [10-11] N/A
+ */
+ unsigned int gpio_cmd_settings_bitmask;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
- __be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE]
+ u16 rx_buf[TI_ADS7950_MAX_CHAN + 2 + TI_ADS7950_TIMESTAMP_SIZE]
____cacheline_aligned;
- __be16 tx_buf[TI_ADS7950_MAX_CHAN];
- __be16 single_tx;
- __be16 single_rx;
+ u16 tx_buf[TI_ADS7950_MAX_CHAN + 2];
+ u16 single_tx;
+ u16 single_rx;
};
@@ -108,7 +148,7 @@
.realbits = bits, \
.storagebits = 16, \
.shift = 12 - (bits), \
- .endianness = IIO_BE, \
+ .endianness = IIO_CPU, \
}, \
}
@@ -248,24 +288,15 @@
len = 0;
for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) {
- cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(i) | st->settings;
- st->tx_buf[len++] = cpu_to_be16(cmd);
+ cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(i));
+ st->tx_buf[len++] = cmd;
}
/* Data for the 1st channel is not returned until the 3rd transfer */
- len += 2;
- for (i = 0; i < len; i++) {
- if ((i + 2) < len)
- st->ring_xfer[i].tx_buf = &st->tx_buf[i];
- if (i >= 2)
- st->ring_xfer[i].rx_buf = &st->rx_buf[i - 2];
- st->ring_xfer[i].len = 2;
- st->ring_xfer[i].cs_change = 1;
- }
- /* make sure last transfer's cs_change is not set */
- st->ring_xfer[len - 1].cs_change = 0;
+ st->tx_buf[len++] = 0;
+ st->tx_buf[len++] = 0;
- spi_message_init_with_transfers(&st->ring_msg, st->ring_xfer, len);
+ st->ring_xfer.len = len * 2;
return 0;
}
@@ -277,14 +308,16 @@
struct ti_ads7950_state *st = iio_priv(indio_dev);
int ret;
+ mutex_lock(&st->slock);
ret = spi_sync(st->spi, &st->ring_msg);
if (ret < 0)
goto out;
- iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
+ iio_push_to_buffers_with_timestamp(indio_dev, &st->rx_buf[2],
iio_get_time_ns(indio_dev));
out:
+ mutex_unlock(&st->slock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
@@ -295,19 +328,18 @@
struct ti_ads7950_state *st = iio_priv(indio_dev);
int ret, cmd;
- mutex_lock(&indio_dev->mlock);
-
- cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings;
- st->single_tx = cpu_to_be16(cmd);
+ mutex_lock(&st->slock);
+ cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(ch));
+ st->single_tx = cmd;
ret = spi_sync(st->spi, &st->scan_single_msg);
if (ret)
goto out;
- ret = be16_to_cpu(st->single_rx);
+ ret = st->single_rx;
out:
- mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->slock);
return ret;
}
@@ -326,7 +358,7 @@
vref /= 1000;
}
- if (st->settings & TI_ADS7950_CR_RANGE_5V)
+ if (st->cmd_settings_bitmask & TI_ADS7950_CR_RANGE_5V)
vref *= 2;
return vref;
@@ -371,6 +403,132 @@
.update_scan_mode = ti_ads7950_update_scan_mode,
};
+static void ti_ads7950_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+
+ mutex_lock(&st->slock);
+
+ if (value)
+ st->cmd_settings_bitmask |= BIT(offset);
+ else
+ st->cmd_settings_bitmask &= ~BIT(offset);
+
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ spi_sync(st->spi, &st->scan_single_msg);
+
+ mutex_unlock(&st->slock);
+}
+
+static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+ int ret;
+
+ mutex_lock(&st->slock);
+
+ /* If set as output, return the output */
+ if (st->gpio_cmd_settings_bitmask & BIT(offset)) {
+ ret = st->cmd_settings_bitmask & BIT(offset);
+ goto out;
+ }
+
+ /* GPIO data bit sets SDO bits 12-15 to GPIO input */
+ st->cmd_settings_bitmask |= TI_ADS7950_CR_GPIO_DATA;
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ goto out;
+
+ ret = ((st->single_rx >> 12) & BIT(offset)) ? 1 : 0;
+
+ /* Revert back to original settings */
+ st->cmd_settings_bitmask &= ~TI_ADS7950_CR_GPIO_DATA;
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ goto out;
+
+out:
+ mutex_unlock(&st->slock);
+
+ return ret;
+}
+
+static int ti_ads7950_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+
+ /* Bitmask is inverted from GPIO framework 0=input/1=output */
+ return !(st->gpio_cmd_settings_bitmask & BIT(offset));
+}
+
+static int _ti_ads7950_set_direction(struct gpio_chip *chip, int offset,
+ int input)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+ int ret = 0;
+
+ mutex_lock(&st->slock);
+
+ /* Only change direction if needed */
+ if (input && (st->gpio_cmd_settings_bitmask & BIT(offset)))
+ st->gpio_cmd_settings_bitmask &= ~BIT(offset);
+ else if (!input && !(st->gpio_cmd_settings_bitmask & BIT(offset)))
+ st->gpio_cmd_settings_bitmask |= BIT(offset);
+ else
+ goto out;
+
+ st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+
+out:
+ mutex_unlock(&st->slock);
+
+ return ret;
+}
+
+static int ti_ads7950_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ return _ti_ads7950_set_direction(chip, offset, 1);
+}
+
+static int ti_ads7950_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ ti_ads7950_set(chip, offset, value);
+
+ return _ti_ads7950_set_direction(chip, offset, 0);
+}
+
+static int ti_ads7950_init_hw(struct ti_ads7950_state *st)
+{
+ int ret = 0;
+
+ mutex_lock(&st->slock);
+
+ /* Settings for Manual/Auto1/Auto2 commands */
+ /* Default to 5v ref */
+ st->cmd_settings_bitmask = TI_ADS7950_CR_RANGE_5V;
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ goto out;
+
+ /* Settings for GPIO command */
+ st->gpio_cmd_settings_bitmask = 0x0;
+ st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+
+out:
+ mutex_unlock(&st->slock);
+
+ return ret;
+}
+
static int ti_ads7950_probe(struct spi_device *spi)
{
struct ti_ads7950_state *st;
@@ -378,6 +536,14 @@
const struct ti_ads7950_chip_info *info;
int ret;
+ spi->bits_per_word = 16;
+ spi->mode |= SPI_CS_WORD;
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Error in spi setup\n");
+ return ret;
+ }
+
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
@@ -387,7 +553,6 @@
spi_set_drvdata(spi, indio_dev);
st->spi = spi;
- st->settings = TI_ADS7950_CR_MANUAL | TI_ADS7950_CR_RANGE_5V;
info = &ti_ads7950_chip_info[spi_get_device_id(spi)->driver_data];
@@ -398,6 +563,16 @@
indio_dev->num_channels = info->num_channels;
indio_dev->info = &ti_ads7950_info;
+ /* build spi ring message */
+ spi_message_init(&st->ring_msg);
+
+ st->ring_xfer.tx_buf = &st->tx_buf[0];
+ st->ring_xfer.rx_buf = &st->rx_buf[0];
+ /* len will be set later */
+ st->ring_xfer.cs_change = true;
+
+ spi_message_add_tail(&st->ring_xfer, &st->ring_msg);
+
/*
* Setup default message. The sample is read at the end of the first
* transfer, then it takes one full cycle to convert the sample and one
@@ -423,16 +598,19 @@
if (ACPI_COMPANION(&spi->dev))
st->vref_mv = TI_ADS7950_VA_MV_ACPI_DEFAULT;
+ mutex_init(&st->slock);
+
st->reg = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(st->reg)) {
dev_err(&spi->dev, "Failed get get regulator \"vref\"\n");
- return PTR_ERR(st->reg);
+ ret = PTR_ERR(st->reg);
+ goto error_destroy_mutex;
}
ret = regulator_enable(st->reg);
if (ret) {
dev_err(&spi->dev, "Failed to enable regulator \"vref\"\n");
- return ret;
+ goto error_destroy_mutex;
}
ret = iio_triggered_buffer_setup(indio_dev, NULL,
@@ -442,18 +620,46 @@
goto error_disable_reg;
}
+ ret = ti_ads7950_init_hw(st);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to init adc chip\n");
+ goto error_cleanup_ring;
+ }
+
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&spi->dev, "Failed to register iio device\n");
goto error_cleanup_ring;
}
+ /* Add GPIO chip */
+ st->chip.label = dev_name(&st->spi->dev);
+ st->chip.parent = &st->spi->dev;
+ st->chip.owner = THIS_MODULE;
+ st->chip.base = -1;
+ st->chip.ngpio = TI_ADS7950_NUM_GPIOS;
+ st->chip.get_direction = ti_ads7950_get_direction;
+ st->chip.direction_input = ti_ads7950_direction_input;
+ st->chip.direction_output = ti_ads7950_direction_output;
+ st->chip.get = ti_ads7950_get;
+ st->chip.set = ti_ads7950_set;
+
+ ret = gpiochip_add_data(&st->chip, st);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to init GPIOs\n");
+ goto error_iio_device;
+ }
+
return 0;
+error_iio_device:
+ iio_device_unregister(indio_dev);
error_cleanup_ring:
iio_triggered_buffer_cleanup(indio_dev);
error_disable_reg:
regulator_disable(st->reg);
+error_destroy_mutex:
+ mutex_destroy(&st->slock);
return ret;
}
@@ -463,9 +669,11 @@
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ti_ads7950_state *st = iio_priv(indio_dev);
+ gpiochip_remove(&st->chip);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(st->reg);
+ mutex_destroy(&st->slock);
return 0;
}
diff --git a/drivers/iio/adc/ti-ads8344.c b/drivers/iio/adc/ti-ads8344.c
new file mode 100644
index 0000000..9a46080
--- /dev/null
+++ b/drivers/iio/adc/ti-ads8344.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ADS8344 16-bit 8-Channel ADC driver
+ *
+ * Author: Gregory CLEMENT <gregory.clement@bootlin.com>
+ *
+ * Datasheet: http://www.ti.com/lit/ds/symlink/ads8344.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define ADS8344_START BIT(7)
+#define ADS8344_SINGLE_END BIT(2)
+#define ADS8344_CHANNEL(channel) ((channel) << 4)
+#define ADS8344_CLOCK_INTERNAL 0x2 /* PD1 = 1 and PD0 = 0 */
+
+struct ads8344 {
+ struct spi_device *spi;
+ struct regulator *reg;
+ /*
+ * Lock protecting access to adc->tx_buff and rx_buff,
+ * especially from concurrent read on sysfs file.
+ */
+ struct mutex lock;
+
+ u8 tx_buf ____cacheline_aligned;
+ u16 rx_buf;
+};
+
+#define ADS8344_VOLTAGE_CHANNEL(chan, si) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ }
+
+#define ADS8344_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan1), \
+ .channel2 = (chan2), \
+ .differential = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ }
+
+static const struct iio_chan_spec ads8344_channels[] = {
+ ADS8344_VOLTAGE_CHANNEL(0, 0),
+ ADS8344_VOLTAGE_CHANNEL(1, 4),
+ ADS8344_VOLTAGE_CHANNEL(2, 1),
+ ADS8344_VOLTAGE_CHANNEL(3, 5),
+ ADS8344_VOLTAGE_CHANNEL(4, 2),
+ ADS8344_VOLTAGE_CHANNEL(5, 6),
+ ADS8344_VOLTAGE_CHANNEL(6, 3),
+ ADS8344_VOLTAGE_CHANNEL(7, 7),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(2, 3, 9),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(4, 5, 10),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(6, 7, 11),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(1, 0, 12),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(3, 2, 13),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(5, 4, 14),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
+};
+
+static int ads8344_adc_conversion(struct ads8344 *adc, int channel,
+ bool differential)
+{
+ struct spi_device *spi = adc->spi;
+ int ret;
+
+ adc->tx_buf = ADS8344_START;
+ if (!differential)
+ adc->tx_buf |= ADS8344_SINGLE_END;
+ adc->tx_buf |= ADS8344_CHANNEL(channel);
+ adc->tx_buf |= ADS8344_CLOCK_INTERNAL;
+
+ ret = spi_write(spi, &adc->tx_buf, 1);
+ if (ret)
+ return ret;
+
+ udelay(9);
+
+ ret = spi_read(spi, &adc->rx_buf, 2);
+ if (ret)
+ return ret;
+
+ return adc->rx_buf;
+}
+
+static int ads8344_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *channel, int *value,
+ int *shift, long mask)
+{
+ struct ads8344 *adc = iio_priv(iio);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+ *value = ads8344_adc_conversion(adc, channel->scan_index,
+ channel->differential);
+ mutex_unlock(&adc->lock);
+ if (*value < 0)
+ return *value;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *value = regulator_get_voltage(adc->reg);
+ if (*value < 0)
+ return *value;
+
+ /* convert regulator output voltage to mV */
+ *value /= 1000;
+ *shift = 16;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ads8344_info = {
+ .read_raw = ads8344_read_raw,
+};
+
+static int ads8344_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ads8344 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+ mutex_init(&adc->lock);
+
+ indio_dev->name = dev_name(&spi->dev);
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->dev.of_node = spi->dev.of_node;
+ indio_dev->info = &ads8344_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ads8344_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ads8344_channels);
+
+ adc->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(adc->reg))
+ return PTR_ERR(adc->reg);
+
+ ret = regulator_enable(adc->reg);
+ if (ret)
+ return ret;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ regulator_disable(adc->reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ads8344_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ads8344 *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ regulator_disable(adc->reg);
+
+ return 0;
+}
+
+static const struct of_device_id ads8344_of_match[] = {
+ { .compatible = "ti,ads8344", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ads8344_of_match);
+
+static struct spi_driver ads8344_driver = {
+ .driver = {
+ .name = "ads8344",
+ .of_match_table = ads8344_of_match,
+ },
+ .probe = ads8344_probe,
+ .remove = ads8344_remove,
+};
+module_spi_driver(ads8344_driver);
+
+MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@bootlin.com>");
+MODULE_DESCRIPTION("ADS8344 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c
index 184d686..14fe7c3 100644
--- a/drivers/iio/adc/ti-ads8688.c
+++ b/drivers/iio/adc/ti-ads8688.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2015 Prevas A/S
- *
- * 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/device.h>
@@ -41,6 +38,7 @@
#define ADS8688_VREF_MV 4096
#define ADS8688_REALBITS 16
+#define ADS8688_MAX_CHANNELS 8
/*
* enum ads8688_range - ADS8688 reference voltage range
@@ -385,7 +383,7 @@
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
- u16 buffer[8];
+ u16 buffer[ADS8688_MAX_CHANNELS + sizeof(s64)/sizeof(u16)];
int i, j = 0;
for (i = 0; i < indio_dev->masklength; i++) {
@@ -396,7 +394,7 @@
}
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
- pf->timestamp);
+ iio_get_time_ns(indio_dev));
iio_trigger_notify_done(indio_dev->trig);
@@ -522,6 +520,6 @@
};
module_spi_driver(ads8688_driver);
-MODULE_AUTHOR("Sean Nyekjaer <sean.nyekjaer@prevas.dk>");
+MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.dk>");
MODULE_DESCRIPTION("Texas Instruments ADS8688 driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c
index 2290024..4965246 100644
--- a/drivers/iio/adc/ti-tlc4541.c
+++ b/drivers/iio/adc/ti-tlc4541.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TI tlc4541 ADC Driver
*
@@ -7,10 +8,6 @@
* http://www.ti.com/lit/gpn/tlc3541
* http://www.ti.com/lit/gpn/tlc4541
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* The tlc4541 requires 24 clock cycles to start a transfer.
* Conversion then takes 2.94us to complete before data is ready
* Data is returned MSB first.
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index 80df5a3..9d984f2 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -142,7 +142,10 @@
stepconfig |= STEPCONFIG_MODE_SWCNT;
tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
- stepconfig | STEPCONFIG_INP(chan));
+ stepconfig | STEPCONFIG_INP(chan) |
+ STEPCONFIG_INM_ADCREFM |
+ STEPCONFIG_RFP_VREFP |
+ STEPCONFIG_RFM_VREFN);
if (adc_dev->open_delay[i] > STEPDELAY_OPEN_MASK) {
dev_warn(dev, "chan %d open delay truncating to 0x3FFFF\n",
@@ -693,16 +696,12 @@
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct tiadc_device *adc_dev = iio_priv(indio_dev);
- struct ti_tscadc_dev *tscadc_dev;
unsigned int idle;
- tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
- if (!device_may_wakeup(tscadc_dev->dev)) {
- idle = tiadc_readl(adc_dev, REG_CTRL);
- idle &= ~(CNTRLREG_TSCSSENB);
- tiadc_writel(adc_dev, REG_CTRL, (idle |
- CNTRLREG_POWERDOWN));
- }
+ idle = tiadc_readl(adc_dev, REG_CTRL);
+ idle &= ~(CNTRLREG_TSCSSENB);
+ tiadc_writel(adc_dev, REG_CTRL, (idle |
+ CNTRLREG_POWERDOWN));
return 0;
}
diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
index 8c019bb..55c5119 100644
--- a/drivers/iio/adc/twl4030-madc.c
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
*
* TWL4030 MADC module driver-This driver monitors the real time
@@ -12,21 +13,6 @@
* Mikko Ylinen <mikko.k.ylinen@nokia.com>
*
* Amit Kucheria <amit.kucheria@canonical.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
#include <linux/device.h>
diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c
index e470510..f24148b 100644
--- a/drivers/iio/adc/twl6030-gpadc.c
+++ b/drivers/iio/adc/twl6030-gpadc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TWL6030 GPADC module driver
*
@@ -12,21 +13,6 @@
* Based on twl4030-madc.c
* Copyright (C) 2008 Nokia Corporation
* Mikko Ylinen <mikko.k.ylinen@nokia.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -919,10 +905,8 @@
}
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "failed to get irq\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_threaded_irq(dev, irq, NULL,
twl6030_gpadc_irq_handler,
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index bbcb7a4..98b3047 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Freescale Vybrid vf610 ADC driver
*
* Copyright 2013 Freescale Semiconductor, Inc.
- *
- * 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>
@@ -834,10 +821,8 @@
return PTR_ERR(info->regs);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_irq(info->dev, irq,
vf610_adc_isr, 0,
diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c
index 53eb5a4..1d2aeb0 100644
--- a/drivers/iio/adc/viperboard_adc.c
+++ b/drivers/iio/adc/viperboard_adc.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Nano River Technologies viperboard IIO ADC driver
*
* (C) 2012 by Lemonage GmbH
* Author: Lars Poeschel <poeschel@lemonage.de>
* 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 as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index 3f6be5a..4fd3896 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Xilinx XADC driver
*
* Copyright 2013-2014 Analog Devices Inc.
* Author: Lars-Peter Clauen <lars@metafoo.de>
*
- * Licensed under the GPL-2.
- *
* Documentation for the parts can be found at:
* - XADC hardmacro: Xilinx UG480
* - ZYNQ XADC interface: Xilinx UG585
@@ -1273,8 +1272,10 @@
xadc->threshold[i] = 0xffff;
else
xadc->threshold[i] = 0;
- xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
+ ret = xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
xadc->threshold[i]);
+ if (ret)
+ goto err_free_irq;
}
/* Go to non-buffered mode */
@@ -1290,6 +1291,7 @@
err_free_irq:
free_irq(xadc->irq, indio_dev);
+ cancel_delayed_work_sync(&xadc->zynq_unmask_work);
err_clk_disable_unprepare:
clk_disable_unprepare(xadc->clk);
err_free_samplerate_trigger:
@@ -1319,8 +1321,8 @@
iio_triggered_buffer_cleanup(indio_dev);
}
free_irq(xadc->irq, indio_dev);
+ cancel_delayed_work_sync(&xadc->zynq_unmask_work);
clk_disable_unprepare(xadc->clk);
- cancel_delayed_work(&xadc->zynq_unmask_work);
kfree(xadc->data);
kfree(indio_dev->channels);
diff --git a/drivers/iio/adc/xilinx-xadc-events.c b/drivers/iio/adc/xilinx-xadc-events.c
index dc06703..dbfd5da 100644
--- a/drivers/iio/adc/xilinx-xadc-events.c
+++ b/drivers/iio/adc/xilinx-xadc-events.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Xilinx XADC driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clauen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/iio/events.h>
diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h
index 8c00095..4017f18 100644
--- a/drivers/iio/adc/xilinx-xadc.h
+++ b/drivers/iio/adc/xilinx-xadc.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Xilinx XADC driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clauen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __IIO_XILINX_XADC__