diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /drivers/iio/adc | |
parent | Initial commit. (diff) | |
download | linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip |
Adding upstream version 5.10.209.upstream/5.10.209
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/iio/adc')
118 files changed, 65527 insertions, 0 deletions
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig new file mode 100644 index 000000000..e39b67912 --- /dev/null +++ b/drivers/iio/adc/Kconfig @@ -0,0 +1,1229 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# ADC drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Analog to digital converters" + +config AB8500_GPADC + bool "ST-Ericsson AB8500 GPADC driver" + depends on AB8500_CORE && REGULATOR_AB8500 + default y + help + AB8500 Analog Baseband, mixed signal integrated circuit GPADC + (General Purpose Analog to Digital Converter) driver used to monitor + internal voltages, convert accessory and battery, AC (charger, mains) + and USB voltages integral to the U8500 platform. + +config AD_SIGMA_DELTA + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config AD7091R5 + tristate "Analog Devices AD7091R5 ADC Driver" + depends on I2C + select REGMAP_I2C + help + Say yes here to build support for Analog Devices AD7091R-5 ADC. + +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 AD7192 + tristate "Analog Devices AD7190 AD7192 AD7193 AD7195 ADC driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7190, + AD7192, AD7193 or AD7195 SPI analog to digital converters (ADC). + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called ad7192. + +config AD7266 + tristate "Analog Devices AD7265/AD7266 ADC driver" + depends on SPI_MASTER + select IIO_BUFFER + select IIO_TRIGGER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7265 and AD7266 + ADCs. + + To compile this driver as a module, choose M here: the module will be + called ad7266. + +config AD7291 + tristate "Analog Devices AD7291 ADC driver" + depends on I2C + help + Say yes here to build support for Analog Devices AD7291 + 8 Channel ADC with temperature sensor. + + To compile this driver as a module, choose M here: the + module will be called ad7291. + +config AD7292 + tristate "Analog Devices AD7292 ADC driver" + depends on SPI + help + Say yes here to build support for Analog Devices AD7292 + 8 Channel ADC with temperature sensor. + + To compile this driver as a module, choose M here: the + module will be called ad7292. + +config AD7298 + tristate "Analog Devices AD7298 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7298 + 8 Channel ADC with temperature sensor. + + To compile this driver as a module, choose M here: the + module will be called ad7298. + +config AD7476 + 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 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 + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7766, AD7766-1, + AD7766-2, AD7767, AD7767-1, AD7767-2 SPI analog to digital converters. + + 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 + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7787, AD7788, AD7789, + AD7790 and AD7791 SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad7791. + +config AD7793 + tristate "Analog Devices AD7793 and similar ADCs driver" + depends on SPI + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7785, AD7792, AD7793, + AD7794 and AD7795 SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the + module will be called AD7793. + +config AD7887 + tristate "Analog Devices AD7887 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices + AD7887 SPI analog to digital converter (ADC). + + To compile this driver as a module, choose M here: the + module will be called ad7887. + +config AD7923 + tristate "Analog Devices AD7923 and similar ADCs driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices + AD7904, AD7914, AD7923, AD7924 4 Channel ADCs. + + 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 + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices: + ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, ad7998 + i2c analog to digital converters (ADC). Provides direct access + via sysfs. + + To compile this driver as a module, choose M here: the module will be + called ad799x. + +config AD9467 + tristate "Analog Devices AD9467 High Speed ADC driver" + depends on SPI + depends on ADI_AXI_ADC + help + Say yes here to build support for Analog Devices: + * AD9467 16-Bit, 200 MSPS/250 MSPS Analog-to-Digital Converter + + The driver requires the assistance of the AXI ADC IP core to operate, + since SPI is used for configuration only, while data has to be + streamed into memory via DMA. + + To compile this driver as a module, choose M here: the module will be + called ad9467. + +config ADI_AXI_ADC + tristate "Analog Devices Generic AXI ADC IP core driver" + select IIO_BUFFER + select IIO_BUFFER_HW_CONSUMER + select IIO_BUFFER_DMAENGINE + depends on HAS_IOMEM + depends on OF + help + Say yes here to build support for Analog Devices Generic + AXI ADC IP core. The IP core is used for interfacing with + analog-to-digital (ADC) converters that require either a high-speed + serial interface (JESD204B/C) or a source synchronous parallel + interface (LVDS/CMOS). + Typically (for such devices) SPI will be used for configuration only, + while this IP core handles the streaming of data into memory via DMA. + + Link: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called adi-axi-adc. + +config ASPEED_ADC + tristate "Aspeed ADC" + depends on ARCH_ASPEED || COMPILE_TEST + depends on COMMON_CLK + help + If you say yes here you get support for the ADC included in Aspeed + BMC SoCs. + + To compile this driver as a module, choose M here: the module will be + called aspeed_adc. + +config AT91_ADC + tristate "Atmel AT91 ADC" + depends on ARCH_AT91 || COMPILE_TEST + depends on INPUT && SYSFS && OF + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Atmel AT91 ADC. + + To compile this driver as a module, choose M here: the module will be + called at91_adc. + +config AT91_SAMA5D2_ADC + tristate "Atmel AT91 SAMA5D2 ADC" + depends on ARCH_AT91 || COMPILE_TEST + depends on HAS_IOMEM + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Atmel SAMA5D2 ADC which is + available on SAMA5D2 SoC family. + + To compile this driver as a module, choose M here: the module will be + called at91-sama5d2_adc. + +config AXP20X_ADC + tristate "X-Powers AXP20X and AXP22X ADC driver" + depends on MFD_AXP20X + help + Say yes here to have support for X-Powers power management IC (PMIC) + AXP20X and AXP22X ADC devices. + + To compile this driver as a module, choose M here: the module will be + called axp20x_adc. + +config AXP288_ADC + tristate "X-Powers AXP288 ADC driver" + depends on MFD_AXP20X + help + Say yes here to have support for X-Powers power management IC (PMIC) ADC + device. Depending on platform configuration, this general purpose ADC can + be used for sampling sensors such as thermal resistors. + + To compile this driver as a module, choose M here: the module will be + called axp288_adc. + +config BCM_IPROC_ADC + tristate "Broadcom IPROC ADC driver" + depends on (ARCH_BCM_IPROC && OF) || COMPILE_TEST + depends on MFD_SYSCON + default ARCH_BCM_CYGNUS + help + Say Y here if you want to add support for the Broadcom static + ADC driver. + + Broadcom iProc ADC driver. Broadcom iProc ADC controller has 8 + channels. The driver allows the user to read voltage values. + +config BERLIN2_ADC + tristate "Marvell Berlin2 ADC driver" + depends on ARCH_BERLIN + help + Marvell Berlin2 ADC driver. This ADC has 8 channels, with one used for + temperature measurement. + +config CC10001_ADC + tristate "Cosmic Circuits 10001 ADC driver" + depends on HAS_IOMEM && HAVE_CLK && REGULATOR + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Cosmic Circuits 10001 ADC. + + This driver can also be built as a module. If so, the module will be + called cc10001_adc. + +config CPCAP_ADC + tristate "Motorola CPCAP PMIC ADC driver" + depends on MFD_CPCAP + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Motorola CPCAP PMIC ADC. + + This driver can also be built as a module. If so, the module will be + called cpcap-adc. + +config DA9150_GPADC + tristate "Dialog DA9150 GPADC driver support" + depends on MFD_DA9150 + help + Say yes here to build support for Dialog DA9150 GPADC. + + This driver can also be built as a module. If chosen, the module name + will be da9150-gpadc. + + To compile this driver as a module, choose M here: the module will be + called berlin2-adc. + +config DLN2_ADC + tristate "Diolan DLN-2 ADC driver support" + depends on MFD_DLN2 + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Diolan DLN-2 ADC. + + This driver can also be built as a module. If so, the module will be + called adc_dln2. + +config ENVELOPE_DETECTOR + tristate "Envelope detector using a DAC and a comparator" + depends on OF + help + Say yes here to build support for an envelope detector using a DAC + and a comparator. + + To compile this driver as a module, choose M here: the module will be + called envelope-detector. + +config EP93XX_ADC + tristate "Cirrus Logic EP93XX ADC driver" + depends on ARCH_EP93XX + help + Driver for the ADC module on the EP93XX series of SoC from Cirrus Logic. + It's recommended to switch on CONFIG_HIGH_RES_TIMERS option, in this + case driver will reduce its CPU usage by 90% in some use cases. + + To compile this driver as a module, choose M here: the module will be + called ep93xx_adc. + +config EXYNOS_ADC + tristate "Exynos ADC driver support" + 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 + of SoCs for drivers such as the touchscreen and hwmon to use to share + this resource. + + To compile this driver as a module, choose M here: the module will be + called exynos_adc. + +config MXS_LRADC_ADC + tristate "Freescale i.MX23/i.MX28 LRADC ADC" + depends on MFD_MXS_LRADC + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for the ADC functions of the + i.MX23/i.MX28 LRADC. This includes general-purpose ADC readings, + battery voltage measurement, and die temperature measurement. + + This driver can also be built as a module. If so, the module will be + called mxs-lradc-adc. + +config FSL_MX25_ADC + tristate "Freescale MX25 ADC driver" + depends on MFD_MX25_TSADC + help + Generic Conversion Queue driver used for general purpose ADC in the + MX25. This driver supports single measurements using the MX25 ADC. + +config HI8435 + tristate "Holt Integrated Circuits HI-8435 threshold detector" + select IIO_TRIGGERED_EVENT + depends on SPI + help + If you say yes here you get support for Holt Integrated Circuits + HI-8435 chip. + + This driver can also be built as a module. If so, the module will be + called hi8435. + +config HX711 + tristate "AVIA HX711 ADC for weight cells" + depends on GPIOLIB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for AVIA HX711 ADC which is used + for weigh cells + + 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 + + Currently the raw value is read from the chip and delivered. + To get an actual weight one needs to subtract the + zero offset and multiply by a scale factor. + This should be done in userspace. + + This driver can also be built as a module. If so, the module will be + called hx711. + +config INA2XX_ADC + tristate "Texas Instruments INA2xx Power Monitors IIO driver" + depends on I2C && !SENSORS_INA2XX + select REGMAP_I2C + select IIO_BUFFER + select IIO_KFIFO_BUF + help + 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 + select IIO_BUFFER + 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 INTEL_MRFLD_ADC + tristate "Intel Merrifield Basin Cove ADC driver" + depends on INTEL_SOC_PMIC_MRFLD + help + Say yes here to have support for Basin Cove power management IC (PMIC) ADC + device. Depending on platform configuration, this general purpose ADC can + be used for sampling sensors such as thermal resistors. + + To compile this driver as a module, choose M here: the module will be + called intel_mrfld_adc. + +config IMX7D_ADC + tristate "Freescale IMX7D ADC driver" + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for IMX7D ADC. + + This driver can also be built as a module. If so, the module will be + called imx7d_adc. + +config LP8788_ADC + tristate "LP8788 ADC driver" + depends on MFD_LP8788 + help + Say yes here to build support for TI LP8788 ADC. + + To compile this driver as a module, choose M here: the module will be + called lp8788_adc. + +config LPC18XX_ADC + tristate "NXP LPC18xx ADC driver" + depends on ARCH_LPC18XX || COMPILE_TEST + depends on OF && HAS_IOMEM + help + Say yes here to build support for NXP LPC18XX ADC. + + To compile this driver as a module, choose M here: the module will be + called lpc18xx_adc. + +config LPC32XX_ADC + tristate "NXP LPC32XX ADC" + depends on ARCH_LPC32XX || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for the integrated ADC inside the + LPC32XX SoC. Note that this feature uses the same hardware as the + touchscreen driver, so you should either select only one of the two + drivers (lpc32xx_adc or lpc32xx_ts) or, in the OpenFirmware case, + activate only one via device tree selection. Provides direct access + via sysfs. + +config LTC2471 + tristate "Linear Technology LTC2471 and LTC2473 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2471 and + LTC2473 16-bit I2C ADC. + + This driver can also be built as a module. If so, the module will + be called ltc2471. + +config LTC2485 + tristate "Linear Technology LTC2485 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2485 ADC. + + To compile this driver as a module, choose M here: the module will be + called ltc2485. + +config LTC2496 + tristate "Linear Technology LTC2496 ADC driver" + depends on SPI + help + Say yes here to build support for Linear Technology LTC2496 + 16-Bit 8-/16-Channel Delta Sigma ADC. + + To compile this driver as a module, choose M here: the module will be + called ltc2496. + +config LTC2497 + tristate "Linear Technology LTC2497 ADC driver" + depends on I2C + help + Say yes here to build support for Linear Technology LTC2497 + 16-Bit 8-/16-Channel Delta Sigma ADC. + + To compile this driver as a module, choose M here: the module will be + called ltc2497. + +config MAX1027 + tristate "Maxim max1027 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Maxim SPI {10,12}-bit ADC models: + max1027, max1029, max1031, max1227, max1229 and max1231. + + To compile this driver as a module, choose M here: the module will be + called max1027. + +config MAX11100 + tristate "Maxim max11100 ADC driver" + depends on SPI_MASTER + help + Say yes here to build support for Maxim max11100 SPI ADC + + To compile this driver as a module, choose M here: the module will be + called max11100. + +config MAX1118 + tristate "Maxim max1117/max1118/max1119 ADCs driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Maxim max1117/max1118/max1119 + 8-bit, dual-channel ADCs. + + To compile this driver as a module, choose M here: the module will be + called max1118. + +config MAX1241 + tristate "Maxim max1241 ADC driver" + depends on SPI_MASTER + help + Say yes here to build support for Maxim max1241 12-bit, single-channel + ADC. + + To compile this driver as a module, choose M here: the module will be + called max1241. + +config MAX1363 + tristate "Maxim max1363 ADC driver" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for many Maxim i2c analog to digital + converters (ADC). (max1361, max1362, max1363, max1364, max1036, + max1037, max1038, max1039, max1136, max1136, max1137, max1138, + max1139, max1236, max1237, max11238, max1239, max11600, max11601, + max11602, max11603, max11604, max11605, max11606, max11607, + max11608, max11609, max11610, max11611, max11612, max11613, + max11614, max11615, max11616, max11617, max11644, max11645, + max11646, max11647) Provides direct access via sysfs and buffered + data via the iio dev interface. + + To compile this driver as a module, choose M here: the module will be + called max1363. + +config MAX9611 + tristate "Maxim max9611/max9612 ADC driver" + depends on I2C + help + Say yes here to build support for Maxim max9611/max9612 current sense + amplifier with 12-bits ADC interface. + + To compile this driver as a module, choose M here: the module will be + called max9611. + +config MCP320X + tristate "Microchip Technology MCP3x01/02/04/08 and MCP3550/1/3" + depends on SPI + help + Say yes here to build support for Microchip Technology's + MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204, + MCP3208, MCP3301, MCP3550, MCP3551 and MCP3553 analog to digital + converters. + + This driver can also be built as a module. If so, the module will be + called mcp320x. + +config MCP3422 + tristate "Microchip Technology MCP3421/2/3/4/5/6/7/8 driver" + depends on I2C + help + Say yes here to build support for Microchip Technology's MCP3421 + MCP3422, MCP3423, MCP3424, MCP3425, MCP3426, MCP3427 or MCP3428 + analog to digital converters. + + 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. + + 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. + +config MEN_Z188_ADC + tristate "MEN 16z188 ADC IP Core support" + depends on MCB + help + Say yes here to enable support for the MEN 16z188 ADC IP-Core on a MCB + carrier. + + This driver can also be built as a module. If so, the module will be + called men_z188_adc. + +config MESON_SARADC + tristate "Amlogic Meson SAR ADC driver" + default ARCH_MESON + depends on OF && COMMON_CLK && (ARCH_MESON || COMPILE_TEST) + select REGMAP_MMIO + help + Say yes here to build support for the SAR ADC found in Amlogic Meson + SoCs. + + To compile this driver as a module, choose M here: the + module will be called meson_saradc. + +config MP2629_ADC + tristate "Monolithic MP2629 ADC driver" + depends on MFD_MP2629 + help + Say yes to have support for battery charger IC MP2629 ADC device + accessed over I2C. + + This driver provides ADC conversion of system, input power supply + and battery voltage & current information. + +config NAU7802 + tristate "Nuvoton NAU7802 ADC driver" + depends on I2C + help + Say yes here to build support for Nuvoton NAU7802 ADC. + + 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 + help + Palmas series pmic chip by Texas Instruments (twl6035/6037) + is used in smartphones and tablets and supports a 16 channel + general purpose ADC. + +config QCOM_VADC_COMMON + tristate + +config QCOM_PM8XXX_XOADC + tristate "Qualcomm SSBI PM8xxx PMIC XOADCs" + depends on MFD_PM8XXX + select QCOM_VADC_COMMON + help + ADC driver for the XOADC portions of the Qualcomm PM8xxx PMICs + using SSBI transport: PM8018, PM8038, PM8058, PM8921. + + To compile this driver as a module, choose M here: the module + will be called qcom-pm8xxx-xoadc. + +config QCOM_SPMI_IADC + tristate "Qualcomm SPMI PMIC current ADC" + depends on SPMI + select REGMAP_SPMI + help + This is the IIO Current ADC driver for Qualcomm QPNP IADC Chip. + + The driver supports single mode operation to read from one of two + channels (external or internal). Hardware have additional + channels internally used for gain and offset calibration. + + To compile this driver as a module, choose M here: the module will + be called qcom-spmi-iadc. + +config QCOM_SPMI_VADC + tristate "Qualcomm SPMI PMIC voltage ADC" + depends on SPMI + select REGMAP_SPMI + select QCOM_VADC_COMMON + help + This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip. + + The driver supports multiple channels read. The VADC is a 15-bit + sigma-delta ADC. Some of the channels are internally used for + calibration. + + To compile this driver as a module, choose M here: the module will + be called qcom-spmi-vadc. + +config 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 + help + Say yes here to build support for the GyroADC found in Renesas + R-Car Gen2 SoCs. This block is a simple SPI offload engine for + reading data out of attached compatible ADCs in a round-robin + fashion. Up to 4 or 8 ADC channels are supported by this block, + depending on which ADCs are attached. + + To compile this driver as a module, choose M here: the + module will be called rcar-gyroadc. + +config RN5T618_ADC + tristate "ADC for the RN5T618/RC5T619 family of chips" + depends on MFD_RN5T618 + help + Say yes here to build support for the integrated ADC inside the + RN5T618/619 series PMICs: + + This driver can also be built as a module. If so, the module + will be called rn5t618-adc. + +config ROCKCHIP_SARADC + tristate "Rockchip SARADC driver" + depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on RESET_CONTROLLER + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for the SARADC found in SoCs from + Rockchip. + + To compile this driver as a module, choose M here: the + module will be called rockchip_saradc. + +config SC27XX_ADC + tristate "Spreadtrum SC27xx series PMICs ADC" + depends on MFD_SC27XX_PMIC || COMPILE_TEST + help + Say yes here to build support for the integrated ADC inside the + Spreadtrum SC27xx series PMICs. + + This driver can also be built as a module. If so, the module + will be called sc27xx_adc. + +config SPEAR_ADC + tristate "ST SPEAr ADC" + depends on PLAT_SPEAR || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for the integrated ADC inside the + ST SPEAr SoC. Provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called spear_adc. + +config SD_ADC_MODULATOR + tristate "Generic sigma delta modulator" + depends on OF + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Select this option to enables sigma delta modulator. This driver can + support generic sigma delta modulators. + + This driver can also be built as a module. If so, the module + will be called sd_adc_modulator. + +config STM32_ADC_CORE + tristate "STMicroelectronics STM32 adc core" + depends on ARCH_STM32 || COMPILE_TEST + depends on OF + depends on REGULATOR + depends on HAS_IOMEM + select IIO_BUFFER + select MFD_STM32_TIMERS + select IIO_STM32_TIMER_TRIGGER + select IIO_TRIGGERED_BUFFER + help + Select this option to enable the core driver for STMicroelectronics + STM32 analog-to-digital converter (ADC). + + This driver can also be built as a module. If so, the module + will be called stm32-adc-core. + +config STM32_ADC + tristate "STMicroelectronics STM32 adc" + depends on STM32_ADC_CORE + help + Say yes here to build support for STMicroelectronics stm32 Analog + to Digital Converter (ADC). + + This driver can also be built as a module. If so, the module + will be called stm32-adc. + +config STM32_DFSDM_CORE + tristate "STMicroelectronics STM32 DFSDM core" + depends on (ARCH_STM32 && OF) || COMPILE_TEST + select REGMAP + select REGMAP_MMIO + help + Select this option to enable the driver for STMicroelectronics + STM32 digital filter for sigma delta converter. + + This driver can also be built as a module. If so, the module + will be called stm32-dfsdm-core. + +config STM32_DFSDM_ADC + tristate "STMicroelectronics STM32 dfsdm adc" + 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. + + 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 + select ISA_BUS_API + select GPIOLIB + help + Say yes here to build support for the Apex Embedded Systems STX104 + integrated analog PC/104 card. + + This driver supports the 16 channels of single-ended (8 channels of + differential) analog inputs, 2 channels of analog output, 4 digital + inputs, and 4 digital outputs provided by the STX104. + + The base port addresses for the devices may be configured via the base + array module parameter. + +config SUN4I_GPADC + tristate "Support for the Allwinner SoCs GPADC" + depends on IIO + depends on MFD_SUN4I_GPADC || MACH_SUN8I + depends on THERMAL || !THERMAL_OF + select REGMAP_IRQ + help + Say yes here to build support for Allwinner (A10, A13 and A31) SoCs + GPADC. This ADC provides 4 channels which can be used as an ADC or as + a touchscreen input and one channel for thermal sensor. + + The thermal sensor slows down ADC readings and can be disabled by + disabling CONFIG_THERMAL_OF. However, the thermal sensor should be + enabled by default since the SoC temperature is usually more critical + than ADC readings. + + To compile this driver as a module, choose M here: the module will be + called sun4i-gpadc-iio. + +config TI_ADC081C + tristate "Texas Instruments ADC081C/ADC101C/ADC121C family" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC081C, + ADC101C and ADC121C ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc081c. + +config TI_ADC0832 + tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC0831, + ADC0832, ADC0834, ADC0838 ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc0832. + +config TI_ADC084S021 + tristate "Texas Instruments ADC084S021" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC084S021 + chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc084s021. + +config TI_ADC12138 + tristate "Texas Instruments ADC12130/ADC12132/ADC12138" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC12130, + ADC12132 and ADC12138 chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc12138. + +config TI_ADC108S102 + tristate "Texas Instruments ADC108S102 and ADC128S102 driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Texas Instruments ADC108S102 and + ADC128S102 ADC. + + To compile this driver as a module, choose M here: the module will + be called ti-adc108s102. + +config TI_ADC128S052 + tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021" + depends on SPI + help + If you say yes here you get support for Texas Instruments ADC128S052, + ADC122S021 and ADC124S021 chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc128s052. + +config TI_ADC161S626 + tristate "Texas Instruments ADC161S626 1-channel differential ADC" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC141S626, + and ADC161S626 chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc161s626. + +config TI_ADS1015 + tristate "Texas Instruments ADS1015 ADC" + depends on I2C + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADS1015 + ADC chip. + + This driver can also be built as a module. If so, the module will be + called ti-ads1015. + +config TI_ADS7950 + tristate "Texas Instruments ADS7950 ADC driver" + depends on SPI && GPIOLIB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Texas Instruments ADS7950, ADS7951, + ADS7952, ADS7953, ADS7954, ADS7955, ADS7956, ADS7957, ADS7958, ADS7959. + ADS7960, ADS7961. + + 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 + help + If you say yes here you get support for Texas Instruments ADS8684 and + and ADS8688 ADC chips + + 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 + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Say yes here to build support for Texas Instruments ADC + driver which is also a MFD client. + + To compile this driver as a module, choose M here: the module will be + called ti_am335x_adc. + +config TI_TLC4541 + tristate "Texas Instruments TLC4541 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Texas Instruments TLC4541 / TLC3541 + ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-tlc4541. + +config TWL4030_MADC + tristate "TWL4030 MADC (Monitoring A/D Converter)" + depends on TWL4030_CORE + help + This driver provides support for Triton TWL4030-MADC. The + driver supports both RT and SW conversion methods. + + This driver can also be built as a module. If so, the module will be + called twl4030-madc. + +config TWL6030_GPADC + tristate "TWL6030 GPADC (General Purpose A/D Converter) Support" + depends on TWL4030_CORE + default n + help + Say yes here if you want support for the TWL6030/TWL6032 General + Purpose A/D Converter. This will add support for battery type + detection, battery voltage and temperature measurement, die + temperature measurement, system supply voltage, audio accessory, + USB ID detection. + + This driver can also be built as a module. If so, the module will be + called twl6030-gpadc. + +config VF610_ADC + tristate "Freescale vf610 ADC driver" + depends on OF + depends on HAS_IOMEM + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to support for Vybrid board analog-to-digital converter. + Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX. + + This driver can also be built as a module. If so, the module will be + called vf610_adc. + +config VIPERBOARD_ADC + tristate "Viperboard ADC support" + depends on MFD_VIPERBOARD && USB + help + Say yes here to access the ADC part of the Nano River + Technologies Viperboard. + + To compile this driver as a module, choose M here: the module will be + called viperboard_adc. + +config XILINX_XADC + tristate "Xilinx XADC driver" + depends on HAS_IOMEM + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to have support for the Xilinx XADC. The driver does support + both the ZYNQ interface to the XADC as well as the AXI-XADC interface. + + The driver can also be build as a module. If so, the module will be called + xilinx-xadc. + +endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile new file mode 100644 index 000000000..90f94ada7 --- /dev/null +++ b/drivers/iio/adc/Makefile @@ -0,0 +1,113 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for IIO ADC drivers +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o +obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o +obj-$(CONFIG_AD7091R5) += ad7091r5.o ad7091r-base.o +obj-$(CONFIG_AD7124) += ad7124.o +obj-$(CONFIG_AD7192) += ad7192.o +obj-$(CONFIG_AD7266) += ad7266.o +obj-$(CONFIG_AD7291) += ad7291.o +obj-$(CONFIG_AD7292) += ad7292.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_AD9467) += ad9467.o +obj-$(CONFIG_ADI_AXI_ADC) += adi-axi-adc.o +obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o +obj-$(CONFIG_AT91_ADC) += at91_adc.o +obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o +obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o +obj-$(CONFIG_AXP288_ADC) += axp288_adc.o +obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o +obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o +obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o +obj-$(CONFIG_CPCAP_ADC) += cpcap-adc.o +obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o +obj-$(CONFIG_DLN2_ADC) += dln2-adc.o +obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o +obj-$(CONFIG_EP93XX_ADC) += ep93xx_adc.o +obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o +obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o +obj-$(CONFIG_HI8435) += hi8435.o +obj-$(CONFIG_HX711) += hx711.o +obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o +obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o +obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o +obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o +obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o +obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o +obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o +obj-$(CONFIG_LTC2471) += ltc2471.o +obj-$(CONFIG_LTC2485) += ltc2485.o +obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o +obj-$(CONFIG_LTC2497) += ltc2497.o ltc2497-core.o +obj-$(CONFIG_MAX1027) += max1027.o +obj-$(CONFIG_MAX11100) += max11100.o +obj-$(CONFIG_MAX1118) += max1118.o +obj-$(CONFIG_MAX1241) += max1241.o +obj-$(CONFIG_MAX1363) += max1363.o +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_MP2629_ADC) += mp2629_adc.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 +obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o +obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o +obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o +obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o +obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o +obj-$(CONFIG_SPEAR_ADC) += spear_adc.o +obj-$(CONFIG_STX104) += stx104.o +obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o +obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o +obj-$(CONFIG_STM32_ADC) += stm32-adc.o +obj-$(CONFIG_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 +obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o +obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o +obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o +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 +obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o +obj-$(CONFIG_VF610_ADC) += vf610_adc.o +obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o +xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o +obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o +obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c new file mode 100644 index 000000000..8d8150528 --- /dev/null +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -0,0 +1,1216 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Arun R Murthy <arun.murthy@stericsson.com> + * Author: Daniel Willerud <daniel.willerud@stericsson.com> + * Author: Johan Palsson <johan.palsson@stericsson.com> + * Author: M'boumba Cedric Madianga + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * AB8500 General Purpose ADC driver. The AB8500 uses reference voltages: + * VinVADC, and VADC relative to GND to do its job. It monitors main and backup + * battery voltages, AC (mains) voltage, USB cable voltage, as well as voltages + * representing the temperature of the chip die and battery, accessory + * detection by resistance measurements using relative voltages and GSM burst + * information. + * + * Some of the voltages are measured on external pins on the IC, such as + * battery temperature or "ADC aux" 1 and 2. Other voltages are internal rails + * from other parts of the ASIC such as main charger voltage, main and battery + * backup voltage or USB VBUS voltage. For this reason drivers for other + * parts of the system are required to obtain handles to the ADC to do work + * for them and the IIO driver provides arbitration among these consumers. + */ +#include <linux/init.h> +#include <linux/bits.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/pm_runtime.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/regulator/consumer.h> +#include <linux/random.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/mfd/abx500.h> +#include <linux/mfd/abx500/ab8500.h> + +/* GPADC register offsets and bit definitions */ + +#define AB8500_GPADC_CTRL1_REG 0x00 +/* GPADC control register 1 bits */ +#define AB8500_GPADC_CTRL1_DISABLE 0x00 +#define AB8500_GPADC_CTRL1_ENABLE BIT(0) +#define AB8500_GPADC_CTRL1_TRIG_ENA BIT(1) +#define AB8500_GPADC_CTRL1_START_SW_CONV BIT(2) +#define AB8500_GPADC_CTRL1_BTEMP_PULL_UP BIT(3) +/* 0 = use rising edge, 1 = use falling edge */ +#define AB8500_GPADC_CTRL1_TRIG_EDGE BIT(4) +/* 0 = use VTVOUT, 1 = use VRTC as pull-up supply for battery temp NTC */ +#define AB8500_GPADC_CTRL1_PUPSUPSEL BIT(5) +#define AB8500_GPADC_CTRL1_BUF_ENA BIT(6) +#define AB8500_GPADC_CTRL1_ICHAR_ENA BIT(7) + +#define AB8500_GPADC_CTRL2_REG 0x01 +#define AB8500_GPADC_CTRL3_REG 0x02 +/* + * GPADC control register 2 and 3 bits + * the bit layout is the same for SW and HW conversion set-up + */ +#define AB8500_GPADC_CTRL2_AVG_1 0x00 +#define AB8500_GPADC_CTRL2_AVG_4 BIT(5) +#define AB8500_GPADC_CTRL2_AVG_8 BIT(6) +#define AB8500_GPADC_CTRL2_AVG_16 (BIT(5) | BIT(6)) + +enum ab8500_gpadc_channel { + AB8500_GPADC_CHAN_UNUSED = 0x00, + AB8500_GPADC_CHAN_BAT_CTRL = 0x01, + AB8500_GPADC_CHAN_BAT_TEMP = 0x02, + /* This is not used on AB8505 */ + AB8500_GPADC_CHAN_MAIN_CHARGER = 0x03, + AB8500_GPADC_CHAN_ACC_DET_1 = 0x04, + AB8500_GPADC_CHAN_ACC_DET_2 = 0x05, + AB8500_GPADC_CHAN_ADC_AUX_1 = 0x06, + AB8500_GPADC_CHAN_ADC_AUX_2 = 0x07, + AB8500_GPADC_CHAN_VBAT_A = 0x08, + AB8500_GPADC_CHAN_VBUS = 0x09, + AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT = 0x0a, + AB8500_GPADC_CHAN_USB_CHARGER_CURRENT = 0x0b, + AB8500_GPADC_CHAN_BACKUP_BAT = 0x0c, + /* Only on AB8505 */ + AB8505_GPADC_CHAN_DIE_TEMP = 0x0d, + AB8500_GPADC_CHAN_ID = 0x0e, + AB8500_GPADC_CHAN_INTERNAL_TEST_1 = 0x0f, + AB8500_GPADC_CHAN_INTERNAL_TEST_2 = 0x10, + AB8500_GPADC_CHAN_INTERNAL_TEST_3 = 0x11, + /* FIXME: Applicable to all ASIC variants? */ + AB8500_GPADC_CHAN_XTAL_TEMP = 0x12, + AB8500_GPADC_CHAN_VBAT_TRUE_MEAS = 0x13, + /* FIXME: Doesn't seem to work with pure AB8500 */ + AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT = 0x1c, + AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT = 0x1d, + AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT = 0x1e, + AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT = 0x1f, + /* + * Virtual channel used only for ibat conversion to ampere. + * Battery current conversion (ibat) cannot be requested as a + * single conversion but it is always requested in combination + * with other input requests. + */ + AB8500_GPADC_CHAN_IBAT_VIRTUAL = 0xFF, +}; + +#define AB8500_GPADC_AUTO_TIMER_REG 0x03 + +#define AB8500_GPADC_STAT_REG 0x04 +#define AB8500_GPADC_STAT_BUSY BIT(0) + +#define AB8500_GPADC_MANDATAL_REG 0x05 +#define AB8500_GPADC_MANDATAH_REG 0x06 +#define AB8500_GPADC_AUTODATAL_REG 0x07 +#define AB8500_GPADC_AUTODATAH_REG 0x08 +#define AB8500_GPADC_MUX_CTRL_REG 0x09 +#define AB8540_GPADC_MANDATA2L_REG 0x09 +#define AB8540_GPADC_MANDATA2H_REG 0x0A +#define AB8540_GPADC_APEAAX_REG 0x10 +#define AB8540_GPADC_APEAAT_REG 0x11 +#define AB8540_GPADC_APEAAM_REG 0x12 +#define AB8540_GPADC_APEAAH_REG 0x13 +#define AB8540_GPADC_APEAAL_REG 0x14 + +/* + * OTP register offsets + * Bank : 0x15 + */ +#define AB8500_GPADC_CAL_1 0x0F +#define AB8500_GPADC_CAL_2 0x10 +#define AB8500_GPADC_CAL_3 0x11 +#define AB8500_GPADC_CAL_4 0x12 +#define AB8500_GPADC_CAL_5 0x13 +#define AB8500_GPADC_CAL_6 0x14 +#define AB8500_GPADC_CAL_7 0x15 +/* New calibration for 8540 */ +#define AB8540_GPADC_OTP4_REG_7 0x38 +#define AB8540_GPADC_OTP4_REG_6 0x39 +#define AB8540_GPADC_OTP4_REG_5 0x3A + +#define AB8540_GPADC_DIS_ZERO 0x00 +#define AB8540_GPADC_EN_VBIAS_XTAL_TEMP 0x02 + +/* GPADC constants from AB8500 spec, UM0836 */ +#define AB8500_ADC_RESOLUTION 1024 +#define AB8500_ADC_CH_BTEMP_MIN 0 +#define AB8500_ADC_CH_BTEMP_MAX 1350 +#define AB8500_ADC_CH_DIETEMP_MIN 0 +#define AB8500_ADC_CH_DIETEMP_MAX 1350 +#define AB8500_ADC_CH_CHG_V_MIN 0 +#define AB8500_ADC_CH_CHG_V_MAX 20030 +#define AB8500_ADC_CH_ACCDET2_MIN 0 +#define AB8500_ADC_CH_ACCDET2_MAX 2500 +#define AB8500_ADC_CH_VBAT_MIN 2300 +#define AB8500_ADC_CH_VBAT_MAX 4800 +#define AB8500_ADC_CH_CHG_I_MIN 0 +#define AB8500_ADC_CH_CHG_I_MAX 1500 +#define AB8500_ADC_CH_BKBAT_MIN 0 +#define AB8500_ADC_CH_BKBAT_MAX 3200 + +/* GPADC constants from AB8540 spec */ +#define AB8500_ADC_CH_IBAT_MIN (-6000) /* mA range measured by ADC for ibat */ +#define AB8500_ADC_CH_IBAT_MAX 6000 +#define AB8500_ADC_CH_IBAT_MIN_V (-60) /* mV range measured by ADC for ibat */ +#define AB8500_ADC_CH_IBAT_MAX_V 60 +#define AB8500_GPADC_IBAT_VDROP_L (-56) /* mV */ +#define AB8500_GPADC_IBAT_VDROP_H 56 + +/* This is used to not lose precision when dividing to get gain and offset */ +#define AB8500_GPADC_CALIB_SCALE 1000 +/* + * Number of bits shift used to not lose precision + * when dividing to get ibat gain. + */ +#define AB8500_GPADC_CALIB_SHIFT_IBAT 20 + +/* Time in ms before disabling regulator */ +#define AB8500_GPADC_AUTOSUSPEND_DELAY 1 + +#define AB8500_GPADC_CONVERSION_TIME 500 /* ms */ + +enum ab8500_cal_channels { + AB8500_CAL_VMAIN = 0, + AB8500_CAL_BTEMP, + AB8500_CAL_VBAT, + AB8500_CAL_IBAT, + AB8500_CAL_NR, +}; + +/** + * struct ab8500_adc_cal_data - Table for storing gain and offset for the + * calibrated ADC channels + * @gain: Gain of the ADC channel + * @offset: Offset of the ADC channel + * @otp_calib_hi: Calibration from OTP + * @otp_calib_lo: Calibration from OTP + */ +struct ab8500_adc_cal_data { + s64 gain; + s64 offset; + u16 otp_calib_hi; + u16 otp_calib_lo; +}; + +/** + * struct ab8500_gpadc_chan_info - per-channel GPADC info + * @name: name of the channel + * @id: the internal AB8500 ID number for the channel + * @hardware_control: indicate that we want to use hardware ADC control + * on this channel, the default is software ADC control. Hardware control + * is normally only used to test the battery voltage during GSM bursts + * and needs a hardware trigger on the GPADCTrig pin of the ASIC. + * @falling_edge: indicate that we want to trigger on falling edge + * rather than rising edge, rising edge is the default + * @avg_sample: how many samples to average: must be 1, 4, 8 or 16. + * @trig_timer: how long to wait for the trigger, in 32kHz periods: + * 0 .. 255 periods + */ +struct ab8500_gpadc_chan_info { + const char *name; + u8 id; + bool hardware_control; + bool falling_edge; + u8 avg_sample; + u8 trig_timer; +}; + +/** + * struct ab8500_gpadc - AB8500 GPADC device information + * @dev: pointer to the containing device + * @ab8500: pointer to the parent AB8500 device + * @chans: internal per-channel information container + * @nchans: number of channels + * @complete: pointer to the completion that indicates + * the completion of an gpadc conversion cycle + * @vddadc: pointer to the regulator supplying VDDADC + * @irq_sw: interrupt number that is used by gpadc for software ADC conversion + * @irq_hw: interrupt number that is used by gpadc for hardware ADC conversion + * @cal_data: array of ADC calibration data structs + */ +struct ab8500_gpadc { + struct device *dev; + struct ab8500 *ab8500; + struct ab8500_gpadc_chan_info *chans; + unsigned int nchans; + struct completion complete; + struct regulator *vddadc; + int irq_sw; + int irq_hw; + struct ab8500_adc_cal_data cal_data[AB8500_CAL_NR]; +}; + +static struct ab8500_gpadc_chan_info * +ab8500_gpadc_get_channel(struct ab8500_gpadc *gpadc, u8 chan) +{ + struct ab8500_gpadc_chan_info *ch; + int i; + + for (i = 0; i < gpadc->nchans; i++) { + ch = &gpadc->chans[i]; + if (ch->id == chan) + break; + } + if (i == gpadc->nchans) + return NULL; + + return ch; +} + +/** + * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage + * @gpadc: GPADC instance + * @ch: the sampled channel this raw value is coming from + * @ad_value: the raw value + */ +static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, + enum ab8500_gpadc_channel ch, + int ad_value) +{ + int res; + + switch (ch) { + case AB8500_GPADC_CHAN_MAIN_CHARGER: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_VMAIN].gain) { + res = AB8500_ADC_CH_CHG_V_MIN + (AB8500_ADC_CH_CHG_V_MAX - + AB8500_ADC_CH_CHG_V_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VMAIN].gain + + gpadc->cal_data[AB8500_CAL_VMAIN].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8500_GPADC_CHAN_BAT_CTRL: + case AB8500_GPADC_CHAN_BAT_TEMP: + case AB8500_GPADC_CHAN_ACC_DET_1: + case AB8500_GPADC_CHAN_ADC_AUX_1: + case AB8500_GPADC_CHAN_ADC_AUX_2: + case AB8500_GPADC_CHAN_XTAL_TEMP: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_BTEMP].gain) { + res = AB8500_ADC_CH_BTEMP_MIN + (AB8500_ADC_CH_BTEMP_MAX - + AB8500_ADC_CH_BTEMP_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_BTEMP].gain + + gpadc->cal_data[AB8500_CAL_BTEMP].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8500_GPADC_CHAN_VBAT_A: + case AB8500_GPADC_CHAN_VBAT_TRUE_MEAS: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_VBAT].gain) { + res = AB8500_ADC_CH_VBAT_MIN + (AB8500_ADC_CH_VBAT_MAX - + AB8500_ADC_CH_VBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_VBAT].gain + + gpadc->cal_data[AB8500_CAL_VBAT].offset) / AB8500_GPADC_CALIB_SCALE; + break; + + case AB8505_GPADC_CHAN_DIE_TEMP: + res = AB8500_ADC_CH_DIETEMP_MIN + + (AB8500_ADC_CH_DIETEMP_MAX - AB8500_ADC_CH_DIETEMP_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_ACC_DET_2: + res = AB8500_ADC_CH_ACCDET2_MIN + + (AB8500_ADC_CH_ACCDET2_MAX - AB8500_ADC_CH_ACCDET2_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_VBUS: + res = AB8500_ADC_CH_CHG_V_MIN + + (AB8500_ADC_CH_CHG_V_MAX - AB8500_ADC_CH_CHG_V_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT: + case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT: + res = AB8500_ADC_CH_CHG_I_MIN + + (AB8500_ADC_CH_CHG_I_MAX - AB8500_ADC_CH_CHG_I_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_BACKUP_BAT: + res = AB8500_ADC_CH_BKBAT_MIN + + (AB8500_ADC_CH_BKBAT_MAX - AB8500_ADC_CH_BKBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + + case AB8500_GPADC_CHAN_IBAT_VIRTUAL: + /* No calibration data available: just interpolate */ + if (!gpadc->cal_data[AB8500_CAL_IBAT].gain) { + res = AB8500_ADC_CH_IBAT_MIN + (AB8500_ADC_CH_IBAT_MAX - + AB8500_ADC_CH_IBAT_MIN) * ad_value / + AB8500_ADC_RESOLUTION; + break; + } + /* Here we can use calibration */ + res = (int) (ad_value * gpadc->cal_data[AB8500_CAL_IBAT].gain + + gpadc->cal_data[AB8500_CAL_IBAT].offset) + >> AB8500_GPADC_CALIB_SHIFT_IBAT; + break; + + default: + dev_err(gpadc->dev, + "unknown channel ID: %d, not possible to convert\n", + ch); + res = -EINVAL; + break; + + } + + return res; +} + +static int ab8500_gpadc_read(struct ab8500_gpadc *gpadc, + const struct ab8500_gpadc_chan_info *ch, + int *ibat) +{ + int ret; + int looplimit = 0; + unsigned long completion_timeout; + u8 val; + u8 low_data, high_data, low_data2, high_data2; + u8 ctrl1; + u8 ctrl23; + unsigned int delay_min = 0; + unsigned int delay_max = 0; + u8 data_low_addr, data_high_addr; + + if (!gpadc) + return -ENODEV; + + /* check if conversion is supported */ + if ((gpadc->irq_sw <= 0) && !ch->hardware_control) + return -ENOTSUPP; + if ((gpadc->irq_hw <= 0) && ch->hardware_control) + return -ENOTSUPP; + + /* Enable vddadc by grabbing PM runtime */ + pm_runtime_get_sync(gpadc->dev); + + /* Check if ADC is not busy, lock and proceed */ + do { + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_STAT_REG, &val); + if (ret < 0) + goto out; + if (!(val & AB8500_GPADC_STAT_BUSY)) + break; + msleep(20); + } while (++looplimit < 10); + if (looplimit >= 10 && (val & AB8500_GPADC_STAT_BUSY)) { + dev_err(gpadc->dev, "gpadc_conversion: GPADC busy"); + ret = -EINVAL; + goto out; + } + + /* Enable GPADC */ + ctrl1 = AB8500_GPADC_CTRL1_ENABLE; + + /* Select the channel source and set average samples */ + switch (ch->avg_sample) { + case 1: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_1; + break; + case 4: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_4; + break; + case 8: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_8; + break; + default: + ctrl23 = ch->id | AB8500_GPADC_CTRL2_AVG_16; + break; + } + + if (ch->hardware_control) { + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL3_REG, ctrl23); + ctrl1 |= AB8500_GPADC_CTRL1_TRIG_ENA; + if (ch->falling_edge) + ctrl1 |= AB8500_GPADC_CTRL1_TRIG_EDGE; + } else { + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL2_REG, ctrl23); + } + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: set avg samples failed\n"); + goto out; + } + + /* + * Enable ADC, buffering, select rising edge and enable ADC path + * charging current sense if it needed, ABB 3.0 needs some special + * treatment too. + */ + switch (ch->id) { + case AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT: + case AB8500_GPADC_CHAN_USB_CHARGER_CURRENT: + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA | + AB8500_GPADC_CTRL1_ICHAR_ENA; + break; + case AB8500_GPADC_CHAN_BAT_TEMP: + if (!is_ab8500_2p0_or_earlier(gpadc->ab8500)) { + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA | + AB8500_GPADC_CTRL1_BTEMP_PULL_UP; + /* + * Delay might be needed for ABB8500 cut 3.0, if not, + * remove when hardware will be available + */ + delay_min = 1000; /* Delay in micro seconds */ + delay_max = 10000; /* large range optimises sleepmode */ + break; + } + fallthrough; + default: + ctrl1 |= AB8500_GPADC_CTRL1_BUF_ENA; + break; + } + + /* Write configuration to control register 1 */ + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ctrl1); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: set Control register failed\n"); + goto out; + } + + if (delay_min != 0) + usleep_range(delay_min, delay_max); + + if (ch->hardware_control) { + /* Set trigger delay timer */ + ret = abx500_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, + ch->trig_timer); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: trig timer failed\n"); + goto out; + } + completion_timeout = 2 * HZ; + data_low_addr = AB8500_GPADC_AUTODATAL_REG; + data_high_addr = AB8500_GPADC_AUTODATAH_REG; + } else { + /* Start SW conversion */ + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + AB8500_GPADC_CTRL1_START_SW_CONV, + AB8500_GPADC_CTRL1_START_SW_CONV); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: start s/w conv failed\n"); + goto out; + } + completion_timeout = msecs_to_jiffies(AB8500_GPADC_CONVERSION_TIME); + data_low_addr = AB8500_GPADC_MANDATAL_REG; + data_high_addr = AB8500_GPADC_MANDATAH_REG; + } + + /* Wait for completion of conversion */ + if (!wait_for_completion_timeout(&gpadc->complete, + completion_timeout)) { + dev_err(gpadc->dev, + "timeout didn't receive GPADC conv interrupt\n"); + ret = -EINVAL; + goto out; + } + + /* Read the converted RAW data */ + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, data_low_addr, &low_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read low data failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, data_high_addr, &high_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read high data failed\n"); + goto out; + } + + /* Check if double conversion is required */ + if ((ch->id == AB8500_GPADC_CHAN_BAT_CTRL_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_VBAT_MEAS_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_VBAT_TRUE_MEAS_AND_IBAT) || + (ch->id == AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT)) { + + if (ch->hardware_control) { + /* not supported */ + ret = -ENOTSUPP; + dev_err(gpadc->dev, + "gpadc_conversion: only SW double conversion supported\n"); + goto out; + } else { + /* Read the converted RAW data 2 */ + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8540_GPADC_MANDATA2L_REG, + &low_data2); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw low data 2 failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8540_GPADC_MANDATA2H_REG, + &high_data2); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read sw high data 2 failed\n"); + goto out; + } + if (ibat != NULL) { + *ibat = (high_data2 << 8) | low_data2; + } else { + dev_warn(gpadc->dev, + "gpadc_conversion: ibat not stored\n"); + } + + } + } + + /* Disable GPADC */ + ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE); + if (ret < 0) { + dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n"); + goto out; + } + + /* This eventually drops the regulator */ + pm_runtime_mark_last_busy(gpadc->dev); + pm_runtime_put_autosuspend(gpadc->dev); + + return (high_data << 8) | low_data; + +out: + /* + * It has shown to be needed to turn off the GPADC if an error occurs, + * otherwise we might have problem when waiting for the busy bit in the + * GPADC status register to go low. In V1.1 there wait_for_completion + * seems to timeout when waiting for an interrupt.. Not seen in V2.0 + */ + (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, AB8500_GPADC_CTRL1_DISABLE); + pm_runtime_put(gpadc->dev); + dev_err(gpadc->dev, + "gpadc_conversion: Failed to AD convert channel %d\n", ch->id); + + return ret; +} + +/** + * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion + * @irq: irq number + * @data: pointer to the data passed during request irq + * + * This is a interrupt service routine for gpadc conversion completion. + * Notifies the gpadc completion is completed and the converted raw value + * can be read from the registers. + * Returns IRQ status(IRQ_HANDLED) + */ +static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *data) +{ + struct ab8500_gpadc *gpadc = data; + + complete(&gpadc->complete); + + return IRQ_HANDLED; +} + +static int otp_cal_regs[] = { + AB8500_GPADC_CAL_1, + AB8500_GPADC_CAL_2, + AB8500_GPADC_CAL_3, + AB8500_GPADC_CAL_4, + AB8500_GPADC_CAL_5, + AB8500_GPADC_CAL_6, + AB8500_GPADC_CAL_7, +}; + +static int otp4_cal_regs[] = { + AB8540_GPADC_OTP4_REG_7, + AB8540_GPADC_OTP4_REG_6, + AB8540_GPADC_OTP4_REG_5, +}; + +static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) +{ + int i; + int ret[ARRAY_SIZE(otp_cal_regs)]; + u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)]; + int ret_otp4[ARRAY_SIZE(otp4_cal_regs)]; + u8 gpadc_otp4[ARRAY_SIZE(otp4_cal_regs)]; + int vmain_high, vmain_low; + int btemp_high, btemp_low; + int vbat_high, vbat_low; + int ibat_high, ibat_low; + s64 V_gain, V_offset, V2A_gain, V2A_offset; + + /* First we read all OTP registers and store the error code */ + for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) { + ret[i] = abx500_get_register_interruptible(gpadc->dev, + AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]); + if (ret[i] < 0) { + /* Continue anyway: maybe the other registers are OK */ + dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n", + __func__, otp_cal_regs[i]); + } else { + /* Put this in the entropy pool as device-unique */ + add_device_randomness(&ret[i], sizeof(ret[i])); + } + } + + /* + * The ADC calibration data is stored in OTP registers. + * The layout of the calibration data is outlined below and a more + * detailed description can be found in UM0836 + * + * vm_h/l = vmain_high/low + * bt_h/l = btemp_high/low + * vb_h/l = vbat_high/low + * + * Data bits 8500/9540: + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h9 | vm_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | + * |.......|.......|.......|.......|.......|.......|.......|....... + * + * Data bits 8540: + * OTP2 + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h9 | vm_h8 | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | + * |.......|.......|.......|.......|.......|.......|.......|....... + * + * Data bits 8540: + * OTP4 + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | ib_h9 | ib_h8 | ib_h7 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | ib_h6 | ib_h5 | ib_h4 | ib_h3 | ib_h2 | ib_h1 | ib_h0 | ib_l5 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | ib_l4 | ib_l3 | ib_l2 | ib_l1 | ib_l0 | + * + * + * Ideal output ADC codes corresponding to injected input voltages + * during manufacturing is: + * + * vmain_high: Vin = 19500mV / ADC ideal code = 997 + * vmain_low: Vin = 315mV / ADC ideal code = 16 + * btemp_high: Vin = 1300mV / ADC ideal code = 985 + * btemp_low: Vin = 21mV / ADC ideal code = 16 + * vbat_high: Vin = 4700mV / ADC ideal code = 982 + * vbat_low: Vin = 2380mV / ADC ideal code = 33 + */ + + if (is_ab8540(gpadc->ab8500)) { + /* Calculate gain and offset for VMAIN if all reads succeeded*/ + if (!(ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[1] & 0xFF) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_hi = + (u16)vmain_high; + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo = + (u16)vmain_low; + + gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE * + 19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[AB8500_CAL_VMAIN].gain = 0; + } + + /* Read IBAT calibration Data */ + for (i = 0; i < ARRAY_SIZE(otp4_cal_regs); i++) { + ret_otp4[i] = abx500_get_register_interruptible( + gpadc->dev, AB8500_OTP_EMUL, + otp4_cal_regs[i], &gpadc_otp4[i]); + if (ret_otp4[i] < 0) + dev_err(gpadc->dev, + "%s: read otp4 reg 0x%02x failed\n", + __func__, otp4_cal_regs[i]); + } + + /* Calculate gain and offset for IBAT if all reads succeeded */ + if (!(ret_otp4[0] < 0 || ret_otp4[1] < 0 || ret_otp4[2] < 0)) { + ibat_high = (((gpadc_otp4[0] & 0x07) << 7) | + ((gpadc_otp4[1] & 0xFE) >> 1)); + ibat_low = (((gpadc_otp4[1] & 0x01) << 5) | + ((gpadc_otp4[2] & 0xF8) >> 3)); + + gpadc->cal_data[AB8500_CAL_IBAT].otp_calib_hi = + (u16)ibat_high; + gpadc->cal_data[AB8500_CAL_IBAT].otp_calib_lo = + (u16)ibat_low; + + V_gain = ((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L) + << AB8500_GPADC_CALIB_SHIFT_IBAT) / (ibat_high - ibat_low); + + V_offset = (AB8500_GPADC_IBAT_VDROP_H << AB8500_GPADC_CALIB_SHIFT_IBAT) - + (((AB8500_GPADC_IBAT_VDROP_H - AB8500_GPADC_IBAT_VDROP_L) << + AB8500_GPADC_CALIB_SHIFT_IBAT) / (ibat_high - ibat_low)) + * ibat_high; + /* + * Result obtained is in mV (at a scale factor), + * we need to calculate gain and offset to get mA + */ + V2A_gain = (AB8500_ADC_CH_IBAT_MAX - AB8500_ADC_CH_IBAT_MIN)/ + (AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V); + V2A_offset = ((AB8500_ADC_CH_IBAT_MAX_V * AB8500_ADC_CH_IBAT_MIN - + AB8500_ADC_CH_IBAT_MAX * AB8500_ADC_CH_IBAT_MIN_V) + << AB8500_GPADC_CALIB_SHIFT_IBAT) + / (AB8500_ADC_CH_IBAT_MAX_V - AB8500_ADC_CH_IBAT_MIN_V); + + gpadc->cal_data[AB8500_CAL_IBAT].gain = + V_gain * V2A_gain; + gpadc->cal_data[AB8500_CAL_IBAT].offset = + V_offset * V2A_gain + V2A_offset; + } else { + gpadc->cal_data[AB8500_CAL_IBAT].gain = 0; + } + } else { + /* Calculate gain and offset for VMAIN if all reads succeeded */ + if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[0] & 0x03) << 8) | + ((gpadc_cal[1] & 0x3F) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_hi = + (u16)vmain_high; + gpadc->cal_data[AB8500_CAL_VMAIN].otp_calib_lo = + (u16)vmain_low; + + gpadc->cal_data[AB8500_CAL_VMAIN].gain = AB8500_GPADC_CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + + gpadc->cal_data[AB8500_CAL_VMAIN].offset = AB8500_GPADC_CALIB_SCALE * + 19500 - (AB8500_GPADC_CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[AB8500_CAL_VMAIN].gain = 0; + } + } + + /* Calculate gain and offset for BTEMP if all reads succeeded */ + if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) { + btemp_high = (((gpadc_cal[2] & 0x01) << 9) | + (gpadc_cal[3] << 1) | ((gpadc_cal[4] & 0x80) >> 7)); + btemp_low = ((gpadc_cal[4] & 0x7C) >> 2); + + gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_hi = (u16)btemp_high; + gpadc->cal_data[AB8500_CAL_BTEMP].otp_calib_lo = (u16)btemp_low; + + gpadc->cal_data[AB8500_CAL_BTEMP].gain = + AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low); + gpadc->cal_data[AB8500_CAL_BTEMP].offset = AB8500_GPADC_CALIB_SCALE * 1300 - + (AB8500_GPADC_CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low)) + * btemp_high; + } else { + gpadc->cal_data[AB8500_CAL_BTEMP].gain = 0; + } + + /* Calculate gain and offset for VBAT if all reads succeeded */ + if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) { + vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]); + vbat_low = ((gpadc_cal[6] & 0xFC) >> 2); + + gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_hi = (u16)vbat_high; + gpadc->cal_data[AB8500_CAL_VBAT].otp_calib_lo = (u16)vbat_low; + + gpadc->cal_data[AB8500_CAL_VBAT].gain = AB8500_GPADC_CALIB_SCALE * + (4700 - 2380) / (vbat_high - vbat_low); + gpadc->cal_data[AB8500_CAL_VBAT].offset = AB8500_GPADC_CALIB_SCALE * 4700 - + (AB8500_GPADC_CALIB_SCALE * (4700 - 2380) / + (vbat_high - vbat_low)) * vbat_high; + } else { + gpadc->cal_data[AB8500_CAL_VBAT].gain = 0; + } +} + +static int ab8500_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + const struct ab8500_gpadc_chan_info *ch; + int raw_val; + int processed; + + ch = ab8500_gpadc_get_channel(gpadc, chan->address); + if (!ch) { + dev_err(gpadc->dev, "no such channel %lu\n", + chan->address); + return -EINVAL; + } + + raw_val = ab8500_gpadc_read(gpadc, ch, NULL); + if (raw_val < 0) + return raw_val; + + if (mask == IIO_CHAN_INFO_RAW) { + *val = raw_val; + return IIO_VAL_INT; + } + + if (mask == IIO_CHAN_INFO_PROCESSED) { + processed = ab8500_gpadc_ad_to_voltage(gpadc, ch->id, raw_val); + if (processed < 0) + return processed; + + /* Return millivolt or milliamps or millicentigrades */ + *val = processed; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ab8500_gpadc_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + int i; + + for (i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info ab8500_gpadc_info = { + .of_xlate = ab8500_gpadc_of_xlate, + .read_raw = ab8500_gpadc_read_raw, +}; + +#ifdef CONFIG_PM +static int ab8500_gpadc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + + regulator_disable(gpadc->vddadc); + + return 0; +} + +static int ab8500_gpadc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(gpadc->vddadc); + if (ret) + dev_err(dev, "Failed to enable vddadc: %d\n", ret); + + return ret; +} +#endif + +/** + * ab8500_gpadc_parse_channel() - process devicetree channel configuration + * @dev: pointer to containing device + * @np: device tree node for the channel to configure + * @ch: channel info to fill in + * @iio_chan: IIO channel specification to fill in + * + * The devicetree will set up the channel for use with the specific device, + * and define usage for things like AUX GPADC inputs more precisely. + */ +static int ab8500_gpadc_parse_channel(struct device *dev, + struct device_node *np, + struct ab8500_gpadc_chan_info *ch, + struct iio_chan_spec *iio_chan) +{ + const char *name = np->name; + u32 chan; + int ret; + + ret = of_property_read_u32(np, "reg", &chan); + if (ret) { + dev_err(dev, "invalid channel number %s\n", name); + return ret; + } + if (chan > AB8500_GPADC_CHAN_BAT_TEMP_AND_IBAT) { + dev_err(dev, "%s channel number out of range %d\n", name, chan); + return -EINVAL; + } + + iio_chan->channel = chan; + iio_chan->datasheet_name = name; + iio_chan->indexed = 1; + iio_chan->address = chan; + iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_PROCESSED); + /* Most are voltages (also temperatures), some are currents */ + if ((chan == AB8500_GPADC_CHAN_MAIN_CHARGER_CURRENT) || + (chan == AB8500_GPADC_CHAN_USB_CHARGER_CURRENT)) + iio_chan->type = IIO_CURRENT; + else + iio_chan->type = IIO_VOLTAGE; + + ch->id = chan; + + /* Sensible defaults */ + ch->avg_sample = 16; + ch->hardware_control = false; + ch->falling_edge = false; + ch->trig_timer = 0; + + return 0; +} + +/** + * ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT + * @gpadc: the GPADC to configure the channels for + * @np: device tree node containing the channel configurations + * @chans: the IIO channels we parsed + * @nchans: the number of IIO channels we parsed + */ +static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc, + struct device_node *np, + struct iio_chan_spec **chans_parsed, + unsigned int *nchans_parsed) +{ + struct device_node *child; + struct ab8500_gpadc_chan_info *ch; + struct iio_chan_spec *iio_chans; + unsigned int nchans; + int i; + + nchans = of_get_available_child_count(np); + if (!nchans) { + dev_err(gpadc->dev, "no channel children\n"); + return -ENODEV; + } + dev_info(gpadc->dev, "found %d ADC channels\n", nchans); + + iio_chans = devm_kcalloc(gpadc->dev, nchans, + sizeof(*iio_chans), GFP_KERNEL); + if (!iio_chans) + return -ENOMEM; + + gpadc->chans = devm_kcalloc(gpadc->dev, nchans, + sizeof(*gpadc->chans), GFP_KERNEL); + if (!gpadc->chans) + return -ENOMEM; + + i = 0; + for_each_available_child_of_node(np, child) { + struct iio_chan_spec *iio_chan; + int ret; + + ch = &gpadc->chans[i]; + iio_chan = &iio_chans[i]; + + ret = ab8500_gpadc_parse_channel(gpadc->dev, child, ch, + iio_chan); + if (ret) { + of_node_put(child); + return ret; + } + i++; + } + gpadc->nchans = nchans; + *chans_parsed = iio_chans; + *nchans_parsed = nchans; + + return 0; +} + +static int ab8500_gpadc_probe(struct platform_device *pdev) +{ + struct ab8500_gpadc *gpadc; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct iio_chan_spec *iio_chans; + unsigned int n_iio_chans; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + gpadc = iio_priv(indio_dev); + + gpadc->dev = dev; + gpadc->ab8500 = dev_get_drvdata(dev->parent); + + ret = ab8500_gpadc_parse_channels(gpadc, np, &iio_chans, &n_iio_chans); + if (ret) + return ret; + + gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END"); + if (gpadc->irq_sw < 0) { + dev_err(dev, "failed to get platform sw_conv_end irq\n"); + return gpadc->irq_sw; + } + + gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END"); + if (gpadc->irq_hw < 0) { + dev_err(dev, "failed to get platform hw_conv_end irq\n"); + return gpadc->irq_hw; + } + + /* Initialize completion used to notify completion of conversion */ + init_completion(&gpadc->complete); + + /* Request interrupts */ + ret = devm_request_threaded_irq(dev, gpadc->irq_sw, NULL, + ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "ab8500-gpadc-sw", gpadc); + if (ret < 0) { + dev_err(dev, + "failed to request sw conversion irq %d\n", + gpadc->irq_sw); + return ret; + } + + ret = devm_request_threaded_irq(dev, gpadc->irq_hw, NULL, + ab8500_bm_gpadcconvend_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "ab8500-gpadc-hw", gpadc); + if (ret < 0) { + dev_err(dev, + "Failed to request hw conversion irq: %d\n", + gpadc->irq_hw); + return ret; + } + + /* The VTVout LDO used to power the AB8500 GPADC */ + gpadc->vddadc = devm_regulator_get(dev, "vddadc"); + if (IS_ERR(gpadc->vddadc)) { + ret = PTR_ERR(gpadc->vddadc); + dev_err(dev, "failed to get vddadc\n"); + return ret; + } + + ret = regulator_enable(gpadc->vddadc); + if (ret) { + dev_err(dev, "failed to enable vddadc: %d\n", ret); + return ret; + } + + /* Enable runtime PM */ + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, AB8500_GPADC_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(dev); + + ab8500_gpadc_read_calibration_data(gpadc); + + pm_runtime_put(dev); + + indio_dev->name = "ab8500-gpadc"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ab8500_gpadc_info; + indio_dev->channels = iio_chans; + indio_dev->num_channels = n_iio_chans; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + goto out_dis_pm; + + return 0; + +out_dis_pm: + pm_runtime_get_sync(dev); + pm_runtime_put_noidle(dev); + pm_runtime_disable(dev); + regulator_disable(gpadc->vddadc); + + return ret; +} + +static int ab8500_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct ab8500_gpadc *gpadc = iio_priv(indio_dev); + + pm_runtime_get_sync(gpadc->dev); + pm_runtime_put_noidle(gpadc->dev); + pm_runtime_disable(gpadc->dev); + regulator_disable(gpadc->vddadc); + + return 0; +} + +static const struct dev_pm_ops ab8500_gpadc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend, + ab8500_gpadc_runtime_resume, + NULL) +}; + +static struct platform_driver ab8500_gpadc_driver = { + .probe = ab8500_gpadc_probe, + .remove = ab8500_gpadc_remove, + .driver = { + .name = "ab8500-gpadc", + .pm = &ab8500_gpadc_pm_ops, + }, +}; +builtin_platform_driver(ab8500_gpadc_driver); diff --git a/drivers/iio/adc/ad7091r-base.c b/drivers/iio/adc/ad7091r-base.c new file mode 100644 index 000000000..811f04448 --- /dev/null +++ b/drivers/iio/adc/ad7091r-base.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7091RX Analog to Digital converter driver + * + * Copyright 2014-2019 Analog Devices Inc. + */ + +#include <linux/bitops.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include "ad7091r-base.h" + +#define AD7091R_REG_RESULT 0 +#define AD7091R_REG_CHANNEL 1 +#define AD7091R_REG_CONF 2 +#define AD7091R_REG_ALERT 3 +#define AD7091R_REG_CH_LOW_LIMIT(ch) ((ch) * 3 + 4) +#define AD7091R_REG_CH_HIGH_LIMIT(ch) ((ch) * 3 + 5) +#define AD7091R_REG_CH_HYSTERESIS(ch) ((ch) * 3 + 6) + +/* AD7091R_REG_RESULT */ +#define AD7091R_REG_RESULT_CH_ID(x) (((x) >> 13) & 0x3) +#define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff) + +/* AD7091R_REG_CONF */ +#define AD7091R_REG_CONF_AUTO BIT(8) +#define AD7091R_REG_CONF_CMD BIT(10) + +#define AD7091R_REG_CONF_MODE_MASK \ + (AD7091R_REG_CONF_AUTO | AD7091R_REG_CONF_CMD) + +enum ad7091r_mode { + AD7091R_MODE_SAMPLE, + AD7091R_MODE_COMMAND, + AD7091R_MODE_AUTOCYCLE, +}; + +struct ad7091r_state { + struct device *dev; + struct regmap *map; + struct regulator *vref; + const struct ad7091r_chip_info *chip_info; + enum ad7091r_mode mode; + struct mutex lock; /*lock to prevent concurent reads */ +}; + +static int ad7091r_set_mode(struct ad7091r_state *st, enum ad7091r_mode mode) +{ + int ret, conf; + + switch (mode) { + case AD7091R_MODE_SAMPLE: + conf = 0; + break; + case AD7091R_MODE_COMMAND: + conf = AD7091R_REG_CONF_CMD; + break; + case AD7091R_MODE_AUTOCYCLE: + conf = AD7091R_REG_CONF_AUTO; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(st->map, AD7091R_REG_CONF, + AD7091R_REG_CONF_MODE_MASK, conf); + if (ret) + return ret; + + st->mode = mode; + + return 0; +} + +static int ad7091r_set_channel(struct ad7091r_state *st, unsigned int channel) +{ + unsigned int dummy; + int ret; + + /* AD7091R_REG_CHANNEL specified which channels to be converted */ + ret = regmap_write(st->map, AD7091R_REG_CHANNEL, + BIT(channel) | (BIT(channel) << 8)); + if (ret) + return ret; + + /* + * There is a latency of one conversion before the channel conversion + * sequence is updated + */ + return regmap_read(st->map, AD7091R_REG_RESULT, &dummy); +} + +static int ad7091r_read_one(struct iio_dev *iio_dev, + unsigned int channel, unsigned int *read_val) +{ + struct ad7091r_state *st = iio_priv(iio_dev); + unsigned int val; + int ret; + + ret = ad7091r_set_channel(st, channel); + if (ret) + return ret; + + ret = regmap_read(st->map, AD7091R_REG_RESULT, &val); + if (ret) + return ret; + + if (AD7091R_REG_RESULT_CH_ID(val) != channel) + return -EIO; + + *read_val = AD7091R_REG_RESULT_CONV_RESULT(val); + + return 0; +} + +static int ad7091r_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ad7091r_state *st = iio_priv(iio_dev); + unsigned int read_val; + int ret; + + mutex_lock(&st->lock); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (st->mode != AD7091R_MODE_COMMAND) { + ret = -EBUSY; + goto unlock; + } + + ret = ad7091r_read_one(iio_dev, chan->channel, &read_val); + if (ret) + goto unlock; + + *val = read_val; + ret = IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_SCALE: + if (st->vref) { + ret = regulator_get_voltage(st->vref); + if (ret < 0) + goto unlock; + + *val = ret / 1000; + } else { + *val = st->chip_info->vref_mV; + } + + *val2 = chan->scan_type.realbits; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + + default: + ret = -EINVAL; + break; + } + +unlock: + mutex_unlock(&st->lock); + return ret; +} + +static const struct iio_info ad7091r_info = { + .read_raw = ad7091r_read_raw, +}; + +static irqreturn_t ad7091r_event_handler(int irq, void *private) +{ + struct iio_dev *iio_dev = private; + struct ad7091r_state *st = iio_priv(iio_dev); + unsigned int i, read_val; + int ret; + s64 timestamp = iio_get_time_ns(iio_dev); + + ret = regmap_read(st->map, AD7091R_REG_ALERT, &read_val); + if (ret) + return IRQ_HANDLED; + + for (i = 0; i < st->chip_info->num_channels; i++) { + if (read_val & BIT(i * 2)) + iio_push_event(iio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), timestamp); + if (read_val & BIT(i * 2 + 1)) + iio_push_event(iio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), timestamp); + } + + return IRQ_HANDLED; +} + +static void ad7091r_remove(void *data) +{ + struct ad7091r_state *st = data; + + regulator_disable(st->vref); +} + +int ad7091r_probe(struct device *dev, const char *name, + const struct ad7091r_chip_info *chip_info, + struct regmap *map, int irq) +{ + struct iio_dev *iio_dev; + struct ad7091r_state *st; + int ret; + + iio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!iio_dev) + return -ENOMEM; + + st = iio_priv(iio_dev); + st->dev = dev; + st->chip_info = chip_info; + st->map = map; + + iio_dev->name = name; + iio_dev->info = &ad7091r_info; + iio_dev->modes = INDIO_DIRECT_MODE; + + iio_dev->num_channels = chip_info->num_channels; + iio_dev->channels = chip_info->channels; + + if (irq) { + ret = devm_request_threaded_irq(dev, irq, NULL, + ad7091r_event_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name, iio_dev); + if (ret) + return ret; + } + + st->vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(st->vref)) { + if (PTR_ERR(st->vref) == -EPROBE_DEFER) + return -EPROBE_DEFER; + st->vref = NULL; + } else { + ret = regulator_enable(st->vref); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, ad7091r_remove, st); + if (ret) + return ret; + } + + /* Use command mode by default to convert only desired channels*/ + ret = ad7091r_set_mode(st, AD7091R_MODE_COMMAND); + if (ret) + return ret; + + return devm_iio_device_register(dev, iio_dev); +} +EXPORT_SYMBOL_GPL(ad7091r_probe); + +static bool ad7091r_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AD7091R_REG_RESULT: + case AD7091R_REG_ALERT: + return false; + default: + return true; + } +} + +static bool ad7091r_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AD7091R_REG_RESULT: + case AD7091R_REG_ALERT: + return true; + default: + return false; + } +} + +const struct regmap_config ad7091r_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .writeable_reg = ad7091r_writeable_reg, + .volatile_reg = ad7091r_volatile_reg, +}; +EXPORT_SYMBOL_GPL(ad7091r_regmap_config); + +MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7091Rx multi-channel converters"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7091r-base.h b/drivers/iio/adc/ad7091r-base.h new file mode 100644 index 000000000..509748aef --- /dev/null +++ b/drivers/iio/adc/ad7091r-base.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * AD7091RX Analog to Digital converter driver + * + * Copyright 2014-2019 Analog Devices Inc. + */ + +#ifndef __DRIVERS_IIO_ADC_AD7091R_BASE_H__ +#define __DRIVERS_IIO_ADC_AD7091R_BASE_H__ + +struct device; +struct ad7091r_state; + +struct ad7091r_chip_info { + unsigned int num_channels; + const struct iio_chan_spec *channels; + unsigned int vref_mV; +}; + +extern const struct regmap_config ad7091r_regmap_config; + +int ad7091r_probe(struct device *dev, const char *name, + const struct ad7091r_chip_info *chip_info, + struct regmap *map, int irq); + +#endif /* __DRIVERS_IIO_ADC_AD7091R_BASE_H__ */ diff --git a/drivers/iio/adc/ad7091r5.c b/drivers/iio/adc/ad7091r5.c new file mode 100644 index 000000000..9665679c3 --- /dev/null +++ b/drivers/iio/adc/ad7091r5.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7091R5 Analog to Digital converter driver + * + * Copyright 2014-2019 Analog Devices Inc. + */ + +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "ad7091r-base.h" + +static const struct iio_event_spec ad7091r5_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +#define AD7091R_CHANNEL(idx, bits, ev, num_ev) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .channel = idx, \ + .event_spec = ev, \ + .num_event_specs = num_ev, \ + .scan_type.storagebits = 16, \ + .scan_type.realbits = bits, \ +} +static const struct iio_chan_spec ad7091r5_channels_irq[] = { + AD7091R_CHANNEL(0, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), + AD7091R_CHANNEL(1, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), + AD7091R_CHANNEL(2, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), + AD7091R_CHANNEL(3, 12, ad7091r5_events, ARRAY_SIZE(ad7091r5_events)), +}; + +static const struct iio_chan_spec ad7091r5_channels_noirq[] = { + AD7091R_CHANNEL(0, 12, NULL, 0), + AD7091R_CHANNEL(1, 12, NULL, 0), + AD7091R_CHANNEL(2, 12, NULL, 0), + AD7091R_CHANNEL(3, 12, NULL, 0), +}; + +static const struct ad7091r_chip_info ad7091r5_chip_info_irq = { + .channels = ad7091r5_channels_irq, + .num_channels = ARRAY_SIZE(ad7091r5_channels_irq), + .vref_mV = 2500, +}; + +static const struct ad7091r_chip_info ad7091r5_chip_info_noirq = { + .channels = ad7091r5_channels_noirq, + .num_channels = ARRAY_SIZE(ad7091r5_channels_noirq), + .vref_mV = 2500, +}; + +static int ad7091r5_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + const struct ad7091r_chip_info *chip_info; + struct regmap *map = devm_regmap_init_i2c(i2c, &ad7091r_regmap_config); + + if (IS_ERR(map)) + return PTR_ERR(map); + + if (i2c->irq) + chip_info = &ad7091r5_chip_info_irq; + else + chip_info = &ad7091r5_chip_info_noirq; + + return ad7091r_probe(&i2c->dev, id->name, chip_info, map, i2c->irq); +} + +static const struct of_device_id ad7091r5_dt_ids[] = { + { .compatible = "adi,ad7091r5" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ad7091r5_dt_ids); + +static const struct i2c_device_id ad7091r5_i2c_ids[] = { + {"ad7091r5", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ad7091r5_i2c_ids); + +static struct i2c_driver ad7091r5_driver = { + .driver = { + .name = "ad7091r5", + .of_match_table = ad7091r5_dt_ids, + }, + .probe = ad7091r5_i2c_probe, + .id_table = ad7091r5_i2c_ids, +}; +module_i2c_driver(ad7091r5_driver); + +MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7091R5 multi-channel ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c new file mode 100644 index 000000000..19ab7d725 --- /dev/null +++ b/drivers/iio/adc/ad7124.c @@ -0,0 +1,846 @@ +// 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/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.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_REF_EN_MSK BIT(8) +#define AD7124_ADC_CTRL_REF_EN(x) FIELD_PREP(AD7124_ADC_CTRL_REF_EN_MSK, x) +#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 ID */ +#define AD7124_DEVICE_ID_MSK GENMASK(7, 4) +#define AD7124_DEVICE_ID_GET(x) FIELD_GET(AD7124_DEVICE_ID_MSK, x) +#define AD7124_SILICON_REV_MSK GENMASK(3, 0) +#define AD7124_SILICON_REV_GET(x) FIELD_GET(AD7124_SILICON_REV_MSK, x) + +#define CHIPID_AD7124_4 0x0 +#define CHIPID_AD7124_8 0x1 + +/* 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(6, 5) +#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) +#define AD7124_FILTER_TYPE_MSK GENMASK(23, 21) +#define AD7124_FILTER_TYPE_SEL(x) FIELD_PREP(AD7124_FILTER_TYPE_MSK, x) + +#define AD7124_SINC3_FILTER 2 +#define AD7124_SINC4_FILTER 0 + +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 unsigned int ad7124_reg_size[] = { + 1, 2, 3, 3, 2, 1, 3, 3, 1, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3 +}; + +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 { + const char *name; + unsigned int chip_id; + 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; + unsigned int filter_type; +}; + +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) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, +}; + +static struct ad7124_chip_info ad7124_chip_info_tbl[] = { + [ID_AD7124_4] = { + .name = "ad7124-4", + .chip_id = CHIPID_AD7124_4, + .num_inputs = 8, + }, + [ID_AD7124_8] = { + .name = "ad7124-8", + .chip_id = CHIPID_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, + .irq_flags = IRQF_TRIGGER_FALLING, +}; + +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_get_3db_filter_freq(struct ad7124_state *st, + unsigned int channel) +{ + unsigned int fadc; + + fadc = st->channel_config[channel].odr; + + switch (st->channel_config[channel].filter_type) { + case AD7124_SINC3_FILTER: + return DIV_ROUND_CLOSEST(fadc * 230, 1000); + case AD7124_SINC4_FILTER: + return DIV_ROUND_CLOSEST(fadc * 262, 1000); + default: + return -EINVAL; + } +} + +static int ad7124_set_3db_filter_freq(struct ad7124_state *st, + unsigned int channel, + unsigned int freq) +{ + unsigned int sinc4_3db_odr; + unsigned int sinc3_3db_odr; + unsigned int new_filter; + unsigned int new_odr; + + sinc4_3db_odr = DIV_ROUND_CLOSEST(freq * 1000, 230); + sinc3_3db_odr = DIV_ROUND_CLOSEST(freq * 1000, 262); + + if (sinc4_3db_odr > sinc3_3db_odr) { + new_filter = AD7124_SINC3_FILTER; + new_odr = sinc4_3db_odr; + } else { + new_filter = AD7124_SINC4_FILTER; + new_odr = sinc3_3db_odr; + } + + if (st->channel_config[channel].filter_type != new_filter) { + int ret; + + st->channel_config[channel].filter_type = new_filter; + ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel), + AD7124_FILTER_TYPE_MSK, + AD7124_FILTER_TYPE_SEL(new_filter), + 3); + if (ret < 0) + return ret; + } + + return ad7124_set_channel_odr(st, channel, new_odr); +} + +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; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *val = ad7124_get_3db_filter_freq(st, chan->scan_index); + 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); + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + if (val2 != 0) + return -EINVAL; + + return ad7124_set_3db_filter_freq(st, chan->address, val); + default: + return -EINVAL; + } +} + +static int ad7124_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad7124_state *st = iio_priv(indio_dev); + int ret; + + if (reg >= ARRAY_SIZE(ad7124_reg_size)) + return -EINVAL; + + if (readval) + ret = ad_sd_read_reg(&st->sd, reg, ad7124_reg_size[reg], + readval); + else + ret = ad_sd_write_reg(&st->sd, reg, ad7124_reg_size[reg], + writeval); + + return ret; +} + +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, + .debugfs_reg_access = &ad7124_reg_access, + .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_check_chip_id(struct ad7124_state *st) +{ + unsigned int readval, chip_id, silicon_rev; + int ret; + + ret = ad_sd_read_reg(&st->sd, AD7124_ID, 1, &readval); + if (ret < 0) + return ret; + + chip_id = AD7124_DEVICE_ID_GET(readval); + silicon_rev = AD7124_SILICON_REV_GET(readval); + + if (chip_id != st->chip_info->chip_id) { + dev_err(&st->sd.spi->dev, + "Chip ID mismatch: expected %u, got %u\n", + st->chip_info->chip_id, chip_id); + return -ENODEV; + } + + if (silicon_rev == 0) { + dev_err(&st->sd.spi->dev, + "Silicon revision empty. Chip may not be present\n"); + return -ENODEV; + } + + return 0; +} + +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; + st->adc_control &= ~AD7124_ADC_CTRL_REF_EN_MSK; + st->adc_control |= AD7124_ADC_CTRL_REF_EN(1); + return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, + 2, st->adc_control); + 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; + + if (channel >= indio_dev->num_channels) { + dev_err(indio_dev->dev.parent, + "Channel index >= number of channels\n"); + ret = -EINVAL; + 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[channel] = ad7124_channel_template; + chan[channel].address = channel; + chan[channel].scan_index = channel; + chan[channel].channel = ain[0]; + chan[channel].channel2 = ain[1]; + } + + 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 void ad7124_reg_disable(void *r) +{ + regulator_disable(r); +} + +static int ad7124_probe(struct spi_device *spi) +{ + const struct ad7124_chip_info *info; + struct ad7124_state *st; + struct iio_dev *indio_dev; + int i, ret; + + info = of_device_get_match_data(&spi->dev); + if (!info) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->chip_info = info; + + ad_sd_init(&st->sd, indio_dev, spi, &ad7124_sigma_delta_info); + + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = st->chip_info->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; + + ret = devm_add_action_or_reset(&spi->dev, ad7124_reg_disable, + st->vref[i]); + 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 = ad7124_soft_reset(st); + if (ret < 0) + goto error_clk_disable_unprepare; + + ret = ad7124_check_chip_id(st); + if (ret) + 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); + + 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); + + iio_device_unregister(indio_dev); + ad_sd_cleanup_buffer_and_trigger(indio_dev); + clk_disable_unprepare(st->mclk); + + return 0; +} + +static const struct of_device_id ad7124_of_match[] = { + { .compatible = "adi,ad7124-4", + .data = &ad7124_chip_info_tbl[ID_AD7124_4], }, + { .compatible = "adi,ad7124-8", + .data = &ad7124_chip_info_tbl[ID_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, +}; +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/ad7192.c b/drivers/iio/adc/ad7192.c new file mode 100644 index 000000000..614c5e807 --- /dev/null +++ b/drivers/iio/adc/ad7192.c @@ -0,0 +1,1062 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AD7190 AD7192 AD7193 AD7195 SPI ADC driver + * + * Copyright 2011-2015 Analog Devices Inc. + */ + +#include <linux/interrupt.h> +#include <linux/clk.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/delay.h> +#include <linux/of_device.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/adc/ad_sigma_delta.h> + +/* Registers */ +#define AD7192_REG_COMM 0 /* Communications Register (WO, 8-bit) */ +#define AD7192_REG_STAT 0 /* Status Register (RO, 8-bit) */ +#define AD7192_REG_MODE 1 /* Mode Register (RW, 24-bit */ +#define AD7192_REG_CONF 2 /* Configuration Register (RW, 24-bit) */ +#define AD7192_REG_DATA 3 /* Data Register (RO, 24/32-bit) */ +#define AD7192_REG_ID 4 /* ID Register (RO, 8-bit) */ +#define AD7192_REG_GPOCON 5 /* GPOCON Register (RO, 8-bit) */ +#define AD7192_REG_OFFSET 6 /* Offset Register (RW, 16-bit */ + /* (AD7792)/24-bit (AD7192)) */ +#define AD7192_REG_FULLSALE 7 /* Full-Scale Register */ + /* (RW, 16-bit (AD7792)/24-bit (AD7192)) */ + +/* Communications Register Bit Designations (AD7192_REG_COMM) */ +#define AD7192_COMM_WEN BIT(7) /* Write Enable */ +#define AD7192_COMM_WRITE 0 /* Write Operation */ +#define AD7192_COMM_READ BIT(6) /* Read Operation */ +#define AD7192_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */ +#define AD7192_COMM_CREAD BIT(2) /* Continuous Read of Data Register */ + +/* Status Register Bit Designations (AD7192_REG_STAT) */ +#define AD7192_STAT_RDY BIT(7) /* Ready */ +#define AD7192_STAT_ERR BIT(6) /* Error (Overrange, Underrange) */ +#define AD7192_STAT_NOREF BIT(5) /* Error no external reference */ +#define AD7192_STAT_PARITY BIT(4) /* Parity */ +#define AD7192_STAT_CH3 BIT(2) /* Channel 3 */ +#define AD7192_STAT_CH2 BIT(1) /* Channel 2 */ +#define AD7192_STAT_CH1 BIT(0) /* Channel 1 */ + +/* Mode Register Bit Designations (AD7192_REG_MODE) */ +#define AD7192_MODE_SEL(x) (((x) & 0x7) << 21) /* Operation Mode Select */ +#define AD7192_MODE_SEL_MASK (0x7 << 21) /* Operation Mode Select Mask */ +#define AD7192_MODE_DAT_STA BIT(20) /* Status Register transmission */ +#define AD7192_MODE_CLKSRC(x) (((x) & 0x3) << 18) /* Clock Source Select */ +#define AD7192_MODE_SINC3 BIT(15) /* SINC3 Filter Select */ +#define AD7192_MODE_ACX BIT(14) /* AC excitation enable(AD7195 only)*/ +#define AD7192_MODE_ENPAR BIT(13) /* Parity Enable */ +#define AD7192_MODE_CLKDIV BIT(12) /* Clock divide by 2 (AD7190/2 only)*/ +#define AD7192_MODE_SCYCLE BIT(11) /* Single cycle conversion */ +#define AD7192_MODE_REJ60 BIT(10) /* 50/60Hz notch filter */ +#define AD7192_MODE_RATE(x) ((x) & 0x3FF) /* Filter Update Rate Select */ + +/* Mode Register: AD7192_MODE_SEL options */ +#define AD7192_MODE_CONT 0 /* Continuous Conversion Mode */ +#define AD7192_MODE_SINGLE 1 /* Single Conversion Mode */ +#define AD7192_MODE_IDLE 2 /* Idle Mode */ +#define AD7192_MODE_PWRDN 3 /* Power-Down Mode */ +#define AD7192_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */ +#define AD7192_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */ +#define AD7192_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */ +#define AD7192_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */ + +/* Mode Register: AD7192_MODE_CLKSRC options */ +#define AD7192_CLK_EXT_MCLK1_2 0 /* External 4.92 MHz Clock connected*/ + /* from MCLK1 to MCLK2 */ +#define AD7192_CLK_EXT_MCLK2 1 /* External Clock applied to MCLK2 */ +#define AD7192_CLK_INT 2 /* Internal 4.92 MHz Clock not */ + /* available at the MCLK2 pin */ +#define AD7192_CLK_INT_CO 3 /* Internal 4.92 MHz Clock available*/ + /* at the MCLK2 pin */ + +/* Configuration Register Bit Designations (AD7192_REG_CONF) */ + +#define AD7192_CONF_CHOP BIT(23) /* CHOP enable */ +#define AD7192_CONF_REFSEL BIT(20) /* REFIN1/REFIN2 Reference Select */ +#define AD7192_CONF_CHAN(x) ((x) << 8) /* Channel select */ +#define AD7192_CONF_CHAN_MASK (0x7FF << 8) /* Channel select mask */ +#define AD7192_CONF_BURN BIT(7) /* Burnout current enable */ +#define AD7192_CONF_REFDET BIT(6) /* Reference detect enable */ +#define AD7192_CONF_BUF BIT(4) /* Buffered Mode Enable */ +#define AD7192_CONF_UNIPOLAR BIT(3) /* Unipolar/Bipolar Enable */ +#define AD7192_CONF_GAIN(x) ((x) & 0x7) /* Gain Select */ + +#define AD7192_CH_AIN1P_AIN2M BIT(0) /* AIN1(+) - AIN2(-) */ +#define AD7192_CH_AIN3P_AIN4M BIT(1) /* AIN3(+) - AIN4(-) */ +#define AD7192_CH_TEMP BIT(2) /* Temp Sensor */ +#define AD7192_CH_AIN2P_AIN2M BIT(3) /* AIN2(+) - AIN2(-) */ +#define AD7192_CH_AIN1 BIT(4) /* AIN1 - AINCOM */ +#define AD7192_CH_AIN2 BIT(5) /* AIN2 - AINCOM */ +#define AD7192_CH_AIN3 BIT(6) /* AIN3 - AINCOM */ +#define AD7192_CH_AIN4 BIT(7) /* AIN4 - AINCOM */ + +#define AD7193_CH_AIN1P_AIN2M 0x001 /* AIN1(+) - AIN2(-) */ +#define AD7193_CH_AIN3P_AIN4M 0x002 /* AIN3(+) - AIN4(-) */ +#define AD7193_CH_AIN5P_AIN6M 0x004 /* AIN5(+) - AIN6(-) */ +#define AD7193_CH_AIN7P_AIN8M 0x008 /* AIN7(+) - AIN8(-) */ +#define AD7193_CH_TEMP 0x100 /* Temp senseor */ +#define AD7193_CH_AIN2P_AIN2M 0x200 /* AIN2(+) - AIN2(-) */ +#define AD7193_CH_AIN1 0x401 /* AIN1 - AINCOM */ +#define AD7193_CH_AIN2 0x402 /* AIN2 - AINCOM */ +#define AD7193_CH_AIN3 0x404 /* AIN3 - AINCOM */ +#define AD7193_CH_AIN4 0x408 /* AIN4 - AINCOM */ +#define AD7193_CH_AIN5 0x410 /* AIN5 - AINCOM */ +#define AD7193_CH_AIN6 0x420 /* AIN6 - AINCOM */ +#define AD7193_CH_AIN7 0x440 /* AIN7 - AINCOM */ +#define AD7193_CH_AIN8 0x480 /* AIN7 - AINCOM */ +#define AD7193_CH_AINCOM 0x600 /* AINCOM - AINCOM */ + +/* ID Register Bit Designations (AD7192_REG_ID) */ +#define CHIPID_AD7190 0x4 +#define CHIPID_AD7192 0x0 +#define CHIPID_AD7193 0x2 +#define CHIPID_AD7195 0x6 +#define AD7192_ID_MASK 0x0F + +/* GPOCON Register Bit Designations (AD7192_REG_GPOCON) */ +#define AD7192_GPOCON_BPDSW BIT(6) /* Bridge power-down switch enable */ +#define AD7192_GPOCON_GP32EN BIT(5) /* Digital Output P3 and P2 enable */ +#define AD7192_GPOCON_GP10EN BIT(4) /* Digital Output P1 and P0 enable */ +#define AD7192_GPOCON_P3DAT BIT(3) /* P3 state */ +#define AD7192_GPOCON_P2DAT BIT(2) /* P2 state */ +#define AD7192_GPOCON_P1DAT BIT(1) /* P1 state */ +#define AD7192_GPOCON_P0DAT BIT(0) /* P0 state */ + +#define AD7192_EXT_FREQ_MHZ_MIN 2457600 +#define AD7192_EXT_FREQ_MHZ_MAX 5120000 +#define AD7192_INT_FREQ_MHZ 4915200 + +#define AD7192_NO_SYNC_FILTER 1 +#define AD7192_SYNC3_FILTER 3 +#define AD7192_SYNC4_FILTER 4 + +/* NOTE: + * The AD7190/2/5 features a dual use data out ready DOUT/RDY output. + * In order to avoid contentions on the SPI bus, it's therefore necessary + * to use spi bus locking. + * + * The DOUT/RDY output must also be wired to an interrupt capable GPIO. + */ + +enum { + AD7192_SYSCALIB_ZERO_SCALE, + AD7192_SYSCALIB_FULL_SCALE, +}; + +enum { + ID_AD7190, + ID_AD7192, + ID_AD7193, + ID_AD7195, +}; + +struct ad7192_chip_info { + unsigned int chip_id; + const char *name; +}; + +struct ad7192_state { + const struct ad7192_chip_info *chip_info; + struct regulator *avdd; + struct regulator *dvdd; + struct clk *mclk; + u16 int_vref_mv; + u32 fclk; + u32 f_order; + u32 mode; + u32 conf; + u32 scale_avail[8][2]; + u8 gpocon; + u8 clock_sel; + struct mutex lock; /* protect sensor state */ + u8 syscalib_mode[8]; + + struct ad_sigma_delta sd; +}; + +static const char * const ad7192_syscalib_modes[] = { + [AD7192_SYSCALIB_ZERO_SCALE] = "zero_scale", + [AD7192_SYSCALIB_FULL_SCALE] = "full_scale", +}; + +static int ad7192_set_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + st->syscalib_mode[chan->channel] = mode; + + return 0; +} + +static int ad7192_get_syscalib_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + return st->syscalib_mode[chan->channel]; +} + +static ssize_t ad7192_write_syscalib(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad7192_state *st = iio_priv(indio_dev); + bool sys_calib; + int ret, temp; + + ret = strtobool(buf, &sys_calib); + if (ret) + return ret; + + temp = st->syscalib_mode[chan->channel]; + if (sys_calib) { + if (temp == AD7192_SYSCALIB_ZERO_SCALE) + ret = ad_sd_calibrate(&st->sd, AD7192_MODE_CAL_SYS_ZERO, + chan->address); + else + ret = ad_sd_calibrate(&st->sd, AD7192_MODE_CAL_SYS_FULL, + chan->address); + } + + return ret ? ret : len; +} + +static const struct iio_enum ad7192_syscalib_mode_enum = { + .items = ad7192_syscalib_modes, + .num_items = ARRAY_SIZE(ad7192_syscalib_modes), + .set = ad7192_set_syscalib_mode, + .get = ad7192_get_syscalib_mode +}; + +static const struct iio_chan_spec_ext_info ad7192_calibsys_ext_info[] = { + { + .name = "sys_calibration", + .write = ad7192_write_syscalib, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("sys_calibration_mode", IIO_SEPARATE, + &ad7192_syscalib_mode_enum), + IIO_ENUM_AVAILABLE("sys_calibration_mode", &ad7192_syscalib_mode_enum), + {} +}; + +static struct ad7192_state *ad_sigma_delta_to_ad7192(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7192_state, sd); +} + +static int ad7192_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd); + + st->conf &= ~AD7192_CONF_CHAN_MASK; + st->conf |= AD7192_CONF_CHAN(channel); + + return ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); +} + +static int ad7192_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd); + + st->mode &= ~AD7192_MODE_SEL_MASK; + st->mode |= AD7192_MODE_SEL(mode); + + return ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); +} + +static const struct ad_sigma_delta_info ad7192_sigma_delta_info = { + .set_channel = ad7192_set_channel, + .set_mode = ad7192_set_mode, + .has_registers = true, + .addr_shift = 3, + .read_mask = BIT(6), + .irq_flags = IRQF_TRIGGER_FALLING, +}; + +static const struct ad_sd_calib_data ad7192_calib_arr[8] = { + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN1}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN1}, + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN2}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN2}, + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN3}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN3}, + {AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN4}, + {AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN4} +}; + +static int ad7192_calibrate_all(struct ad7192_state *st) +{ + return ad_sd_calibrate_all(&st->sd, ad7192_calib_arr, + ARRAY_SIZE(ad7192_calib_arr)); +} + +static inline bool ad7192_valid_external_frequency(u32 freq) +{ + return (freq >= AD7192_EXT_FREQ_MHZ_MIN && + freq <= AD7192_EXT_FREQ_MHZ_MAX); +} + +static int ad7192_of_clock_select(struct ad7192_state *st) +{ + struct device_node *np = st->sd.spi->dev.of_node; + unsigned int clock_sel; + + clock_sel = AD7192_CLK_INT; + + /* use internal clock */ + if (PTR_ERR(st->mclk) == -ENOENT) { + if (of_property_read_bool(np, "adi,int-clock-output-enable")) + clock_sel = AD7192_CLK_INT_CO; + } else { + if (of_property_read_bool(np, "adi,clock-xtal")) + clock_sel = AD7192_CLK_EXT_MCLK1_2; + else + clock_sel = AD7192_CLK_EXT_MCLK2; + } + + return clock_sel; +} + +static int ad7192_setup(struct ad7192_state *st, struct device_node *np) +{ + struct iio_dev *indio_dev = spi_get_drvdata(st->sd.spi); + bool rej60_en, refin2_en; + bool buf_en, bipolar, burnout_curr_en; + unsigned long long scale_uv; + int i, ret, id; + + /* reset the serial interface */ + ret = ad_sd_reset(&st->sd, 48); + if (ret < 0) + return ret; + usleep_range(500, 1000); /* Wait for at least 500us */ + + /* write/read test for device presence */ + ret = ad_sd_read_reg(&st->sd, AD7192_REG_ID, 1, &id); + if (ret) + return ret; + + id &= AD7192_ID_MASK; + + if (id != st->chip_info->chip_id) + dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n", + id); + + st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) | + AD7192_MODE_CLKSRC(st->clock_sel) | + AD7192_MODE_RATE(480); + + st->conf = AD7192_CONF_GAIN(0); + + rej60_en = of_property_read_bool(np, "adi,rejection-60-Hz-enable"); + if (rej60_en) + st->mode |= AD7192_MODE_REJ60; + + refin2_en = of_property_read_bool(np, "adi,refin2-pins-enable"); + if (refin2_en && st->chip_info->chip_id != CHIPID_AD7195) + st->conf |= AD7192_CONF_REFSEL; + + st->conf &= ~AD7192_CONF_CHOP; + st->f_order = AD7192_NO_SYNC_FILTER; + + buf_en = of_property_read_bool(np, "adi,buffer-enable"); + if (buf_en) + st->conf |= AD7192_CONF_BUF; + + bipolar = of_property_read_bool(np, "bipolar"); + if (!bipolar) + st->conf |= AD7192_CONF_UNIPOLAR; + + burnout_curr_en = of_property_read_bool(np, + "adi,burnout-currents-enable"); + if (burnout_curr_en && buf_en) { + st->conf |= AD7192_CONF_BURN; + } else if (burnout_curr_en) { + dev_warn(&st->sd.spi->dev, + "Can't enable burnout currents: see CHOP or buffer\n"); + } + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + if (ret) + return ret; + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); + if (ret) + return ret; + + ret = ad7192_calibrate_all(st); + if (ret) + return ret; + + /* Populate available ADC input ranges */ + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + scale_uv = ((u64)st->int_vref_mv * 100000000) + >> (indio_dev->channels[0].scan_type.realbits - + ((st->conf & AD7192_CONF_UNIPOLAR) ? 0 : 1)); + scale_uv >>= i; + + st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10; + st->scale_avail[i][0] = scale_uv; + } + + return 0; +} + +static ssize_t ad7192_show_ac_excitation(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7192_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", !!(st->mode & AD7192_MODE_ACX)); +} + +static ssize_t ad7192_show_bridge_switch(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7192_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", !!(st->gpocon & AD7192_GPOCON_BPDSW)); +} + +static ssize_t ad7192_set(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7192_state *st = iio_priv(indio_dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + int ret; + bool val; + + ret = strtobool(buf, &val); + if (ret < 0) + return ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + switch ((u32)this_attr->address) { + case AD7192_REG_GPOCON: + if (val) + st->gpocon |= AD7192_GPOCON_BPDSW; + else + st->gpocon &= ~AD7192_GPOCON_BPDSW; + + ad_sd_write_reg(&st->sd, AD7192_REG_GPOCON, 1, st->gpocon); + break; + case AD7192_REG_MODE: + if (val) + st->mode |= AD7192_MODE_ACX; + else + st->mode &= ~AD7192_MODE_ACX; + + ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + break; + default: + ret = -EINVAL; + } + + iio_device_release_direct_mode(indio_dev); + + return ret ? ret : len; +} + +static void ad7192_get_available_filter_freq(struct ad7192_state *st, + int *freq) +{ + unsigned int fadc; + + /* Formulas for filter at page 25 of the datasheet */ + fadc = DIV_ROUND_CLOSEST(st->fclk, + AD7192_SYNC4_FILTER * AD7192_MODE_RATE(st->mode)); + freq[0] = DIV_ROUND_CLOSEST(fadc * 240, 1024); + + fadc = DIV_ROUND_CLOSEST(st->fclk, + AD7192_SYNC3_FILTER * AD7192_MODE_RATE(st->mode)); + freq[1] = DIV_ROUND_CLOSEST(fadc * 240, 1024); + + fadc = DIV_ROUND_CLOSEST(st->fclk, AD7192_MODE_RATE(st->mode)); + freq[2] = DIV_ROUND_CLOSEST(fadc * 230, 1024); + freq[3] = DIV_ROUND_CLOSEST(fadc * 272, 1024); +} + +static ssize_t ad7192_show_filter_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad7192_state *st = iio_priv(indio_dev); + unsigned int freq_avail[4], i; + size_t len = 0; + + ad7192_get_available_filter_freq(st, freq_avail); + + for (i = 0; i < ARRAY_SIZE(freq_avail); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, + "%d.%d ", freq_avail[i] / 1000, + freq_avail[i] % 1000); + + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEVICE_ATTR(filter_low_pass_3db_frequency_available, + 0444, ad7192_show_filter_avail, NULL, 0); + +static IIO_DEVICE_ATTR(bridge_switch_en, 0644, + ad7192_show_bridge_switch, ad7192_set, + AD7192_REG_GPOCON); + +static IIO_DEVICE_ATTR(ac_excitation_en, 0644, + ad7192_show_ac_excitation, ad7192_set, + AD7192_REG_MODE); + +static struct attribute *ad7192_attributes[] = { + &iio_dev_attr_filter_low_pass_3db_frequency_available.dev_attr.attr, + &iio_dev_attr_bridge_switch_en.dev_attr.attr, + &iio_dev_attr_ac_excitation_en.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7192_attribute_group = { + .attrs = ad7192_attributes, +}; + +static struct attribute *ad7195_attributes[] = { + &iio_dev_attr_filter_low_pass_3db_frequency_available.dev_attr.attr, + &iio_dev_attr_bridge_switch_en.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7195_attribute_group = { + .attrs = ad7195_attributes, +}; + +static unsigned int ad7192_get_temp_scale(bool unipolar) +{ + return unipolar ? 2815 * 2 : 2815; +} + +static int ad7192_set_3db_filter_freq(struct ad7192_state *st, + int val, int val2) +{ + int freq_avail[4], i, ret, freq; + unsigned int diff_new, diff_old; + int idx = 0; + + diff_old = U32_MAX; + freq = val * 1000 + val2; + + ad7192_get_available_filter_freq(st, freq_avail); + + for (i = 0; i < ARRAY_SIZE(freq_avail); i++) { + diff_new = abs(freq - freq_avail[i]); + if (diff_new < diff_old) { + diff_old = diff_new; + idx = i; + } + } + + switch (idx) { + case 0: + st->f_order = AD7192_SYNC4_FILTER; + st->mode &= ~AD7192_MODE_SINC3; + + st->conf |= AD7192_CONF_CHOP; + break; + case 1: + st->f_order = AD7192_SYNC3_FILTER; + st->mode |= AD7192_MODE_SINC3; + + st->conf |= AD7192_CONF_CHOP; + break; + case 2: + st->f_order = AD7192_NO_SYNC_FILTER; + st->mode &= ~AD7192_MODE_SINC3; + + st->conf &= ~AD7192_CONF_CHOP; + break; + case 3: + st->f_order = AD7192_NO_SYNC_FILTER; + st->mode |= AD7192_MODE_SINC3; + + st->conf &= ~AD7192_CONF_CHOP; + break; + } + + ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + if (ret < 0) + return ret; + + return ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf); +} + +static int ad7192_get_3db_filter_freq(struct ad7192_state *st) +{ + unsigned int fadc; + + fadc = DIV_ROUND_CLOSEST(st->fclk, + st->f_order * AD7192_MODE_RATE(st->mode)); + + if (st->conf & AD7192_CONF_CHOP) + return DIV_ROUND_CLOSEST(fadc * 240, 1024); + if (st->mode & AD7192_MODE_SINC3) + return DIV_ROUND_CLOSEST(fadc * 272, 1024); + else + return DIV_ROUND_CLOSEST(fadc * 230, 1024); +} + +static int ad7192_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad7192_state *st = iio_priv(indio_dev); + bool unipolar = !!(st->conf & AD7192_CONF_UNIPOLAR); + + switch (m) { + case IIO_CHAN_INFO_RAW: + return ad_sigma_delta_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + mutex_lock(&st->lock); + *val = st->scale_avail[AD7192_CONF_GAIN(st->conf)][0]; + *val2 = st->scale_avail[AD7192_CONF_GAIN(st->conf)][1]; + mutex_unlock(&st->lock); + return IIO_VAL_INT_PLUS_NANO; + case IIO_TEMP: + *val = 0; + *val2 = 1000000000 / ad7192_get_temp_scale(unipolar); + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + if (!unipolar) + *val = -(1 << (chan->scan_type.realbits - 1)); + else + *val = 0; + /* Kelvin to Celsius */ + if (chan->type == IIO_TEMP) + *val -= 273 * ad7192_get_temp_scale(unipolar); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->fclk / + (st->f_order * 1024 * AD7192_MODE_RATE(st->mode)); + return IIO_VAL_INT; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *val = ad7192_get_3db_filter_freq(st); + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static int ad7192_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad7192_state *st = iio_priv(indio_dev); + int ret, i, div; + unsigned int tmp; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = -EINVAL; + mutex_lock(&st->lock); + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + if (val2 == st->scale_avail[i][1]) { + ret = 0; + tmp = st->conf; + st->conf &= ~AD7192_CONF_GAIN(-1); + st->conf |= AD7192_CONF_GAIN(i); + if (tmp == st->conf) + break; + ad_sd_write_reg(&st->sd, AD7192_REG_CONF, + 3, st->conf); + ad7192_calibrate_all(st); + break; + } + mutex_unlock(&st->lock); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!val) { + ret = -EINVAL; + break; + } + + div = st->fclk / (val * st->f_order * 1024); + if (div < 1 || div > 1023) { + ret = -EINVAL; + break; + } + + st->mode &= ~AD7192_MODE_RATE(-1); + st->mode |= AD7192_MODE_RATE(div); + ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode); + break; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + ret = ad7192_set_3db_filter_freq(st, val, val2 / 1000); + break; + default: + ret = -EINVAL; + } + + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static int ad7192_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int ad7192_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_avail; + *type = IIO_VAL_INT_PLUS_NANO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(st->scale_avail) * 2; + + return IIO_AVAIL_LIST; + } + + return -EINVAL; +} + +static const struct iio_info ad7192_info = { + .read_raw = ad7192_read_raw, + .write_raw = ad7192_write_raw, + .write_raw_get_fmt = ad7192_write_raw_get_fmt, + .read_avail = ad7192_read_avail, + .attrs = &ad7192_attribute_group, + .validate_trigger = ad_sd_validate_trigger, +}; + +static const struct iio_info ad7195_info = { + .read_raw = ad7192_read_raw, + .write_raw = ad7192_write_raw, + .write_raw_get_fmt = ad7192_write_raw_get_fmt, + .read_avail = ad7192_read_avail, + .attrs = &ad7195_attribute_group, + .validate_trigger = ad_sd_validate_trigger, +}; + +#define __AD719x_CHANNEL(_si, _channel1, _channel2, _address, _extend_name, \ + _type, _mask_type_av, _ext_info) \ + { \ + .type = (_type), \ + .differential = ((_channel2) == -1 ? 0 : 1), \ + .indexed = 1, \ + .channel = (_channel1), \ + .channel2 = (_channel2), \ + .address = (_address), \ + .extend_name = (_extend_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = (_mask_type_av), \ + .ext_info = (_ext_info), \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 24, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD719x_DIFF_CHANNEL(_si, _channel1, _channel2, _address) \ + __AD719x_CHANNEL(_si, _channel1, _channel2, _address, NULL, \ + IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE), \ + ad7192_calibsys_ext_info) + +#define AD719x_CHANNEL(_si, _channel1, _address) \ + __AD719x_CHANNEL(_si, _channel1, -1, _address, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info) + +#define AD719x_TEMP_CHANNEL(_si, _address) \ + __AD719x_CHANNEL(_si, 0, -1, _address, NULL, IIO_TEMP, 0, NULL) + +static const struct iio_chan_spec ad7192_channels[] = { + AD719x_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M), + AD719x_DIFF_CHANNEL(1, 3, 4, AD7192_CH_AIN3P_AIN4M), + AD719x_TEMP_CHANNEL(2, AD7192_CH_TEMP), + AD719x_DIFF_CHANNEL(3, 2, 2, AD7192_CH_AIN2P_AIN2M), + AD719x_CHANNEL(4, 1, AD7192_CH_AIN1), + AD719x_CHANNEL(5, 2, AD7192_CH_AIN2), + AD719x_CHANNEL(6, 3, AD7192_CH_AIN3), + AD719x_CHANNEL(7, 4, AD7192_CH_AIN4), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_chan_spec ad7193_channels[] = { + AD719x_DIFF_CHANNEL(0, 1, 2, AD7193_CH_AIN1P_AIN2M), + AD719x_DIFF_CHANNEL(1, 3, 4, AD7193_CH_AIN3P_AIN4M), + AD719x_DIFF_CHANNEL(2, 5, 6, AD7193_CH_AIN5P_AIN6M), + AD719x_DIFF_CHANNEL(3, 7, 8, AD7193_CH_AIN7P_AIN8M), + AD719x_TEMP_CHANNEL(4, AD7193_CH_TEMP), + AD719x_DIFF_CHANNEL(5, 2, 2, AD7193_CH_AIN2P_AIN2M), + AD719x_CHANNEL(6, 1, AD7193_CH_AIN1), + AD719x_CHANNEL(7, 2, AD7193_CH_AIN2), + AD719x_CHANNEL(8, 3, AD7193_CH_AIN3), + AD719x_CHANNEL(9, 4, AD7193_CH_AIN4), + AD719x_CHANNEL(10, 5, AD7193_CH_AIN5), + AD719x_CHANNEL(11, 6, AD7193_CH_AIN6), + AD719x_CHANNEL(12, 7, AD7193_CH_AIN7), + AD719x_CHANNEL(13, 8, AD7193_CH_AIN8), + IIO_CHAN_SOFT_TIMESTAMP(14), +}; + +static const struct ad7192_chip_info ad7192_chip_info_tbl[] = { + [ID_AD7190] = { + .chip_id = CHIPID_AD7190, + .name = "ad7190", + }, + [ID_AD7192] = { + .chip_id = CHIPID_AD7192, + .name = "ad7192", + }, + [ID_AD7193] = { + .chip_id = CHIPID_AD7193, + .name = "ad7193", + }, + [ID_AD7195] = { + .chip_id = CHIPID_AD7195, + .name = "ad7195", + }, +}; + +static int ad7192_channels_config(struct iio_dev *indio_dev) +{ + struct ad7192_state *st = iio_priv(indio_dev); + + switch (st->chip_info->chip_id) { + case CHIPID_AD7193: + indio_dev->channels = ad7193_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7193_channels); + break; + default: + indio_dev->channels = ad7192_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7192_channels); + break; + } + + return 0; +} + +static int ad7192_probe(struct spi_device *spi) +{ + struct ad7192_state *st; + struct iio_dev *indio_dev; + int ret; + + if (!spi->irq) { + dev_err(&spi->dev, "no IRQ?\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + mutex_init(&st->lock); + + st->avdd = devm_regulator_get(&spi->dev, "avdd"); + if (IS_ERR(st->avdd)) + return PTR_ERR(st->avdd); + + ret = regulator_enable(st->avdd); + if (ret) { + dev_err(&spi->dev, "Failed to enable specified AVdd supply\n"); + return ret; + } + + st->dvdd = devm_regulator_get(&spi->dev, "dvdd"); + if (IS_ERR(st->dvdd)) { + ret = PTR_ERR(st->dvdd); + goto error_disable_avdd; + } + + ret = regulator_enable(st->dvdd); + if (ret) { + dev_err(&spi->dev, "Failed to enable specified DVdd supply\n"); + goto error_disable_avdd; + } + + ret = regulator_get_voltage(st->avdd); + if (ret < 0) { + dev_err(&spi->dev, "Device tree error, reference voltage undefined\n"); + goto error_disable_avdd; + } + st->int_vref_mv = ret / 1000; + + spi_set_drvdata(spi, indio_dev); + st->chip_info = of_device_get_match_data(&spi->dev); + indio_dev->name = st->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = ad7192_channels_config(indio_dev); + if (ret < 0) + goto error_disable_dvdd; + + if (st->chip_info->chip_id == CHIPID_AD7195) + indio_dev->info = &ad7195_info; + else + indio_dev->info = &ad7192_info; + + ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info); + + ret = ad_sd_setup_buffer_and_trigger(indio_dev); + if (ret) + goto error_disable_dvdd; + + st->fclk = AD7192_INT_FREQ_MHZ; + + st->mclk = devm_clk_get(&st->sd.spi->dev, "mclk"); + if (IS_ERR(st->mclk) && PTR_ERR(st->mclk) != -ENOENT) { + ret = PTR_ERR(st->mclk); + goto error_remove_trigger; + } + + st->clock_sel = ad7192_of_clock_select(st); + + if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 || + st->clock_sel == AD7192_CLK_EXT_MCLK2) { + ret = clk_prepare_enable(st->mclk); + if (ret < 0) + goto error_remove_trigger; + + st->fclk = clk_get_rate(st->mclk); + if (!ad7192_valid_external_frequency(st->fclk)) { + ret = -EINVAL; + dev_err(&spi->dev, + "External clock frequency out of bounds\n"); + goto error_disable_clk; + } + } + + ret = ad7192_setup(st, spi->dev.of_node); + if (ret) + goto error_disable_clk; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto error_disable_clk; + return 0; + +error_disable_clk: + if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 || + st->clock_sel == AD7192_CLK_EXT_MCLK2) + clk_disable_unprepare(st->mclk); +error_remove_trigger: + ad_sd_cleanup_buffer_and_trigger(indio_dev); +error_disable_dvdd: + regulator_disable(st->dvdd); +error_disable_avdd: + regulator_disable(st->avdd); + + return ret; +} + +static int ad7192_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7192_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 || + st->clock_sel == AD7192_CLK_EXT_MCLK2) + clk_disable_unprepare(st->mclk); + ad_sd_cleanup_buffer_and_trigger(indio_dev); + + regulator_disable(st->dvdd); + regulator_disable(st->avdd); + + return 0; +} + +static const struct of_device_id ad7192_of_match[] = { + { .compatible = "adi,ad7190", .data = &ad7192_chip_info_tbl[ID_AD7190] }, + { .compatible = "adi,ad7192", .data = &ad7192_chip_info_tbl[ID_AD7192] }, + { .compatible = "adi,ad7193", .data = &ad7192_chip_info_tbl[ID_AD7193] }, + { .compatible = "adi,ad7195", .data = &ad7192_chip_info_tbl[ID_AD7195] }, + {} +}; +MODULE_DEVICE_TABLE(of, ad7192_of_match); + +static struct spi_driver ad7192_driver = { + .driver = { + .name = "ad7192", + .of_match_table = ad7192_of_match, + }, + .probe = ad7192_probe, + .remove = ad7192_remove, +}; +module_spi_driver(ad7192_driver); + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7190, AD7192, AD7193, AD7195 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c new file mode 100644 index 000000000..a8ec3efd6 --- /dev/null +++ b/drivers/iio/adc/ad7266.c @@ -0,0 +1,513 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7266/65 SPI ADC driver + * + * Copyright 2012 Analog Devices Inc. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> + +#include <linux/interrupt.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/platform_data/ad7266.h> + +struct ad7266_state { + struct spi_device *spi; + struct regulator *reg; + unsigned long vref_mv; + + struct spi_transfer single_xfer[3]; + struct spi_message single_msg; + + enum ad7266_range range; + enum ad7266_mode mode; + bool fixed_addr; + struct gpio_desc *gpios[3]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * The buffer needs to be large enough to hold two samples (4 bytes) and + * the naturally aligned timestamp (8 bytes). + */ + struct { + __be16 sample[2]; + s64 timestamp; + } data ____cacheline_aligned; +}; + +static int ad7266_wakeup(struct ad7266_state *st) +{ + /* Any read with >= 2 bytes will wake the device */ + return spi_read(st->spi, &st->data.sample[0], 2); +} + +static int ad7266_powerdown(struct ad7266_state *st) +{ + /* Any read with < 2 bytes will powerdown the device */ + return spi_read(st->spi, &st->data.sample[0], 1); +} + +static int ad7266_preenable(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + return ad7266_wakeup(st); +} + +static int ad7266_postdisable(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + return ad7266_powerdown(st); +} + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .preenable = &ad7266_preenable, + .postdisable = &ad7266_postdisable, +}; + +static irqreturn_t ad7266_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7266_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_read(st->spi, st->data.sample, 4); + if (ret == 0) { + iio_push_to_buffers_with_timestamp(indio_dev, &st->data, + pf->timestamp); + } + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void ad7266_select_input(struct ad7266_state *st, unsigned int nr) +{ + unsigned int i; + + if (st->fixed_addr) + return; + + switch (st->mode) { + case AD7266_MODE_SINGLE_ENDED: + nr >>= 1; + break; + case AD7266_MODE_PSEUDO_DIFF: + nr |= 1; + break; + case AD7266_MODE_DIFF: + nr &= ~1; + break; + } + + for (i = 0; i < 3; ++i) + gpiod_set_value(st->gpios[i], (bool)(nr & BIT(i))); +} + +static int ad7266_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7266_state *st = iio_priv(indio_dev); + unsigned int nr = find_first_bit(scan_mask, indio_dev->masklength); + + ad7266_select_input(st, nr); + + return 0; +} + +static int ad7266_read_single(struct ad7266_state *st, int *val, + unsigned int address) +{ + int ret; + + ad7266_select_input(st, address); + + ret = spi_sync(st->spi, &st->single_msg); + *val = be16_to_cpu(st->data.sample[address % 2]); + + return ret; +} + +static int ad7266_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long m) +{ + struct ad7266_state *st = iio_priv(indio_dev); + unsigned long scale_mv; + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = ad7266_read_single(st, val, chan->address); + iio_device_release_direct_mode(indio_dev); + + *val = (*val >> 2) & 0xfff; + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, 11); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_mv = st->vref_mv; + if (st->mode == AD7266_MODE_DIFF) + scale_mv *= 2; + if (st->range == AD7266_RANGE_2VREF) + scale_mv *= 2; + + *val = scale_mv; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + if (st->range == AD7266_RANGE_2VREF && + st->mode != AD7266_MODE_DIFF) + *val = 2048; + else + *val = 0; + return IIO_VAL_INT; + } + return -EINVAL; +} + +#define AD7266_CHAN(_chan, _sign) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = (_chan), \ + .scan_type = { \ + .sign = (_sign), \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 2, \ + .endianness = IIO_BE, \ + }, \ +} + +#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_##_name[] = { \ + AD7266_CHAN(0, (_sign)), \ + AD7266_CHAN(1, (_sign)), \ + AD7266_CHAN(2, (_sign)), \ + AD7266_CHAN(3, (_sign)), \ + AD7266_CHAN(4, (_sign)), \ + AD7266_CHAN(5, (_sign)), \ + AD7266_CHAN(6, (_sign)), \ + AD7266_CHAN(7, (_sign)), \ + AD7266_CHAN(8, (_sign)), \ + AD7266_CHAN(9, (_sign)), \ + AD7266_CHAN(10, (_sign)), \ + AD7266_CHAN(11, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(13), \ +} + +#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_##_name##_fixed[] = { \ + AD7266_CHAN(0, (_sign)), \ + AD7266_CHAN(1, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(2), \ +} + +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(u, 'u'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(s, 's'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(u, 'u'); +static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(s, 's'); + +#define AD7266_CHAN_DIFF(_chan, _sign) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan) * 2, \ + .channel2 = (_chan) * 2 + 1, \ + .address = (_chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = (_chan), \ + .scan_type = { \ + .sign = _sign, \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 2, \ + .endianness = IIO_BE, \ + }, \ + .differential = 1, \ +} + +#define AD7266_DECLARE_DIFF_CHANNELS(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_diff_##_name[] = { \ + AD7266_CHAN_DIFF(0, (_sign)), \ + AD7266_CHAN_DIFF(1, (_sign)), \ + AD7266_CHAN_DIFF(2, (_sign)), \ + AD7266_CHAN_DIFF(3, (_sign)), \ + AD7266_CHAN_DIFF(4, (_sign)), \ + AD7266_CHAN_DIFF(5, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(6), \ +} + +static AD7266_DECLARE_DIFF_CHANNELS(s, 's'); +static AD7266_DECLARE_DIFF_CHANNELS(u, 'u'); + +#define AD7266_DECLARE_DIFF_CHANNELS_FIXED(_name, _sign) \ +const struct iio_chan_spec ad7266_channels_diff_fixed_##_name[] = { \ + AD7266_CHAN_DIFF(0, (_sign)), \ + AD7266_CHAN_DIFF(1, (_sign)), \ + IIO_CHAN_SOFT_TIMESTAMP(2), \ +} + +static AD7266_DECLARE_DIFF_CHANNELS_FIXED(s, 's'); +static AD7266_DECLARE_DIFF_CHANNELS_FIXED(u, 'u'); + +static const struct iio_info ad7266_info = { + .read_raw = &ad7266_read_raw, + .update_scan_mode = &ad7266_update_scan_mode, +}; + +static const unsigned long ad7266_available_scan_masks[] = { + 0x003, + 0x00c, + 0x030, + 0x0c0, + 0x300, + 0xc00, + 0x000, +}; + +static const unsigned long ad7266_available_scan_masks_diff[] = { + 0x003, + 0x00c, + 0x030, + 0x000, +}; + +static const unsigned long ad7266_available_scan_masks_fixed[] = { + 0x003, + 0x000, +}; + +struct ad7266_chan_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + const unsigned long *scan_masks; +}; + +#define AD7266_CHAN_INFO_INDEX(_differential, _signed, _fixed) \ + (((_differential) << 2) | ((_signed) << 1) | ((_fixed) << 0)) + +static const struct ad7266_chan_info ad7266_chan_infos[] = { + [AD7266_CHAN_INFO_INDEX(0, 0, 0)] = { + .channels = ad7266_channels_u, + .num_channels = ARRAY_SIZE(ad7266_channels_u), + .scan_masks = ad7266_available_scan_masks, + }, + [AD7266_CHAN_INFO_INDEX(0, 0, 1)] = { + .channels = ad7266_channels_u_fixed, + .num_channels = ARRAY_SIZE(ad7266_channels_u_fixed), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(0, 1, 0)] = { + .channels = ad7266_channels_s, + .num_channels = ARRAY_SIZE(ad7266_channels_s), + .scan_masks = ad7266_available_scan_masks, + }, + [AD7266_CHAN_INFO_INDEX(0, 1, 1)] = { + .channels = ad7266_channels_s_fixed, + .num_channels = ARRAY_SIZE(ad7266_channels_s_fixed), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(1, 0, 0)] = { + .channels = ad7266_channels_diff_u, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_u), + .scan_masks = ad7266_available_scan_masks_diff, + }, + [AD7266_CHAN_INFO_INDEX(1, 0, 1)] = { + .channels = ad7266_channels_diff_fixed_u, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_u), + .scan_masks = ad7266_available_scan_masks_fixed, + }, + [AD7266_CHAN_INFO_INDEX(1, 1, 0)] = { + .channels = ad7266_channels_diff_s, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_s), + .scan_masks = ad7266_available_scan_masks_diff, + }, + [AD7266_CHAN_INFO_INDEX(1, 1, 1)] = { + .channels = ad7266_channels_diff_fixed_s, + .num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_s), + .scan_masks = ad7266_available_scan_masks_fixed, + }, +}; + +static void ad7266_init_channels(struct iio_dev *indio_dev) +{ + struct ad7266_state *st = iio_priv(indio_dev); + bool is_differential, is_signed; + const struct ad7266_chan_info *chan_info; + int i; + + is_differential = st->mode != AD7266_MODE_SINGLE_ENDED; + is_signed = (st->range == AD7266_RANGE_2VREF) | + (st->mode == AD7266_MODE_DIFF); + + i = AD7266_CHAN_INFO_INDEX(is_differential, is_signed, st->fixed_addr); + chan_info = &ad7266_chan_infos[i]; + + indio_dev->channels = chan_info->channels; + indio_dev->num_channels = chan_info->num_channels; + indio_dev->available_scan_masks = chan_info->scan_masks; + indio_dev->masklength = chan_info->num_channels - 1; +} + +static const char * const ad7266_gpio_labels[] = { + "ad0", "ad1", "ad2", +}; + +static int ad7266_probe(struct spi_device *spi) +{ + struct ad7266_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad7266_state *st; + unsigned int i; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = devm_regulator_get_optional(&spi->dev, "vref"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_disable_reg; + + st->vref_mv = ret / 1000; + } else { + /* Any other error indicates that the regulator does exist */ + if (PTR_ERR(st->reg) != -ENODEV) + return PTR_ERR(st->reg); + /* Use internal reference */ + st->vref_mv = 2500; + } + + if (pdata) { + st->fixed_addr = pdata->fixed_addr; + st->mode = pdata->mode; + st->range = pdata->range; + + if (!st->fixed_addr) { + for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) { + st->gpios[i] = devm_gpiod_get(&spi->dev, + ad7266_gpio_labels[i], + GPIOD_OUT_LOW); + if (IS_ERR(st->gpios[i])) { + ret = PTR_ERR(st->gpios[i]); + goto error_disable_reg; + } + } + } + } else { + st->fixed_addr = true; + st->range = AD7266_RANGE_VREF; + st->mode = AD7266_MODE_DIFF; + } + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7266_info; + + ad7266_init_channels(indio_dev); + + /* wakeup */ + st->single_xfer[0].rx_buf = &st->data.sample[0]; + st->single_xfer[0].len = 2; + st->single_xfer[0].cs_change = 1; + /* conversion */ + st->single_xfer[1].rx_buf = st->data.sample; + st->single_xfer[1].len = 4; + st->single_xfer[1].cs_change = 1; + /* powerdown */ + st->single_xfer[2].tx_buf = &st->data.sample[0]; + st->single_xfer[2].len = 1; + + spi_message_init(&st->single_msg); + spi_message_add_tail(&st->single_xfer[0], &st->single_msg); + spi_message_add_tail(&st->single_xfer[1], &st->single_msg); + spi_message_add_tail(&st->single_xfer[2], &st->single_msg); + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &ad7266_trigger_handler, &iio_triggered_buffer_setup_ops); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_buffer_cleanup; + + return 0; + +error_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); + + return ret; +} + +static int ad7266_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7266_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); + + return 0; +} + +static const struct spi_device_id ad7266_id[] = { + {"ad7265", 0}, + {"ad7266", 0}, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7266_id); + +static struct spi_driver ad7266_driver = { + .driver = { + .name = "ad7266", + }, + .probe = ad7266_probe, + .remove = ad7266_remove, + .id_table = ad7266_id, +}; +module_spi_driver(ad7266_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Analog Devices AD7266/65 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c new file mode 100644 index 000000000..2301a0e27 --- /dev/null +++ b/drivers/iio/adc/ad7291.c @@ -0,0 +1,589 @@ +// 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. + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/sysfs.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> + +/* + * Simplified handling + * + * If no events enabled - single polled channel read + * If event enabled direct reads disable unless channel + * is in the read mask. + * + * The noise-delayed bit as per datasheet suggestion is always enabled. + */ + +/* + * AD7291 registers definition + */ +#define AD7291_COMMAND 0x00 +#define AD7291_VOLTAGE 0x01 +#define AD7291_T_SENSE 0x02 +#define AD7291_T_AVERAGE 0x03 +#define AD7291_DATA_HIGH(x) ((x) * 3 + 0x4) +#define AD7291_DATA_LOW(x) ((x) * 3 + 0x5) +#define AD7291_HYST(x) ((x) * 3 + 0x6) +#define AD7291_VOLTAGE_ALERT_STATUS 0x1F +#define AD7291_T_ALERT_STATUS 0x20 + +#define AD7291_BITS 12 +#define AD7291_VOLTAGE_LIMIT_COUNT 8 + + +/* + * AD7291 command + */ +#define AD7291_AUTOCYCLE BIT(0) +#define AD7291_RESET BIT(1) +#define AD7291_ALERT_CLEAR BIT(2) +#define AD7291_ALERT_POLARITY BIT(3) +#define AD7291_EXT_REF BIT(4) +#define AD7291_NOISE_DELAY BIT(5) +#define AD7291_T_SENSE_MASK BIT(7) +#define AD7291_VOLTAGE_MASK GENMASK(15, 8) +#define AD7291_VOLTAGE_OFFSET 8 + +/* + * AD7291 value masks + */ +#define AD7291_VALUE_MASK GENMASK(11, 0) + +/* + * AD7291 alert register bits + */ +#define AD7291_T_LOW BIT(0) +#define AD7291_T_HIGH BIT(1) +#define AD7291_T_AVG_LOW BIT(2) +#define AD7291_T_AVG_HIGH BIT(3) +#define AD7291_V_LOW(x) BIT((x) * 2) +#define AD7291_V_HIGH(x) BIT((x) * 2 + 1) + + +struct ad7291_chip_info { + struct i2c_client *client; + struct regulator *reg; + u16 command; + u16 c_mask; /* Active voltage channels for events */ + struct mutex state_lock; +}; + +static int ad7291_i2c_read(struct ad7291_chip_info *chip, u8 reg, u16 *data) +{ + struct i2c_client *client = chip->client; + int ret = 0; + + ret = i2c_smbus_read_word_swapped(client, reg); + if (ret < 0) { + dev_err(&client->dev, "I2C read error\n"); + return ret; + } + + *data = ret; + + return 0; +} + +static int ad7291_i2c_write(struct ad7291_chip_info *chip, u8 reg, u16 data) +{ + return i2c_smbus_write_word_swapped(chip->client, reg, data); +} + +static irqreturn_t ad7291_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad7291_chip_info *chip = iio_priv(private); + u16 t_status, v_status; + u16 command; + int i; + s64 timestamp = iio_get_time_ns(indio_dev); + + if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status)) + return IRQ_HANDLED; + + if (ad7291_i2c_read(chip, AD7291_VOLTAGE_ALERT_STATUS, &v_status)) + return IRQ_HANDLED; + + if (!(t_status || v_status)) + return IRQ_HANDLED; + + command = chip->command | AD7291_ALERT_CLEAR; + ad7291_i2c_write(chip, AD7291_COMMAND, command); + + command = chip->command & ~AD7291_ALERT_CLEAR; + ad7291_i2c_write(chip, AD7291_COMMAND, command); + + /* For now treat t_sense and t_sense_average the same */ + if ((t_status & AD7291_T_LOW) || (t_status & AD7291_T_AVG_LOW)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + if ((t_status & AD7291_T_HIGH) || (t_status & AD7291_T_AVG_HIGH)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_TEMP, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + + for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) { + if (v_status & AD7291_V_LOW(i)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + timestamp); + if (v_status & AD7291_V_HIGH(i)) + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + i, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + timestamp); + } + + return IRQ_HANDLED; +} + +static unsigned int ad7291_threshold_reg(const struct iio_chan_spec *chan, + enum iio_event_direction dir, + enum iio_event_info info) +{ + unsigned int offset; + + switch (chan->type) { + case IIO_VOLTAGE: + offset = chan->channel; + break; + case IIO_TEMP: + offset = AD7291_VOLTAGE_OFFSET; + break; + default: + return 0; + } + + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_FALLING) + return AD7291_DATA_HIGH(offset); + else + return AD7291_DATA_LOW(offset); + case IIO_EV_INFO_HYSTERESIS: + return AD7291_HYST(offset); + default: + break; + } + return 0; +} + +static int ad7291_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct ad7291_chip_info *chip = iio_priv(indio_dev); + int ret; + u16 uval; + + ret = ad7291_i2c_read(chip, ad7291_threshold_reg(chan, dir, info), + &uval); + if (ret < 0) + return ret; + + if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) + *val = uval & AD7291_VALUE_MASK; + + else + *val = sign_extend32(uval, 11); + + return IIO_VAL_INT; +} + +static int ad7291_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct ad7291_chip_info *chip = iio_priv(indio_dev); + + if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) { + if (val > AD7291_VALUE_MASK || val < 0) + return -EINVAL; + } else { + if (val > 2047 || val < -2048) + return -EINVAL; + } + + return ad7291_i2c_write(chip, ad7291_threshold_reg(chan, dir, info), + val); +} + +static int ad7291_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7291_chip_info *chip = iio_priv(indio_dev); + /* + * To be enabled the channel must simply be on. If any are enabled + * we are in continuous sampling mode + */ + + switch (chan->type) { + case IIO_VOLTAGE: + return !!(chip->c_mask & BIT(15 - chan->channel)); + case IIO_TEMP: + /* always on */ + return 1; + default: + return -EINVAL; + } + +} + +static int ad7291_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + int ret = 0; + struct ad7291_chip_info *chip = iio_priv(indio_dev); + unsigned int mask; + u16 regval; + + mutex_lock(&chip->state_lock); + regval = chip->command; + /* + * To be enabled the channel must simply be on. If any are enabled + * use continuous sampling mode. + * Possible to disable temp as well but that makes single read tricky. + */ + + mask = BIT(15 - chan->channel); + + switch (chan->type) { + case IIO_VOLTAGE: + if ((!state) && (chip->c_mask & mask)) + chip->c_mask &= ~mask; + else if (state && (!(chip->c_mask & mask))) + chip->c_mask |= mask; + else + break; + + regval &= ~AD7291_AUTOCYCLE; + regval |= chip->c_mask; + if (chip->c_mask) /* Enable autocycle? */ + regval |= AD7291_AUTOCYCLE; + + ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval); + if (ret < 0) + goto error_ret; + + chip->command = regval; + break; + default: + ret = -EINVAL; + } + +error_ret: + mutex_unlock(&chip->state_lock); + return ret; +} + +static int ad7291_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + int ret; + struct ad7291_chip_info *chip = iio_priv(indio_dev); + u16 regval; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + mutex_lock(&chip->state_lock); + /* If in autocycle mode drop through */ + if (chip->command & AD7291_AUTOCYCLE) { + mutex_unlock(&chip->state_lock); + return -EBUSY; + } + /* Enable this channel alone */ + regval = chip->command & (~AD7291_VOLTAGE_MASK); + regval |= BIT(15 - chan->channel); + ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval); + if (ret < 0) { + mutex_unlock(&chip->state_lock); + return ret; + } + /* Read voltage */ + ret = i2c_smbus_read_word_swapped(chip->client, + AD7291_VOLTAGE); + if (ret < 0) { + mutex_unlock(&chip->state_lock); + return ret; + } + *val = ret & AD7291_VALUE_MASK; + mutex_unlock(&chip->state_lock); + return IIO_VAL_INT; + case IIO_TEMP: + /* Assumes tsense bit of command register always set */ + ret = i2c_smbus_read_word_swapped(chip->client, + AD7291_T_SENSE); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 11); + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_AVERAGE_RAW: + ret = i2c_smbus_read_word_swapped(chip->client, + AD7291_T_AVERAGE); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 11); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chip->reg) { + int vref; + + vref = regulator_get_voltage(chip->reg); + if (vref < 0) + return vref; + *val = vref / 1000; + } else { + *val = 2500; + } + *val2 = AD7291_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + /* + * One LSB of the ADC corresponds to 0.25 deg C. + * The temperature reading is in 12-bit twos + * complement format + */ + *val = 250; + return IIO_VAL_INT; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_event_spec ad7291_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +#define AD7291_VOLTAGE_CHAN(_chan) \ +{ \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .channel = _chan, \ + .event_spec = ad7291_events, \ + .num_event_specs = ARRAY_SIZE(ad7291_events), \ +} + +static const struct iio_chan_spec ad7291_channels[] = { + AD7291_VOLTAGE_CHAN(0), + AD7291_VOLTAGE_CHAN(1), + AD7291_VOLTAGE_CHAN(2), + AD7291_VOLTAGE_CHAN(3), + AD7291_VOLTAGE_CHAN(4), + AD7291_VOLTAGE_CHAN(5), + AD7291_VOLTAGE_CHAN(6), + AD7291_VOLTAGE_CHAN(7), + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_AVERAGE_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = 0, + .event_spec = ad7291_events, + .num_event_specs = ARRAY_SIZE(ad7291_events), + } +}; + +static const struct iio_info ad7291_info = { + .read_raw = &ad7291_read_raw, + .read_event_config = &ad7291_read_event_config, + .write_event_config = &ad7291_write_event_config, + .read_event_value = &ad7291_read_event_value, + .write_event_value = &ad7291_write_event_value, +}; + +static int ad7291_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ad7291_chip_info *chip; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + chip = iio_priv(indio_dev); + + mutex_init(&chip->state_lock); + /* this is only used for device removal purposes */ + i2c_set_clientdata(client, indio_dev); + + chip->client = client; + + chip->command = AD7291_NOISE_DELAY | + AD7291_T_SENSE_MASK | /* Tsense always enabled */ + AD7291_ALERT_POLARITY; /* set irq polarity low level */ + + chip->reg = devm_regulator_get_optional(&client->dev, "vref"); + if (IS_ERR(chip->reg)) { + if (PTR_ERR(chip->reg) != -ENODEV) + return PTR_ERR(chip->reg); + + chip->reg = NULL; + } + + if (chip->reg) { + ret = regulator_enable(chip->reg); + if (ret) + return ret; + + chip->command |= AD7291_EXT_REF; + } + + indio_dev->name = id->name; + indio_dev->channels = ad7291_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7291_channels); + + indio_dev->info = &ad7291_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = ad7291_i2c_write(chip, AD7291_COMMAND, AD7291_RESET); + if (ret) { + ret = -EIO; + goto error_disable_reg; + } + + ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command); + if (ret) { + ret = -EIO; + goto error_disable_reg; + } + + if (client->irq > 0) { + ret = request_threaded_irq(client->irq, + NULL, + &ad7291_event_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + id->name, + indio_dev); + if (ret) + goto error_disable_reg; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unreg_irq; + + return 0; + +error_unreg_irq: + if (client->irq) + free_irq(client->irq, indio_dev); +error_disable_reg: + if (chip->reg) + regulator_disable(chip->reg); + + return ret; +} + +static int ad7291_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ad7291_chip_info *chip = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (client->irq) + free_irq(client->irq, indio_dev); + + if (chip->reg) + regulator_disable(chip->reg); + + return 0; +} + +static const struct i2c_device_id ad7291_id[] = { + { "ad7291", 0 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ad7291_id); + +static const struct of_device_id ad7291_of_match[] = { + { .compatible = "adi,ad7291" }, + {} +}; +MODULE_DEVICE_TABLE(of, ad7291_of_match); + +static struct i2c_driver ad7291_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ad7291_of_match, + }, + .probe = ad7291_probe, + .remove = ad7291_remove, + .id_table = ad7291_id, +}; +module_i2c_driver(ad7291_driver); + +MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7291 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c new file mode 100644 index 000000000..3e6ece058 --- /dev/null +++ b/drivers/iio/adc/ad7292.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7292 SPI ADC driver + * + * Copyright 2019 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include <linux/iio/iio.h> + +#define ADI_VENDOR_ID 0x0018 + +/* AD7292 registers definition */ +#define AD7292_REG_VENDOR_ID 0x00 +#define AD7292_REG_CONF_BANK 0x05 +#define AD7292_REG_CONV_COMM 0x0E +#define AD7292_REG_ADC_CH(x) (0x10 + (x)) + +/* AD7292 configuration bank subregisters definition */ +#define AD7292_BANK_REG_VIN_RNG0 0x10 +#define AD7292_BANK_REG_VIN_RNG1 0x11 +#define AD7292_BANK_REG_SAMP_MODE 0x12 + +#define AD7292_RD_FLAG_MSK(x) (BIT(7) | ((x) & 0x3F)) + +/* AD7292_REG_ADC_CONVERSION */ +#define AD7292_ADC_DATA_MASK GENMASK(15, 6) +#define AD7292_ADC_DATA(x) FIELD_GET(AD7292_ADC_DATA_MASK, x) + +/* AD7292_CHANNEL_SAMPLING_MODE */ +#define AD7292_CH_SAMP_MODE(reg, ch) (((reg) >> 8) & BIT(ch)) + +/* AD7292_CHANNEL_VIN_RANGE */ +#define AD7292_CH_VIN_RANGE(reg, ch) ((reg) & BIT(ch)) + +#define AD7292_VOLTAGE_CHAN(_chan) \ +{ \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = 1, \ + .channel = _chan, \ +} + +static const struct iio_chan_spec ad7292_channels[] = { + AD7292_VOLTAGE_CHAN(0), + AD7292_VOLTAGE_CHAN(1), + AD7292_VOLTAGE_CHAN(2), + AD7292_VOLTAGE_CHAN(3), + AD7292_VOLTAGE_CHAN(4), + AD7292_VOLTAGE_CHAN(5), + AD7292_VOLTAGE_CHAN(6), + AD7292_VOLTAGE_CHAN(7) +}; + +static const struct iio_chan_spec ad7292_channels_diff[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .indexed = 1, + .differential = 1, + .channel = 0, + .channel2 = 1, + }, + AD7292_VOLTAGE_CHAN(2), + AD7292_VOLTAGE_CHAN(3), + AD7292_VOLTAGE_CHAN(4), + AD7292_VOLTAGE_CHAN(5), + AD7292_VOLTAGE_CHAN(6), + AD7292_VOLTAGE_CHAN(7) +}; + +struct ad7292_state { + struct spi_device *spi; + struct regulator *reg; + unsigned short vref_mv; + + __be16 d16 ____cacheline_aligned; + u8 d8[2]; +}; + +static int ad7292_spi_reg_read(struct ad7292_state *st, unsigned int addr) +{ + int ret; + + st->d8[0] = AD7292_RD_FLAG_MSK(addr); + + ret = spi_write_then_read(st->spi, st->d8, 1, &st->d16, 2); + if (ret < 0) + return ret; + + return be16_to_cpu(st->d16); +} + +static int ad7292_spi_subreg_read(struct ad7292_state *st, unsigned int addr, + unsigned int sub_addr, unsigned int len) +{ + unsigned int shift = 16 - (8 * len); + int ret; + + st->d8[0] = AD7292_RD_FLAG_MSK(addr); + st->d8[1] = sub_addr; + + ret = spi_write_then_read(st->spi, st->d8, 2, &st->d16, len); + if (ret < 0) + return ret; + + return (be16_to_cpu(st->d16) >> shift); +} + +static int ad7292_single_conversion(struct ad7292_state *st, + unsigned int chan_addr) +{ + int ret; + + struct spi_transfer t[] = { + { + .tx_buf = &st->d8, + .len = 4, + .delay = { + .value = 6, + .unit = SPI_DELAY_UNIT_USECS + }, + }, { + .rx_buf = &st->d16, + .len = 2, + }, + }; + + st->d8[0] = chan_addr; + st->d8[1] = AD7292_RD_FLAG_MSK(AD7292_REG_CONV_COMM); + + ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); + + if (ret < 0) + return ret; + + return be16_to_cpu(st->d16); +} + +static int ad7292_vin_range_multiplier(struct ad7292_state *st, int channel) +{ + int samp_mode, range0, range1, factor = 1; + + /* + * Every AD7292 ADC channel may have its input range adjusted according + * to the settings at the ADC sampling mode and VIN range subregisters. + * For a given channel, the minimum input range is equal to Vref, and it + * may be increased by a multiplier factor of 2 or 4 according to the + * following rule: + * If channel is being sampled with respect to AGND: + * factor = 4 if VIN range0 and VIN range1 equal 0 + * factor = 2 if only one of VIN ranges equal 1 + * factor = 1 if both VIN range0 and VIN range1 equal 1 + * If channel is being sampled with respect to AVDD: + * factor = 4 if VIN range0 and VIN range1 equal 0 + * Behavior is undefined if any of VIN range doesn't equal 0 + */ + + samp_mode = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK, + AD7292_BANK_REG_SAMP_MODE, 2); + + if (samp_mode < 0) + return samp_mode; + + range0 = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK, + AD7292_BANK_REG_VIN_RNG0, 2); + + if (range0 < 0) + return range0; + + range1 = ad7292_spi_subreg_read(st, AD7292_REG_CONF_BANK, + AD7292_BANK_REG_VIN_RNG1, 2); + + if (range1 < 0) + return range1; + + if (AD7292_CH_SAMP_MODE(samp_mode, channel)) { + /* Sampling with respect to AGND */ + if (!AD7292_CH_VIN_RANGE(range0, channel)) + factor *= 2; + + if (!AD7292_CH_VIN_RANGE(range1, channel)) + factor *= 2; + + } else { + /* Sampling with respect to AVDD */ + if (AD7292_CH_VIN_RANGE(range0, channel) || + AD7292_CH_VIN_RANGE(range1, channel)) + return -EPERM; + + factor = 4; + } + + return factor; +} + +static int ad7292_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long info) +{ + struct ad7292_state *st = iio_priv(indio_dev); + unsigned int ch_addr; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ch_addr = AD7292_REG_ADC_CH(chan->channel); + ret = ad7292_single_conversion(st, ch_addr); + if (ret < 0) + return ret; + + *val = AD7292_ADC_DATA(ret); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * To convert a raw value to standard units, the IIO defines + * this formula: Scaled value = (raw + offset) * scale. + * For the scale to be a correct multiplier for (raw + offset), + * it must be calculated as the input range divided by the + * number of possible distinct input values. Given the ADC data + * is 10 bit long, it may assume 2^10 distinct values. + * Hence, scale = range / 2^10. The IIO_VAL_FRACTIONAL_LOG2 + * return type indicates to the IIO API to divide *val by 2 to + * the power of *val2 when returning from read_raw. + */ + + ret = ad7292_vin_range_multiplier(st, chan->channel); + if (ret < 0) + return ret; + + *val = st->vref_mv * ret; + *val2 = 10; + return IIO_VAL_FRACTIONAL_LOG2; + default: + break; + } + return -EINVAL; +} + +static const struct iio_info ad7292_info = { + .read_raw = ad7292_read_raw, +}; + +static void ad7292_regulator_disable(void *data) +{ + struct ad7292_state *st = data; + + regulator_disable(st->reg); +} + +static int ad7292_probe(struct spi_device *spi) +{ + struct ad7292_state *st; + struct iio_dev *indio_dev; + struct device_node *child; + bool diff_channels = 0; + 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; + + ret = ad7292_spi_reg_read(st, AD7292_REG_VENDOR_ID); + if (ret != ADI_VENDOR_ID) { + dev_err(&spi->dev, "Wrong vendor id 0x%x\n", ret); + return -EINVAL; + } + + spi_set_drvdata(spi, indio_dev); + + st->reg = devm_regulator_get_optional(&spi->dev, "vref"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) { + dev_err(&spi->dev, + "Failed to enable external vref supply\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, + ad7292_regulator_disable, st); + if (ret) + return ret; + + ret = regulator_get_voltage(st->reg); + if (ret < 0) + return ret; + + st->vref_mv = ret / 1000; + } else { + /* Use the internal voltage reference. */ + st->vref_mv = 1250; + } + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7292_info; + + for_each_available_child_of_node(spi->dev.of_node, child) { + diff_channels = of_property_read_bool(child, "diff-channels"); + if (diff_channels) { + of_node_put(child); + break; + } + } + + if (diff_channels) { + indio_dev->num_channels = ARRAY_SIZE(ad7292_channels_diff); + indio_dev->channels = ad7292_channels_diff; + } else { + indio_dev->num_channels = ARRAY_SIZE(ad7292_channels); + indio_dev->channels = ad7292_channels; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7292_id_table[] = { + { "ad7292", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7292_id_table); + +static const struct of_device_id ad7292_of_match[] = { + { .compatible = "adi,ad7292" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ad7292_of_match); + +static struct spi_driver ad7292_driver = { + .driver = { + .name = "ad7292", + .of_match_table = ad7292_of_match, + }, + .probe = ad7292_probe, + .id_table = ad7292_id_table, +}; +module_spi_driver(ad7292_driver); + +MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt1@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices AD7292 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c new file mode 100644 index 000000000..48d43cb0f --- /dev/null +++ b/drivers/iio/adc/ad7298.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7298 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc. + */ + +#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/delay.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/bitops.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/platform_data/ad7298.h> + +#define AD7298_WRITE BIT(15) /* write to the control register */ +#define AD7298_REPEAT BIT(14) /* repeated conversion enable */ +#define AD7298_CH(x) BIT(13 - (x)) /* channel select */ +#define AD7298_TSENSE BIT(5) /* temperature conversion enable */ +#define AD7298_EXTREF BIT(2) /* external reference enable */ +#define AD7298_TAVG BIT(1) /* temperature sensor averaging enable */ +#define AD7298_PDD BIT(0) /* partial power down enable */ + +#define AD7298_MAX_CHAN 8 +#define AD7298_INTREF_mV 2500 + +#define AD7298_CH_TEMP 9 + +struct ad7298_state { + struct spi_device *spi; + struct regulator *reg; + unsigned ext_ref; + struct spi_transfer ring_xfer[10]; + struct spi_transfer scan_single_xfer[3]; + struct spi_message ring_msg; + struct spi_message scan_single_msg; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be16 rx_buf[12] ____cacheline_aligned; + __be16 tx_buf[2]; +}; + +#define AD7298_V_CHAN(index) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec ad7298_channels[] = { + { + .type = IIO_TEMP, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .address = AD7298_CH_TEMP, + .scan_index = -1, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + }, + }, + AD7298_V_CHAN(0), + AD7298_V_CHAN(1), + AD7298_V_CHAN(2), + AD7298_V_CHAN(3), + AD7298_V_CHAN(4), + AD7298_V_CHAN(5), + AD7298_V_CHAN(6), + AD7298_V_CHAN(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +/* + * ad7298_update_scan_mode() setup the spi transfer buffer for the new scan mask + */ +static int ad7298_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad7298_state *st = iio_priv(indio_dev); + int i, m; + unsigned short command; + int scan_count; + + /* Now compute overall size */ + scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength); + + command = AD7298_WRITE | st->ext_ref; + + for (i = 0, m = AD7298_CH(0); i < AD7298_MAX_CHAN; i++, m >>= 1) + if (test_bit(i, active_scan_mask)) + command |= m; + + st->tx_buf[0] = cpu_to_be16(command); + + /* build spi ring message */ + st->ring_xfer[0].tx_buf = &st->tx_buf[0]; + st->ring_xfer[0].len = 2; + st->ring_xfer[0].cs_change = 1; + st->ring_xfer[1].tx_buf = &st->tx_buf[1]; + st->ring_xfer[1].len = 2; + st->ring_xfer[1].cs_change = 1; + + spi_message_init(&st->ring_msg); + spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg); + spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg); + + for (i = 0; i < scan_count; i++) { + st->ring_xfer[i + 2].rx_buf = &st->rx_buf[i]; + st->ring_xfer[i + 2].len = 2; + st->ring_xfer[i + 2].cs_change = 1; + spi_message_add_tail(&st->ring_xfer[i + 2], &st->ring_msg); + } + /* make sure last transfer cs_change is not set */ + st->ring_xfer[i + 1].cs_change = 0; + + return 0; +} + +/* + * ad7298_trigger_handler() bh of trigger launched polling to ring buffer + * + * Currently there is no option in this driver to disable the saving of + * timestamps within the ring. + */ +static irqreturn_t ad7298_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7298_state *st = iio_priv(indio_dev); + int b_sent; + + b_sent = spi_sync(st->spi, &st->ring_msg); + if (b_sent) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad7298_scan_direct(struct ad7298_state *st, unsigned ch) +{ + int ret; + st->tx_buf[0] = cpu_to_be16(AD7298_WRITE | st->ext_ref | + (AD7298_CH(0) >> ch)); + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + return be16_to_cpu(st->rx_buf[0]); +} + +static int ad7298_scan_temp(struct ad7298_state *st, int *val) +{ + int ret; + __be16 buf; + + buf = cpu_to_be16(AD7298_WRITE | AD7298_TSENSE | + AD7298_TAVG | st->ext_ref); + + ret = spi_write(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + buf = cpu_to_be16(0); + + ret = spi_write(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + usleep_range(101, 1000); /* sleep > 100us */ + + ret = spi_read(st->spi, (u8 *)&buf, 2); + if (ret) + return ret; + + *val = sign_extend32(be16_to_cpu(buf), 11); + + return 0; +} + +static int ad7298_get_ref_voltage(struct ad7298_state *st) +{ + int vref; + + if (st->ext_ref) { + vref = regulator_get_voltage(st->reg); + if (vref < 0) + return vref; + + return vref / 1000; + } else { + return AD7298_INTREF_mV; + } +} + +static int ad7298_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7298_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; + + if (chan->address == AD7298_CH_TEMP) + ret = ad7298_scan_temp(st, val); + else + ret = ad7298_scan_direct(st, chan->address); + + iio_device_release_direct_mode(indio_dev); + + if (ret < 0) + return ret; + + if (chan->address != AD7298_CH_TEMP) + *val = ret & GENMASK(chan->scan_type.realbits - 1, 0); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = ad7298_get_ref_voltage(st); + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + *val = ad7298_get_ref_voltage(st); + *val2 = 10; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + *val = 1093 - 2732500 / ad7298_get_ref_voltage(st); + return IIO_VAL_INT; + } + return -EINVAL; +} + +static const struct iio_info ad7298_info = { + .read_raw = &ad7298_read_raw, + .update_scan_mode = ad7298_update_scan_mode, +}; + +static int ad7298_probe(struct spi_device *spi) +{ + struct ad7298_platform_data *pdata = spi->dev.platform_data; + struct ad7298_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + if (pdata && pdata->ext_ref) + st->ext_ref = AD7298_EXTREF; + + if (st->ext_ref) { + st->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + } + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7298_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7298_channels); + indio_dev->info = &ad7298_info; + + /* Setup default message */ + + st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; + st->scan_single_xfer[0].len = 2; + st->scan_single_xfer[0].cs_change = 1; + st->scan_single_xfer[1].tx_buf = &st->tx_buf[1]; + st->scan_single_xfer[1].len = 2; + st->scan_single_xfer[1].cs_change = 1; + st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[2].len = 2; + + spi_message_init(&st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[2], &st->scan_single_msg); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &ad7298_trigger_handler, NULL); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_ring; + + return 0; + +error_cleanup_ring: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + if (st->ext_ref) + regulator_disable(st->reg); + + return ret; +} + +static int ad7298_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7298_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (st->ext_ref) + regulator_disable(st->reg); + + return 0; +} + +static const struct spi_device_id ad7298_id[] = { + {"ad7298", 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7298_id); + +static struct spi_driver ad7298_driver = { + .driver = { + .name = "ad7298", + }, + .probe = ad7298_probe, + .remove = ad7298_remove, + .id_table = ad7298_id, +}; +module_spi_driver(ad7298_driver); + +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 new file mode 100644 index 000000000..bf5572670 --- /dev/null +++ b/drivers/iio/adc/ad7476.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver + * TI ADC081S/ADC101S/ADC121S 8/10/12-bit SPI ADC driver + * + * Copyright 2010 Analog Devices Inc. + */ + +#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/gpio/consumer.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/bitops.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +struct ad7476_state; + +struct ad7476_chip_info { + unsigned int int_vref_uv; + struct iio_chan_spec channel[2]; + /* channels used when convst gpio is defined */ + struct iio_chan_spec convst_channel[2]; + void (*reset)(struct ad7476_state *); +}; + +struct ad7476_state { + struct spi_device *spi; + const struct ad7476_chip_info *chip_info; + struct regulator *reg; + struct gpio_desc *convst_gpio; + struct spi_transfer xfer; + struct spi_message msg; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * Make the buffer large enough for one 16 bit sample and one 64 bit + * aligned 64 bit timestamp. + */ + unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)] + ____cacheline_aligned; +}; + +enum ad7476_supported_device_ids { + ID_AD7091R, + ID_AD7276, + ID_AD7277, + ID_AD7278, + ID_AD7466, + ID_AD7467, + ID_AD7468, + ID_AD7495, + ID_AD7940, + ID_ADC081S, + ID_ADC101S, + ID_ADC121S, + ID_ADS7866, + ID_ADS7867, + ID_ADS7868, +}; + +static void ad7091_convst(struct ad7476_state *st) +{ + if (!st->convst_gpio) + return; + + gpiod_set_value(st->convst_gpio, 0); + udelay(1); /* CONVST pulse width: 10 ns min */ + gpiod_set_value(st->convst_gpio, 1); + udelay(1); /* Conversion time: 650 ns max */ +} + +static irqreturn_t ad7476_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7476_state *st = iio_priv(indio_dev); + int b_sent; + + ad7091_convst(st); + + b_sent = spi_sync(st->spi, &st->msg); + if (b_sent < 0) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, st->data, + iio_get_time_ns(indio_dev)); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static void ad7091_reset(struct ad7476_state *st) +{ + /* Any transfers with 8 scl cycles will reset the device */ + spi_read(st->spi, st->data, 1); +} + +static int ad7476_scan_direct(struct ad7476_state *st) +{ + int ret; + + ad7091_convst(st); + + ret = spi_sync(st->spi, &st->msg); + if (ret) + return ret; + + return be16_to_cpup((__be16 *)st->data); +} + +static int ad7476_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7476_state *st = iio_priv(indio_dev); + int scale_uv; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = ad7476_scan_direct(st); + iio_device_release_direct_mode(indio_dev); + + if (ret < 0) + return ret; + *val = (ret >> st->chip_info->channel[0].scan_type.shift) & + GENMASK(st->chip_info->channel[0].scan_type.realbits - 1, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (!st->chip_info->int_vref_uv) { + scale_uv = regulator_get_voltage(st->reg); + if (scale_uv < 0) + return scale_uv; + } else { + scale_uv = st->chip_info->int_vref_uv; + } + *val = scale_uv / 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + +#define _AD7476_CHAN(bits, _shift, _info_mask_sep) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = _info_mask_sep, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = (_shift), \ + .endianness = IIO_BE, \ + }, \ +} + +#define ADC081S_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \ + BIT(IIO_CHAN_INFO_RAW)) +#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \ + BIT(IIO_CHAN_INFO_RAW)) +#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 AD7091R_CONVST_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), \ + BIT(IIO_CHAN_INFO_RAW)) +#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] = { + .channel[0] = AD7091R_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .convst_channel[0] = AD7091R_CONVST_CHAN(12), + .convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .reset = ad7091_reset, + }, + [ID_AD7276] = { + .channel[0] = AD7940_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_AD7277] = { + .channel[0] = AD7940_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_AD7278] = { + .channel[0] = AD7940_CHAN(8), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_AD7466] = { + .channel[0] = AD7476_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_AD7467] = { + .channel[0] = AD7476_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_AD7468] = { + .channel[0] = AD7476_CHAN(8), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_AD7495] = { + .channel[0] = AD7476_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .int_vref_uv = 2500000, + }, + [ID_AD7940] = { + .channel[0] = AD7940_CHAN(14), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_ADC081S] = { + .channel[0] = ADC081S_CHAN(8), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_ADC101S] = { + .channel[0] = ADC081S_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_ADC121S] = { + .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 = { + .read_raw = &ad7476_read_raw, +}; + +static void ad7476_reg_disable(void *data) +{ + struct ad7476_state *st = data; + + regulator_disable(st->reg); +} + +static int ad7476_probe(struct spi_device *spi) +{ + struct ad7476_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->chip_info = + &ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + st->reg = devm_regulator_get(&spi->dev, "vcc"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, + st); + if (ret) + return ret; + + st->convst_gpio = devm_gpiod_get_optional(&spi->dev, + "adi,conversion-start", + GPIOD_OUT_LOW); + if (IS_ERR(st->convst_gpio)) + return PTR_ERR(st->convst_gpio); + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + 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 = 2; + indio_dev->info = &ad7476_info; + + if (st->convst_gpio) + indio_dev->channels = st->chip_info->convst_channel; + /* Setup default message */ + + st->xfer.rx_buf = &st->data; + st->xfer.len = st->chip_info->channel[0].scan_type.storagebits / 8; + + spi_message_init(&st->msg); + spi_message_add_tail(&st->xfer, &st->msg); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + &ad7476_trigger_handler, NULL); + if (ret) + return ret; + + if (st->chip_info->reset) + st->chip_info->reset(st); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7476_id[] = { + {"ad7091", ID_AD7091R}, + {"ad7091r", ID_AD7091R}, + {"ad7273", ID_AD7277}, + {"ad7274", ID_AD7276}, + {"ad7276", ID_AD7276}, + {"ad7277", ID_AD7277}, + {"ad7278", ID_AD7278}, + {"ad7466", ID_AD7466}, + {"ad7467", ID_AD7467}, + {"ad7468", ID_AD7468}, + {"ad7475", ID_AD7466}, + {"ad7476", ID_AD7466}, + {"ad7476a", ID_AD7466}, + {"ad7477", ID_AD7467}, + {"ad7477a", ID_AD7467}, + {"ad7478", ID_AD7468}, + {"ad7478a", ID_AD7468}, + {"ad7495", ID_AD7495}, + {"ad7910", ID_AD7467}, + {"ad7920", ID_AD7466}, + {"ad7940", ID_AD7940}, + {"adc081s", ID_ADC081S}, + {"adc101s", ID_ADC101S}, + {"adc121s", ID_ADC121S}, + {"ads7866", ID_ADS7866}, + {"ads7867", ID_ADS7867}, + {"ads7868", ID_ADS7868}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7476_id); + +static struct spi_driver ad7476_driver = { + .driver = { + .name = "ad7476", + }, + .probe = ad7476_probe, + .id_table = ad7476_id, +}; +module_spi_driver(ad7476_driver); + +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 000000000..ee7b10868 --- /dev/null +++ b/drivers/iio/adc/ad7606.c @@ -0,0 +1,734 @@ +// 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 - 1; + 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); + + 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 0; +} + +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; + + 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 000000000..9350ef1f6 --- /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 000000000..f732b3ac7 --- /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 000000000..29945ad07 --- /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 new file mode 100644 index 000000000..b6b6765be --- /dev/null +++ b/drivers/iio/adc/ad7766.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AD7766/AD7767 SPI ADC driver + * + * Copyright 2016 Analog Devices Inc. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +struct ad7766_chip_info { + unsigned int decimation_factor; +}; + +enum { + AD7766_SUPPLY_AVDD = 0, + AD7766_SUPPLY_DVDD = 1, + AD7766_SUPPLY_VREF = 2, + AD7766_NUM_SUPPLIES = 3 +}; + +struct ad7766 { + const struct ad7766_chip_info *chip_info; + struct spi_device *spi; + struct clk *mclk; + struct gpio_desc *pd_gpio; + struct regulator_bulk_data reg[AD7766_NUM_SUPPLIES]; + + struct iio_trigger *trig; + + struct spi_transfer xfer; + struct spi_message msg; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * Make the buffer large enough for one 24 bit sample and one 64 bit + * aligned 64 bit timestamp. + */ + unsigned char data[ALIGN(3, sizeof(s64)) + sizeof(s64)] + ____cacheline_aligned; +}; + +/* + * AD7766 and AD7767 variations are interface compatible, the main difference is + * analog performance. Both parts will use the same ID. + */ +enum ad7766_device_ids { + ID_AD7766, + ID_AD7766_1, + ID_AD7766_2, +}; + +static irqreturn_t ad7766_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7766 *ad7766 = iio_priv(indio_dev); + int ret; + + ret = spi_sync(ad7766->spi, &ad7766->msg); + if (ret < 0) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, ad7766->data, + pf->timestamp); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad7766_preenable(struct iio_dev *indio_dev) +{ + struct ad7766 *ad7766 = iio_priv(indio_dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ad7766->reg), ad7766->reg); + if (ret < 0) { + dev_err(&ad7766->spi->dev, "Failed to enable supplies: %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(ad7766->mclk); + if (ret < 0) { + dev_err(&ad7766->spi->dev, "Failed to enable MCLK: %d\n", ret); + regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg); + return ret; + } + + gpiod_set_value(ad7766->pd_gpio, 0); + + return 0; +} + +static int ad7766_postdisable(struct iio_dev *indio_dev) +{ + struct ad7766 *ad7766 = iio_priv(indio_dev); + + gpiod_set_value(ad7766->pd_gpio, 1); + + /* + * The PD pin is synchronous to the clock, so give it some time to + * notice the change before we disable the clock. + */ + msleep(20); + + clk_disable_unprepare(ad7766->mclk); + regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg); + + return 0; +} + +static int ad7766_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, int *val2, long info) +{ + struct ad7766 *ad7766 = iio_priv(indio_dev); + struct regulator *vref = ad7766->reg[AD7766_SUPPLY_VREF].consumer; + int scale_uv; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + scale_uv = regulator_get_voltage(vref); + if (scale_uv < 0) + return scale_uv; + *val = scale_uv / 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(ad7766->mclk) / + ad7766->chip_info->decimation_factor; + return IIO_VAL_INT; + } + return -EINVAL; +} + +static const struct iio_chan_spec ad7766_channels[] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 's', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct ad7766_chip_info ad7766_chip_info[] = { + [ID_AD7766] = { + .decimation_factor = 8, + }, + [ID_AD7766_1] = { + .decimation_factor = 16, + }, + [ID_AD7766_2] = { + .decimation_factor = 32, + }, +}; + +static const struct iio_buffer_setup_ops ad7766_buffer_setup_ops = { + .preenable = &ad7766_preenable, + .postdisable = &ad7766_postdisable, +}; + +static const struct iio_info ad7766_info = { + .read_raw = &ad7766_read_raw, +}; + +static irqreturn_t ad7766_irq(int irq, void *private) +{ + iio_trigger_poll(private); + return IRQ_HANDLED; +} + +static int ad7766_set_trigger_state(struct iio_trigger *trig, bool enable) +{ + struct ad7766 *ad7766 = iio_trigger_get_drvdata(trig); + + if (enable) + enable_irq(ad7766->spi->irq); + else + disable_irq(ad7766->spi->irq); + + return 0; +} + +static const struct iio_trigger_ops ad7766_trigger_ops = { + .set_trigger_state = ad7766_set_trigger_state, + .validate_device = iio_trigger_validate_own_device, +}; + +static int ad7766_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct ad7766 *ad7766; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ad7766)); + if (!indio_dev) + return -ENOMEM; + + ad7766 = iio_priv(indio_dev); + ad7766->chip_info = &ad7766_chip_info[id->driver_data]; + + ad7766->mclk = devm_clk_get(&spi->dev, "mclk"); + if (IS_ERR(ad7766->mclk)) + return PTR_ERR(ad7766->mclk); + + ad7766->reg[AD7766_SUPPLY_AVDD].supply = "avdd"; + ad7766->reg[AD7766_SUPPLY_DVDD].supply = "dvdd"; + ad7766->reg[AD7766_SUPPLY_VREF].supply = "vref"; + + ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(ad7766->reg), + ad7766->reg); + if (ret) + return ret; + + ad7766->pd_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(ad7766->pd_gpio)) + return PTR_ERR(ad7766->pd_gpio); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7766_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7766_channels); + indio_dev->info = &ad7766_info; + + if (spi->irq > 0) { + ad7766->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + if (!ad7766->trig) + return -ENOMEM; + + ad7766->trig->ops = &ad7766_trigger_ops; + ad7766->trig->dev.parent = &spi->dev; + iio_trigger_set_drvdata(ad7766->trig, ad7766); + + ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq, + IRQF_TRIGGER_FALLING, dev_name(&spi->dev), + ad7766->trig); + if (ret < 0) + return ret; + + /* + * The device generates interrupts as long as it is powered up. + * Some platforms might not allow the option to power it down so + * disable the interrupt to avoid extra load on the system + */ + disable_irq(spi->irq); + + ret = devm_iio_trigger_register(&spi->dev, ad7766->trig); + if (ret) + return ret; + } + + spi_set_drvdata(spi, indio_dev); + + ad7766->spi = spi; + + /* First byte always 0 */ + ad7766->xfer.rx_buf = &ad7766->data[1]; + ad7766->xfer.len = 3; + + spi_message_init(&ad7766->msg); + spi_message_add_tail(&ad7766->xfer, &ad7766->msg); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, &ad7766_trigger_handler, + &ad7766_buffer_setup_ops); + if (ret) + return ret; + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return ret; + return 0; +} + +static const struct spi_device_id ad7766_id[] = { + {"ad7766", ID_AD7766}, + {"ad7766-1", ID_AD7766_1}, + {"ad7766-2", ID_AD7766_2}, + {"ad7767", ID_AD7766}, + {"ad7767-1", ID_AD7766_1}, + {"ad7767-2", ID_AD7766_2}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7766_id); + +static struct spi_driver ad7766_driver = { + .driver = { + .name = "ad7766", + }, + .probe = ad7766_probe, + .id_table = ad7766_id, +}; +module_spi_driver(ad7766_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Analog Devices AD7766 and AD7767 ADCs driver support"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c new file mode 100644 index 000000000..4afa50e5c --- /dev/null +++ b/drivers/iio/adc/ad7768-1.c @@ -0,0 +1,652 @@ +// 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 { + struct { + __be32 chan; + s64 timestamp; + } scan; + __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.scan.chan, 3); + if (ret < 0) + goto err_unlock; + + iio_push_to_buffers_with_timestamp(indio_dev, &st->data.scan, + iio_get_time_ns(indio_dev)); + +err_unlock: + iio_trigger_notify_done(indio_dev->trig); + 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); + + /* + * 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); + + /* + * To exit continuous read mode, perform a single read of the ADC_DATA + * reg (0x2C), which allows further configuration of the device. + */ + return ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3); +} + +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->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 000000000..c70048bc7 --- /dev/null +++ b/drivers/iio/adc/ad7780.c @@ -0,0 +1,395 @@ +// 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, + .irq_flags = IRQF_TRIGGER_FALLING, +}; + +#define _AD7780_CHANNEL(_bits, _wordsize, _mask_all) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = _mask_all, \ + .scan_index = 1, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = 32, \ + .shift = (_wordsize) - (_bits), \ + .endianness = IIO_BE, \ + }, \ +} + +#define AD7780_CHANNEL(_bits, _wordsize) \ + _AD7780_CHANNEL(_bits, _wordsize, BIT(IIO_CHAN_INFO_SAMP_FREQ)) +#define AD7170_CHANNEL(_bits, _wordsize) \ + _AD7780_CHANNEL(_bits, _wordsize, 0) + +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->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) + return ret; + + 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 new file mode 100644 index 000000000..f3502f126 --- /dev/null +++ b/drivers/iio/adc/ad7791.c @@ -0,0 +1,494 @@ +// 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> + */ + +#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/delay.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/adc/ad_sigma_delta.h> + +#include <linux/platform_data/ad7791.h> + +#define AD7791_REG_COMM 0x0 /* For writes */ +#define AD7791_REG_STATUS 0x0 /* For reads */ +#define AD7791_REG_MODE 0x1 +#define AD7791_REG_FILTER 0x2 +#define AD7791_REG_DATA 0x3 + +#define AD7791_MODE_CONTINUOUS 0x00 +#define AD7791_MODE_SINGLE 0x02 +#define AD7791_MODE_POWERDOWN 0x03 + +#define AD7791_CH_AIN1P_AIN1N 0x00 +#define AD7791_CH_AIN2 0x01 +#define AD7791_CH_AIN1N_AIN1N 0x02 +#define AD7791_CH_AVDD_MONITOR 0x03 + +#define AD7791_FILTER_CLK_DIV_1 (0x0 << 4) +#define AD7791_FILTER_CLK_DIV_2 (0x1 << 4) +#define AD7791_FILTER_CLK_DIV_4 (0x2 << 4) +#define AD7791_FILTER_CLK_DIV_8 (0x3 << 4) +#define AD7791_FILTER_CLK_MASK (0x3 << 4) +#define AD7791_FILTER_RATE_120 0x0 +#define AD7791_FILTER_RATE_100 0x1 +#define AD7791_FILTER_RATE_33_3 0x2 +#define AD7791_FILTER_RATE_20 0x3 +#define AD7791_FILTER_RATE_16_6 0x4 +#define AD7791_FILTER_RATE_16_7 0x5 +#define AD7791_FILTER_RATE_13_3 0x6 +#define AD7791_FILTER_RATE_9_5 0x7 +#define AD7791_FILTER_RATE_MASK 0x7 + +#define AD7791_MODE_BUFFER BIT(1) +#define AD7791_MODE_UNIPOLAR BIT(2) +#define AD7791_MODE_BURNOUT BIT(3) +#define AD7791_MODE_SEL_MASK (0x3 << 6) +#define AD7791_MODE_SEL(x) ((x) << 6) + +#define __AD7991_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, _extend_name, _type, _mask_all) \ + { \ + .type = (_type), \ + .differential = (_channel2 == -1 ? 0 : 1), \ + .indexed = 1, \ + .channel = (_channel1), \ + .channel2 = (_channel2), \ + .address = (_address), \ + .extend_name = (_extend_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = _mask_all, \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = (_storagebits), \ + .shift = (_shift), \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD7991_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7991_CHANNEL(_si, _channel, _channel, _address, _bits, \ + _storagebits, _shift, "shorted", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7991_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7991_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7991_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift) \ + __AD7991_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7991_SUPPLY_CHANNEL(_si, _channel, _address, _bits, _storagebits, \ + _shift) \ + __AD7991_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, "supply", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \ +const struct iio_chan_spec name[] = { \ + AD7991_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ + (bits), (storagebits), 0), \ + AD7991_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \ + AD7991_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \ + (bits), (storagebits), 0), \ + AD7991_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \ + (bits), (storagebits), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \ +const struct iio_chan_spec name[] = { \ + AD7991_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \ + (bits), (storagebits), 0), \ + AD7991_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \ + (bits), (storagebits), 0), \ + AD7991_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \ + (bits), (storagebits), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(3), \ +} + +static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32); +static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16); +static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32); + +enum { + AD7787, + AD7788, + AD7789, + AD7790, + AD7791, +}; + +enum ad7791_chip_info_flags { + AD7791_FLAG_HAS_FILTER = (1 << 0), + AD7791_FLAG_HAS_BUFFER = (1 << 1), + AD7791_FLAG_HAS_UNIPOLAR = (1 << 2), + AD7791_FLAG_HAS_BURNOUT = (1 << 3), +}; + +struct ad7791_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + enum ad7791_chip_info_flags flags; +}; + +static const struct ad7791_chip_info ad7791_chip_infos[] = { + [AD7787] = { + .channels = ad7787_channels, + .num_channels = ARRAY_SIZE(ad7787_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT, + }, + [AD7788] = { + .channels = ad7790_channels, + .num_channels = ARRAY_SIZE(ad7790_channels), + .flags = AD7791_FLAG_HAS_UNIPOLAR, + }, + [AD7789] = { + .channels = ad7791_channels, + .num_channels = ARRAY_SIZE(ad7791_channels), + .flags = AD7791_FLAG_HAS_UNIPOLAR, + }, + [AD7790] = { + .channels = ad7790_channels, + .num_channels = ARRAY_SIZE(ad7790_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_BURNOUT, + }, + [AD7791] = { + .channels = ad7791_channels, + .num_channels = ARRAY_SIZE(ad7791_channels), + .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER | + AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT, + }, +}; + +struct ad7791_state { + struct ad_sigma_delta sd; + uint8_t mode; + uint8_t filter; + + struct regulator *reg; + const struct ad7791_chip_info *info; +}; + +static const int ad7791_sample_freq_avail[8][2] = { + [AD7791_FILTER_RATE_120] = { 120, 0 }, + [AD7791_FILTER_RATE_100] = { 100, 0 }, + [AD7791_FILTER_RATE_33_3] = { 33, 300000 }, + [AD7791_FILTER_RATE_20] = { 20, 0 }, + [AD7791_FILTER_RATE_16_6] = { 16, 600000 }, + [AD7791_FILTER_RATE_16_7] = { 16, 700000 }, + [AD7791_FILTER_RATE_13_3] = { 13, 300000 }, + [AD7791_FILTER_RATE_9_5] = { 9, 500000 }, +}; + +static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7791_state, sd); +} + +static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + ad_sd_set_comm(sd, channel); + + return 0; +} + +static int ad7791_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd); + + switch (mode) { + case AD_SD_MODE_CONTINUOUS: + mode = AD7791_MODE_CONTINUOUS; + break; + case AD_SD_MODE_SINGLE: + mode = AD7791_MODE_SINGLE; + break; + case AD_SD_MODE_IDLE: + case AD_SD_MODE_POWERDOWN: + mode = AD7791_MODE_POWERDOWN; + break; + } + + st->mode &= ~AD7791_MODE_SEL_MASK; + st->mode |= AD7791_MODE_SEL(mode); + + return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode); +} + +static const struct ad_sigma_delta_info ad7791_sigma_delta_info = { + .set_channel = ad7791_set_channel, + .set_mode = ad7791_set_mode, + .has_registers = true, + .addr_shift = 4, + .read_mask = BIT(3), + .irq_flags = IRQF_TRIGGER_FALLING, +}; + +static int ad7791_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, int *val2, long info) +{ + struct ad7791_state *st = iio_priv(indio_dev); + bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR); + unsigned int rate; + + switch (info) { + case IIO_CHAN_INFO_RAW: + return ad_sigma_delta_single_conversion(indio_dev, chan, val); + case IIO_CHAN_INFO_OFFSET: + /** + * Unipolar: 0 to VREF + * Bipolar -VREF to VREF + **/ + if (unipolar) + *val = 0; + else + *val = -(1 << (chan->scan_type.realbits - 1)); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* The monitor channel uses an internal reference. */ + if (chan->address == AD7791_CH_AVDD_MONITOR) { + /* + * The signal is attenuated by a factor of 5 and + * compared against a 1.17V internal reference. + */ + *val = 1170 * 5; + } else { + int voltage_uv; + + voltage_uv = regulator_get_voltage(st->reg); + if (voltage_uv < 0) + return voltage_uv; + + *val = voltage_uv / 1000; + } + if (unipolar) + *val2 = chan->scan_type.realbits; + else + *val2 = chan->scan_type.realbits - 1; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + rate = st->filter & AD7791_FILTER_RATE_MASK; + *val = ad7791_sample_freq_avail[rate][0]; + *val2 = ad7791_sample_freq_avail[rate][1]; + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; +} + +static int ad7791_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct ad7791_state *st = iio_priv(indio_dev); + int ret, i; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) { + if (ad7791_sample_freq_avail[i][0] == val && + ad7791_sample_freq_avail[i][1] == val2) + break; + } + + if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) { + ret = -EINVAL; + break; + } + + st->filter &= ~AD7791_FILTER_RATE_MASK; + st->filter |= i; + ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, + sizeof(st->filter), + st->filter); + break; + default: + ret = -EINVAL; + } + + iio_device_release_direct_mode(indio_dev); + return ret; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5"); + +static struct attribute *ad7791_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7791_attribute_group = { + .attrs = ad7791_attributes, +}; + +static const struct iio_info ad7791_info = { + .read_raw = &ad7791_read_raw, + .write_raw = &ad7791_write_raw, + .attrs = &ad7791_attribute_group, + .validate_trigger = ad_sd_validate_trigger, +}; + +static const struct iio_info ad7791_no_filter_info = { + .read_raw = &ad7791_read_raw, + .write_raw = &ad7791_write_raw, + .validate_trigger = ad_sd_validate_trigger, +}; + +static int ad7791_setup(struct ad7791_state *st, + struct ad7791_platform_data *pdata) +{ + /* Set to poweron-reset default values */ + st->mode = AD7791_MODE_BUFFER; + st->filter = AD7791_FILTER_RATE_16_6; + + if (!pdata) + return 0; + + if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered) + st->mode &= ~AD7791_MODE_BUFFER; + + if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) && + pdata->burnout_current) + st->mode |= AD7791_MODE_BURNOUT; + + if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar) + st->mode |= AD7791_MODE_UNIPOLAR; + + return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode), + st->mode); +} + +static int ad7791_probe(struct spi_device *spi) +{ + struct ad7791_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev; + struct ad7791_state *st; + int ret; + + if (!spi->irq) { + dev_err(&spi->dev, "Missing IRQ.\n"); + return -ENXIO; + } + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = devm_regulator_get(&spi->dev, "refin"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data]; + ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info); + + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->info->channels; + indio_dev->num_channels = st->info->num_channels; + if (st->info->flags & AD7791_FLAG_HAS_FILTER) + indio_dev->info = &ad7791_info; + else + indio_dev->info = &ad7791_no_filter_info; + + ret = ad_sd_setup_buffer_and_trigger(indio_dev); + if (ret) + goto error_disable_reg; + + ret = ad7791_setup(st, pdata); + if (ret) + goto error_remove_trigger; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_remove_trigger; + + return 0; + +error_remove_trigger: + ad_sd_cleanup_buffer_and_trigger(indio_dev); +error_disable_reg: + regulator_disable(st->reg); + + return ret; +} + +static int ad7791_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7791_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 ad7791_spi_ids[] = { + { "ad7787", AD7787 }, + { "ad7788", AD7788 }, + { "ad7789", AD7789 }, + { "ad7790", AD7790 }, + { "ad7791", AD7791 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7791_spi_ids); + +static struct spi_driver ad7791_driver = { + .driver = { + .name = "ad7791", + }, + .probe = ad7791_probe, + .remove = ad7791_remove, + .id_table = ad7791_spi_ids, +}; +module_spi_driver(ad7791_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Analog Devices AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c new file mode 100644 index 000000000..7c9c95c25 --- /dev/null +++ b/drivers/iio/adc/ad7793.c @@ -0,0 +1,892 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7785/AD7792/AD7793/AD7794/AD7795 SPI ADC driver + * + * Copyright 2011-2012 Analog Devices Inc. + */ + +#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/delay.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/adc/ad_sigma_delta.h> +#include <linux/platform_data/ad7793.h> + +/* Registers */ +#define AD7793_REG_COMM 0 /* Communications Register (WO, 8-bit) */ +#define AD7793_REG_STAT 0 /* Status Register (RO, 8-bit) */ +#define AD7793_REG_MODE 1 /* Mode Register (RW, 16-bit */ +#define AD7793_REG_CONF 2 /* Configuration Register (RW, 16-bit) */ +#define AD7793_REG_DATA 3 /* Data Register (RO, 16-/24-bit) */ +#define AD7793_REG_ID 4 /* ID Register (RO, 8-bit) */ +#define AD7793_REG_IO 5 /* IO Register (RO, 8-bit) */ +#define AD7793_REG_OFFSET 6 /* Offset Register (RW, 16-bit + * (AD7792)/24-bit (AD7793)) */ +#define AD7793_REG_FULLSALE 7 /* Full-Scale Register + * (RW, 16-bit (AD7792)/24-bit (AD7793)) */ + +/* Communications Register Bit Designations (AD7793_REG_COMM) */ +#define AD7793_COMM_WEN (1 << 7) /* Write Enable */ +#define AD7793_COMM_WRITE (0 << 6) /* Write Operation */ +#define AD7793_COMM_READ (1 << 6) /* Read Operation */ +#define AD7793_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */ +#define AD7793_COMM_CREAD (1 << 2) /* Continuous Read of Data Register */ + +/* Status Register Bit Designations (AD7793_REG_STAT) */ +#define AD7793_STAT_RDY (1 << 7) /* Ready */ +#define AD7793_STAT_ERR (1 << 6) /* Error (Overrange, Underrange) */ +#define AD7793_STAT_CH3 (1 << 2) /* Channel 3 */ +#define AD7793_STAT_CH2 (1 << 1) /* Channel 2 */ +#define AD7793_STAT_CH1 (1 << 0) /* Channel 1 */ + +/* Mode Register Bit Designations (AD7793_REG_MODE) */ +#define AD7793_MODE_SEL(x) (((x) & 0x7) << 13) /* Operation Mode Select */ +#define AD7793_MODE_SEL_MASK (0x7 << 13) /* Operation Mode Select mask */ +#define AD7793_MODE_CLKSRC(x) (((x) & 0x3) << 6) /* ADC Clock Source Select */ +#define AD7793_MODE_RATE(x) ((x) & 0xF) /* Filter Update Rate Select */ + +#define AD7793_MODE_CONT 0 /* Continuous Conversion Mode */ +#define AD7793_MODE_SINGLE 1 /* Single Conversion Mode */ +#define AD7793_MODE_IDLE 2 /* Idle Mode */ +#define AD7793_MODE_PWRDN 3 /* Power-Down Mode */ +#define AD7793_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */ +#define AD7793_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */ +#define AD7793_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */ +#define AD7793_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */ + +#define AD7793_CLK_INT 0 /* Internal 64 kHz Clock not + * available at the CLK pin */ +#define AD7793_CLK_INT_CO 1 /* Internal 64 kHz Clock available + * at the CLK pin */ +#define AD7793_CLK_EXT 2 /* External 64 kHz Clock */ +#define AD7793_CLK_EXT_DIV2 3 /* External Clock divided by 2 */ + +/* Configuration Register Bit Designations (AD7793_REG_CONF) */ +#define AD7793_CONF_VBIAS(x) (((x) & 0x3) << 14) /* Bias Voltage + * Generator Enable */ +#define AD7793_CONF_BO_EN (1 << 13) /* Burnout Current Enable */ +#define AD7793_CONF_UNIPOLAR (1 << 12) /* Unipolar/Bipolar Enable */ +#define AD7793_CONF_BOOST (1 << 11) /* Boost Enable */ +#define AD7793_CONF_GAIN(x) (((x) & 0x7) << 8) /* Gain Select */ +#define AD7793_CONF_REFSEL(x) ((x) << 6) /* INT/EXT Reference Select */ +#define AD7793_CONF_BUF (1 << 4) /* Buffered Mode Enable */ +#define AD7793_CONF_CHAN(x) ((x) & 0xf) /* Channel select */ +#define AD7793_CONF_CHAN_MASK 0xf /* Channel select mask */ + +#define AD7793_CH_AIN1P_AIN1M 0 /* AIN1(+) - AIN1(-) */ +#define AD7793_CH_AIN2P_AIN2M 1 /* AIN2(+) - AIN2(-) */ +#define AD7793_CH_AIN3P_AIN3M 2 /* AIN3(+) - AIN3(-) */ +#define AD7793_CH_AIN1M_AIN1M 3 /* AIN1(-) - AIN1(-) */ +#define AD7793_CH_TEMP 6 /* Temp Sensor */ +#define AD7793_CH_AVDD_MONITOR 7 /* AVDD Monitor */ + +#define AD7795_CH_AIN4P_AIN4M 4 /* AIN4(+) - AIN4(-) */ +#define AD7795_CH_AIN5P_AIN5M 5 /* AIN5(+) - AIN5(-) */ +#define AD7795_CH_AIN6P_AIN6M 6 /* AIN6(+) - AIN6(-) */ +#define AD7795_CH_AIN1M_AIN1M 8 /* AIN1(-) - AIN1(-) */ + +/* ID Register Bit Designations (AD7793_REG_ID) */ +#define AD7785_ID 0x3 +#define AD7792_ID 0xA +#define AD7793_ID 0xB +#define AD7794_ID 0xF +#define AD7795_ID 0xF +#define AD7796_ID 0xA +#define AD7797_ID 0xB +#define AD7798_ID 0x8 +#define AD7799_ID 0x9 +#define AD7793_ID_MASK 0xF + +/* IO (Excitation Current Sources) Register Bit Designations (AD7793_REG_IO) */ +#define AD7793_IO_IEXC1_IOUT1_IEXC2_IOUT2 0 /* IEXC1 connect to IOUT1, + * IEXC2 connect to IOUT2 */ +#define AD7793_IO_IEXC1_IOUT2_IEXC2_IOUT1 1 /* IEXC1 connect to IOUT2, + * IEXC2 connect to IOUT1 */ +#define AD7793_IO_IEXC1_IEXC2_IOUT1 2 /* Both current sources + * IEXC1,2 connect to IOUT1 */ +#define AD7793_IO_IEXC1_IEXC2_IOUT2 3 /* Both current sources + * IEXC1,2 connect to IOUT2 */ + +#define AD7793_IO_IXCEN_10uA (1 << 0) /* Excitation Current 10uA */ +#define AD7793_IO_IXCEN_210uA (2 << 0) /* Excitation Current 210uA */ +#define AD7793_IO_IXCEN_1mA (3 << 0) /* Excitation Current 1mA */ + +/* NOTE: + * The AD7792/AD7793 features a dual use data out ready DOUT/RDY output. + * In order to avoid contentions on the SPI bus, it's therefore necessary + * to use spi bus locking. + * + * The DOUT/RDY output must also be wired to an interrupt capable GPIO. + */ + +#define AD7793_FLAG_HAS_CLKSEL BIT(0) +#define AD7793_FLAG_HAS_REFSEL BIT(1) +#define AD7793_FLAG_HAS_VBIAS BIT(2) +#define AD7793_HAS_EXITATION_CURRENT BIT(3) +#define AD7793_FLAG_HAS_GAIN BIT(4) +#define AD7793_FLAG_HAS_BUFFER BIT(5) + +struct ad7793_chip_info { + unsigned int id; + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned int flags; + + const struct iio_info *iio_info; + const u16 *sample_freq_avail; +}; + +struct ad7793_state { + const struct ad7793_chip_info *chip_info; + struct regulator *reg; + u16 int_vref_mv; + u16 mode; + u16 conf; + u32 scale_avail[8][2]; + + struct ad_sigma_delta sd; + +}; + +enum ad7793_supported_device_ids { + ID_AD7785, + ID_AD7792, + ID_AD7793, + ID_AD7794, + ID_AD7795, + ID_AD7796, + ID_AD7797, + ID_AD7798, + ID_AD7799, +}; + +static struct ad7793_state *ad_sigma_delta_to_ad7793(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7793_state, sd); +} + +static int ad7793_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd); + + st->conf &= ~AD7793_CONF_CHAN_MASK; + st->conf |= AD7793_CONF_CHAN(channel); + + return ad_sd_write_reg(&st->sd, AD7793_REG_CONF, 2, st->conf); +} + +static int ad7793_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd); + + st->mode &= ~AD7793_MODE_SEL_MASK; + st->mode |= AD7793_MODE_SEL(mode); + + return ad_sd_write_reg(&st->sd, AD7793_REG_MODE, 2, st->mode); +} + +static const struct ad_sigma_delta_info ad7793_sigma_delta_info = { + .set_channel = ad7793_set_channel, + .set_mode = ad7793_set_mode, + .has_registers = true, + .addr_shift = 3, + .read_mask = BIT(6), + .irq_flags = IRQF_TRIGGER_FALLING, +}; + +static const struct ad_sd_calib_data ad7793_calib_arr[6] = { + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN1P_AIN1M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN1P_AIN1M}, + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN2P_AIN2M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN2P_AIN2M}, + {AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN3P_AIN3M}, + {AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN3P_AIN3M} +}; + +static int ad7793_calibrate_all(struct ad7793_state *st) +{ + return ad_sd_calibrate_all(&st->sd, ad7793_calib_arr, + ARRAY_SIZE(ad7793_calib_arr)); +} + +static int ad7793_check_platform_data(struct ad7793_state *st, + const struct ad7793_platform_data *pdata) +{ + if ((pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT1 || + pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT2) && + ((pdata->exitation_current != AD7793_IX_10uA) && + (pdata->exitation_current != AD7793_IX_210uA))) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL) && + pdata->clock_src != AD7793_CLK_SRC_INT) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_REFSEL) && + pdata->refsel != AD7793_REFSEL_REFIN1) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) && + pdata->bias_voltage != AD7793_BIAS_VOLTAGE_DISABLED) + return -EINVAL; + + if (!(st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) && + pdata->exitation_current != AD7793_IX_DISABLED) + return -EINVAL; + + return 0; +} + +static int ad7793_setup(struct iio_dev *indio_dev, + const struct ad7793_platform_data *pdata, + unsigned int vref_mv) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int i, ret; + unsigned long long scale_uv; + u32 id; + + ret = ad7793_check_platform_data(st, pdata); + if (ret) + return ret; + + /* reset the serial interface */ + ret = ad_sd_reset(&st->sd, 32); + if (ret < 0) + goto out; + usleep_range(500, 2000); /* Wait for at least 500us */ + + /* write/read test for device presence */ + ret = ad_sd_read_reg(&st->sd, AD7793_REG_ID, 1, &id); + if (ret) + goto out; + + id &= AD7793_ID_MASK; + + if (id != st->chip_info->id) { + ret = -ENODEV; + dev_err(&st->sd.spi->dev, "device ID query failed\n"); + goto out; + } + + st->mode = AD7793_MODE_RATE(1); + st->conf = 0; + + if (st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL) + st->mode |= AD7793_MODE_CLKSRC(pdata->clock_src); + if (st->chip_info->flags & AD7793_FLAG_HAS_REFSEL) + st->conf |= AD7793_CONF_REFSEL(pdata->refsel); + if (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) + st->conf |= AD7793_CONF_VBIAS(pdata->bias_voltage); + if (pdata->buffered || !(st->chip_info->flags & AD7793_FLAG_HAS_BUFFER)) + st->conf |= AD7793_CONF_BUF; + if (pdata->boost_enable && + (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS)) + st->conf |= AD7793_CONF_BOOST; + if (pdata->burnout_current) + st->conf |= AD7793_CONF_BO_EN; + if (pdata->unipolar) + st->conf |= AD7793_CONF_UNIPOLAR; + + if (!(st->chip_info->flags & AD7793_FLAG_HAS_GAIN)) + st->conf |= AD7793_CONF_GAIN(7); + + ret = ad7793_set_mode(&st->sd, AD_SD_MODE_IDLE); + if (ret) + goto out; + + ret = ad7793_set_channel(&st->sd, 0); + if (ret) + goto out; + + if (st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) { + ret = ad_sd_write_reg(&st->sd, AD7793_REG_IO, 1, + pdata->exitation_current | + (pdata->current_source_direction << 2)); + if (ret) + goto out; + } + + ret = ad7793_calibrate_all(st); + if (ret) + goto out; + + /* Populate available ADC input ranges */ + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) { + scale_uv = ((u64)vref_mv * 100000000) + >> (st->chip_info->channels[0].scan_type.realbits - + (!!(st->conf & AD7793_CONF_UNIPOLAR) ? 0 : 1)); + scale_uv >>= i; + + st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10; + st->scale_avail[i][0] = scale_uv; + } + + return 0; +out: + dev_err(&st->sd.spi->dev, "setup failed\n"); + return ret; +} + +static const u16 ad7793_sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39, + 33, 19, 17, 16, 12, 10, 8, 6, 4}; + +static const u16 ad7797_sample_freq_avail[16] = {0, 0, 0, 123, 62, 50, 0, + 33, 0, 17, 16, 12, 10, 8, 6, 4}; + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( + "470 242 123 62 50 39 33 19 17 16 12 10 8 6 4"); + +static IIO_CONST_ATTR_NAMED(sampling_frequency_available_ad7797, + sampling_frequency_available, "123 62 50 33 17 16 12 10 8 6 4"); + +static int ad7793_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct ad7793_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (int *)st->scale_avail; + *type = IIO_VAL_INT_PLUS_NANO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(st->scale_avail) * 2; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static struct attribute *ad7793_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7793_attribute_group = { + .attrs = ad7793_attributes, +}; + +static struct attribute *ad7797_attributes[] = { + &iio_const_attr_sampling_frequency_available_ad7797.dev_attr.attr, + NULL +}; + +static const struct attribute_group ad7797_attribute_group = { + .attrs = ad7797_attributes, +}; + +static int ad7793_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int ret; + unsigned long long scale_uv; + bool unipolar = !!(st->conf & AD7793_CONF_UNIPOLAR); + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ad_sigma_delta_single_conversion(indio_dev, chan, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->differential) { + *val = st-> + scale_avail[(st->conf >> 8) & 0x7][0]; + *val2 = st-> + scale_avail[(st->conf >> 8) & 0x7][1]; + return IIO_VAL_INT_PLUS_NANO; + } + /* 1170mV / 2^23 * 6 */ + scale_uv = (1170ULL * 1000000000ULL * 6ULL); + break; + case IIO_TEMP: + /* 1170mV / 0.81 mV/C / 2^23 */ + scale_uv = 1444444444444444ULL; + break; + default: + return -EINVAL; + } + + scale_uv >>= (chan->scan_type.realbits - (unipolar ? 0 : 1)); + *val = 0; + *val2 = scale_uv; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + if (!unipolar) + *val = -(1 << (chan->scan_type.realbits - 1)); + else + *val = 0; + + /* Kelvin to Celsius */ + if (chan->type == IIO_TEMP) { + unsigned long long offset; + unsigned int shift; + + shift = chan->scan_type.realbits - (unipolar ? 0 : 1); + offset = 273ULL << shift; + do_div(offset, 1444); + *val -= offset; + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->chip_info + ->sample_freq_avail[AD7793_MODE_RATE(st->mode)]; + return IIO_VAL_INT; + } + return -EINVAL; +} + +static int ad7793_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad7793_state *st = iio_priv(indio_dev); + int ret, i; + unsigned int tmp; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = -EINVAL; + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + if (val2 == st->scale_avail[i][1]) { + ret = 0; + tmp = st->conf; + st->conf &= ~AD7793_CONF_GAIN(-1); + st->conf |= AD7793_CONF_GAIN(i); + + if (tmp == st->conf) + break; + + ad_sd_write_reg(&st->sd, AD7793_REG_CONF, + sizeof(st->conf), st->conf); + ad7793_calibrate_all(st); + break; + } + break; + case IIO_CHAN_INFO_SAMP_FREQ: + if (!val) { + ret = -EINVAL; + break; + } + + for (i = 0; i < 16; i++) + if (val == st->chip_info->sample_freq_avail[i]) + break; + + if (i == 16) { + ret = -EINVAL; + break; + } + + st->mode &= ~AD7793_MODE_RATE(-1); + st->mode |= AD7793_MODE_RATE(i); + ad_sd_write_reg(&st->sd, AD7793_REG_MODE, sizeof(st->mode), + st->mode); + break; + default: + ret = -EINVAL; + } + + iio_device_release_direct_mode(indio_dev); + return ret; +} + +static int ad7793_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static const struct iio_info ad7793_info = { + .read_raw = &ad7793_read_raw, + .write_raw = &ad7793_write_raw, + .write_raw_get_fmt = &ad7793_write_raw_get_fmt, + .read_avail = ad7793_read_avail, + .attrs = &ad7793_attribute_group, + .validate_trigger = ad_sd_validate_trigger, +}; + +static const struct iio_info ad7797_info = { + .read_raw = &ad7793_read_raw, + .write_raw = &ad7793_write_raw, + .write_raw_get_fmt = &ad7793_write_raw_get_fmt, + .attrs = &ad7797_attribute_group, + .validate_trigger = ad_sd_validate_trigger, +}; + +#define __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, _extend_name, _type, _mask_type_av, _mask_all) \ + { \ + .type = (_type), \ + .differential = (_channel2 == -1 ? 0 : 1), \ + .indexed = 1, \ + .channel = (_channel1), \ + .channel2 = (_channel2), \ + .address = (_address), \ + .extend_name = (_extend_name), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = (_mask_type_av), \ + .info_mask_shared_by_all = _mask_all, \ + .scan_index = (_si), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = (_storagebits), \ + .shift = (_shift), \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD7793_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7793_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel, _channel, _address, _bits, \ + _storagebits, _shift, "shorted", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7793_TEMP_CHANNEL(_si, _address, _bits, _storagebits, _shift) \ + __AD7793_CHANNEL(_si, 0, -1, _address, _bits, \ + _storagebits, _shift, NULL, IIO_TEMP, \ + 0, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7793_SUPPLY_CHANNEL(_si, _channel, _address, _bits, _storagebits, \ + _shift) \ + __AD7793_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, "supply", IIO_VOLTAGE, \ + 0, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7797_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + 0, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD7797_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD7793_CHANNEL(_si, _channel, _channel, _address, _bits, \ + _storagebits, _shift, "shorted", IIO_VOLTAGE, \ + 0, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define DECLARE_AD7793_CHANNELS(_name, _b, _sb, _s) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD7793_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), (_s)), \ + AD7793_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), (_s)), \ + AD7793_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), (_s)), \ + AD7793_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), (_s)), \ + AD7793_TEMP_CHANNEL(4, AD7793_CH_TEMP, (_b), (_sb), (_s)), \ + AD7793_SUPPLY_CHANNEL(5, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), (_s)), \ + IIO_CHAN_SOFT_TIMESTAMP(6), \ +} + +#define DECLARE_AD7795_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD7793_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(3, 3, 3, AD7795_CH_AIN4P_AIN4M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(4, 4, 4, AD7795_CH_AIN5P_AIN5M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(5, 5, 5, AD7795_CH_AIN6P_AIN6M, (_b), (_sb), 0), \ + AD7793_SHORTED_CHANNEL(6, 0, AD7795_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD7793_TEMP_CHANNEL(7, AD7793_CH_TEMP, (_b), (_sb), 0), \ + AD7793_SUPPLY_CHANNEL(8, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(9), \ +} + +#define DECLARE_AD7797_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD7797_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD7797_SHORTED_CHANNEL(1, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD7793_TEMP_CHANNEL(2, AD7793_CH_TEMP, (_b), (_sb), 0), \ + AD7793_SUPPLY_CHANNEL(3, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_AD7799_CHANNELS(_name, _b, _sb) \ +const struct iio_chan_spec _name##_channels[] = { \ + AD7793_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \ + AD7793_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \ + AD7793_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \ + AD7793_SUPPLY_CHANNEL(4, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \ + IIO_CHAN_SOFT_TIMESTAMP(5), \ +} + +static DECLARE_AD7793_CHANNELS(ad7785, 20, 32, 4); +static DECLARE_AD7793_CHANNELS(ad7792, 16, 32, 0); +static DECLARE_AD7793_CHANNELS(ad7793, 24, 32, 0); +static DECLARE_AD7795_CHANNELS(ad7794, 16, 32); +static DECLARE_AD7795_CHANNELS(ad7795, 24, 32); +static DECLARE_AD7797_CHANNELS(ad7796, 16, 16); +static DECLARE_AD7797_CHANNELS(ad7797, 24, 32); +static DECLARE_AD7799_CHANNELS(ad7798, 16, 16); +static DECLARE_AD7799_CHANNELS(ad7799, 24, 32); + +static const struct ad7793_chip_info ad7793_chip_info_tbl[] = { + [ID_AD7785] = { + .id = AD7785_ID, + .channels = ad7785_channels, + .num_channels = ARRAY_SIZE(ad7785_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7792] = { + .id = AD7792_ID, + .channels = ad7792_channels, + .num_channels = ARRAY_SIZE(ad7792_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7793] = { + .id = AD7793_ID, + .channels = ad7793_channels, + .num_channels = ARRAY_SIZE(ad7793_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7794] = { + .id = AD7794_ID, + .channels = ad7794_channels, + .num_channels = ARRAY_SIZE(ad7794_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7795] = { + .id = AD7795_ID, + .channels = ad7795_channels, + .num_channels = ARRAY_SIZE(ad7795_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL | + AD7793_FLAG_HAS_REFSEL | + AD7793_FLAG_HAS_VBIAS | + AD7793_HAS_EXITATION_CURRENT | + AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7796] = { + .id = AD7796_ID, + .channels = ad7796_channels, + .num_channels = ARRAY_SIZE(ad7796_channels), + .iio_info = &ad7797_info, + .sample_freq_avail = ad7797_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL, + }, + [ID_AD7797] = { + .id = AD7797_ID, + .channels = ad7797_channels, + .num_channels = ARRAY_SIZE(ad7797_channels), + .iio_info = &ad7797_info, + .sample_freq_avail = ad7797_sample_freq_avail, + .flags = AD7793_FLAG_HAS_CLKSEL, + }, + [ID_AD7798] = { + .id = AD7798_ID, + .channels = ad7798_channels, + .num_channels = ARRAY_SIZE(ad7798_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, + [ID_AD7799] = { + .id = AD7799_ID, + .channels = ad7799_channels, + .num_channels = ARRAY_SIZE(ad7799_channels), + .iio_info = &ad7793_info, + .sample_freq_avail = ad7793_sample_freq_avail, + .flags = AD7793_FLAG_HAS_GAIN | + AD7793_FLAG_HAS_BUFFER, + }, +}; + +static int ad7793_probe(struct spi_device *spi) +{ + const struct ad7793_platform_data *pdata = spi->dev.platform_data; + struct ad7793_state *st; + struct iio_dev *indio_dev; + int ret, vref_mv = 0; + + if (!pdata) { + dev_err(&spi->dev, "no platform data?\n"); + return -ENODEV; + } + + if (!spi->irq) { + dev_err(&spi->dev, "no IRQ?\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + ad_sd_init(&st->sd, indio_dev, spi, &ad7793_sigma_delta_info); + + if (pdata->refsel != AD7793_REFSEL_INTERNAL) { + st->reg = devm_regulator_get(&spi->dev, "refin"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + vref_mv = regulator_get_voltage(st->reg); + if (vref_mv < 0) { + ret = vref_mv; + goto error_disable_reg; + } + + vref_mv /= 1000; + } else { + vref_mv = 1170; /* Build-in ref */ + } + + st->chip_info = + &ad7793_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = st->chip_info->iio_info; + + ret = ad_sd_setup_buffer_and_trigger(indio_dev); + if (ret) + goto error_disable_reg; + + ret = ad7793_setup(indio_dev, pdata, vref_mv); + if (ret) + goto error_remove_trigger; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_remove_trigger; + + return 0; + +error_remove_trigger: + ad_sd_cleanup_buffer_and_trigger(indio_dev); +error_disable_reg: + if (pdata->refsel != AD7793_REFSEL_INTERNAL) + regulator_disable(st->reg); + + return ret; +} + +static int ad7793_remove(struct spi_device *spi) +{ + const struct ad7793_platform_data *pdata = spi->dev.platform_data; + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7793_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + ad_sd_cleanup_buffer_and_trigger(indio_dev); + + if (pdata->refsel != AD7793_REFSEL_INTERNAL) + regulator_disable(st->reg); + + return 0; +} + +static const struct spi_device_id ad7793_id[] = { + {"ad7785", ID_AD7785}, + {"ad7792", ID_AD7792}, + {"ad7793", ID_AD7793}, + {"ad7794", ID_AD7794}, + {"ad7795", ID_AD7795}, + {"ad7796", ID_AD7796}, + {"ad7797", ID_AD7797}, + {"ad7798", ID_AD7798}, + {"ad7799", ID_AD7799}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7793_id); + +static struct spi_driver ad7793_driver = { + .driver = { + .name = "ad7793", + }, + .probe = ad7793_probe, + .remove = ad7793_remove, + .id_table = ad7793_id, +}; +module_spi_driver(ad7793_driver); + +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 new file mode 100644 index 000000000..037bcb476 --- /dev/null +++ b/drivers/iio/adc/ad7887.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7887 SPI ADC driver + * + * Copyright 2010-2011 Analog Devices Inc. + */ + +#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/module.h> +#include <linux/interrupt.h> +#include <linux/bitops.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> + +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/platform_data/ad7887.h> + +#define AD7887_REF_DIS BIT(5) /* on-chip reference disable */ +#define AD7887_DUAL BIT(4) /* dual-channel mode */ +#define AD7887_CH_AIN1 BIT(3) /* convert on channel 1, DUAL=1 */ +#define AD7887_CH_AIN0 0 /* convert on channel 0, DUAL=0,1 */ +#define AD7887_PM_MODE1 0 /* CS based shutdown */ +#define AD7887_PM_MODE2 1 /* full on */ +#define AD7887_PM_MODE3 2 /* auto shutdown after conversion */ +#define AD7887_PM_MODE4 3 /* standby mode */ + +enum ad7887_channels { + AD7887_CH0, + AD7887_CH0_CH1, + AD7887_CH1, +}; + +/** + * struct ad7887_chip_info - chip specifc information + * @int_vref_mv: the internal reference voltage + * @channels: channels specification + * @num_channels: number of channels + * @dual_channels: channels specification in dual mode + * @num_dual_channels: number of channels in dual mode + */ +struct ad7887_chip_info { + u16 int_vref_mv; + const struct iio_chan_spec *channels; + unsigned int num_channels; + const struct iio_chan_spec *dual_channels; + unsigned int num_dual_channels; +}; + +struct ad7887_state { + struct spi_device *spi; + const struct ad7887_chip_info *chip_info; + struct regulator *reg; + struct spi_transfer xfer[4]; + struct spi_message msg[3]; + struct spi_message *ring_msg; + unsigned char tx_cmd_buf[4]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * Buffer needs to be large enough to hold two 16 bit samples and a + * 64 bit aligned 64 bit timestamp. + */ + unsigned char data[ALIGN(4, sizeof(s64)) + sizeof(s64)] + ____cacheline_aligned; +}; + +enum ad7887_supported_device_ids { + ID_AD7887 +}; + +static int ad7887_ring_preenable(struct iio_dev *indio_dev) +{ + struct ad7887_state *st = iio_priv(indio_dev); + + /* We know this is a single long so can 'cheat' */ + switch (*indio_dev->active_scan_mask) { + case (1 << 0): + st->ring_msg = &st->msg[AD7887_CH0]; + break; + case (1 << 1): + st->ring_msg = &st->msg[AD7887_CH1]; + /* Dummy read: push CH1 setting down to hardware */ + spi_sync(st->spi, st->ring_msg); + break; + case ((1 << 1) | (1 << 0)): + st->ring_msg = &st->msg[AD7887_CH0_CH1]; + break; + } + + return 0; +} + +static int ad7887_ring_postdisable(struct iio_dev *indio_dev) +{ + struct ad7887_state *st = iio_priv(indio_dev); + + /* dummy read: restore default CH0 settin */ + return spi_sync(st->spi, &st->msg[AD7887_CH0]); +} + +/* + * ad7887_trigger_handler() bh of trigger launched polling to ring buffer + * + * Currently there is no option in this driver to disable the saving of + * timestamps within the ring. + **/ +static irqreturn_t ad7887_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7887_state *st = iio_priv(indio_dev); + int b_sent; + + b_sent = spi_sync(st->spi, st->ring_msg); + if (b_sent) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, st->data, + iio_get_time_ns(indio_dev)); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_buffer_setup_ops ad7887_ring_setup_ops = { + .preenable = &ad7887_ring_preenable, + .postdisable = &ad7887_ring_postdisable, +}; + +static int ad7887_scan_direct(struct ad7887_state *st, unsigned ch) +{ + int ret = spi_sync(st->spi, &st->msg[ch]); + if (ret) + return ret; + + return (st->data[(ch * 2)] << 8) | st->data[(ch * 2) + 1]; +} + +static int ad7887_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7887_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 = ad7887_scan_direct(st, chan->address); + iio_device_release_direct_mode(indio_dev); + + if (ret < 0) + return ret; + *val = ret >> chan->scan_type.shift; + *val &= GENMASK(chan->scan_type.realbits - 1, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (st->reg) { + *val = regulator_get_voltage(st->reg); + if (*val < 0) + return *val; + *val /= 1000; + } else { + *val = st->chip_info->int_vref_mv; + } + + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + +#define AD7887_CHANNEL(x) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (x), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = (x), \ + .scan_index = (x), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 0, \ + .endianness = IIO_BE, \ + }, \ +} + +static const struct iio_chan_spec ad7887_channels[] = { + AD7887_CHANNEL(0), + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec ad7887_dual_channels[] = { + AD7887_CHANNEL(0), + AD7887_CHANNEL(1), + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const struct ad7887_chip_info ad7887_chip_info_tbl[] = { + /* + * More devices added in future + */ + [ID_AD7887] = { + .channels = ad7887_channels, + .num_channels = ARRAY_SIZE(ad7887_channels), + .dual_channels = ad7887_dual_channels, + .num_dual_channels = ARRAY_SIZE(ad7887_dual_channels), + .int_vref_mv = 2500, + }, +}; + +static const struct iio_info ad7887_info = { + .read_raw = &ad7887_read_raw, +}; + +static int ad7887_probe(struct spi_device *spi) +{ + struct ad7887_platform_data *pdata = spi->dev.platform_data; + struct ad7887_state *st; + struct iio_dev *indio_dev; + uint8_t mode; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + if (!pdata || !pdata->use_onchip_ref) { + st->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + } + + st->chip_info = + &ad7887_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad7887_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + /* Setup default message */ + + mode = AD7887_PM_MODE4; + if (!pdata || !pdata->use_onchip_ref) + mode |= AD7887_REF_DIS; + if (pdata && pdata->en_dual) + mode |= AD7887_DUAL; + + st->tx_cmd_buf[0] = AD7887_CH_AIN0 | mode; + + st->xfer[0].rx_buf = &st->data[0]; + st->xfer[0].tx_buf = &st->tx_cmd_buf[0]; + st->xfer[0].len = 2; + + spi_message_init(&st->msg[AD7887_CH0]); + spi_message_add_tail(&st->xfer[0], &st->msg[AD7887_CH0]); + + if (pdata && pdata->en_dual) { + st->tx_cmd_buf[2] = AD7887_CH_AIN1 | mode; + + st->xfer[1].rx_buf = &st->data[0]; + st->xfer[1].tx_buf = &st->tx_cmd_buf[2]; + st->xfer[1].len = 2; + + st->xfer[2].rx_buf = &st->data[2]; + st->xfer[2].tx_buf = &st->tx_cmd_buf[0]; + st->xfer[2].len = 2; + + spi_message_init(&st->msg[AD7887_CH0_CH1]); + spi_message_add_tail(&st->xfer[1], &st->msg[AD7887_CH0_CH1]); + spi_message_add_tail(&st->xfer[2], &st->msg[AD7887_CH0_CH1]); + + st->xfer[3].rx_buf = &st->data[2]; + st->xfer[3].tx_buf = &st->tx_cmd_buf[2]; + st->xfer[3].len = 2; + + spi_message_init(&st->msg[AD7887_CH1]); + spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]); + + indio_dev->channels = st->chip_info->dual_channels; + indio_dev->num_channels = st->chip_info->num_dual_channels; + } else { + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + } + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &ad7887_trigger_handler, &ad7887_ring_setup_ops); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unregister_ring; + + return 0; +error_unregister_ring: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + if (st->reg) + regulator_disable(st->reg); + + return ret; +} + +static int ad7887_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7887_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (st->reg) + regulator_disable(st->reg); + + return 0; +} + +static const struct spi_device_id ad7887_id[] = { + {"ad7887", ID_AD7887}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7887_id); + +static struct spi_driver ad7887_driver = { + .driver = { + .name = "ad7887", + }, + .probe = ad7887_probe, + .remove = ad7887_remove, + .id_table = ad7887_id, +}; +module_spi_driver(ad7887_driver); + +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 new file mode 100644 index 000000000..96eeda433 --- /dev/null +++ b/drivers/iio/adc/ad7923.c @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7904/AD7914/AD7923/AD7924/AD7908/AD7918/AD7928 SPI ADC driver + * + * Copyright 2011 Analog Devices Inc (from AD7923 Driver) + * Copyright 2012 CS Systemes d'Information + */ + +#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/delay.h> +#include <linux/module.h> +#include <linux/interrupt.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#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 */ +#define AD7923_SEQUENCE_OFF (0) /* no sequence fonction */ +#define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */ +#define AD7923_SEQUENCE_ON (3) /* continuous sequence */ + + +#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)) + +struct ad7923_state { + struct spi_device *spi; + struct spi_transfer ring_xfer[5]; + struct spi_transfer scan_single_xfer[2]; + struct spi_message ring_msg; + struct spi_message scan_single_msg; + + struct regulator *reg; + + unsigned int settings; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * Ensure rx_buf can be directly used in iio_push_to_buffers_with_timetamp + * Length = 8 channels + 4 extra for 8 byte timestamp + */ + __be16 rx_buf[12] ____cacheline_aligned; + __be16 tx_buf[4]; +}; + +struct ad7923_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +enum ad7923_id { + AD7904, + AD7914, + AD7924, + AD7908, + AD7918, + AD7928 +}; + +#define AD7923_V_CHAN(index, bits) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = 12 - (bits), \ + .endianness = IIO_BE, \ + }, \ + } + +#define DECLARE_AD7923_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + AD7923_V_CHAN(0, bits), \ + AD7923_V_CHAN(1, bits), \ + AD7923_V_CHAN(2, bits), \ + AD7923_V_CHAN(3, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_AD7908_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + AD7923_V_CHAN(0, bits), \ + AD7923_V_CHAN(1, bits), \ + AD7923_V_CHAN(2, bits), \ + AD7923_V_CHAN(3, bits), \ + AD7923_V_CHAN(4, bits), \ + AD7923_V_CHAN(5, bits), \ + AD7923_V_CHAN(6, bits), \ + AD7923_V_CHAN(7, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(8), \ +} + +static DECLARE_AD7923_CHANNELS(ad7904, 8); +static DECLARE_AD7923_CHANNELS(ad7914, 10); +static DECLARE_AD7923_CHANNELS(ad7924, 12); +static DECLARE_AD7908_CHANNELS(ad7908, 8); +static DECLARE_AD7908_CHANNELS(ad7918, 10); +static DECLARE_AD7908_CHANNELS(ad7928, 12); + +static const struct ad7923_chip_info ad7923_chip_info[] = { + [AD7904] = { + .channels = ad7904_channels, + .num_channels = ARRAY_SIZE(ad7904_channels), + }, + [AD7914] = { + .channels = ad7914_channels, + .num_channels = ARRAY_SIZE(ad7914_channels), + }, + [AD7924] = { + .channels = ad7924_channels, + .num_channels = ARRAY_SIZE(ad7924_channels), + }, + [AD7908] = { + .channels = ad7908_channels, + .num_channels = ARRAY_SIZE(ad7908_channels), + }, + [AD7918] = { + .channels = ad7918_channels, + .num_channels = ARRAY_SIZE(ad7918_channels), + }, + [AD7928] = { + .channels = ad7928_channels, + .num_channels = ARRAY_SIZE(ad7928_channels), + }, +}; + +/* + * 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) +{ + struct ad7923_state *st = iio_priv(indio_dev); + int i, cmd, len; + + len = 0; + /* + * For this driver the last channel is always the software timestamp so + * skip that one. + */ + for_each_set_bit(i, active_scan_mask, indio_dev->num_channels - 1) { + cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(i) | + AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) | + st->settings; + cmd <<= AD7923_SHIFT_REGISTER; + st->tx_buf[len++] = cpu_to_be16(cmd); + } + /* build spi ring message */ + st->ring_xfer[0].tx_buf = &st->tx_buf[0]; + st->ring_xfer[0].len = len; + st->ring_xfer[0].cs_change = 1; + + spi_message_init(&st->ring_msg); + spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg); + + for (i = 0; i < len; i++) { + st->ring_xfer[i + 1].rx_buf = &st->rx_buf[i]; + st->ring_xfer[i + 1].len = 2; + st->ring_xfer[i + 1].cs_change = 1; + spi_message_add_tail(&st->ring_xfer[i + 1], &st->ring_msg); + } + /* make sure last transfer cs_change is not set */ + st->ring_xfer[i + 1].cs_change = 0; + + return 0; +} + +/* + * ad7923_trigger_handler() bh of trigger launched polling to ring buffer + * + * Currently there is no option in this driver to disable the saving of + * timestamps within the ring. + */ +static irqreturn_t ad7923_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7923_state *st = iio_priv(indio_dev); + int b_sent; + + b_sent = spi_sync(st->spi, &st->ring_msg); + if (b_sent) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad7923_scan_direct(struct ad7923_state *st, unsigned int ch) +{ + int ret, cmd; + + cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(ch) | + AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) | + st->settings; + cmd <<= AD7923_SHIFT_REGISTER; + st->tx_buf[0] = cpu_to_be16(cmd); + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + return be16_to_cpu(st->rx_buf[0]); +} + +static int ad7923_get_range(struct ad7923_state *st) +{ + int vref; + + vref = regulator_get_voltage(st->reg); + if (vref < 0) + return vref; + + vref /= 1000; + + if (!(st->settings & AD7923_RANGE)) + vref *= 2; + + return vref; +} + +static int ad7923_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad7923_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 = ad7923_scan_direct(st, chan->address); + iio_device_release_direct_mode(indio_dev); + + if (ret < 0) + return ret; + + if (chan->address == EXTRACT(ret, 12, 4)) + *val = EXTRACT(ret, chan->scan_type.shift, + chan->scan_type.realbits); + else + return -EIO; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = ad7923_get_range(st); + if (ret < 0) + return ret; + *val = ret; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + +static const struct iio_info ad7923_info = { + .read_raw = &ad7923_read_raw, + .update_scan_mode = ad7923_update_scan_mode, +}; + +static int ad7923_probe(struct spi_device *spi) +{ + struct ad7923_state *st; + struct iio_dev *indio_dev; + const struct ad7923_chip_info *info; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + st->settings = AD7923_CODING | AD7923_RANGE | + AD7923_PM_MODE_WRITE(AD7923_PM_MODE_OPS); + + info = &ad7923_chip_info[spi_get_device_id(spi)->driver_data]; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = info->channels; + indio_dev->num_channels = info->num_channels; + indio_dev->info = &ad7923_info; + + /* Setup default message */ + + st->scan_single_xfer[0].tx_buf = &st->tx_buf[0]; + st->scan_single_xfer[0].len = 2; + st->scan_single_xfer[0].cs_change = 1; + st->scan_single_xfer[1].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[1].len = 2; + + spi_message_init(&st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg); + spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg); + + st->reg = devm_regulator_get(&spi->dev, "refin"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &ad7923_trigger_handler, NULL); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_ring; + + return 0; + +error_cleanup_ring: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + regulator_disable(st->reg); + + return ret; +} + +static int ad7923_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7923_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(st->reg); + + return 0; +} + +static const struct spi_device_id ad7923_id[] = { + {"ad7904", AD7904}, + {"ad7914", AD7914}, + {"ad7923", AD7924}, + {"ad7924", AD7924}, + {"ad7908", AD7908}, + {"ad7918", AD7918}, + {"ad7928", AD7928}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7923_id); + +static const struct of_device_id ad7923_of_match[] = { + { .compatible = "adi,ad7904", }, + { .compatible = "adi,ad7914", }, + { .compatible = "adi,ad7923", }, + { .compatible = "adi,ad7924", }, + { .compatible = "adi,ad7908", }, + { .compatible = "adi,ad7918", }, + { .compatible = "adi,ad7928", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ad7923_of_match); + +static struct spi_driver ad7923_driver = { + .driver = { + .name = "ad7923", + .of_match_table = ad7923_of_match, + }, + .probe = ad7923_probe, + .remove = ad7923_remove, + .id_table = ad7923_id, +}; +module_spi_driver(ad7923_driver); + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>"); +MODULE_DESCRIPTION("Analog Devices AD7923 and similar ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c new file mode 100644 index 000000000..1b4b3203e --- /dev/null +++ b/drivers/iio/adc/ad7949.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0 +/* ad7949.c - Analog Devices ADC driver 14/16 bits 4/8 channels + * + * Copyright (C) 2018 CMC NV + * + * https://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 + * @indio_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; + u16 buffer ____cacheline_aligned; +}; + +static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val, + u16 mask) +{ + int ret; + int bits_per_word = ad7949_adc->resolution; + int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS; + struct spi_message msg; + struct spi_transfer tx[] = { + { + .tx_buf = &ad7949_adc->buffer, + .len = 2, + .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 i; + int bits_per_word = ad7949_adc->resolution; + int mask = GENMASK(ad7949_adc->resolution - 1, 0); + struct spi_message msg; + struct spi_transfer tx[] = { + { + .rx_buf = &ad7949_adc->buffer, + .len = 2, + .bits_per_word = bits_per_word, + }, + }; + + /* + * 1: write CFG for sample N and read old data (sample N-2) + * 2: if CFG was not changed since sample N-1 then we'll get good data + * at the next xfer, so we bail out now, otherwise we write something + * and we read garbage (sample N-1 configuration). + */ + for (i = 0; i < 2; i++) { + ret = ad7949_spi_write_cfg(ad7949_adc, + channel << AD7949_OFFSET_CHANNEL_SEL, + AD7949_MASK_CHANNEL_SEL); + if (ret) + return ret; + if (channel == ad7949_adc->current_channel) + break; + } + + /* 3: write something and read actual data */ + 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; + + *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->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 new file mode 100644 index 000000000..1575b7670 --- /dev/null +++ b/drivers/iio/adc/ad799x.c @@ -0,0 +1,945 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * iio/adc/ad799x.c + * Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc. + * + * based on iio/adc/max1363 + * Copyright (C) 2008-2010 Jonathan Cameron + * + * based on linux/drivers/i2c/chips/max123x + * Copyright (C) 2002-2004 Stefan Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * ad799x.c + * + * Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, + * ad7998 and similar chips. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/i2c.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/bitops.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define AD799X_CHANNEL_SHIFT 4 + +/* + * AD7991, AD7995 and AD7999 defines + */ + +#define AD7991_REF_SEL 0x08 +#define AD7991_FLTR 0x04 +#define AD7991_BIT_TRIAL_DELAY 0x02 +#define AD7991_SAMPLE_DELAY 0x01 + +/* + * AD7992, AD7993, AD7994, AD7997 and AD7998 defines + */ + +#define AD7998_FLTR BIT(3) +#define AD7998_ALERT_EN BIT(2) +#define AD7998_BUSY_ALERT BIT(1) +#define AD7998_BUSY_ALERT_POL BIT(0) + +#define AD7998_CONV_RES_REG 0x0 +#define AD7998_ALERT_STAT_REG 0x1 +#define AD7998_CONF_REG 0x2 +#define AD7998_CYCLE_TMR_REG 0x3 + +#define AD7998_DATALOW_REG(x) ((x) * 3 + 0x4) +#define AD7998_DATAHIGH_REG(x) ((x) * 3 + 0x5) +#define AD7998_HYST_REG(x) ((x) * 3 + 0x6) + +#define AD7998_CYC_MASK GENMASK(2, 0) +#define AD7998_CYC_DIS 0x0 +#define AD7998_CYC_TCONF_32 0x1 +#define AD7998_CYC_TCONF_64 0x2 +#define AD7998_CYC_TCONF_128 0x3 +#define AD7998_CYC_TCONF_256 0x4 +#define AD7998_CYC_TCONF_512 0x5 +#define AD7998_CYC_TCONF_1024 0x6 +#define AD7998_CYC_TCONF_2048 0x7 + +#define AD7998_ALERT_STAT_CLEAR 0xFF + +/* + * AD7997 and AD7997 defines + */ + +#define AD7997_8_READ_SINGLE BIT(7) +#define AD7997_8_READ_SEQUENCE (BIT(6) | BIT(5) | BIT(4)) + +enum { + ad7991, + ad7995, + ad7999, + ad7992, + ad7993, + ad7994, + ad7997, + ad7998 +}; + +/** + * struct ad799x_chip_config - chip specific information + * @channel: channel specification + * @default_config: device default configuration + * @info: pointer to iio_info struct + */ +struct ad799x_chip_config { + const struct iio_chan_spec channel[9]; + u16 default_config; + const struct iio_info *info; +}; + +/** + * struct ad799x_chip_info - chip specific information + * @num_channels: number of channels + * @noirq_config: device configuration w/o IRQ + * @irq_config: device configuration w/IRQ + */ +struct ad799x_chip_info { + int num_channels; + const struct ad799x_chip_config noirq_config; + const struct ad799x_chip_config irq_config; +}; + +struct ad799x_state { + struct i2c_client *client; + const struct ad799x_chip_config *chip_config; + struct regulator *reg; + struct regulator *vref; + unsigned id; + u16 config; + + u8 *rx_buf; + unsigned int transfer_size; +}; + +static int ad799x_write_config(struct ad799x_state *st, u16 val) +{ + switch (st->id) { + case ad7997: + case ad7998: + return i2c_smbus_write_word_swapped(st->client, AD7998_CONF_REG, + val); + case ad7992: + case ad7993: + case ad7994: + return i2c_smbus_write_byte_data(st->client, AD7998_CONF_REG, + val); + default: + /* Will be written when doing a conversion */ + st->config = val; + return 0; + } +} + +static int ad799x_read_config(struct ad799x_state *st) +{ + switch (st->id) { + case ad7997: + case ad7998: + return i2c_smbus_read_word_swapped(st->client, AD7998_CONF_REG); + case ad7992: + case ad7993: + case ad7994: + return i2c_smbus_read_byte_data(st->client, AD7998_CONF_REG); + default: + /* No readback support */ + return st->config; + } +} + +static int ad799x_update_config(struct ad799x_state *st, u16 config) +{ + int ret; + + ret = ad799x_write_config(st, config); + if (ret < 0) + return ret; + ret = ad799x_read_config(st); + if (ret < 0) + return ret; + st->config = ret; + + return 0; +} + +/* + * ad799x_trigger_handler() bh of trigger launched polling to ring buffer + * + * Currently there is no option in this driver to disable the saving of + * timestamps within the ring. + **/ +static irqreturn_t ad799x_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad799x_state *st = iio_priv(indio_dev); + int b_sent; + u8 cmd; + + switch (st->id) { + case ad7991: + case ad7995: + case ad7999: + cmd = st->config | + (*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT); + break; + case ad7992: + case ad7993: + case ad7994: + cmd = (*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT) | + AD7998_CONV_RES_REG; + break; + case ad7997: + case ad7998: + cmd = AD7997_8_READ_SEQUENCE | AD7998_CONV_RES_REG; + break; + default: + cmd = 0; + } + + b_sent = i2c_smbus_read_i2c_block_data(st->client, + cmd, st->transfer_size, st->rx_buf); + if (b_sent < 0) + goto out; + + iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, + iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad799x_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad799x_state *st = iio_priv(indio_dev); + + kfree(st->rx_buf); + st->rx_buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); + if (!st->rx_buf) + return -ENOMEM; + + st->transfer_size = bitmap_weight(scan_mask, indio_dev->masklength) * 2; + + switch (st->id) { + case ad7992: + case ad7993: + case ad7994: + case ad7997: + case ad7998: + st->config &= ~(GENMASK(7, 0) << AD799X_CHANNEL_SHIFT); + st->config |= (*scan_mask << AD799X_CHANNEL_SHIFT); + return ad799x_write_config(st, st->config); + default: + return 0; + } +} + +static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch) +{ + u8 cmd; + + switch (st->id) { + case ad7991: + case ad7995: + case ad7999: + cmd = st->config | (BIT(ch) << AD799X_CHANNEL_SHIFT); + break; + case ad7992: + case ad7993: + case ad7994: + cmd = BIT(ch) << AD799X_CHANNEL_SHIFT; + break; + case ad7997: + case ad7998: + cmd = (ch << AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE; + break; + default: + return -EINVAL; + } + + return i2c_smbus_read_word_swapped(st->client, cmd); +} + +static int ad799x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad799x_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 = ad799x_scan_direct(st, chan->scan_index); + iio_device_release_direct_mode(indio_dev); + + if (ret < 0) + return ret; + *val = (ret >> chan->scan_type.shift) & + GENMASK(chan->scan_type.realbits - 1, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(st->vref); + if (ret < 0) + return ret; + *val = ret / 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} +static const unsigned int ad7998_frequencies[] = { + [AD7998_CYC_DIS] = 0, + [AD7998_CYC_TCONF_32] = 15625, + [AD7998_CYC_TCONF_64] = 7812, + [AD7998_CYC_TCONF_128] = 3906, + [AD7998_CYC_TCONF_512] = 976, + [AD7998_CYC_TCONF_1024] = 488, + [AD7998_CYC_TCONF_2048] = 244, +}; + +static ssize_t ad799x_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad799x_state *st = iio_priv(indio_dev); + + int ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", ad7998_frequencies[ret & AD7998_CYC_MASK]); +} + +static ssize_t ad799x_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad799x_state *st = iio_priv(indio_dev); + + long val; + int ret, i; + + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG); + if (ret < 0) + goto error_ret_mutex; + /* Wipe the bits clean */ + ret &= ~AD7998_CYC_MASK; + + for (i = 0; i < ARRAY_SIZE(ad7998_frequencies); i++) + if (val == ad7998_frequencies[i]) + break; + if (i == ARRAY_SIZE(ad7998_frequencies)) { + ret = -EINVAL; + goto error_ret_mutex; + } + + ret = i2c_smbus_write_byte_data(st->client, AD7998_CYCLE_TMR_REG, + ret | i); + if (ret < 0) + goto error_ret_mutex; + ret = len; + +error_ret_mutex: + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad799x_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad799x_state *st = iio_priv(indio_dev); + + if (!(st->config & AD7998_ALERT_EN)) + return 0; + + if ((st->config >> AD799X_CHANNEL_SHIFT) & BIT(chan->scan_index)) + return 1; + + return 0; +} + +static int ad799x_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct ad799x_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + if (state) + st->config |= BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT; + else + st->config &= ~(BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT); + + if (st->config >> AD799X_CHANNEL_SHIFT) + st->config |= AD7998_ALERT_EN; + else + st->config &= ~AD7998_ALERT_EN; + + ret = ad799x_write_config(st, st->config); + iio_device_release_direct_mode(indio_dev); + return ret; +} + +static unsigned int ad799x_threshold_reg(const struct iio_chan_spec *chan, + enum iio_event_direction dir, + enum iio_event_info info) +{ + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_FALLING) + return AD7998_DATALOW_REG(chan->channel); + else + return AD7998_DATAHIGH_REG(chan->channel); + case IIO_EV_INFO_HYSTERESIS: + return AD7998_HYST_REG(chan->channel); + default: + return -EINVAL; + } + + return 0; +} + +static int ad799x_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + int ret; + struct ad799x_state *st = iio_priv(indio_dev); + + if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0)) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + ret = i2c_smbus_write_word_swapped(st->client, + ad799x_threshold_reg(chan, dir, info), + val << chan->scan_type.shift); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad799x_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + int ret; + struct ad799x_state *st = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + ret = i2c_smbus_read_word_swapped(st->client, + ad799x_threshold_reg(chan, dir, info)); + mutex_unlock(&indio_dev->mlock); + if (ret < 0) + return ret; + *val = (ret >> chan->scan_type.shift) & + GENMASK(chan->scan_type.realbits - 1, 0); + + return IIO_VAL_INT; +} + +static irqreturn_t ad799x_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad799x_state *st = iio_priv(private); + int i, ret; + + ret = i2c_smbus_read_byte_data(st->client, AD7998_ALERT_STAT_REG); + if (ret <= 0) + goto done; + + if (i2c_smbus_write_byte_data(st->client, AD7998_ALERT_STAT_REG, + AD7998_ALERT_STAT_CLEAR) < 0) + goto done; + + for (i = 0; i < 8; i++) { + if (ret & BIT(i)) + iio_push_event(indio_dev, + i & 0x1 ? + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + (i >> 1), + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING) : + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + (i >> 1), + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + } + +done: + return IRQ_HANDLED; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + ad799x_read_frequency, + ad799x_write_frequency); +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("15625 7812 3906 1953 976 488 244 0"); + +static struct attribute *ad799x_event_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad799x_event_attrs_group = { + .attrs = ad799x_event_attributes, +}; + +static const struct iio_info ad7991_info = { + .read_raw = &ad799x_read_raw, + .update_scan_mode = ad799x_update_scan_mode, +}; + +static const struct iio_info ad7993_4_7_8_noirq_info = { + .read_raw = &ad799x_read_raw, + .update_scan_mode = ad799x_update_scan_mode, +}; + +static const struct iio_info ad7993_4_7_8_irq_info = { + .read_raw = &ad799x_read_raw, + .event_attrs = &ad799x_event_attrs_group, + .read_event_config = &ad799x_read_event_config, + .write_event_config = &ad799x_write_event_config, + .read_event_value = &ad799x_read_event_value, + .write_event_value = &ad799x_write_event_value, + .update_scan_mode = ad799x_update_scan_mode, +}; + +static const struct iio_event_spec ad799x_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +#define _AD799X_CHANNEL(_index, _realbits, _ev_spec, _num_ev_spec) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_realbits), \ + .storagebits = 16, \ + .shift = 12 - (_realbits), \ + .endianness = IIO_BE, \ + }, \ + .event_spec = _ev_spec, \ + .num_event_specs = _num_ev_spec, \ +} + +#define AD799X_CHANNEL(_index, _realbits) \ + _AD799X_CHANNEL(_index, _realbits, NULL, 0) + +#define AD799X_CHANNEL_WITH_EVENTS(_index, _realbits) \ + _AD799X_CHANNEL(_index, _realbits, ad799x_events, \ + ARRAY_SIZE(ad799x_events)) + +static const struct ad799x_chip_info ad799x_chip_info_tbl[] = { + [ad7991] = { + .num_channels = 5, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 12), + AD799X_CHANNEL(1, 12), + AD799X_CHANNEL(2, 12), + AD799X_CHANNEL(3, 12), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .info = &ad7991_info, + }, + }, + [ad7995] = { + .num_channels = 5, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 10), + AD799X_CHANNEL(1, 10), + AD799X_CHANNEL(2, 10), + AD799X_CHANNEL(3, 10), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .info = &ad7991_info, + }, + }, + [ad7999] = { + .num_channels = 5, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 8), + AD799X_CHANNEL(1, 8), + AD799X_CHANNEL(2, 8), + AD799X_CHANNEL(3, 8), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .info = &ad7991_info, + }, + }, + [ad7992] = { + .num_channels = 3, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 12), + AD799X_CHANNEL(1, 12), + IIO_CHAN_SOFT_TIMESTAMP(3), + }, + .info = &ad7993_4_7_8_noirq_info, + }, + .irq_config = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 12), + AD799X_CHANNEL_WITH_EVENTS(1, 12), + IIO_CHAN_SOFT_TIMESTAMP(3), + }, + .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT, + .info = &ad7993_4_7_8_irq_info, + }, + }, + [ad7993] = { + .num_channels = 5, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 10), + AD799X_CHANNEL(1, 10), + AD799X_CHANNEL(2, 10), + AD799X_CHANNEL(3, 10), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .info = &ad7993_4_7_8_noirq_info, + }, + .irq_config = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 10), + AD799X_CHANNEL_WITH_EVENTS(1, 10), + AD799X_CHANNEL_WITH_EVENTS(2, 10), + AD799X_CHANNEL_WITH_EVENTS(3, 10), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT, + .info = &ad7993_4_7_8_irq_info, + }, + }, + [ad7994] = { + .num_channels = 5, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 12), + AD799X_CHANNEL(1, 12), + AD799X_CHANNEL(2, 12), + AD799X_CHANNEL(3, 12), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .info = &ad7993_4_7_8_noirq_info, + }, + .irq_config = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 12), + AD799X_CHANNEL_WITH_EVENTS(1, 12), + AD799X_CHANNEL_WITH_EVENTS(2, 12), + AD799X_CHANNEL_WITH_EVENTS(3, 12), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT, + .info = &ad7993_4_7_8_irq_info, + }, + }, + [ad7997] = { + .num_channels = 9, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 10), + AD799X_CHANNEL(1, 10), + AD799X_CHANNEL(2, 10), + AD799X_CHANNEL(3, 10), + AD799X_CHANNEL(4, 10), + AD799X_CHANNEL(5, 10), + AD799X_CHANNEL(6, 10), + AD799X_CHANNEL(7, 10), + IIO_CHAN_SOFT_TIMESTAMP(8), + }, + .info = &ad7993_4_7_8_noirq_info, + }, + .irq_config = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 10), + AD799X_CHANNEL_WITH_EVENTS(1, 10), + AD799X_CHANNEL_WITH_EVENTS(2, 10), + AD799X_CHANNEL_WITH_EVENTS(3, 10), + AD799X_CHANNEL(4, 10), + AD799X_CHANNEL(5, 10), + AD799X_CHANNEL(6, 10), + AD799X_CHANNEL(7, 10), + IIO_CHAN_SOFT_TIMESTAMP(8), + }, + .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT, + .info = &ad7993_4_7_8_irq_info, + }, + }, + [ad7998] = { + .num_channels = 9, + .noirq_config = { + .channel = { + AD799X_CHANNEL(0, 12), + AD799X_CHANNEL(1, 12), + AD799X_CHANNEL(2, 12), + AD799X_CHANNEL(3, 12), + AD799X_CHANNEL(4, 12), + AD799X_CHANNEL(5, 12), + AD799X_CHANNEL(6, 12), + AD799X_CHANNEL(7, 12), + IIO_CHAN_SOFT_TIMESTAMP(8), + }, + .info = &ad7993_4_7_8_noirq_info, + }, + .irq_config = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 12), + AD799X_CHANNEL_WITH_EVENTS(1, 12), + AD799X_CHANNEL_WITH_EVENTS(2, 12), + AD799X_CHANNEL_WITH_EVENTS(3, 12), + AD799X_CHANNEL(4, 12), + AD799X_CHANNEL(5, 12), + AD799X_CHANNEL(6, 12), + AD799X_CHANNEL(7, 12), + IIO_CHAN_SOFT_TIMESTAMP(8), + }, + .default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT, + .info = &ad7993_4_7_8_irq_info, + }, + }, +}; + +static int ad799x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct ad799x_state *st; + struct iio_dev *indio_dev; + const struct ad799x_chip_info *chip_info = + &ad799x_chip_info_tbl[id->driver_data]; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + /* this is only used for device removal purposes */ + i2c_set_clientdata(client, indio_dev); + + st->id = id->driver_data; + if (client->irq > 0 && chip_info->irq_config.info) + st->chip_config = &chip_info->irq_config; + else + st->chip_config = &chip_info->noirq_config; + + /* TODO: Add pdata options for filtering and bit delay */ + + st->reg = devm_regulator_get(&client->dev, "vcc"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + ret = regulator_enable(st->reg); + if (ret) + return ret; + st->vref = devm_regulator_get(&client->dev, "vref"); + if (IS_ERR(st->vref)) { + ret = PTR_ERR(st->vref); + goto error_disable_reg; + } + ret = regulator_enable(st->vref); + if (ret) + goto error_disable_reg; + + st->client = client; + + indio_dev->name = id->name; + indio_dev->info = st->chip_config->info; + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_config->channel; + indio_dev->num_channels = chip_info->num_channels; + + ret = ad799x_update_config(st, st->chip_config->default_config); + if (ret) + goto error_disable_vref; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &ad799x_trigger_handler, NULL); + if (ret) + goto error_disable_vref; + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, + client->irq, + NULL, + ad799x_event_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + client->name, + indio_dev); + if (ret) + goto error_cleanup_ring; + } + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_ring; + + return 0; + +error_cleanup_ring: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_vref: + regulator_disable(st->vref); +error_disable_reg: + regulator_disable(st->reg); + + return ret; +} + +static int ad799x_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ad799x_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(st->vref); + regulator_disable(st->reg); + kfree(st->rx_buf); + + return 0; +} + +static int __maybe_unused ad799x_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ad799x_state *st = iio_priv(indio_dev); + + regulator_disable(st->vref); + regulator_disable(st->reg); + + return 0; +} + +static int __maybe_unused ad799x_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ad799x_state *st = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(st->reg); + if (ret) { + dev_err(dev, "Unable to enable vcc regulator\n"); + return ret; + } + ret = regulator_enable(st->vref); + if (ret) { + regulator_disable(st->reg); + dev_err(dev, "Unable to enable vref regulator\n"); + return ret; + } + + /* resync config */ + ret = ad799x_update_config(st, st->config); + if (ret) { + regulator_disable(st->vref); + regulator_disable(st->reg); + return ret; + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(ad799x_pm_ops, ad799x_suspend, ad799x_resume); + +static const struct i2c_device_id ad799x_id[] = { + { "ad7991", ad7991 }, + { "ad7995", ad7995 }, + { "ad7999", ad7999 }, + { "ad7992", ad7992 }, + { "ad7993", ad7993 }, + { "ad7994", ad7994 }, + { "ad7997", ad7997 }, + { "ad7998", ad7998 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ad799x_id); + +static struct i2c_driver ad799x_driver = { + .driver = { + .name = "ad799x", + .pm = &ad799x_pm_ops, + }, + .probe = ad799x_probe, + .remove = ad799x_remove, + .id_table = ad799x_id, +}; +module_i2c_driver(ad799x_driver); + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD799x ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c new file mode 100644 index 000000000..6b9627bfe --- /dev/null +++ b/drivers/iio/adc/ad9467.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD9467 SPI ADC driver + * + * Copyright 2012-2020 Analog Devices Inc. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/of_device.h> + + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include <linux/clk.h> + +#include <linux/iio/adc/adi-axi-adc.h> + +/* + * ADI High-Speed ADC common spi interface registers + * See Application-Note AN-877: + * https://www.analog.com/media/en/technical-documentation/application-notes/AN-877.pdf + */ + +#define AN877_ADC_REG_CHIP_PORT_CONF 0x00 +#define AN877_ADC_REG_CHIP_ID 0x01 +#define AN877_ADC_REG_CHIP_GRADE 0x02 +#define AN877_ADC_REG_CHAN_INDEX 0x05 +#define AN877_ADC_REG_TRANSFER 0xFF +#define AN877_ADC_REG_MODES 0x08 +#define AN877_ADC_REG_TEST_IO 0x0D +#define AN877_ADC_REG_ADC_INPUT 0x0F +#define AN877_ADC_REG_OFFSET 0x10 +#define AN877_ADC_REG_OUTPUT_MODE 0x14 +#define AN877_ADC_REG_OUTPUT_ADJUST 0x15 +#define AN877_ADC_REG_OUTPUT_PHASE 0x16 +#define AN877_ADC_REG_OUTPUT_DELAY 0x17 +#define AN877_ADC_REG_VREF 0x18 +#define AN877_ADC_REG_ANALOG_INPUT 0x2C + +/* AN877_ADC_REG_TEST_IO */ +#define AN877_ADC_TESTMODE_OFF 0x0 +#define AN877_ADC_TESTMODE_MIDSCALE_SHORT 0x1 +#define AN877_ADC_TESTMODE_POS_FULLSCALE 0x2 +#define AN877_ADC_TESTMODE_NEG_FULLSCALE 0x3 +#define AN877_ADC_TESTMODE_ALT_CHECKERBOARD 0x4 +#define AN877_ADC_TESTMODE_PN23_SEQ 0x5 +#define AN877_ADC_TESTMODE_PN9_SEQ 0x6 +#define AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE 0x7 +#define AN877_ADC_TESTMODE_USER 0x8 +#define AN877_ADC_TESTMODE_BIT_TOGGLE 0x9 +#define AN877_ADC_TESTMODE_SYNC 0xA +#define AN877_ADC_TESTMODE_ONE_BIT_HIGH 0xB +#define AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY 0xC +#define AN877_ADC_TESTMODE_RAMP 0xF + +/* AN877_ADC_REG_TRANSFER */ +#define AN877_ADC_TRANSFER_SYNC 0x1 + +/* AN877_ADC_REG_OUTPUT_MODE */ +#define AN877_ADC_OUTPUT_MODE_OFFSET_BINARY 0x0 +#define AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT 0x1 +#define AN877_ADC_OUTPUT_MODE_GRAY_CODE 0x2 + +/* AN877_ADC_REG_OUTPUT_PHASE */ +#define AN877_ADC_OUTPUT_EVEN_ODD_MODE_EN 0x20 +#define AN877_ADC_INVERT_DCO_CLK 0x80 + +/* AN877_ADC_REG_OUTPUT_DELAY */ +#define AN877_ADC_DCO_DELAY_ENABLE 0x80 + +/* + * Analog Devices AD9265 16-Bit, 125/105/80 MSPS ADC + */ + +#define CHIPID_AD9265 0x64 +#define AD9265_DEF_OUTPUT_MODE 0x40 +#define AD9265_REG_VREF_MASK 0xC0 + +/* + * Analog Devices AD9434 12-Bit, 370/500 MSPS ADC + */ + +#define CHIPID_AD9434 0x6A +#define AD9434_DEF_OUTPUT_MODE 0x00 +#define AD9434_REG_VREF_MASK 0xC0 + +/* + * Analog Devices AD9467 16-Bit, 200/250 MSPS ADC + */ + +#define CHIPID_AD9467 0x50 +#define AD9467_DEF_OUTPUT_MODE 0x08 +#define AD9467_REG_VREF_MASK 0x0F + +enum { + ID_AD9265, + ID_AD9434, + ID_AD9467, +}; + +struct ad9467_chip_info { + struct adi_axi_adc_chip_info axi_adc_info; + unsigned int default_output_mode; + unsigned int vref_mask; +}; + +#define to_ad9467_chip_info(_info) \ + container_of(_info, struct ad9467_chip_info, axi_adc_info) + +struct ad9467_state { + struct spi_device *spi; + struct clk *clk; + unsigned int output_mode; + unsigned int (*scales)[2]; + + struct gpio_desc *pwrdown_gpio; +}; + +static int ad9467_spi_read(struct spi_device *spi, unsigned int reg) +{ + unsigned char tbuf[2], rbuf[1]; + int ret; + + tbuf[0] = 0x80 | (reg >> 8); + tbuf[1] = reg & 0xFF; + + ret = spi_write_then_read(spi, + tbuf, ARRAY_SIZE(tbuf), + rbuf, ARRAY_SIZE(rbuf)); + + if (ret < 0) + return ret; + + return rbuf[0]; +} + +static int ad9467_spi_write(struct spi_device *spi, unsigned int reg, + unsigned int val) +{ + unsigned char buf[3]; + + buf[0] = reg >> 8; + buf[1] = reg & 0xFF; + buf[2] = val; + + return spi_write(spi, buf, ARRAY_SIZE(buf)); +} + +static int ad9467_reg_access(struct adi_axi_adc_conv *conv, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + struct spi_device *spi = st->spi; + int ret; + + if (readval == NULL) { + ret = ad9467_spi_write(spi, reg, writeval); + if (ret) + return ret; + return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); + } + + ret = ad9467_spi_read(spi, reg); + if (ret < 0) + return ret; + *readval = ret; + + return 0; +} + +static const unsigned int ad9265_scale_table[][2] = { + {1250, 0x00}, {1500, 0x40}, {1750, 0x80}, {2000, 0xC0}, +}; + +static const unsigned int ad9434_scale_table[][2] = { + {1600, 0x1C}, {1580, 0x1D}, {1550, 0x1E}, {1520, 0x1F}, {1500, 0x00}, + {1470, 0x01}, {1440, 0x02}, {1420, 0x03}, {1390, 0x04}, {1360, 0x05}, + {1340, 0x06}, {1310, 0x07}, {1280, 0x08}, {1260, 0x09}, {1230, 0x0A}, + {1200, 0x0B}, {1180, 0x0C}, +}; + +static const unsigned int ad9467_scale_table[][2] = { + {2000, 0}, {2100, 6}, {2200, 7}, + {2300, 8}, {2400, 9}, {2500, 10}, +}; + +static void __ad9467_get_scale(struct adi_axi_adc_conv *conv, int index, + unsigned int *val, unsigned int *val2) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + const struct iio_chan_spec *chan = &info->channels[0]; + unsigned int tmp; + + tmp = (info->scale_table[index][0] * 1000000ULL) >> + chan->scan_type.realbits; + *val = tmp / 1000000; + *val2 = tmp % 1000000; +} + +#define AD9467_CHAN(_chan, _si, _bits, _sign) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = _si, \ + .scan_type = { \ + .sign = _sign, \ + .realbits = _bits, \ + .storagebits = 16, \ + }, \ +} + +static const struct iio_chan_spec ad9434_channels[] = { + AD9467_CHAN(0, 0, 12, 'S'), +}; + +static const struct iio_chan_spec ad9467_channels[] = { + AD9467_CHAN(0, 0, 16, 'S'), +}; + +static const struct ad9467_chip_info ad9467_chip_tbl[] = { + [ID_AD9265] = { + .axi_adc_info = { + .id = CHIPID_AD9265, + .max_rate = 125000000UL, + .scale_table = ad9265_scale_table, + .num_scales = ARRAY_SIZE(ad9265_scale_table), + .channels = ad9467_channels, + .num_channels = ARRAY_SIZE(ad9467_channels), + }, + .default_output_mode = AD9265_DEF_OUTPUT_MODE, + .vref_mask = AD9265_REG_VREF_MASK, + }, + [ID_AD9434] = { + .axi_adc_info = { + .id = CHIPID_AD9434, + .max_rate = 500000000UL, + .scale_table = ad9434_scale_table, + .num_scales = ARRAY_SIZE(ad9434_scale_table), + .channels = ad9434_channels, + .num_channels = ARRAY_SIZE(ad9434_channels), + }, + .default_output_mode = AD9434_DEF_OUTPUT_MODE, + .vref_mask = AD9434_REG_VREF_MASK, + }, + [ID_AD9467] = { + .axi_adc_info = { + .id = CHIPID_AD9467, + .max_rate = 250000000UL, + .scale_table = ad9467_scale_table, + .num_scales = ARRAY_SIZE(ad9467_scale_table), + .channels = ad9467_channels, + .num_channels = ARRAY_SIZE(ad9467_channels), + }, + .default_output_mode = AD9467_DEF_OUTPUT_MODE, + .vref_mask = AD9467_REG_VREF_MASK, + }, +}; + +static int ad9467_get_scale(struct adi_axi_adc_conv *conv, int *val, int *val2) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + const struct ad9467_chip_info *info1 = to_ad9467_chip_info(info); + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + unsigned int i, vref_val; + int ret; + + ret = ad9467_spi_read(st->spi, AN877_ADC_REG_VREF); + if (ret < 0) + return ret; + + vref_val = ret & info1->vref_mask; + + for (i = 0; i < info->num_scales; i++) { + if (vref_val == info->scale_table[i][1]) + break; + } + + if (i == info->num_scales) + return -ERANGE; + + __ad9467_get_scale(conv, i, val, val2); + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + unsigned int scale_val[2]; + unsigned int i; + int ret; + + if (val != 0) + return -EINVAL; + + for (i = 0; i < info->num_scales; i++) { + __ad9467_get_scale(conv, i, &scale_val[0], &scale_val[1]); + if (scale_val[0] != val || scale_val[1] != val2) + continue; + + ret = ad9467_spi_write(st->spi, AN877_ADC_REG_VREF, + info->scale_table[i][1]); + if (ret < 0) + return ret; + + return ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); + } + + return -EINVAL; +} + +static int ad9467_read_raw(struct adi_axi_adc_conv *conv, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + + switch (m) { + case IIO_CHAN_INFO_SCALE: + return ad9467_get_scale(conv, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(st->clk); + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad9467_write_raw(struct adi_axi_adc_conv *conv, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + long r_clk; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return ad9467_set_scale(conv, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + r_clk = clk_round_rate(st->clk, val); + if (r_clk < 0 || r_clk > info->max_rate) { + dev_warn(&st->spi->dev, + "Error setting ADC sample rate %ld", r_clk); + return -EINVAL; + } + + return clk_set_rate(st->clk, r_clk); + default: + return -EINVAL; + } +} + +static int ad9467_read_avail(struct adi_axi_adc_conv *conv, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)st->scales; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = info->num_scales * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode) +{ + int ret; + + ret = ad9467_spi_write(spi, AN877_ADC_REG_OUTPUT_MODE, mode); + if (ret < 0) + return ret; + + return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); +} + +static int ad9467_scale_fill(struct adi_axi_adc_conv *conv) +{ + const struct adi_axi_adc_chip_info *info = conv->chip_info; + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + unsigned int i, val1, val2; + + st->scales = devm_kmalloc_array(&st->spi->dev, info->num_scales, + sizeof(*st->scales), GFP_KERNEL); + if (!st->scales) + return -ENOMEM; + + for (i = 0; i < info->num_scales; i++) { + __ad9467_get_scale(conv, i, &val1, &val2); + st->scales[i][0] = val1; + st->scales[i][1] = val2; + } + + return 0; +} + +static int ad9467_preenable_setup(struct adi_axi_adc_conv *conv) +{ + struct ad9467_state *st = adi_axi_adc_conv_priv(conv); + + return ad9467_outputmode_set(st->spi, st->output_mode); +} + +static int ad9467_reset(struct device *dev) +{ + struct gpio_desc *gpio; + + gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR_OR_NULL(gpio)) + return PTR_ERR_OR_ZERO(gpio); + + fsleep(1); + gpiod_set_value_cansleep(gpio, 0); + fsleep(10 * USEC_PER_MSEC); + + return 0; +} + +static int ad9467_probe(struct spi_device *spi) +{ + const struct ad9467_chip_info *info; + struct adi_axi_adc_conv *conv; + struct ad9467_state *st; + unsigned int id; + int ret; + + info = of_device_get_match_data(&spi->dev); + if (!info) + return -ENODEV; + + conv = devm_adi_axi_adc_conv_register(&spi->dev, sizeof(*st)); + if (IS_ERR(conv)) + return PTR_ERR(conv); + + st = adi_axi_adc_conv_priv(conv); + st->spi = spi; + + st->clk = devm_clk_get_enabled(&spi->dev, "adc-clk"); + if (IS_ERR(st->clk)) + return PTR_ERR(st->clk); + + st->pwrdown_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown", + GPIOD_OUT_LOW); + if (IS_ERR(st->pwrdown_gpio)) + return PTR_ERR(st->pwrdown_gpio); + + ret = ad9467_reset(&spi->dev); + if (ret) + return ret; + + spi_set_drvdata(spi, st); + + conv->chip_info = &info->axi_adc_info; + + ret = ad9467_scale_fill(conv); + if (ret) + return ret; + + id = ad9467_spi_read(spi, AN877_ADC_REG_CHIP_ID); + if (id != conv->chip_info->id) { + dev_err(&spi->dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n", + id, conv->chip_info->id); + return -ENODEV; + } + + conv->reg_access = ad9467_reg_access; + conv->write_raw = ad9467_write_raw; + conv->read_raw = ad9467_read_raw; + conv->read_avail = ad9467_read_avail; + conv->preenable_setup = ad9467_preenable_setup; + + st->output_mode = info->default_output_mode | + AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT; + + return 0; +} + +static const struct of_device_id ad9467_of_match[] = { + { .compatible = "adi,ad9265", .data = &ad9467_chip_tbl[ID_AD9265], }, + { .compatible = "adi,ad9434", .data = &ad9467_chip_tbl[ID_AD9434], }, + { .compatible = "adi,ad9467", .data = &ad9467_chip_tbl[ID_AD9467], }, + {} +}; +MODULE_DEVICE_TABLE(of, ad9467_of_match); + +static struct spi_driver ad9467_driver = { + .driver = { + .name = "ad9467", + .of_match_table = ad9467_of_match, + }, + .probe = ad9467_probe, +}; +module_spi_driver(ad9467_driver); + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD9467 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c new file mode 100644 index 000000000..496cb2b26 --- /dev/null +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -0,0 +1,585 @@ +// 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> + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/err.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/adc/ad_sigma_delta.h> + +#include <asm/unaligned.h> + + +#define AD_SD_COMM_CHAN_MASK 0x3 + +#define AD_SD_REG_COMM 0x00 +#define AD_SD_REG_DATA 0x03 + +/** + * ad_sd_set_comm() - Set communications register + * + * @sigma_delta: The sigma delta device + * @comm: New value for the communications register + */ +void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm) +{ + /* Some variants use the lower two bits of the communications register + * to select the channel */ + sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK; +} +EXPORT_SYMBOL_GPL(ad_sd_set_comm); + +/** + * ad_sd_write_reg() - Write a register + * + * @sigma_delta: The sigma delta device + * @reg: Address of the register + * @size: Size of the register (0-3) + * @val: Value to write to the register + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, + unsigned int size, unsigned int val) +{ + uint8_t *data = sigma_delta->tx_buf; + struct spi_transfer t = { + .tx_buf = data, + .len = size + 1, + .cs_change = sigma_delta->keep_cs_asserted, + }; + struct spi_message m; + int ret; + + data[0] = (reg << sigma_delta->info->addr_shift) | sigma_delta->comm; + + switch (size) { + case 3: + put_unaligned_be24(val, &data[1]); + break; + case 2: + put_unaligned_be16(val, &data[1]); + break; + case 1: + data[1] = val; + break; + case 0: + break; + default: + return -EINVAL; + } + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + if (sigma_delta->bus_locked) + ret = spi_sync_locked(sigma_delta->spi, &m); + else + ret = spi_sync(sigma_delta->spi, &m); + + return ret; +} +EXPORT_SYMBOL_GPL(ad_sd_write_reg); + +static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta, + unsigned int reg, unsigned int size, uint8_t *val) +{ + uint8_t *data = sigma_delta->tx_buf; + int ret; + struct spi_transfer t[] = { + { + .tx_buf = data, + .len = 1, + }, { + .rx_buf = val, + .len = size, + .cs_change = sigma_delta->bus_locked, + }, + }; + struct spi_message m; + + spi_message_init(&m); + + 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); + + if (sigma_delta->bus_locked) + ret = spi_sync_locked(sigma_delta->spi, &m); + else + ret = spi_sync(sigma_delta->spi, &m); + + return ret; +} + +/** + * ad_sd_read_reg() - Read a register + * + * @sigma_delta: The sigma delta device + * @reg: Address of the register + * @size: Size of the register (1-4) + * @val: Read value + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, + unsigned int reg, unsigned int size, unsigned int *val) +{ + int ret; + + ret = ad_sd_read_reg_raw(sigma_delta, reg, size, sigma_delta->rx_buf); + if (ret < 0) + goto out; + + switch (size) { + case 4: + *val = get_unaligned_be32(sigma_delta->rx_buf); + break; + case 3: + *val = get_unaligned_be24(sigma_delta->rx_buf); + break; + case 2: + *val = get_unaligned_be16(sigma_delta->rx_buf); + break; + case 1: + *val = sigma_delta->rx_buf[0]; + break; + default: + ret = -EINVAL; + break; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(ad_sd_read_reg); + +/** + * ad_sd_reset() - Reset the serial interface + * + * @sigma_delta: The sigma delta device + * @reset_length: Number of SCLKs with DIN = 1 + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_reset(struct ad_sigma_delta *sigma_delta, + unsigned int reset_length) +{ + uint8_t *buf; + unsigned int size; + int ret; + + size = DIV_ROUND_UP(reset_length, 8); + buf = kcalloc(size, sizeof(*buf), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memset(buf, 0xff, size); + ret = spi_write(sigma_delta->spi, buf, size); + kfree(buf); + + return ret; +} +EXPORT_SYMBOL_GPL(ad_sd_reset); + +int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, + unsigned int mode, unsigned int channel) +{ + int ret; + unsigned long timeout; + + ret = ad_sigma_delta_set_channel(sigma_delta, channel); + if (ret) + return ret; + + 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); + if (ret < 0) + goto out; + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->spi->irq); + timeout = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ); + if (timeout == 0) { + sigma_delta->irq_dis = true; + disable_irq_nosync(sigma_delta->spi->irq); + ret = -EIO; + } else { + 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); + + return ret; +} +EXPORT_SYMBOL_GPL(ad_sd_calibrate); + +/** + * ad_sd_calibrate_all() - Performs channel calibration + * @sigma_delta: The sigma delta device + * @cb: Array of channels and calibration type to perform + * @n: Number of items in cb + * + * Returns 0 on success, an error code otherwise. + **/ +int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta, + const struct ad_sd_calib_data *cb, unsigned int n) +{ + unsigned int i; + int ret; + + for (i = 0; i < n; i++) { + ret = ad_sd_calibrate(sigma_delta, cb[i].mode, cb[i].channel); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ad_sd_calibrate_all); + +/** + * ad_sigma_delta_single_conversion() - Performs a single data conversion + * @indio_dev: The IIO device + * @chan: The conversion is done for this channel + * @val: Pointer to the location where to store the read value + * + * Returns: 0 on success, an error value otherwise. + */ +int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + unsigned int sample, raw_sample; + unsigned int data_reg; + int ret = 0; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ad_sigma_delta_set_channel(sigma_delta, chan->address); + + 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); + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->spi->irq); + ret = wait_for_completion_interruptible_timeout( + &sigma_delta->completion, HZ); + + if (ret == 0) + ret = -EIO; + if (ret < 0) + goto out; + + 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); + +out: + if (!sigma_delta->irq_dis) { + disable_irq_nosync(sigma_delta->spi->irq); + 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); + iio_device_release_direct_mode(indio_dev); + + if (ret) + return ret; + + sample = raw_sample >> chan->scan_type.shift; + sample &= (1 << chan->scan_type.realbits) - 1; + *val = sample; + + ret = ad_sigma_delta_postprocess_sample(sigma_delta, raw_sample); + if (ret) + return ret; + + return IIO_VAL_INT; +} +EXPORT_SYMBOL_GPL(ad_sigma_delta_single_conversion); + +static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + unsigned int channel; + int ret; + + channel = find_first_bit(indio_dev->active_scan_mask, + indio_dev->masklength); + ret = ad_sigma_delta_set_channel(sigma_delta, + indio_dev->channels[channel].address); + if (ret) + 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; + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->spi->irq); + + return 0; + +err_unlock: + spi_bus_unlock(sigma_delta->spi->master); + + return ret; +} + +static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + + reinit_completion(&sigma_delta->completion); + wait_for_completion_timeout(&sigma_delta->completion, HZ); + + if (!sigma_delta->irq_dis) { + disable_irq_nosync(sigma_delta->spi->irq); + 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; + return spi_bus_unlock(sigma_delta->spi->master); +} + +static irqreturn_t ad_sd_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + uint8_t *data = sigma_delta->rx_buf; + unsigned int reg_size; + unsigned int data_reg; + + reg_size = indio_dev->channels[0].scan_type.realbits + + 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: + 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. */ + ad_sd_read_reg_raw(sigma_delta, data_reg, reg_size, &data[1]); + break; + } + + iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp); + + iio_trigger_notify_done(indio_dev->trig); + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->spi->irq); + + return IRQ_HANDLED; +} + +static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = { + .postenable = &ad_sd_buffer_postenable, + .postdisable = &ad_sd_buffer_postdisable, + .validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + +static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) +{ + struct ad_sigma_delta *sigma_delta = private; + + complete(&sigma_delta->completion); + disable_irq_nosync(irq); + sigma_delta->irq_dis = true; + iio_trigger_poll(sigma_delta->trig); + + return IRQ_HANDLED; +} + +/** + * ad_sd_validate_trigger() - validate_trigger callback for ad_sigma_delta devices + * @indio_dev: The IIO device + * @trig: The new trigger + * + * Returns: 0 if the 'trig' matches the trigger registered by the ad_sigma_delta + * device, -EINVAL otherwise. + */ +int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + + if (sigma_delta->trig != trig) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(ad_sd_validate_trigger); + +static const struct iio_trigger_ops ad_sd_trigger_ops = { +}; + +static int ad_sd_probe_trigger(struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + int ret; + + sigma_delta->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, + indio_dev->id); + if (sigma_delta->trig == NULL) { + ret = -ENOMEM; + goto error_ret; + } + sigma_delta->trig->ops = &ad_sd_trigger_ops; + init_completion(&sigma_delta->completion); + + ret = request_irq(sigma_delta->spi->irq, + ad_sd_data_rdy_trig_poll, + sigma_delta->info->irq_flags, + indio_dev->name, + sigma_delta); + if (ret) + goto error_free_trig; + + if (!sigma_delta->irq_dis) { + sigma_delta->irq_dis = true; + disable_irq_nosync(sigma_delta->spi->irq); + } + sigma_delta->trig->dev.parent = &sigma_delta->spi->dev; + iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta); + + ret = iio_trigger_register(sigma_delta->trig); + if (ret) + goto error_free_irq; + + /* select default trigger */ + indio_dev->trig = iio_trigger_get(sigma_delta->trig); + + return 0; + +error_free_irq: + free_irq(sigma_delta->spi->irq, sigma_delta); +error_free_trig: + iio_trigger_free(sigma_delta->trig); +error_ret: + return ret; +} + +static void ad_sd_remove_trigger(struct iio_dev *indio_dev) +{ + struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); + + iio_trigger_unregister(sigma_delta->trig); + free_irq(sigma_delta->spi->irq, sigma_delta); + iio_trigger_free(sigma_delta->trig); +} + +/** + * ad_sd_setup_buffer_and_trigger() - + * @indio_dev: The IIO device + */ +int ad_sd_setup_buffer_and_trigger(struct iio_dev *indio_dev) +{ + int ret; + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + &ad_sd_trigger_handler, &ad_sd_buffer_setup_ops); + if (ret) + return ret; + + ret = ad_sd_probe_trigger(indio_dev); + if (ret) { + iio_triggered_buffer_cleanup(indio_dev); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ad_sd_setup_buffer_and_trigger); + +/** + * ad_sd_cleanup_buffer_and_trigger() - + * @indio_dev: The IIO device + */ +void ad_sd_cleanup_buffer_and_trigger(struct iio_dev *indio_dev) +{ + ad_sd_remove_trigger(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); +} +EXPORT_SYMBOL_GPL(ad_sd_cleanup_buffer_and_trigger); + +/** + * ad_sd_init() - Initializes a ad_sigma_delta struct + * @sigma_delta: The ad_sigma_delta device + * @indio_dev: The IIO device which the Sigma Delta device is used for + * @spi: The SPI device for the ad_sigma_delta device + * @info: Device specific callbacks and options + * + * This function needs to be called before any other operations are performed on + * the ad_sigma_delta struct. + */ +int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, + struct spi_device *spi, const struct ad_sigma_delta_info *info) +{ + sigma_delta->spi = spi; + sigma_delta->info = info; + iio_device_set_drvdata(indio_dev, sigma_delta); + + return 0; +} +EXPORT_SYMBOL_GPL(ad_sd_init); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c new file mode 100644 index 000000000..d721fb1bc --- /dev/null +++ b/drivers/iio/adc/adi-axi-adc.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices Generic AXI ADC IP core + * Link: https://wiki.analog.com/resources/fpga/docs/axi_adc_ip + * + * Copyright 2012-2020 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/buffer-dmaengine.h> + +#include <linux/fpga/adi-axi-common.h> +#include <linux/iio/adc/adi-axi-adc.h> + +/** + * Register definitions: + * https://wiki.analog.com/resources/fpga/docs/axi_adc_ip#register_map + */ + +/* ADC controls */ + +#define ADI_AXI_REG_RSTN 0x0040 +#define ADI_AXI_REG_RSTN_CE_N BIT(2) +#define ADI_AXI_REG_RSTN_MMCM_RSTN BIT(1) +#define ADI_AXI_REG_RSTN_RSTN BIT(0) + +/* ADC Channel controls */ + +#define ADI_AXI_REG_CHAN_CTRL(c) (0x0400 + (c) * 0x40) +#define ADI_AXI_REG_CHAN_CTRL_LB_OWR BIT(11) +#define ADI_AXI_REG_CHAN_CTRL_PN_SEL_OWR BIT(10) +#define ADI_AXI_REG_CHAN_CTRL_IQCOR_EN BIT(9) +#define ADI_AXI_REG_CHAN_CTRL_DCFILT_EN BIT(8) +#define ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT BIT(6) +#define ADI_AXI_REG_CHAN_CTRL_FMT_TYPE BIT(5) +#define ADI_AXI_REG_CHAN_CTRL_FMT_EN BIT(4) +#define ADI_AXI_REG_CHAN_CTRL_PN_TYPE_OWR BIT(1) +#define ADI_AXI_REG_CHAN_CTRL_ENABLE BIT(0) + +#define ADI_AXI_REG_CHAN_CTRL_DEFAULTS \ + (ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT | \ + ADI_AXI_REG_CHAN_CTRL_FMT_EN | \ + ADI_AXI_REG_CHAN_CTRL_ENABLE) + +struct adi_axi_adc_core_info { + unsigned int version; +}; + +struct adi_axi_adc_state { + struct mutex lock; + + struct adi_axi_adc_client *client; + void __iomem *regs; +}; + +struct adi_axi_adc_client { + struct list_head entry; + struct adi_axi_adc_conv conv; + struct adi_axi_adc_state *state; + struct device *dev; + const struct adi_axi_adc_core_info *info; +}; + +static LIST_HEAD(registered_clients); +static DEFINE_MUTEX(registered_clients_lock); + +static struct adi_axi_adc_client *conv_to_client(struct adi_axi_adc_conv *conv) +{ + return container_of(conv, struct adi_axi_adc_client, conv); +} + +void *adi_axi_adc_conv_priv(struct adi_axi_adc_conv *conv) +{ + struct adi_axi_adc_client *cl = conv_to_client(conv); + + return (char *)cl + ALIGN(sizeof(struct adi_axi_adc_client), IIO_ALIGN); +} +EXPORT_SYMBOL_GPL(adi_axi_adc_conv_priv); + +static void adi_axi_adc_write(struct adi_axi_adc_state *st, + unsigned int reg, + unsigned int val) +{ + iowrite32(val, st->regs + reg); +} + +static unsigned int adi_axi_adc_read(struct adi_axi_adc_state *st, + unsigned int reg) +{ + return ioread32(st->regs + reg); +} + +static int adi_axi_adc_config_dma_buffer(struct device *dev, + struct iio_dev *indio_dev) +{ + struct iio_buffer *buffer; + const char *dma_name; + + if (!device_property_present(dev, "dmas")) + return 0; + + if (device_property_read_string(dev, "dma-names", &dma_name)) + dma_name = "rx"; + + buffer = devm_iio_dmaengine_buffer_alloc(indio_dev->dev.parent, + dma_name); + if (IS_ERR(buffer)) + return PTR_ERR(buffer); + + indio_dev->modes |= INDIO_BUFFER_HARDWARE; + iio_device_attach_buffer(indio_dev, buffer); + + return 0; +} + +static int adi_axi_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct adi_axi_adc_state *st = iio_priv(indio_dev); + struct adi_axi_adc_conv *conv = &st->client->conv; + + if (!conv->read_raw) + return -EOPNOTSUPP; + + return conv->read_raw(conv, chan, val, val2, mask); +} + +static int adi_axi_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct adi_axi_adc_state *st = iio_priv(indio_dev); + struct adi_axi_adc_conv *conv = &st->client->conv; + + if (!conv->write_raw) + return -EOPNOTSUPP; + + return conv->write_raw(conv, chan, val, val2, mask); +} + +static int adi_axi_adc_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct adi_axi_adc_state *st = iio_priv(indio_dev); + struct adi_axi_adc_conv *conv = &st->client->conv; + + if (!conv->read_avail) + return -EOPNOTSUPP; + + return conv->read_avail(conv, chan, vals, type, length, mask); +} + +static int adi_axi_adc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct adi_axi_adc_state *st = iio_priv(indio_dev); + struct adi_axi_adc_conv *conv = &st->client->conv; + unsigned int i, ctrl; + + for (i = 0; i < conv->chip_info->num_channels; i++) { + ctrl = adi_axi_adc_read(st, ADI_AXI_REG_CHAN_CTRL(i)); + + if (test_bit(i, scan_mask)) + ctrl |= ADI_AXI_REG_CHAN_CTRL_ENABLE; + else + ctrl &= ~ADI_AXI_REG_CHAN_CTRL_ENABLE; + + adi_axi_adc_write(st, ADI_AXI_REG_CHAN_CTRL(i), ctrl); + } + + return 0; +} + +static struct adi_axi_adc_conv *adi_axi_adc_conv_register(struct device *dev, + size_t sizeof_priv) +{ + struct adi_axi_adc_client *cl; + size_t alloc_size; + + alloc_size = ALIGN(sizeof(struct adi_axi_adc_client), IIO_ALIGN); + if (sizeof_priv) + alloc_size += ALIGN(sizeof_priv, IIO_ALIGN); + + cl = kzalloc(alloc_size, GFP_KERNEL); + if (!cl) + return ERR_PTR(-ENOMEM); + + mutex_lock(®istered_clients_lock); + + cl->dev = get_device(dev); + + list_add_tail(&cl->entry, ®istered_clients); + + mutex_unlock(®istered_clients_lock); + + return &cl->conv; +} + +static void adi_axi_adc_conv_unregister(struct adi_axi_adc_conv *conv) +{ + struct adi_axi_adc_client *cl = conv_to_client(conv); + + mutex_lock(®istered_clients_lock); + + list_del(&cl->entry); + put_device(cl->dev); + + mutex_unlock(®istered_clients_lock); + + kfree(cl); +} + +static void devm_adi_axi_adc_conv_release(struct device *dev, void *res) +{ + adi_axi_adc_conv_unregister(*(struct adi_axi_adc_conv **)res); +} + +struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev, + size_t sizeof_priv) +{ + struct adi_axi_adc_conv **ptr, *conv; + + ptr = devres_alloc(devm_adi_axi_adc_conv_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + conv = adi_axi_adc_conv_register(dev, sizeof_priv); + if (IS_ERR(conv)) { + devres_free(ptr); + return ERR_CAST(conv); + } + + *ptr = conv; + devres_add(dev, ptr); + + return conv; +} +EXPORT_SYMBOL_GPL(devm_adi_axi_adc_conv_register); + +static const struct iio_info adi_axi_adc_info = { + .read_raw = &adi_axi_adc_read_raw, + .write_raw = &adi_axi_adc_write_raw, + .update_scan_mode = &adi_axi_adc_update_scan_mode, + .read_avail = &adi_axi_adc_read_avail, +}; + +static const struct adi_axi_adc_core_info adi_axi_adc_10_0_a_info = { + .version = ADI_AXI_PCORE_VER(10, 0, 'a'), +}; + +static struct adi_axi_adc_client *adi_axi_adc_attach_client(struct device *dev) +{ + const struct adi_axi_adc_core_info *info; + struct adi_axi_adc_client *cl; + struct device_node *cln; + + info = of_device_get_match_data(dev); + if (!info) + return ERR_PTR(-ENODEV); + + cln = of_parse_phandle(dev->of_node, "adi,adc-dev", 0); + if (!cln) { + dev_err(dev, "No 'adi,adc-dev' node defined\n"); + return ERR_PTR(-ENODEV); + } + + mutex_lock(®istered_clients_lock); + + list_for_each_entry(cl, ®istered_clients, entry) { + if (!cl->dev) + continue; + + if (cl->dev->of_node != cln) + continue; + + if (!try_module_get(cl->dev->driver->owner)) { + mutex_unlock(®istered_clients_lock); + of_node_put(cln); + return ERR_PTR(-ENODEV); + } + + get_device(cl->dev); + cl->info = info; + mutex_unlock(®istered_clients_lock); + of_node_put(cln); + return cl; + } + + mutex_unlock(®istered_clients_lock); + of_node_put(cln); + + return ERR_PTR(-EPROBE_DEFER); +} + +static int adi_axi_adc_setup_channels(struct device *dev, + struct adi_axi_adc_state *st) +{ + struct adi_axi_adc_conv *conv = &st->client->conv; + int i, ret; + + if (conv->preenable_setup) { + ret = conv->preenable_setup(conv); + if (ret) + return ret; + } + + for (i = 0; i < conv->chip_info->num_channels; i++) { + adi_axi_adc_write(st, ADI_AXI_REG_CHAN_CTRL(i), + ADI_AXI_REG_CHAN_CTRL_DEFAULTS); + } + + return 0; +} + +static void axi_adc_reset(struct adi_axi_adc_state *st) +{ + adi_axi_adc_write(st, ADI_AXI_REG_RSTN, 0); + mdelay(10); + adi_axi_adc_write(st, ADI_AXI_REG_RSTN, ADI_AXI_REG_RSTN_MMCM_RSTN); + mdelay(10); + adi_axi_adc_write(st, ADI_AXI_REG_RSTN, + ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN); +} + +static void adi_axi_adc_cleanup(void *data) +{ + struct adi_axi_adc_client *cl = data; + + put_device(cl->dev); + module_put(cl->dev->driver->owner); +} + +static int adi_axi_adc_probe(struct platform_device *pdev) +{ + struct adi_axi_adc_conv *conv; + struct iio_dev *indio_dev; + struct adi_axi_adc_client *cl; + struct adi_axi_adc_state *st; + unsigned int ver; + int ret; + + cl = adi_axi_adc_attach_client(&pdev->dev); + if (IS_ERR(cl)) + return PTR_ERR(cl); + + ret = devm_add_action_or_reset(&pdev->dev, adi_axi_adc_cleanup, cl); + if (ret) + return ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->client = cl; + cl->state = st; + mutex_init(&st->lock); + + st->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(st->regs)) + return PTR_ERR(st->regs); + + conv = &st->client->conv; + + axi_adc_reset(st); + + ver = adi_axi_adc_read(st, ADI_AXI_REG_VERSION); + + if (cl->info->version > ver) { + dev_err(&pdev->dev, + "IP core version is too old. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n", + ADI_AXI_PCORE_VER_MAJOR(cl->info->version), + ADI_AXI_PCORE_VER_MINOR(cl->info->version), + ADI_AXI_PCORE_VER_PATCH(cl->info->version), + ADI_AXI_PCORE_VER_MAJOR(ver), + ADI_AXI_PCORE_VER_MINOR(ver), + ADI_AXI_PCORE_VER_PATCH(ver)); + return -ENODEV; + } + + indio_dev->info = &adi_axi_adc_info; + indio_dev->name = "adi-axi-adc"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->num_channels = conv->chip_info->num_channels; + indio_dev->channels = conv->chip_info->channels; + + ret = adi_axi_adc_config_dma_buffer(&pdev->dev, indio_dev); + if (ret) + return ret; + + ret = adi_axi_adc_setup_channels(&pdev->dev, st); + if (ret) + return ret; + + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret) + return ret; + + dev_info(&pdev->dev, "AXI ADC IP core (%d.%.2d.%c) probed\n", + ADI_AXI_PCORE_VER_MAJOR(ver), + ADI_AXI_PCORE_VER_MINOR(ver), + ADI_AXI_PCORE_VER_PATCH(ver)); + + return 0; +} + +/* Match table for of_platform binding */ +static const struct of_device_id adi_axi_adc_of_match[] = { + { .compatible = "adi,axi-adc-10.0.a", .data = &adi_axi_adc_10_0_a_info }, + { /* end of list */ } +}; +MODULE_DEVICE_TABLE(of, adi_axi_adc_of_match); + +static struct platform_driver adi_axi_adc_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = adi_axi_adc_of_match, + }, + .probe = adi_axi_adc_probe, +}; +module_platform_driver(adi_axi_adc_driver); + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_DESCRIPTION("Analog Devices Generic AXI ADC IP core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c new file mode 100644 index 000000000..34ec0c28b --- /dev/null +++ b/drivers/iio/adc/aspeed_adc.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Aspeed AST2400/2500 ADC + * + * Copyright (C) 2017 Google, Inc. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/reset.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> +#include <linux/iopoll.h> + +#define ASPEED_RESOLUTION_BITS 10 +#define ASPEED_CLOCKS_PER_SAMPLE 12 + +#define ASPEED_REG_ENGINE_CONTROL 0x00 +#define ASPEED_REG_INTERRUPT_CONTROL 0x04 +#define ASPEED_REG_VGA_DETECT_CONTROL 0x08 +#define ASPEED_REG_CLOCK_CONTROL 0x0C +#define ASPEED_REG_MAX 0xC0 + +#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1) +#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1) +#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1) + +#define ASPEED_ENGINE_ENABLE BIT(0) + +#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) + +#define ASPEED_ADC_INIT_POLLING_TIME 500 +#define ASPEED_ADC_INIT_TIMEOUT 500000 + +struct aspeed_adc_model_data { + const char *model_name; + unsigned int min_sampling_rate; // Hz + unsigned int max_sampling_rate; // Hz + unsigned int vref_voltage; // mV + bool wait_init_sequence; +}; + +struct aspeed_adc_data { + struct device *dev; + void __iomem *base; + spinlock_t clk_lock; + struct clk_hw *clk_prescaler; + struct clk_hw *clk_scaler; + struct reset_control *rst; +}; + +#define ASPEED_CHAN(_idx, _data_reg_addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .address = (_data_reg_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec aspeed_adc_iio_channels[] = { + ASPEED_CHAN(0, 0x10), + ASPEED_CHAN(1, 0x12), + ASPEED_CHAN(2, 0x14), + ASPEED_CHAN(3, 0x16), + ASPEED_CHAN(4, 0x18), + ASPEED_CHAN(5, 0x1A), + ASPEED_CHAN(6, 0x1C), + ASPEED_CHAN(7, 0x1E), + ASPEED_CHAN(8, 0x20), + ASPEED_CHAN(9, 0x22), + ASPEED_CHAN(10, 0x24), + ASPEED_CHAN(11, 0x26), + ASPEED_CHAN(12, 0x28), + ASPEED_CHAN(13, 0x2A), + ASPEED_CHAN(14, 0x2C), + ASPEED_CHAN(15, 0x2E), +}; + +static int aspeed_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + const struct aspeed_adc_model_data *model_data = + of_device_get_match_data(data->dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = readw(data->base + chan->address); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = model_data->vref_voltage; + *val2 = ASPEED_RESOLUTION_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(data->clk_scaler->clk) / + ASPEED_CLOCKS_PER_SAMPLE; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int aspeed_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + const struct aspeed_adc_model_data *model_data = + of_device_get_match_data(data->dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < model_data->min_sampling_rate || + val > model_data->max_sampling_rate) + return -EINVAL; + + clk_set_rate(data->clk_scaler->clk, + val * ASPEED_CLOCKS_PER_SAMPLE); + return 0; + + case IIO_CHAN_INFO_SCALE: + case IIO_CHAN_INFO_RAW: + /* + * Technically, these could be written but the only reasons + * for doing so seem better handled in userspace. EPERM is + * returned to signal this is a policy choice rather than a + * hardware limitation. + */ + return -EPERM; + + default: + return -EINVAL; + } +} + +static int aspeed_adc_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (!readval || reg % 4 || reg > ASPEED_REG_MAX) + return -EINVAL; + + *readval = readl(data->base + reg); + + return 0; +} + +static const struct iio_info aspeed_adc_iio_info = { + .read_raw = aspeed_adc_read_raw, + .write_raw = aspeed_adc_write_raw, + .debugfs_reg_access = aspeed_adc_reg_access, +}; + +static int aspeed_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct aspeed_adc_data *data; + const struct aspeed_adc_model_data *model_data; + const char *clk_parent_name; + int ret; + u32 adc_engine_control_reg_val; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->dev = &pdev->dev; + platform_set_drvdata(pdev, indio_dev); + + data->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + /* Register ADC clock prescaler with source specified by device tree. */ + spin_lock_init(&data->clk_lock); + clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); + + data->clk_prescaler = clk_hw_register_divider( + &pdev->dev, "prescaler", clk_parent_name, 0, + data->base + ASPEED_REG_CLOCK_CONTROL, + 17, 15, 0, &data->clk_lock); + if (IS_ERR(data->clk_prescaler)) + return PTR_ERR(data->clk_prescaler); + + /* + * Register ADC clock scaler downstream from the prescaler. Allow rate + * setting to adjust the prescaler as well. + */ + data->clk_scaler = clk_hw_register_divider( + &pdev->dev, "scaler", "prescaler", + CLK_SET_RATE_PARENT, + data->base + ASPEED_REG_CLOCK_CONTROL, + 0, 10, 0, &data->clk_lock); + if (IS_ERR(data->clk_scaler)) { + ret = PTR_ERR(data->clk_scaler); + goto scaler_error; + } + + data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(data->rst)) { + dev_err(&pdev->dev, + "invalid or missing reset controller device tree entry"); + ret = PTR_ERR(data->rst); + goto reset_error; + } + reset_control_deassert(data->rst); + + model_data = of_device_get_match_data(&pdev->dev); + + if (model_data->wait_init_sequence) { + /* Enable engine in normal mode. */ + writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE, + data->base + ASPEED_REG_ENGINE_CONTROL); + + /* Wait for initial sequence complete. */ + ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL, + adc_engine_control_reg_val, + adc_engine_control_reg_val & + ASPEED_ADC_CTRL_INIT_RDY, + ASPEED_ADC_INIT_POLLING_TIME, + ASPEED_ADC_INIT_TIMEOUT); + if (ret) + goto poll_timeout_error; + } + + /* Start all channels in normal mode. */ + ret = clk_prepare_enable(data->clk_scaler->clk); + if (ret) + goto clk_enable_error; + + adc_engine_control_reg_val = GENMASK(31, 16) | + ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE; + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + + model_data = of_device_get_match_data(&pdev->dev); + indio_dev->name = model_data->model_name; + indio_dev->info = &aspeed_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = aspeed_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) + goto iio_register_error; + + return 0; + +iio_register_error: + writel(ASPEED_OPERATION_MODE_POWER_DOWN, + data->base + ASPEED_REG_ENGINE_CONTROL); + clk_disable_unprepare(data->clk_scaler->clk); +clk_enable_error: +poll_timeout_error: + reset_control_assert(data->rst); +reset_error: + clk_hw_unregister_divider(data->clk_scaler); +scaler_error: + clk_hw_unregister_divider(data->clk_prescaler); + return ret; +} + +static int aspeed_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct aspeed_adc_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + writel(ASPEED_OPERATION_MODE_POWER_DOWN, + data->base + ASPEED_REG_ENGINE_CONTROL); + clk_disable_unprepare(data->clk_scaler->clk); + reset_control_assert(data->rst); + clk_hw_unregister_divider(data->clk_scaler); + clk_hw_unregister_divider(data->clk_prescaler); + + return 0; +} + +static const struct aspeed_adc_model_data ast2400_model_data = { + .model_name = "ast2400-adc", + .vref_voltage = 2500, // mV + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, +}; + +static const struct aspeed_adc_model_data ast2500_model_data = { + .model_name = "ast2500-adc", + .vref_voltage = 1800, // mV + .min_sampling_rate = 1, + .max_sampling_rate = 1000000, + .wait_init_sequence = true, +}; + +static const struct of_device_id aspeed_adc_matches[] = { + { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data }, + { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, aspeed_adc_matches); + +static struct platform_driver aspeed_adc_driver = { + .probe = aspeed_adc_probe, + .remove = aspeed_adc_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = aspeed_adc_matches, + } +}; + +module_platform_driver(aspeed_adc_driver); + +MODULE_AUTHOR("Rick Altherr <raltherr@google.com>"); +MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c new file mode 100644 index 000000000..b806c1ab9 --- /dev/null +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -0,0 +1,1995 @@ +// 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> + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/pinctrl/consumer.h> +#include <linux/regulator/consumer.h> + +/* Control Register */ +#define AT91_SAMA5D2_CR 0x00 +/* Software Reset */ +#define AT91_SAMA5D2_CR_SWRST BIT(0) +/* Start Conversion */ +#define AT91_SAMA5D2_CR_START BIT(1) +/* Touchscreen Calibration */ +#define AT91_SAMA5D2_CR_TSCALIB BIT(2) +/* Comparison Restart */ +#define AT91_SAMA5D2_CR_CMPRST BIT(4) + +/* Mode Register */ +#define AT91_SAMA5D2_MR 0x04 +/* Trigger Selection */ +#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1) +/* ADTRG */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0 +/* TIOA0 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1 +/* TIOA1 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2 +/* TIOA2 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3 +/* PWM event line 0 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4 +/* PWM event line 1 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5 +/* TIOA3 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6 +/* RTCOUT0 */ +#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7 +/* Sleep Mode */ +#define AT91_SAMA5D2_MR_SLEEP BIT(5) +/* Fast Wake Up */ +#define AT91_SAMA5D2_MR_FWUP BIT(6) +/* Prescaler Rate Selection */ +#define AT91_SAMA5D2_MR_PRESCAL(v) ((v) << AT91_SAMA5D2_MR_PRESCAL_OFFSET) +#define AT91_SAMA5D2_MR_PRESCAL_OFFSET 8 +#define AT91_SAMA5D2_MR_PRESCAL_MAX 0xff +#define AT91_SAMA5D2_MR_PRESCAL_MASK GENMASK(15, 8) +/* Startup Time */ +#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16) +#define AT91_SAMA5D2_MR_STARTUP_MASK GENMASK(19, 16) +/* Analog Change */ +#define AT91_SAMA5D2_MR_ANACH BIT(23) +/* Tracking Time */ +#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24) +#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xf +/* Transfer Time */ +#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28) +#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3 +/* Use Sequence Enable */ +#define AT91_SAMA5D2_MR_USEQ BIT(31) + +/* Channel Sequence Register 1 */ +#define AT91_SAMA5D2_SEQR1 0x08 +/* Channel Sequence Register 2 */ +#define AT91_SAMA5D2_SEQR2 0x0c +/* Channel Enable Register */ +#define AT91_SAMA5D2_CHER 0x10 +/* Channel Disable Register */ +#define AT91_SAMA5D2_CHDR 0x14 +/* Channel Status Register */ +#define AT91_SAMA5D2_CHSR 0x18 +/* Last Converted Data Register */ +#define AT91_SAMA5D2_LCDR 0x20 +/* Interrupt Enable Register */ +#define AT91_SAMA5D2_IER 0x24 +/* Interrupt Enable Register - TS X measurement ready */ +#define AT91_SAMA5D2_IER_XRDY BIT(20) +/* Interrupt Enable Register - TS Y measurement ready */ +#define AT91_SAMA5D2_IER_YRDY BIT(21) +/* Interrupt Enable Register - TS pressure measurement ready */ +#define AT91_SAMA5D2_IER_PRDY BIT(22) +/* Interrupt Enable Register - Data ready */ +#define AT91_SAMA5D2_IER_DRDY BIT(24) +/* Interrupt Enable Register - general overrun error */ +#define AT91_SAMA5D2_IER_GOVRE BIT(25) +/* Interrupt Enable Register - Pen detect */ +#define AT91_SAMA5D2_IER_PEN BIT(29) +/* Interrupt Enable Register - No pen detect */ +#define AT91_SAMA5D2_IER_NOPEN BIT(30) +/* Interrupt Disable Register */ +#define AT91_SAMA5D2_IDR 0x28 +/* Interrupt Mask Register */ +#define AT91_SAMA5D2_IMR 0x2c +/* Interrupt Status Register */ +#define AT91_SAMA5D2_ISR 0x30 +/* Interrupt Status Register - Pen touching sense status */ +#define AT91_SAMA5D2_ISR_PENS BIT(31) +/* Last Channel Trigger Mode Register */ +#define AT91_SAMA5D2_LCTMR 0x34 +/* Last Channel Compare Window Register */ +#define AT91_SAMA5D2_LCCWR 0x38 +/* Overrun Status Register */ +#define AT91_SAMA5D2_OVER 0x3c +/* Extended Mode Register */ +#define AT91_SAMA5D2_EMR 0x40 +/* Extended Mode Register - Oversampling rate */ +#define AT91_SAMA5D2_EMR_OSR(V) ((V) << 16) +#define AT91_SAMA5D2_EMR_OSR_MASK GENMASK(17, 16) +#define AT91_SAMA5D2_EMR_OSR_1SAMPLES 0 +#define AT91_SAMA5D2_EMR_OSR_4SAMPLES 1 +#define AT91_SAMA5D2_EMR_OSR_16SAMPLES 2 + +/* Extended Mode Register - Averaging on single trigger event */ +#define AT91_SAMA5D2_EMR_ASTE(V) ((V) << 20) +/* Compare Window Register */ +#define AT91_SAMA5D2_CWR 0x44 +/* Channel Gain Register */ +#define AT91_SAMA5D2_CGR 0x48 + +/* Channel Offset Register */ +#define AT91_SAMA5D2_COR 0x4c +#define AT91_SAMA5D2_COR_DIFF_OFFSET 16 + +/* Channel Data Register 0 */ +#define AT91_SAMA5D2_CDR0 0x50 +/* Analog Control Register */ +#define AT91_SAMA5D2_ACR 0x94 +/* Analog Control Register - Pen detect sensitivity mask */ +#define AT91_SAMA5D2_ACR_PENDETSENS_MASK GENMASK(1, 0) + +/* Touchscreen Mode Register */ +#define AT91_SAMA5D2_TSMR 0xb0 +/* Touchscreen Mode Register - No touch mode */ +#define AT91_SAMA5D2_TSMR_TSMODE_NONE 0 +/* Touchscreen Mode Register - 4 wire screen, no pressure measurement */ +#define AT91_SAMA5D2_TSMR_TSMODE_4WIRE_NO_PRESS 1 +/* Touchscreen Mode Register - 4 wire screen, pressure measurement */ +#define AT91_SAMA5D2_TSMR_TSMODE_4WIRE_PRESS 2 +/* Touchscreen Mode Register - 5 wire screen */ +#define AT91_SAMA5D2_TSMR_TSMODE_5WIRE 3 +/* Touchscreen Mode Register - Average samples mask */ +#define AT91_SAMA5D2_TSMR_TSAV_MASK GENMASK(5, 4) +/* Touchscreen Mode Register - Average samples */ +#define AT91_SAMA5D2_TSMR_TSAV(x) ((x) << 4) +/* Touchscreen Mode Register - Touch/trigger frequency ratio mask */ +#define AT91_SAMA5D2_TSMR_TSFREQ_MASK GENMASK(11, 8) +/* Touchscreen Mode Register - Touch/trigger frequency ratio */ +#define AT91_SAMA5D2_TSMR_TSFREQ(x) ((x) << 8) +/* Touchscreen Mode Register - Pen Debounce Time mask */ +#define AT91_SAMA5D2_TSMR_PENDBC_MASK GENMASK(31, 28) +/* Touchscreen Mode Register - Pen Debounce Time */ +#define AT91_SAMA5D2_TSMR_PENDBC(x) ((x) << 28) +/* Touchscreen Mode Register - No DMA for touch measurements */ +#define AT91_SAMA5D2_TSMR_NOTSDMA BIT(22) +/* Touchscreen Mode Register - Disable pen detection */ +#define AT91_SAMA5D2_TSMR_PENDET_DIS (0 << 24) +/* Touchscreen Mode Register - Enable pen detection */ +#define AT91_SAMA5D2_TSMR_PENDET_ENA BIT(24) + +/* Touchscreen X Position Register */ +#define AT91_SAMA5D2_XPOSR 0xb4 +/* Touchscreen Y Position Register */ +#define AT91_SAMA5D2_YPOSR 0xb8 +/* Touchscreen Pressure Register */ +#define AT91_SAMA5D2_PRESSR 0xbc +/* Trigger Register */ +#define AT91_SAMA5D2_TRGR 0xc0 +/* Mask for TRGMOD field of TRGR register */ +#define AT91_SAMA5D2_TRGR_TRGMOD_MASK GENMASK(2, 0) +/* No trigger, only software trigger can start conversions */ +#define AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER 0 +/* Trigger Mode external trigger rising edge */ +#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE 1 +/* Trigger Mode external trigger falling edge */ +#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL 2 +/* Trigger Mode external trigger any edge */ +#define AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY 3 +/* Trigger Mode internal periodic */ +#define AT91_SAMA5D2_TRGR_TRGMOD_PERIODIC 5 +/* Trigger Mode - trigger period mask */ +#define AT91_SAMA5D2_TRGR_TRGPER_MASK GENMASK(31, 16) +/* Trigger Mode - trigger period */ +#define AT91_SAMA5D2_TRGR_TRGPER(x) ((x) << 16) + +/* Correction Select Register */ +#define AT91_SAMA5D2_COSR 0xd0 +/* Correction Value Register */ +#define AT91_SAMA5D2_CVR 0xd4 +/* Channel Error Correction Register */ +#define AT91_SAMA5D2_CECR 0xd8 +/* Write Protection Mode Register */ +#define AT91_SAMA5D2_WPMR 0xe4 +/* Write Protection Status Register */ +#define AT91_SAMA5D2_WPSR 0xe8 +/* Version Register */ +#define AT91_SAMA5D2_VERSION 0xfc + +#define AT91_SAMA5D2_HW_TRIG_CNT 3 +#define AT91_SAMA5D2_SINGLE_CHAN_CNT 12 +#define AT91_SAMA5D2_DIFF_CHAN_CNT 6 + +#define AT91_SAMA5D2_TIMESTAMP_CHAN_IDX (AT91_SAMA5D2_SINGLE_CHAN_CNT + \ + AT91_SAMA5D2_DIFF_CHAN_CNT + 1) + +#define AT91_SAMA5D2_TOUCH_X_CHAN_IDX (AT91_SAMA5D2_SINGLE_CHAN_CNT + \ + AT91_SAMA5D2_DIFF_CHAN_CNT * 2) +#define AT91_SAMA5D2_TOUCH_Y_CHAN_IDX (AT91_SAMA5D2_TOUCH_X_CHAN_IDX + 1) +#define AT91_SAMA5D2_TOUCH_P_CHAN_IDX (AT91_SAMA5D2_TOUCH_Y_CHAN_IDX + 1) +#define AT91_SAMA5D2_MAX_CHAN_IDX AT91_SAMA5D2_TOUCH_P_CHAN_IDX + +#define AT91_SAMA5D2_TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */ +#define AT91_SAMA5D2_TOUCH_PEN_DETECT_DEBOUNCE_US 200 + +#define AT91_SAMA5D2_XYZ_MASK GENMASK(11, 0) + +#define AT91_SAMA5D2_MAX_POS_BITS 12 + +/* + * Maximum number of bytes to hold conversion from all channels + * without the timestamp. + */ +#define AT91_BUFFER_MAX_CONVERSION_BYTES ((AT91_SAMA5D2_SINGLE_CHAN_CNT + \ + AT91_SAMA5D2_DIFF_CHAN_CNT) * 2) + +/* This total must also include the timestamp */ +#define AT91_BUFFER_MAX_BYTES (AT91_BUFFER_MAX_CONVERSION_BYTES + 8) + +#define AT91_BUFFER_MAX_HWORDS (AT91_BUFFER_MAX_BYTES / 2) + +#define AT91_HWFIFO_MAX_SIZE_STR "128" +#define AT91_HWFIFO_MAX_SIZE 128 + +/* Possible values for oversampling ratio */ +#define AT91_OSR_1SAMPLES 1 +#define AT91_OSR_4SAMPLES 4 +#define AT91_OSR_16SAMPLES 16 + +#define AT91_SAMA5D2_CHAN_SINGLE(num, addr) \ + { \ + .type = IIO_VOLTAGE, \ + .channel = num, \ + .address = addr, \ + .scan_index = num, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 14, \ + .storagebits = 16, \ + }, \ + .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)|\ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = "CH"#num, \ + .indexed = 1, \ + } + +#define AT91_SAMA5D2_CHAN_DIFF(num, num2, addr) \ + { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .channel = num, \ + .channel2 = num2, \ + .address = addr, \ + .scan_index = num + AT91_SAMA5D2_SINGLE_CHAN_CNT, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 14, \ + .storagebits = 16, \ + }, \ + .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)|\ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = "CH"#num"-CH"#num2, \ + .indexed = 1, \ + } + +#define AT91_SAMA5D2_CHAN_TOUCH(num, name, mod) \ + { \ + .type = IIO_POSITIONRELATIVE, \ + .modified = 1, \ + .channel = num, \ + .channel2 = mod, \ + .scan_index = num, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = name, \ + } +#define AT91_SAMA5D2_CHAN_PRESSURE(num, name) \ + { \ + .type = IIO_PRESSURE, \ + .channel = num, \ + .scan_index = num, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .datasheet_name = name, \ + } + +#define at91_adc_readl(st, reg) readl_relaxed(st->base + reg) +#define at91_adc_writel(st, reg, val) writel_relaxed(val, st->base + reg) + +struct at91_adc_soc_info { + unsigned startup_time; + unsigned min_sample_rate; + unsigned max_sample_rate; +}; + +struct at91_adc_trigger { + char *name; + unsigned int trgmod_value; + unsigned int edge_type; + bool hw_trig; +}; + +/** + * struct at91_adc_dma - at91-sama5d2 dma information struct + * @dma_chan: the dma channel acquired + * @rx_buf: dma coherent allocated area + * @rx_dma_buf: dma handler for the buffer + * @phys_addr: physical address of the ADC base register + * @buf_idx: index inside the dma buffer where reading was last done + * @rx_buf_sz: size of buffer used by DMA operation + * @watermark: number of conversions to copy before DMA triggers irq + * @dma_ts: hold the start timestamp of dma operation + */ +struct at91_adc_dma { + struct dma_chan *dma_chan; + u8 *rx_buf; + dma_addr_t rx_dma_buf; + phys_addr_t phys_addr; + int buf_idx; + int rx_buf_sz; + int watermark; + s64 dma_ts; +}; + +/** + * struct at91_adc_touch - at91-sama5d2 touchscreen information struct + * @sample_period_val: the value for periodic trigger interval + * @touching: is the pen touching the screen or not + * @x_pos: temporary placeholder for pressure computation + * @channels_bitmask: bitmask with the touchscreen channels enabled + * @workq: workqueue for buffer data pushing + */ +struct at91_adc_touch { + u16 sample_period_val; + bool touching; + u16 x_pos; + unsigned long channels_bitmask; + struct work_struct workq; +}; + +struct at91_adc_state { + void __iomem *base; + int irq; + struct clk *per_clk; + struct regulator *reg; + struct regulator *vref; + int vref_uv; + unsigned int current_sample_rate; + struct iio_trigger *trig; + const struct at91_adc_trigger *selected_trig; + const struct iio_chan_spec *chan; + bool conversion_done; + u32 conversion_value; + unsigned int oversampling_ratio; + struct at91_adc_soc_info soc_info; + wait_queue_head_t wq_data_available; + struct at91_adc_dma dma_st; + struct at91_adc_touch touch_st; + struct iio_dev *indio_dev; + /* Ensure naturally aligned timestamp */ + u16 buffer[AT91_BUFFER_MAX_HWORDS] __aligned(8); + /* + * lock to prevent concurrent 'single conversion' requests through + * sysfs. + */ + struct mutex lock; +}; + +static const struct at91_adc_trigger at91_adc_trigger_list[] = { + { + .name = "external_rising", + .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_RISE, + .edge_type = IRQ_TYPE_EDGE_RISING, + .hw_trig = true, + }, + { + .name = "external_falling", + .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_FALL, + .edge_type = IRQ_TYPE_EDGE_FALLING, + .hw_trig = true, + }, + { + .name = "external_any", + .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_EXT_TRIG_ANY, + .edge_type = IRQ_TYPE_EDGE_BOTH, + .hw_trig = true, + }, + { + .name = "software", + .trgmod_value = AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER, + .edge_type = IRQ_TYPE_NONE, + .hw_trig = false, + }, +}; + +static const struct iio_chan_spec at91_adc_channels[] = { + AT91_SAMA5D2_CHAN_SINGLE(0, 0x50), + AT91_SAMA5D2_CHAN_SINGLE(1, 0x54), + AT91_SAMA5D2_CHAN_SINGLE(2, 0x58), + AT91_SAMA5D2_CHAN_SINGLE(3, 0x5c), + AT91_SAMA5D2_CHAN_SINGLE(4, 0x60), + AT91_SAMA5D2_CHAN_SINGLE(5, 0x64), + AT91_SAMA5D2_CHAN_SINGLE(6, 0x68), + AT91_SAMA5D2_CHAN_SINGLE(7, 0x6c), + AT91_SAMA5D2_CHAN_SINGLE(8, 0x70), + AT91_SAMA5D2_CHAN_SINGLE(9, 0x74), + AT91_SAMA5D2_CHAN_SINGLE(10, 0x78), + AT91_SAMA5D2_CHAN_SINGLE(11, 0x7c), + AT91_SAMA5D2_CHAN_DIFF(0, 1, 0x50), + AT91_SAMA5D2_CHAN_DIFF(2, 3, 0x58), + AT91_SAMA5D2_CHAN_DIFF(4, 5, 0x60), + AT91_SAMA5D2_CHAN_DIFF(6, 7, 0x68), + AT91_SAMA5D2_CHAN_DIFF(8, 9, 0x70), + AT91_SAMA5D2_CHAN_DIFF(10, 11, 0x78), + IIO_CHAN_SOFT_TIMESTAMP(AT91_SAMA5D2_TIMESTAMP_CHAN_IDX), + AT91_SAMA5D2_CHAN_TOUCH(AT91_SAMA5D2_TOUCH_X_CHAN_IDX, "x", IIO_MOD_X), + AT91_SAMA5D2_CHAN_TOUCH(AT91_SAMA5D2_TOUCH_Y_CHAN_IDX, "y", IIO_MOD_Y), + AT91_SAMA5D2_CHAN_PRESSURE(AT91_SAMA5D2_TOUCH_P_CHAN_IDX, "pressure"), +}; + +static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan) +{ + int i; + + for (i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].scan_index == chan) + return i; + } + return -EINVAL; +} + +static inline struct iio_chan_spec const * +at91_adc_chan_get(struct iio_dev *indio_dev, int chan) +{ + int index = at91_adc_chan_xlate(indio_dev, chan); + + if (index < 0) + return NULL; + return indio_dev->channels + index; +} + +static inline int at91_adc_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + return at91_adc_chan_xlate(indio_dev, iiospec->args[0]); +} + +static unsigned int at91_adc_active_scan_mask_to_reg(struct iio_dev *indio_dev) +{ + u32 mask = 0; + u8 bit; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + mask |= BIT(chan->channel); + } + + return mask & GENMASK(11, 0); +} + +static void at91_adc_config_emr(struct at91_adc_state *st) +{ + /* configure the extended mode register */ + unsigned int emr = at91_adc_readl(st, AT91_SAMA5D2_EMR); + + /* select oversampling per single trigger event */ + emr |= AT91_SAMA5D2_EMR_ASTE(1); + + /* delete leftover content if it's the case */ + emr &= ~AT91_SAMA5D2_EMR_OSR_MASK; + + /* select oversampling ratio from configuration */ + switch (st->oversampling_ratio) { + case AT91_OSR_1SAMPLES: + emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_1SAMPLES) & + AT91_SAMA5D2_EMR_OSR_MASK; + break; + case AT91_OSR_4SAMPLES: + emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_4SAMPLES) & + AT91_SAMA5D2_EMR_OSR_MASK; + break; + case AT91_OSR_16SAMPLES: + emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES) & + AT91_SAMA5D2_EMR_OSR_MASK; + break; + } + + at91_adc_writel(st, AT91_SAMA5D2_EMR, emr); +} + +static int at91_adc_adjust_val_osr(struct at91_adc_state *st, int *val) +{ + if (st->oversampling_ratio == AT91_OSR_1SAMPLES) { + /* + * in this case we only have 12 bits of real data, but channel + * is registered as 14 bits, so shift left two bits + */ + *val <<= 2; + } else if (st->oversampling_ratio == AT91_OSR_4SAMPLES) { + /* + * in this case we have 13 bits of real data, but channel + * is registered as 14 bits, so left shift one bit + */ + *val <<= 1; + } + + return IIO_VAL_INT; +} + +static void at91_adc_adjust_val_osr_array(struct at91_adc_state *st, void *buf, + int len) +{ + int i = 0, val; + u16 *buf_u16 = (u16 *) buf; + + /* + * We are converting each two bytes (each sample). + * First convert the byte based array to u16, and convert each sample + * separately. + * Each value is two bytes in an array of chars, so to not shift + * more than we need, save the value separately. + * len is in bytes, so divide by two to get number of samples. + */ + while (i < len / 2) { + val = buf_u16[i]; + at91_adc_adjust_val_osr(st, &val); + buf_u16[i] = val; + i++; + } +} + +static int at91_adc_configure_touch(struct at91_adc_state *st, bool state) +{ + u32 clk_khz = st->current_sample_rate / 1000; + int i = 0; + u16 pendbc; + u32 tsmr, acr; + + if (!state) { + /* disabling touch IRQs and setting mode to no touch enabled */ + at91_adc_writel(st, AT91_SAMA5D2_IDR, + AT91_SAMA5D2_IER_PEN | AT91_SAMA5D2_IER_NOPEN); + at91_adc_writel(st, AT91_SAMA5D2_TSMR, 0); + return 0; + } + /* + * debounce time is in microseconds, we need it in milliseconds to + * multiply with kilohertz, so, divide by 1000, but after the multiply. + * round up to make sure pendbc is at least 1 + */ + pendbc = round_up(AT91_SAMA5D2_TOUCH_PEN_DETECT_DEBOUNCE_US * + clk_khz / 1000, 1); + + /* get the required exponent */ + while (pendbc >> i++) + ; + + pendbc = i; + + tsmr = AT91_SAMA5D2_TSMR_TSMODE_4WIRE_PRESS; + + tsmr |= AT91_SAMA5D2_TSMR_TSAV(2) & AT91_SAMA5D2_TSMR_TSAV_MASK; + tsmr |= AT91_SAMA5D2_TSMR_PENDBC(pendbc) & + AT91_SAMA5D2_TSMR_PENDBC_MASK; + tsmr |= AT91_SAMA5D2_TSMR_NOTSDMA; + tsmr |= AT91_SAMA5D2_TSMR_PENDET_ENA; + tsmr |= AT91_SAMA5D2_TSMR_TSFREQ(2) & AT91_SAMA5D2_TSMR_TSFREQ_MASK; + + at91_adc_writel(st, AT91_SAMA5D2_TSMR, tsmr); + + acr = at91_adc_readl(st, AT91_SAMA5D2_ACR); + acr &= ~AT91_SAMA5D2_ACR_PENDETSENS_MASK; + acr |= 0x02 & AT91_SAMA5D2_ACR_PENDETSENS_MASK; + at91_adc_writel(st, AT91_SAMA5D2_ACR, acr); + + /* Sample Period Time = (TRGPER + 1) / ADCClock */ + st->touch_st.sample_period_val = + round_up((AT91_SAMA5D2_TOUCH_SAMPLE_PERIOD_US * + clk_khz / 1000) - 1, 1); + /* enable pen detect IRQ */ + at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_PEN); + + return 0; +} + +static u16 at91_adc_touch_pos(struct at91_adc_state *st, int reg) +{ + u32 val; + u32 scale, result, pos; + + /* + * to obtain the actual position we must divide by scale + * and multiply with max, where + * max = 2^AT91_SAMA5D2_MAX_POS_BITS - 1 + */ + /* first half of register is the x or y, second half is the scale */ + val = at91_adc_readl(st, reg); + if (!val) + dev_dbg(&st->indio_dev->dev, "pos is 0\n"); + + pos = val & AT91_SAMA5D2_XYZ_MASK; + result = (pos << AT91_SAMA5D2_MAX_POS_BITS) - pos; + scale = (val >> 16) & AT91_SAMA5D2_XYZ_MASK; + if (scale == 0) { + dev_err(&st->indio_dev->dev, "scale is 0\n"); + return 0; + } + result /= scale; + + return result; +} + +static u16 at91_adc_touch_x_pos(struct at91_adc_state *st) +{ + st->touch_st.x_pos = at91_adc_touch_pos(st, AT91_SAMA5D2_XPOSR); + return st->touch_st.x_pos; +} + +static u16 at91_adc_touch_y_pos(struct at91_adc_state *st) +{ + return at91_adc_touch_pos(st, AT91_SAMA5D2_YPOSR); +} + +static u16 at91_adc_touch_pressure(struct at91_adc_state *st) +{ + u32 val; + u32 z1, z2; + u32 pres; + u32 rxp = 1; + u32 factor = 1000; + + /* calculate the pressure */ + val = at91_adc_readl(st, AT91_SAMA5D2_PRESSR); + z1 = val & AT91_SAMA5D2_XYZ_MASK; + z2 = (val >> 16) & AT91_SAMA5D2_XYZ_MASK; + + if (z1 != 0) + pres = rxp * (st->touch_st.x_pos * factor / 1024) * + (z2 * factor / z1 - factor) / + factor; + else + pres = 0xFFFF; /* no pen contact */ + + /* + * The pressure from device grows down, minimum is 0xFFFF, maximum 0x0. + * We compute it this way, but let's return it in the expected way, + * growing from 0 to 0xFFFF. + */ + return 0xFFFF - pres; +} + +static int at91_adc_read_position(struct at91_adc_state *st, int chan, u16 *val) +{ + *val = 0; + if (!st->touch_st.touching) + return -ENODATA; + if (chan == AT91_SAMA5D2_TOUCH_X_CHAN_IDX) + *val = at91_adc_touch_x_pos(st); + else if (chan == AT91_SAMA5D2_TOUCH_Y_CHAN_IDX) + *val = at91_adc_touch_y_pos(st); + else + return -ENODATA; + + return IIO_VAL_INT; +} + +static int at91_adc_read_pressure(struct at91_adc_state *st, int chan, u16 *val) +{ + *val = 0; + if (!st->touch_st.touching) + return -ENODATA; + if (chan == AT91_SAMA5D2_TOUCH_P_CHAN_IDX) + *val = at91_adc_touch_pressure(st); + else + return -ENODATA; + + return IIO_VAL_INT; +} + +static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio = iio_trigger_get_drvdata(trig); + struct at91_adc_state *st = iio_priv(indio); + u32 status = at91_adc_readl(st, AT91_SAMA5D2_TRGR); + + /* clear TRGMOD */ + status &= ~AT91_SAMA5D2_TRGR_TRGMOD_MASK; + + if (state) + status |= st->selected_trig->trgmod_value; + + /* set/unset hw trigger */ + at91_adc_writel(st, AT91_SAMA5D2_TRGR, status); + + return 0; +} + +static int at91_adc_reenable_trigger(struct iio_trigger *trig) +{ + struct iio_dev *indio = iio_trigger_get_drvdata(trig); + struct at91_adc_state *st = iio_priv(indio); + + /* if we are using DMA, we must not reenable irq after each trigger */ + if (st->dma_st.dma_chan) + return 0; + + enable_irq(st->irq); + + /* Needed to ACK the DRDY interruption */ + at91_adc_readl(st, AT91_SAMA5D2_LCDR); + + return 0; +} + +static const struct iio_trigger_ops at91_adc_trigger_ops = { + .set_trigger_state = &at91_adc_configure_trigger, + .try_reenable = &at91_adc_reenable_trigger, + .validate_device = iio_trigger_validate_own_device, +}; + +static int at91_adc_dma_size_done(struct at91_adc_state *st) +{ + struct dma_tx_state state; + enum dma_status status; + int i, size; + + status = dmaengine_tx_status(st->dma_st.dma_chan, + st->dma_st.dma_chan->cookie, + &state); + if (status != DMA_IN_PROGRESS) + return 0; + + /* Transferred length is size in bytes from end of buffer */ + i = st->dma_st.rx_buf_sz - state.residue; + + /* Return available bytes */ + if (i >= st->dma_st.buf_idx) + size = i - st->dma_st.buf_idx; + else + size = st->dma_st.rx_buf_sz + i - st->dma_st.buf_idx; + return size; +} + +static void at91_dma_buffer_done(void *data) +{ + struct iio_dev *indio_dev = data; + + iio_trigger_poll_chained(indio_dev->trig); +} + +static int at91_adc_dma_start(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + int ret; + u8 bit; + + if (!st->dma_st.dma_chan) + return 0; + + /* we start a new DMA, so set buffer index to start */ + st->dma_st.buf_idx = 0; + + /* + * compute buffer size w.r.t. watermark and enabled channels. + * scan_bytes is aligned so we need an exact size for DMA + */ + st->dma_st.rx_buf_sz = 0; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + + if (!chan) + continue; + + st->dma_st.rx_buf_sz += chan->scan_type.storagebits / 8; + } + st->dma_st.rx_buf_sz *= st->dma_st.watermark; + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(st->dma_st.dma_chan, + st->dma_st.rx_dma_buf, + st->dma_st.rx_buf_sz, + st->dma_st.rx_buf_sz / 2, + DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT); + + if (!desc) { + dev_err(&indio_dev->dev, "cannot prepare DMA cyclic\n"); + return -EBUSY; + } + + desc->callback = at91_dma_buffer_done; + desc->callback_param = indio_dev; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) { + dev_err(&indio_dev->dev, "cannot submit DMA cyclic\n"); + dmaengine_terminate_async(st->dma_st.dma_chan); + return ret; + } + + /* enable general overrun error signaling */ + at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_GOVRE); + /* Issue pending DMA requests */ + dma_async_issue_pending(st->dma_st.dma_chan); + + /* consider current time as DMA start time for timestamps */ + st->dma_st.dma_ts = iio_get_time_ns(indio_dev); + + dev_dbg(&indio_dev->dev, "DMA cyclic started\n"); + + return 0; +} + +static bool at91_adc_buffer_check_use_irq(struct iio_dev *indio, + struct at91_adc_state *st) +{ + /* if using DMA, we do not use our own IRQ (we use DMA-controller) */ + if (st->dma_st.dma_chan) + return false; + /* if the trigger is not ours, then it has its own IRQ */ + if (iio_trigger_validate_own_device(indio->trig, indio)) + return false; + return true; +} + +static bool at91_adc_current_chan_is_touch(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + return !!bitmap_subset(indio_dev->active_scan_mask, + &st->touch_st.channels_bitmask, + AT91_SAMA5D2_MAX_CHAN_IDX + 1); +} + +static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) +{ + int ret; + u8 bit; + struct at91_adc_state *st = iio_priv(indio_dev); + + /* check if we are enabling triggered buffer or the touchscreen */ + if (at91_adc_current_chan_is_touch(indio_dev)) + return at91_adc_configure_touch(st, true); + + /* if we are not in triggered mode, we cannot enable the buffer. */ + if (!(indio_dev->currentmode & INDIO_ALL_TRIGGERED_MODES)) + return -EINVAL; + + /* we continue with the triggered buffer */ + ret = at91_adc_dma_start(indio_dev); + if (ret) { + dev_err(&indio_dev->dev, "buffer prepare failed\n"); + return ret; + } + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + u32 cor; + + if (!chan) + continue; + /* these channel types cannot be handled by this trigger */ + if (chan->type == IIO_POSITIONRELATIVE || + chan->type == IIO_PRESSURE) + continue; + + cor = at91_adc_readl(st, AT91_SAMA5D2_COR); + + if (chan->differential) + cor |= (BIT(chan->channel) | BIT(chan->channel2)) << + AT91_SAMA5D2_COR_DIFF_OFFSET; + else + cor &= ~(BIT(chan->channel) << + AT91_SAMA5D2_COR_DIFF_OFFSET); + + at91_adc_writel(st, AT91_SAMA5D2_COR, cor); + + at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel)); + } + + if (at91_adc_buffer_check_use_irq(indio_dev, st)) + at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_DRDY); + + return 0; +} + +static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + u8 bit; + + /* check if we are disabling triggered buffer or the touchscreen */ + if (at91_adc_current_chan_is_touch(indio_dev)) + return at91_adc_configure_touch(st, false); + + /* if we are not in triggered mode, nothing to do here */ + if (!(indio_dev->currentmode & INDIO_ALL_TRIGGERED_MODES)) + return -EINVAL; + + /* + * For each enable channel we must disable it in hardware. + * In the case of DMA, we must read the last converted value + * to clear EOC status and not get a possible interrupt later. + * This value is being read by DMA from LCDR anyway, so it's not lost. + */ + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + + if (!chan) + continue; + /* these channel types are virtual, no need to do anything */ + if (chan->type == IIO_POSITIONRELATIVE || + chan->type == IIO_PRESSURE) + continue; + + at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel)); + + if (st->dma_st.dma_chan) + at91_adc_readl(st, chan->address); + } + + if (at91_adc_buffer_check_use_irq(indio_dev, st)) + at91_adc_writel(st, AT91_SAMA5D2_IDR, AT91_SAMA5D2_IER_DRDY); + + /* read overflow register to clear possible overflow status */ + at91_adc_readl(st, AT91_SAMA5D2_OVER); + + /* if we are using DMA we must clear registers and end DMA */ + if (st->dma_st.dma_chan) + dmaengine_terminate_sync(st->dma_st.dma_chan); + + return 0; +} + +static const struct iio_buffer_setup_ops at91_buffer_setup_ops = { + .postdisable = &at91_adc_buffer_postdisable, +}; + +static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio, + char *trigger_name) +{ + struct iio_trigger *trig; + int ret; + + trig = devm_iio_trigger_alloc(&indio->dev, "%s-dev%d-%s", indio->name, + indio->id, trigger_name); + if (!trig) + return ERR_PTR(-ENOMEM); + + trig->dev.parent = indio->dev.parent; + iio_trigger_set_drvdata(trig, indio); + trig->ops = &at91_adc_trigger_ops; + + ret = devm_iio_trigger_register(&indio->dev, trig); + if (ret) + return ERR_PTR(ret); + + return trig; +} + +static int at91_adc_trigger_init(struct iio_dev *indio) +{ + struct at91_adc_state *st = iio_priv(indio); + + st->trig = at91_adc_allocate_trigger(indio, st->selected_trig->name); + if (IS_ERR(st->trig)) { + dev_err(&indio->dev, + "could not allocate trigger\n"); + return PTR_ERR(st->trig); + } + + return 0; +} + +static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev, + struct iio_poll_func *pf) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int i = 0; + int val; + u8 bit; + u32 mask = at91_adc_active_scan_mask_to_reg(indio_dev); + unsigned int timeout = 50; + + /* + * Check if the conversion is ready. If not, wait a little bit, and + * in case of timeout exit with an error. + */ + while ((at91_adc_readl(st, AT91_SAMA5D2_ISR) & mask) != mask && + timeout) { + usleep_range(50, 100); + timeout--; + } + + /* Cannot read data, not ready. Continue without reporting data */ + if (!timeout) + return; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->num_channels) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + + if (!chan) + continue; + /* + * Our external trigger only supports the voltage channels. + * In case someone requested a different type of channel + * just put zeroes to buffer. + * This should not happen because we check the scan mode + * and scan mask when we enable the buffer, and we don't allow + * the buffer to start with a mixed mask (voltage and something + * else). + * Thus, emit a warning. + */ + if (chan->type == IIO_VOLTAGE) { + val = at91_adc_readl(st, chan->address); + at91_adc_adjust_val_osr(st, &val); + st->buffer[i] = val; + } else { + st->buffer[i] = 0; + WARN(true, "This trigger cannot handle this type of channel"); + } + i++; + } + iio_push_to_buffers_with_timestamp(indio_dev, st->buffer, + pf->timestamp); +} + +static void at91_adc_trigger_handler_dma(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int transferred_len = at91_adc_dma_size_done(st); + s64 ns = iio_get_time_ns(indio_dev); + s64 interval; + int sample_index = 0, sample_count, sample_size; + + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR); + /* if we reached this point, we cannot sample faster */ + if (status & AT91_SAMA5D2_IER_GOVRE) + pr_info_ratelimited("%s: conversion overrun detected\n", + indio_dev->name); + + sample_size = div_s64(st->dma_st.rx_buf_sz, st->dma_st.watermark); + + sample_count = div_s64(transferred_len, sample_size); + + /* + * interval between samples is total time since last transfer handling + * divided by the number of samples (total size divided by sample size) + */ + interval = div_s64((ns - st->dma_st.dma_ts), sample_count); + + while (transferred_len >= sample_size) { + /* + * for all the values in the current sample, + * adjust the values inside the buffer for oversampling + */ + at91_adc_adjust_val_osr_array(st, + &st->dma_st.rx_buf[st->dma_st.buf_idx], + sample_size); + + iio_push_to_buffers_with_timestamp(indio_dev, + (st->dma_st.rx_buf + st->dma_st.buf_idx), + (st->dma_st.dma_ts + interval * sample_index)); + /* adjust remaining length */ + transferred_len -= sample_size; + /* adjust buffer index */ + st->dma_st.buf_idx += sample_size; + /* in case of reaching end of buffer, reset index */ + if (st->dma_st.buf_idx >= st->dma_st.rx_buf_sz) + st->dma_st.buf_idx = 0; + sample_index++; + } + /* adjust saved time for next transfer handling */ + st->dma_st.dma_ts = iio_get_time_ns(indio_dev); +} + +static irqreturn_t at91_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct at91_adc_state *st = iio_priv(indio_dev); + + /* + * If it's not our trigger, start a conversion now, as we are + * actually polling the trigger now. + */ + if (iio_trigger_validate_own_device(indio_dev->trig, indio_dev)) + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START); + + if (st->dma_st.dma_chan) + at91_adc_trigger_handler_dma(indio_dev); + else + at91_adc_trigger_handler_nodma(indio_dev, pf); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int at91_adc_buffer_init(struct iio_dev *indio) +{ + return devm_iio_triggered_buffer_setup(&indio->dev, indio, + &iio_pollfunc_store_time, + &at91_adc_trigger_handler, &at91_buffer_setup_ops); +} + +static unsigned at91_adc_startup_time(unsigned startup_time_min, + unsigned adc_clk_khz) +{ + static const unsigned int startup_lookup[] = { + 0, 8, 16, 24, + 64, 80, 96, 112, + 512, 576, 640, 704, + 768, 832, 896, 960 + }; + unsigned ticks_min, i; + + /* + * Since the adc frequency is checked before, there is no reason + * to not meet the startup time constraint. + */ + + ticks_min = startup_time_min * adc_clk_khz / 1000; + for (i = 0; i < ARRAY_SIZE(startup_lookup); i++) + if (startup_lookup[i] > ticks_min) + break; + + return i; +} + +static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + unsigned f_per, prescal, startup, mr; + + f_per = clk_get_rate(st->per_clk); + prescal = (f_per / (2 * freq)) - 1; + + startup = at91_adc_startup_time(st->soc_info.startup_time, + freq / 1000); + + mr = at91_adc_readl(st, AT91_SAMA5D2_MR); + mr &= ~(AT91_SAMA5D2_MR_STARTUP_MASK | AT91_SAMA5D2_MR_PRESCAL_MASK); + mr |= AT91_SAMA5D2_MR_STARTUP(startup); + mr |= AT91_SAMA5D2_MR_PRESCAL(prescal); + at91_adc_writel(st, AT91_SAMA5D2_MR, mr); + + dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n", + freq, startup, prescal); + st->current_sample_rate = freq; +} + +static inline unsigned at91_adc_get_sample_freq(struct at91_adc_state *st) +{ + return st->current_sample_rate; +} + +static void at91_adc_touch_data_handler(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + u8 bit; + u16 val; + int i = 0; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + AT91_SAMA5D2_MAX_CHAN_IDX + 1) { + struct iio_chan_spec const *chan = + at91_adc_chan_get(indio_dev, bit); + + if (chan->type == IIO_POSITIONRELATIVE) + at91_adc_read_position(st, chan->channel, &val); + else if (chan->type == IIO_PRESSURE) + at91_adc_read_pressure(st, chan->channel, &val); + else + continue; + st->buffer[i] = val; + i++; + } + /* + * Schedule work to push to buffers. + * This is intended to push to the callback buffer that another driver + * registered. We are still in a handler from our IRQ. If we push + * directly, it means the other driver has it's callback called + * from our IRQ context. Which is something we better avoid. + * Let's schedule it after our IRQ is completed. + */ + schedule_work(&st->touch_st.workq); +} + +static void at91_adc_pen_detect_interrupt(struct at91_adc_state *st) +{ + at91_adc_writel(st, AT91_SAMA5D2_IDR, AT91_SAMA5D2_IER_PEN); + at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_NOPEN | + AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | + AT91_SAMA5D2_IER_PRDY); + at91_adc_writel(st, AT91_SAMA5D2_TRGR, + AT91_SAMA5D2_TRGR_TRGMOD_PERIODIC | + AT91_SAMA5D2_TRGR_TRGPER(st->touch_st.sample_period_val)); + st->touch_st.touching = true; +} + +static void at91_adc_no_pen_detect_interrupt(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + at91_adc_writel(st, AT91_SAMA5D2_TRGR, + AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER); + at91_adc_writel(st, AT91_SAMA5D2_IDR, AT91_SAMA5D2_IER_NOPEN | + AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | + AT91_SAMA5D2_IER_PRDY); + st->touch_st.touching = false; + + at91_adc_touch_data_handler(indio_dev); + + at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_PEN); +} + +static void at91_adc_workq_handler(struct work_struct *workq) +{ + struct at91_adc_touch *touch_st = container_of(workq, + struct at91_adc_touch, workq); + struct at91_adc_state *st = container_of(touch_st, + struct at91_adc_state, touch_st); + struct iio_dev *indio_dev = st->indio_dev; + + iio_push_to_buffers(indio_dev, st->buffer); +} + +static irqreturn_t at91_adc_interrupt(int irq, void *private) +{ + struct iio_dev *indio = private; + struct at91_adc_state *st = iio_priv(indio); + u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR); + u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR); + u32 rdy_mask = AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | + AT91_SAMA5D2_IER_PRDY; + + if (!(status & imr)) + return IRQ_NONE; + if (status & AT91_SAMA5D2_IER_PEN) { + /* pen detected IRQ */ + at91_adc_pen_detect_interrupt(st); + } else if ((status & AT91_SAMA5D2_IER_NOPEN)) { + /* nopen detected IRQ */ + at91_adc_no_pen_detect_interrupt(indio); + } else if ((status & AT91_SAMA5D2_ISR_PENS) && + ((status & rdy_mask) == rdy_mask)) { + /* periodic trigger IRQ - during pen sense */ + at91_adc_touch_data_handler(indio); + } else if (status & AT91_SAMA5D2_ISR_PENS) { + /* + * touching, but the measurements are not ready yet. + * read and ignore. + */ + status = at91_adc_readl(st, AT91_SAMA5D2_XPOSR); + status = at91_adc_readl(st, AT91_SAMA5D2_YPOSR); + status = at91_adc_readl(st, AT91_SAMA5D2_PRESSR); + } else if (iio_buffer_enabled(indio) && + (status & AT91_SAMA5D2_IER_DRDY)) { + /* triggered buffer without DMA */ + disable_irq_nosync(irq); + iio_trigger_poll(indio->trig); + } else if (iio_buffer_enabled(indio) && st->dma_st.dma_chan) { + /* triggered buffer with DMA - should not happen */ + disable_irq_nosync(irq); + WARN(true, "Unexpected irq occurred\n"); + } else if (!iio_buffer_enabled(indio)) { + /* software requested conversion */ + st->conversion_value = at91_adc_readl(st, st->chan->address); + st->conversion_done = true; + wake_up_interruptible(&st->wq_data_available); + } + return IRQ_HANDLED; +} + +static int at91_adc_read_info_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + u32 cor = 0; + u16 tmp_val; + int ret; + + /* + * Keep in mind that we cannot use software trigger or touchscreen + * if external trigger is enabled + */ + if (chan->type == IIO_POSITIONRELATIVE) { + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&st->lock); + + ret = at91_adc_read_position(st, chan->channel, + &tmp_val); + *val = tmp_val; + if (ret > 0) + ret = at91_adc_adjust_val_osr(st, val); + mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); + + return ret; + } + if (chan->type == IIO_PRESSURE) { + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&st->lock); + + ret = at91_adc_read_pressure(st, chan->channel, + &tmp_val); + *val = tmp_val; + if (ret > 0) + ret = at91_adc_adjust_val_osr(st, val); + mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); + + return ret; + } + + /* in this case we have a voltage channel */ + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&st->lock); + + st->chan = chan; + + if (chan->differential) + cor = (BIT(chan->channel) | BIT(chan->channel2)) << + AT91_SAMA5D2_COR_DIFF_OFFSET; + + at91_adc_writel(st, AT91_SAMA5D2_COR, cor); + at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel)); + at91_adc_writel(st, AT91_SAMA5D2_IER, BIT(chan->channel)); + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START); + + ret = wait_event_interruptible_timeout(st->wq_data_available, + st->conversion_done, + msecs_to_jiffies(1000)); + if (ret == 0) + ret = -ETIMEDOUT; + + if (ret > 0) { + *val = st->conversion_value; + ret = at91_adc_adjust_val_osr(st, val); + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, + chan->scan_type.realbits - 1); + st->conversion_done = false; + } + + at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel)); + at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel)); + + /* Needed to ACK the DRDY interruption */ + at91_adc_readl(st, AT91_SAMA5D2_LCDR); + + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + return ret; +} + +static int at91_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return at91_adc_read_info_raw(indio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + *val = st->vref_uv / 1000; + if (chan->differential) + *val *= 2; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = at91_adc_get_sample_freq(st); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = st->oversampling_ratio; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int at91_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if ((val != AT91_OSR_1SAMPLES) && (val != AT91_OSR_4SAMPLES) && + (val != AT91_OSR_16SAMPLES)) + return -EINVAL; + /* if no change, optimize out */ + if (val == st->oversampling_ratio) + return 0; + mutex_lock(&st->lock); + st->oversampling_ratio = val; + /* update ratio */ + at91_adc_config_emr(st); + mutex_unlock(&st->lock); + return 0; + case IIO_CHAN_INFO_SAMP_FREQ: + if (val < st->soc_info.min_sample_rate || + val > st->soc_info.max_sample_rate) + return -EINVAL; + + mutex_lock(&st->lock); + at91_adc_setup_samp_freq(indio_dev, val); + mutex_unlock(&st->lock); + return 0; + default: + return -EINVAL; + }; +} + +static void at91_adc_dma_init(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct at91_adc_state *st = iio_priv(indio_dev); + struct dma_slave_config config = {0}; + /* + * We make the buffer double the size of the fifo, + * such that DMA uses one half of the buffer (full fifo size) + * and the software uses the other half to read/write. + */ + unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * + AT91_BUFFER_MAX_CONVERSION_BYTES * 2, + PAGE_SIZE); + + if (st->dma_st.dma_chan) + return; + + st->dma_st.dma_chan = dma_request_chan(&pdev->dev, "rx"); + if (IS_ERR(st->dma_st.dma_chan)) { + dev_info(&pdev->dev, "can't get DMA channel\n"); + st->dma_st.dma_chan = NULL; + goto dma_exit; + } + + st->dma_st.rx_buf = dma_alloc_coherent(st->dma_st.dma_chan->device->dev, + pages * PAGE_SIZE, + &st->dma_st.rx_dma_buf, + GFP_KERNEL); + if (!st->dma_st.rx_buf) { + dev_info(&pdev->dev, "can't allocate coherent DMA area\n"); + goto dma_chan_disable; + } + + /* Configure DMA channel to read data register */ + config.direction = DMA_DEV_TO_MEM; + config.src_addr = (phys_addr_t)(st->dma_st.phys_addr + + AT91_SAMA5D2_LCDR); + config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + config.src_maxburst = 1; + config.dst_maxburst = 1; + + if (dmaengine_slave_config(st->dma_st.dma_chan, &config)) { + dev_info(&pdev->dev, "can't configure DMA slave\n"); + goto dma_free_area; + } + + dev_info(&pdev->dev, "using %s for rx DMA transfers\n", + dma_chan_name(st->dma_st.dma_chan)); + + return; + +dma_free_area: + dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE, + st->dma_st.rx_buf, st->dma_st.rx_dma_buf); +dma_chan_disable: + dma_release_channel(st->dma_st.dma_chan); + st->dma_st.dma_chan = NULL; +dma_exit: + dev_info(&pdev->dev, "continuing without DMA support\n"); +} + +static void at91_adc_dma_disable(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct at91_adc_state *st = iio_priv(indio_dev); + unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * + AT91_BUFFER_MAX_CONVERSION_BYTES * 2, + PAGE_SIZE); + + /* if we are not using DMA, just return */ + if (!st->dma_st.dma_chan) + return; + + /* wait for all transactions to be terminated first*/ + dmaengine_terminate_sync(st->dma_st.dma_chan); + + dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE, + st->dma_st.rx_buf, st->dma_st.rx_dma_buf); + dma_release_channel(st->dma_st.dma_chan); + st->dma_st.dma_chan = NULL; + + dev_info(&pdev->dev, "continuing without DMA support\n"); +} + +static int at91_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + + if (val > AT91_HWFIFO_MAX_SIZE) + return -EINVAL; + + if (!st->selected_trig->hw_trig) { + dev_dbg(&indio_dev->dev, "we need hw trigger for DMA\n"); + return 0; + } + + dev_dbg(&indio_dev->dev, "new watermark is %u\n", val); + st->dma_st.watermark = val; + + /* + * The logic here is: if we have watermark 1, it means we do + * each conversion with it's own IRQ, thus we don't need DMA. + * If the watermark is higher, we do DMA to do all the transfers in bulk + */ + + if (val == 1) + at91_adc_dma_disable(to_platform_device(&indio_dev->dev)); + else if (val > 1) + at91_adc_dma_init(to_platform_device(&indio_dev->dev)); + + /* + * We can start the DMA only after setting the watermark and + * having the DMA initialization completed + */ + ret = at91_adc_buffer_prepare(indio_dev); + if (ret) + at91_adc_dma_disable(to_platform_device(&indio_dev->dev)); + + return ret; +} + +static int at91_adc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + if (bitmap_subset(scan_mask, &st->touch_st.channels_bitmask, + AT91_SAMA5D2_MAX_CHAN_IDX + 1)) + return 0; + /* + * if the new bitmap is a combination of touchscreen and regular + * channels, then we are not fine + */ + if (bitmap_intersects(&st->touch_st.channels_bitmask, scan_mask, + AT91_SAMA5D2_MAX_CHAN_IDX + 1)) + return -EINVAL; + return 0; +} + +static void at91_adc_hw_init(struct iio_dev *indio_dev) +{ + struct at91_adc_state *st = iio_priv(indio_dev); + + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST); + at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff); + /* + * Transfer field must be set to 2 according to the datasheet and + * allows different analog settings for each channel. + */ + at91_adc_writel(st, AT91_SAMA5D2_MR, + AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH); + + at91_adc_setup_samp_freq(indio_dev, st->soc_info.min_sample_rate); + + /* configure extended mode register */ + at91_adc_config_emr(st); +} + +static ssize_t at91_adc_get_fifo_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + 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); +} + +static ssize_t at91_adc_get_watermark(struct device *dev, + struct device_attribute *attr, char *buf) +{ + 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); +} + +static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, + at91_adc_get_fifo_state, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, + at91_adc_get_watermark, NULL, 0); + +static IIO_CONST_ATTR(hwfifo_watermark_min, "2"); +static IIO_CONST_ATTR(hwfifo_watermark_max, AT91_HWFIFO_MAX_SIZE_STR); + +static IIO_CONST_ATTR(oversampling_ratio_available, + __stringify(AT91_OSR_1SAMPLES) " " + __stringify(AT91_OSR_4SAMPLES) " " + __stringify(AT91_OSR_16SAMPLES)); + +static struct attribute *at91_adc_attributes[] = { + &iio_const_attr_oversampling_ratio_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group at91_adc_attribute_group = { + .attrs = at91_adc_attributes, +}; + +static const struct attribute *at91_adc_fifo_attributes[] = { + &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, + &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark.dev_attr.attr, + &iio_dev_attr_hwfifo_enabled.dev_attr.attr, + NULL, +}; + +static const struct iio_info at91_adc_info = { + .attrs = &at91_adc_attribute_group, + .read_raw = &at91_adc_read_raw, + .write_raw = &at91_adc_write_raw, + .update_scan_mode = &at91_adc_update_scan_mode, + .of_xlate = &at91_adc_of_xlate, + .hwfifo_set_watermark = &at91_adc_set_watermark, +}; + +static int at91_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct at91_adc_state *st; + struct resource *res; + int ret, i; + u32 edge_type = IRQ_TYPE_NONE; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->info = &at91_adc_info; + indio_dev->channels = at91_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels); + + st = iio_priv(indio_dev); + st->indio_dev = indio_dev; + + bitmap_set(&st->touch_st.channels_bitmask, + AT91_SAMA5D2_TOUCH_X_CHAN_IDX, 1); + bitmap_set(&st->touch_st.channels_bitmask, + AT91_SAMA5D2_TOUCH_Y_CHAN_IDX, 1); + bitmap_set(&st->touch_st.channels_bitmask, + AT91_SAMA5D2_TOUCH_P_CHAN_IDX, 1); + + st->oversampling_ratio = AT91_OSR_1SAMPLES; + + ret = of_property_read_u32(pdev->dev.of_node, + "atmel,min-sample-rate-hz", + &st->soc_info.min_sample_rate); + if (ret) { + dev_err(&pdev->dev, + "invalid or missing value for atmel,min-sample-rate-hz\n"); + return ret; + } + + ret = of_property_read_u32(pdev->dev.of_node, + "atmel,max-sample-rate-hz", + &st->soc_info.max_sample_rate); + if (ret) { + dev_err(&pdev->dev, + "invalid or missing value for atmel,max-sample-rate-hz\n"); + return ret; + } + + ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms", + &st->soc_info.startup_time); + if (ret) { + dev_err(&pdev->dev, + "invalid or missing value for atmel,startup-time-ms\n"); + return ret; + } + + ret = of_property_read_u32(pdev->dev.of_node, + "atmel,trigger-edge-type", &edge_type); + if (ret) { + dev_dbg(&pdev->dev, + "atmel,trigger-edge-type not specified, only software trigger available\n"); + } + + st->selected_trig = NULL; + + /* find the right trigger, or no trigger at all */ + for (i = 0; i < AT91_SAMA5D2_HW_TRIG_CNT + 1; i++) + if (at91_adc_trigger_list[i].edge_type == edge_type) { + st->selected_trig = &at91_adc_trigger_list[i]; + break; + } + + if (!st->selected_trig) { + dev_err(&pdev->dev, "invalid external trigger edge value\n"); + return -EINVAL; + } + + init_waitqueue_head(&st->wq_data_available); + mutex_init(&st->lock); + INIT_WORK(&st->touch_st.workq, at91_adc_workq_handler); + + st->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(st->base)) + return PTR_ERR(st->base); + + /* if we plan to use DMA, we need the physical address of the regs */ + st->dma_st.phys_addr = res->start; + + st->irq = platform_get_irq(pdev, 0); + if (st->irq <= 0) { + if (!st->irq) + st->irq = -ENXIO; + + return st->irq; + } + + st->per_clk = devm_clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->per_clk)) + return PTR_ERR(st->per_clk); + + st->reg = devm_regulator_get(&pdev->dev, "vddana"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + st->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(st->vref)) + return PTR_ERR(st->vref); + + ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0, + pdev->dev.driver->name, indio_dev); + if (ret) + return ret; + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = regulator_enable(st->vref); + if (ret) + goto reg_disable; + + st->vref_uv = regulator_get_voltage(st->vref); + if (st->vref_uv <= 0) { + ret = -EINVAL; + goto vref_disable; + } + + at91_adc_hw_init(indio_dev); + + ret = clk_prepare_enable(st->per_clk); + if (ret) + goto vref_disable; + + platform_set_drvdata(pdev, indio_dev); + + ret = at91_adc_buffer_init(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "couldn't initialize the buffer.\n"); + goto per_clk_disable_unprepare; + } + + if (st->selected_trig->hw_trig) { + ret = at91_adc_trigger_init(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "couldn't setup the triggers.\n"); + goto per_clk_disable_unprepare; + } + /* + * Initially the iio buffer has a length of 2 and + * a watermark of 1 + */ + st->dma_st.watermark = 1; + + iio_buffer_set_attrs(indio_dev->buffer, + at91_adc_fifo_attributes); + } + + if (dma_coerce_mask_and_coherent(&indio_dev->dev, DMA_BIT_MASK(32))) + dev_info(&pdev->dev, "cannot set DMA mask to 32-bit\n"); + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto dma_disable; + + if (st->selected_trig->hw_trig) + dev_info(&pdev->dev, "setting up trigger as %s\n", + st->selected_trig->name); + + dev_info(&pdev->dev, "version: %x\n", + readl_relaxed(st->base + AT91_SAMA5D2_VERSION)); + + return 0; + +dma_disable: + at91_adc_dma_disable(pdev); +per_clk_disable_unprepare: + clk_disable_unprepare(st->per_clk); +vref_disable: + regulator_disable(st->vref); +reg_disable: + regulator_disable(st->reg); + return ret; +} + +static int at91_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct at91_adc_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + at91_adc_dma_disable(pdev); + + clk_disable_unprepare(st->per_clk); + + regulator_disable(st->vref); + regulator_disable(st->reg); + + return 0; +} + +static __maybe_unused int at91_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + at91_adc_buffer_postdisable(indio_dev); + + /* + * Do a sofware reset of the ADC before we go to suspend. + * this will ensure that all pins are free from being muxed by the ADC + * and can be used by for other devices. + * Otherwise, ADC will hog them and we can't go to suspend mode. + */ + at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST); + + clk_disable_unprepare(st->per_clk); + regulator_disable(st->vref); + regulator_disable(st->reg); + + return pinctrl_pm_select_sleep_state(dev); +} + +static __maybe_unused int at91_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(indio_dev); + int ret; + + ret = pinctrl_pm_select_default_state(dev); + if (ret) + goto resume_failed; + + ret = regulator_enable(st->reg); + if (ret) + goto resume_failed; + + ret = regulator_enable(st->vref); + if (ret) + goto reg_disable_resume; + + ret = clk_prepare_enable(st->per_clk); + if (ret) + goto vref_disable_resume; + + at91_adc_hw_init(indio_dev); + + /* reconfiguring trigger hardware state */ + if (!iio_buffer_enabled(indio_dev)) + return 0; + + ret = at91_adc_buffer_prepare(indio_dev); + if (ret) + goto vref_disable_resume; + + return at91_adc_configure_trigger(st->trig, true); + +vref_disable_resume: + regulator_disable(st->vref); +reg_disable_resume: + regulator_disable(st->reg); +resume_failed: + dev_err(&indio_dev->dev, "failed to resume\n"); + return ret; +} + +static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume); + +static const struct of_device_id at91_adc_dt_match[] = { + { + .compatible = "atmel,sama5d2-adc", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, at91_adc_dt_match); + +static struct platform_driver at91_adc_driver = { + .probe = at91_adc_probe, + .remove = at91_adc_remove, + .driver = { + .name = "at91-sama5d2_adc", + .of_match_table = at91_adc_dt_match, + .pm = &at91_adc_pm_ops, + }, +}; +module_platform_driver(at91_adc_driver) + +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>"); +MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c new file mode 100644 index 000000000..38d4a910b --- /dev/null +++ b/drivers/iio/adc/at91_adc.c @@ -0,0 +1,1483 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for the ADC present in the Atmel AT91 evaluation boards. + * + * Copyright 2011 Free Electrons + */ + +#include <linux/bitmap.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/wait.h> + +#include <linux/platform_data/at91_adc.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/pinctrl/consumer.h> + +/* Registers */ +#define AT91_ADC_CR 0x00 /* Control Register */ +#define AT91_ADC_SWRST (1 << 0) /* Software Reset */ +#define AT91_ADC_START (1 << 1) /* Start Conversion */ + +#define AT91_ADC_MR 0x04 /* Mode Register */ +#define AT91_ADC_TSAMOD (3 << 0) /* ADC mode */ +#define AT91_ADC_TSAMOD_ADC_ONLY_MODE (0 << 0) /* ADC Mode */ +#define AT91_ADC_TSAMOD_TS_ONLY_MODE (1 << 0) /* Touch Screen Only Mode */ +#define AT91_ADC_TRGEN (1 << 0) /* Trigger Enable */ +#define AT91_ADC_TRGSEL (7 << 1) /* Trigger Selection */ +#define AT91_ADC_TRGSEL_TC0 (0 << 1) +#define AT91_ADC_TRGSEL_TC1 (1 << 1) +#define AT91_ADC_TRGSEL_TC2 (2 << 1) +#define AT91_ADC_TRGSEL_EXTERNAL (6 << 1) +#define AT91_ADC_LOWRES (1 << 4) /* Low Resolution */ +#define AT91_ADC_SLEEP (1 << 5) /* Sleep Mode */ +#define AT91_ADC_PENDET (1 << 6) /* Pen contact detection enable */ +#define AT91_ADC_PRESCAL_9260 (0x3f << 8) /* Prescalar Rate Selection */ +#define AT91_ADC_PRESCAL_9G45 (0xff << 8) +#define AT91_ADC_PRESCAL_(x) ((x) << 8) +#define AT91_ADC_STARTUP_9260 (0x1f << 16) /* Startup Up Time */ +#define AT91_ADC_STARTUP_9G45 (0x7f << 16) +#define AT91_ADC_STARTUP_9X5 (0xf << 16) +#define AT91_ADC_STARTUP_(x) ((x) << 16) +#define AT91_ADC_SHTIM (0xf << 24) /* Sample & Hold Time */ +#define AT91_ADC_SHTIM_(x) ((x) << 24) +#define AT91_ADC_PENDBC (0x0f << 28) /* Pen Debounce time */ +#define AT91_ADC_PENDBC_(x) ((x) << 28) + +#define AT91_ADC_TSR 0x0C +#define AT91_ADC_TSR_SHTIM (0xf << 24) /* Sample & Hold Time */ +#define AT91_ADC_TSR_SHTIM_(x) ((x) << 24) + +#define AT91_ADC_CHER 0x10 /* Channel Enable Register */ +#define AT91_ADC_CHDR 0x14 /* Channel Disable Register */ +#define AT91_ADC_CHSR 0x18 /* Channel Status Register */ +#define AT91_ADC_CH(n) (1 << (n)) /* Channel Number */ + +#define AT91_ADC_SR 0x1C /* Status Register */ +#define AT91_ADC_EOC(n) (1 << (n)) /* End of Conversion on Channel N */ +#define AT91_ADC_OVRE(n) (1 << ((n) + 8))/* Overrun Error on Channel N */ +#define AT91_ADC_DRDY (1 << 16) /* Data Ready */ +#define AT91_ADC_GOVRE (1 << 17) /* General Overrun Error */ +#define AT91_ADC_ENDRX (1 << 18) /* End of RX Buffer */ +#define AT91_ADC_RXFUFF (1 << 19) /* RX Buffer Full */ + +#define AT91_ADC_SR_9X5 0x30 /* Status Register for 9x5 */ +#define AT91_ADC_SR_DRDY_9X5 (1 << 24) /* Data Ready */ + +#define AT91_ADC_LCDR 0x20 /* Last Converted Data Register */ +#define AT91_ADC_LDATA (0x3ff) + +#define AT91_ADC_IER 0x24 /* Interrupt Enable Register */ +#define AT91_ADC_IDR 0x28 /* Interrupt Disable Register */ +#define AT91_ADC_IMR 0x2C /* Interrupt Mask Register */ +#define AT91RL_ADC_IER_PEN (1 << 20) +#define AT91RL_ADC_IER_NOPEN (1 << 21) +#define AT91_ADC_IER_PEN (1 << 29) +#define AT91_ADC_IER_NOPEN (1 << 30) +#define AT91_ADC_IER_XRDY (1 << 20) +#define AT91_ADC_IER_YRDY (1 << 21) +#define AT91_ADC_IER_PRDY (1 << 22) +#define AT91_ADC_ISR_PENS (1 << 31) + +#define AT91_ADC_CHR(n) (0x30 + ((n) * 4)) /* Channel Data Register N */ +#define AT91_ADC_DATA (0x3ff) + +#define AT91_ADC_CDR0_9X5 (0x50) /* Channel Data Register 0 for 9X5 */ + +#define AT91_ADC_ACR 0x94 /* Analog Control Register */ +#define AT91_ADC_ACR_PENDETSENS (0x3 << 0) /* pull-up resistor */ + +#define AT91_ADC_TSMR 0xB0 +#define AT91_ADC_TSMR_TSMODE (3 << 0) /* Touch Screen Mode */ +#define AT91_ADC_TSMR_TSMODE_NONE (0 << 0) +#define AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS (1 << 0) +#define AT91_ADC_TSMR_TSMODE_4WIRE_PRESS (2 << 0) +#define AT91_ADC_TSMR_TSMODE_5WIRE (3 << 0) +#define AT91_ADC_TSMR_TSAV (3 << 4) /* Averages samples */ +#define AT91_ADC_TSMR_TSAV_(x) ((x) << 4) +#define AT91_ADC_TSMR_SCTIM (0x0f << 16) /* Switch closure time */ +#define AT91_ADC_TSMR_SCTIM_(x) ((x) << 16) +#define AT91_ADC_TSMR_PENDBC (0x0f << 28) /* Pen Debounce time */ +#define AT91_ADC_TSMR_PENDBC_(x) ((x) << 28) +#define AT91_ADC_TSMR_NOTSDMA (1 << 22) /* No Touchscreen DMA */ +#define AT91_ADC_TSMR_PENDET_DIS (0 << 24) /* Pen contact detection disable */ +#define AT91_ADC_TSMR_PENDET_ENA (1 << 24) /* Pen contact detection enable */ + +#define AT91_ADC_TSXPOSR 0xB4 +#define AT91_ADC_TSYPOSR 0xB8 +#define AT91_ADC_TSPRESSR 0xBC + +#define AT91_ADC_TRGR_9260 AT91_ADC_MR +#define AT91_ADC_TRGR_9G45 0x08 +#define AT91_ADC_TRGR_9X5 0xC0 + +/* Trigger Register bit field */ +#define AT91_ADC_TRGR_TRGPER (0xffff << 16) +#define AT91_ADC_TRGR_TRGPER_(x) ((x) << 16) +#define AT91_ADC_TRGR_TRGMOD (0x7 << 0) +#define AT91_ADC_TRGR_NONE (0 << 0) +#define AT91_ADC_TRGR_MOD_PERIOD_TRIG (5 << 0) + +#define AT91_ADC_CHAN(st, ch) \ + (st->registers->channel_base + (ch * 4)) +#define at91_adc_readl(st, reg) \ + (readl_relaxed(st->reg_base + reg)) +#define at91_adc_writel(st, reg, val) \ + (writel_relaxed(val, st->reg_base + reg)) + +#define DRIVER_NAME "at91_adc" +#define MAX_POS_BITS 12 + +#define TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */ +#define TOUCH_PEN_DETECT_DEBOUNCE_US 200 + +#define MAX_RLPOS_BITS 10 +#define TOUCH_SAMPLE_PERIOD_US_RL 10000 /* 10ms, the SoC can't keep up with 2ms */ +#define TOUCH_SHTIM 0xa +#define TOUCH_SCTIM_US 10 /* 10us for the Touchscreen Switches Closure Time */ + +/** + * struct at91_adc_reg_desc - Various informations relative to registers + * @channel_base: Base offset for the channel data registers + * @drdy_mask: Mask of the DRDY field in the relevant registers + * (Interruptions registers mostly) + * @status_register: Offset of the Interrupt Status Register + * @trigger_register: Offset of the Trigger setup register + * @mr_prescal_mask: Mask of the PRESCAL field in the adc MR register + * @mr_startup_mask: Mask of the STARTUP field in the adc MR register + */ +struct at91_adc_reg_desc { + u8 channel_base; + u32 drdy_mask; + u8 status_register; + u8 trigger_register; + u32 mr_prescal_mask; + u32 mr_startup_mask; +}; + +struct at91_adc_caps { + bool has_ts; /* Support touch screen */ + bool has_tsmr; /* only at91sam9x5, sama5d3 have TSMR reg */ + /* + * Numbers of sampling data will be averaged. Can be 0~3. + * Hardware can average (2 ^ ts_filter_average) sample data. + */ + u8 ts_filter_average; + /* Pen Detection input pull-up resistor, can be 0~3 */ + u8 ts_pen_detect_sensitivity; + + /* startup time calculate function */ + u32 (*calc_startup_ticks)(u32 startup_time, u32 adc_clk_khz); + + u8 num_channels; + struct at91_adc_reg_desc registers; +}; + +struct at91_adc_state { + struct clk *adc_clk; + u16 *buffer; + unsigned long channels_mask; + struct clk *clk; + bool done; + int irq; + u16 last_value; + int chnb; + struct mutex lock; + u8 num_channels; + void __iomem *reg_base; + struct at91_adc_reg_desc *registers; + u32 startup_time; + u8 sample_hold_time; + bool sleep_mode; + struct iio_trigger **trig; + struct at91_adc_trigger *trigger_list; + u32 trigger_number; + bool use_external; + u32 vref_mv; + u32 res; /* resolution used for convertions */ + bool low_res; /* the resolution corresponds to the lowest one */ + wait_queue_head_t wq_data_avail; + struct at91_adc_caps *caps; + + /* + * Following ADC channels are shared by touchscreen: + * + * CH0 -- Touch screen XP/UL + * CH1 -- Touch screen XM/UR + * CH2 -- Touch screen YP/LL + * CH3 -- Touch screen YM/Sense + * CH4 -- Touch screen LR(5-wire only) + * + * The bitfields below represents the reserved channel in the + * touchscreen mode. + */ +#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 0) +#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 0) + enum atmel_adc_ts_type touchscreen_type; + struct input_dev *ts_input; + + u16 ts_sample_period_val; + u32 ts_pressure_threshold; + u16 ts_pendbc; + + bool ts_bufferedmeasure; + u32 ts_prev_absx; + u32 ts_prev_absy; +}; + +static irqreturn_t at91_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *idev = pf->indio_dev; + struct at91_adc_state *st = iio_priv(idev); + struct iio_chan_spec const *chan; + int i, j = 0; + + for (i = 0; i < idev->masklength; i++) { + if (!test_bit(i, idev->active_scan_mask)) + continue; + chan = idev->channels + i; + st->buffer[j] = at91_adc_readl(st, AT91_ADC_CHAN(st, chan->channel)); + j++; + } + + iio_push_to_buffers_with_timestamp(idev, st->buffer, pf->timestamp); + + iio_trigger_notify_done(idev->trig); + + /* Needed to ACK the DRDY interruption */ + at91_adc_readl(st, AT91_ADC_LCDR); + + enable_irq(st->irq); + + return IRQ_HANDLED; +} + +/* Handler for classic adc channel eoc trigger */ +static void handle_adc_eoc_trigger(int irq, struct iio_dev *idev) +{ + struct at91_adc_state *st = iio_priv(idev); + + if (iio_buffer_enabled(idev)) { + disable_irq_nosync(irq); + iio_trigger_poll(idev->trig); + } else { + st->last_value = at91_adc_readl(st, AT91_ADC_CHAN(st, st->chnb)); + /* Needed to ACK the DRDY interruption */ + at91_adc_readl(st, AT91_ADC_LCDR); + st->done = true; + wake_up_interruptible(&st->wq_data_avail); + } +} + +static int at91_ts_sample(struct iio_dev *idev) +{ + struct at91_adc_state *st = iio_priv(idev); + unsigned int xscale, yscale, reg, z1, z2; + unsigned int x, y, pres, xpos, ypos; + unsigned int rxp = 1; + unsigned int factor = 1000; + + unsigned int xyz_mask_bits = st->res; + unsigned int xyz_mask = (1 << xyz_mask_bits) - 1; + + /* calculate position */ + /* x position = (x / xscale) * max, max = 2^MAX_POS_BITS - 1 */ + reg = at91_adc_readl(st, AT91_ADC_TSXPOSR); + xpos = reg & xyz_mask; + x = (xpos << MAX_POS_BITS) - xpos; + xscale = (reg >> 16) & xyz_mask; + if (xscale == 0) { + dev_err(&idev->dev, "Error: xscale == 0!\n"); + return -1; + } + x /= xscale; + + /* y position = (y / yscale) * max, max = 2^MAX_POS_BITS - 1 */ + reg = at91_adc_readl(st, AT91_ADC_TSYPOSR); + ypos = reg & xyz_mask; + y = (ypos << MAX_POS_BITS) - ypos; + yscale = (reg >> 16) & xyz_mask; + if (yscale == 0) { + dev_err(&idev->dev, "Error: yscale == 0!\n"); + return -1; + } + y /= yscale; + + /* calculate the pressure */ + reg = at91_adc_readl(st, AT91_ADC_TSPRESSR); + z1 = reg & xyz_mask; + z2 = (reg >> 16) & xyz_mask; + + if (z1 != 0) + pres = rxp * (x * factor / 1024) * (z2 * factor / z1 - factor) + / factor; + else + pres = st->ts_pressure_threshold; /* no pen contacted */ + + dev_dbg(&idev->dev, "xpos = %d, xscale = %d, ypos = %d, yscale = %d, z1 = %d, z2 = %d, press = %d\n", + xpos, xscale, ypos, yscale, z1, z2, pres); + + if (pres < st->ts_pressure_threshold) { + dev_dbg(&idev->dev, "x = %d, y = %d, pressure = %d\n", + x, y, pres / factor); + input_report_abs(st->ts_input, ABS_X, x); + input_report_abs(st->ts_input, ABS_Y, y); + input_report_abs(st->ts_input, ABS_PRESSURE, pres); + input_report_key(st->ts_input, BTN_TOUCH, 1); + input_sync(st->ts_input); + } else { + dev_dbg(&idev->dev, "pressure too low: not reporting\n"); + } + + return 0; +} + +static irqreturn_t at91_adc_rl_interrupt(int irq, void *private) +{ + struct iio_dev *idev = private; + struct at91_adc_state *st = iio_priv(idev); + u32 status = at91_adc_readl(st, st->registers->status_register); + unsigned int reg; + + status &= at91_adc_readl(st, AT91_ADC_IMR); + if (status & GENMASK(st->num_channels - 1, 0)) + handle_adc_eoc_trigger(irq, idev); + + if (status & AT91RL_ADC_IER_PEN) { + /* Disabling pen debounce is required to get a NOPEN irq */ + reg = at91_adc_readl(st, AT91_ADC_MR); + reg &= ~AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN); + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_NOPEN + | AT91_ADC_EOC(3)); + /* Set up period trigger for sampling */ + at91_adc_writel(st, st->registers->trigger_register, + AT91_ADC_TRGR_MOD_PERIOD_TRIG | + AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val)); + } else if (status & AT91RL_ADC_IER_NOPEN) { + reg = at91_adc_readl(st, AT91_ADC_MR); + reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + at91_adc_writel(st, st->registers->trigger_register, + AT91_ADC_TRGR_NONE); + + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_NOPEN + | AT91_ADC_EOC(3)); + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN); + st->ts_bufferedmeasure = false; + input_report_key(st->ts_input, BTN_TOUCH, 0); + input_sync(st->ts_input); + } else if (status & AT91_ADC_EOC(3) && st->ts_input) { + /* Conversion finished and we've a touchscreen */ + if (st->ts_bufferedmeasure) { + /* + * Last measurement is always discarded, since it can + * be erroneous. + * Always report previous measurement + */ + input_report_abs(st->ts_input, ABS_X, st->ts_prev_absx); + input_report_abs(st->ts_input, ABS_Y, st->ts_prev_absy); + input_report_key(st->ts_input, BTN_TOUCH, 1); + input_sync(st->ts_input); + } else + st->ts_bufferedmeasure = true; + + /* Now make new measurement */ + st->ts_prev_absx = at91_adc_readl(st, AT91_ADC_CHAN(st, 3)) + << MAX_RLPOS_BITS; + st->ts_prev_absx /= at91_adc_readl(st, AT91_ADC_CHAN(st, 2)); + + st->ts_prev_absy = at91_adc_readl(st, AT91_ADC_CHAN(st, 1)) + << MAX_RLPOS_BITS; + st->ts_prev_absy /= at91_adc_readl(st, AT91_ADC_CHAN(st, 0)); + } + + return IRQ_HANDLED; +} + +static irqreturn_t at91_adc_9x5_interrupt(int irq, void *private) +{ + struct iio_dev *idev = private; + struct at91_adc_state *st = iio_priv(idev); + u32 status = at91_adc_readl(st, st->registers->status_register); + const uint32_t ts_data_irq_mask = + AT91_ADC_IER_XRDY | + AT91_ADC_IER_YRDY | + AT91_ADC_IER_PRDY; + + if (status & GENMASK(st->num_channels - 1, 0)) + handle_adc_eoc_trigger(irq, idev); + + if (status & AT91_ADC_IER_PEN) { + at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN); + at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN | + ts_data_irq_mask); + /* Set up period trigger for sampling */ + at91_adc_writel(st, st->registers->trigger_register, + AT91_ADC_TRGR_MOD_PERIOD_TRIG | + AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val)); + } else if (status & AT91_ADC_IER_NOPEN) { + at91_adc_writel(st, st->registers->trigger_register, 0); + at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN | + ts_data_irq_mask); + at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN); + + input_report_key(st->ts_input, BTN_TOUCH, 0); + input_sync(st->ts_input); + } else if ((status & ts_data_irq_mask) == ts_data_irq_mask) { + /* Now all touchscreen data is ready */ + + if (status & AT91_ADC_ISR_PENS) { + /* validate data by pen contact */ + at91_ts_sample(idev); + } else { + /* triggered by event that is no pen contact, just read + * them to clean the interrupt and discard all. + */ + at91_adc_readl(st, AT91_ADC_TSXPOSR); + at91_adc_readl(st, AT91_ADC_TSYPOSR); + at91_adc_readl(st, AT91_ADC_TSPRESSR); + } + } + + return IRQ_HANDLED; +} + +static int at91_adc_channel_init(struct iio_dev *idev) +{ + struct at91_adc_state *st = iio_priv(idev); + struct iio_chan_spec *chan_array, *timestamp; + int bit, idx = 0; + unsigned long rsvd_mask = 0; + + /* If touchscreen is enable, then reserve the adc channels */ + if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE) + rsvd_mask = CHAN_MASK_TOUCHSCREEN_4WIRE; + else if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_5WIRE) + rsvd_mask = CHAN_MASK_TOUCHSCREEN_5WIRE; + + /* set up the channel mask to reserve touchscreen channels */ + st->channels_mask &= ~rsvd_mask; + + idev->num_channels = bitmap_weight(&st->channels_mask, + st->num_channels) + 1; + + chan_array = devm_kzalloc(&idev->dev, + ((idev->num_channels + 1) * + sizeof(struct iio_chan_spec)), + GFP_KERNEL); + + if (!chan_array) + return -ENOMEM; + + for_each_set_bit(bit, &st->channels_mask, st->num_channels) { + struct iio_chan_spec *chan = chan_array + idx; + + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = bit; + chan->scan_index = idx; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = st->res; + chan->scan_type.storagebits = 16; + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + idx++; + } + timestamp = chan_array + idx; + + timestamp->type = IIO_TIMESTAMP; + timestamp->channel = -1; + timestamp->scan_index = idx; + timestamp->scan_type.sign = 's'; + timestamp->scan_type.realbits = 64; + timestamp->scan_type.storagebits = 64; + + idev->channels = chan_array; + return idev->num_channels; +} + +static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev, + struct at91_adc_trigger *triggers, + const char *trigger_name) +{ + struct at91_adc_state *st = iio_priv(idev); + int i; + + for (i = 0; i < st->trigger_number; i++) { + char *name = kasprintf(GFP_KERNEL, + "%s-dev%d-%s", + idev->name, + idev->id, + triggers[i].name); + if (!name) + return -ENOMEM; + + if (strcmp(trigger_name, name) == 0) { + kfree(name); + if (triggers[i].value == 0) + return -EINVAL; + return triggers[i].value; + } + + kfree(name); + } + + return -EINVAL; +} + +static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) +{ + struct iio_dev *idev = iio_trigger_get_drvdata(trig); + struct at91_adc_state *st = iio_priv(idev); + struct at91_adc_reg_desc *reg = st->registers; + u32 status = at91_adc_readl(st, reg->trigger_register); + int value; + u8 bit; + + value = at91_adc_get_trigger_value_by_name(idev, + st->trigger_list, + idev->trig->name); + if (value < 0) + return value; + + if (state) { + st->buffer = kmalloc(idev->scan_bytes, GFP_KERNEL); + if (st->buffer == NULL) + return -ENOMEM; + + at91_adc_writel(st, reg->trigger_register, + status | value); + + for_each_set_bit(bit, idev->active_scan_mask, + st->num_channels) { + struct iio_chan_spec const *chan = idev->channels + bit; + at91_adc_writel(st, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + } + + at91_adc_writel(st, AT91_ADC_IER, reg->drdy_mask); + + } else { + at91_adc_writel(st, AT91_ADC_IDR, reg->drdy_mask); + + at91_adc_writel(st, reg->trigger_register, + status & ~value); + + for_each_set_bit(bit, idev->active_scan_mask, + st->num_channels) { + struct iio_chan_spec const *chan = idev->channels + bit; + at91_adc_writel(st, AT91_ADC_CHDR, + AT91_ADC_CH(chan->channel)); + } + kfree(st->buffer); + } + + return 0; +} + +static const struct iio_trigger_ops at91_adc_trigger_ops = { + .set_trigger_state = &at91_adc_configure_trigger, +}; + +static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev, + struct at91_adc_trigger *trigger) +{ + struct iio_trigger *trig; + int ret; + + trig = iio_trigger_alloc("%s-dev%d-%s", idev->name, + idev->id, trigger->name); + if (trig == NULL) + return NULL; + + trig->dev.parent = idev->dev.parent; + iio_trigger_set_drvdata(trig, idev); + trig->ops = &at91_adc_trigger_ops; + + ret = iio_trigger_register(trig); + if (ret) { + iio_trigger_free(trig); + return NULL; + } + + return trig; +} + +static int at91_adc_trigger_init(struct iio_dev *idev) +{ + struct at91_adc_state *st = iio_priv(idev); + int i, ret; + + st->trig = devm_kcalloc(&idev->dev, + st->trigger_number, sizeof(*st->trig), + GFP_KERNEL); + + if (st->trig == NULL) { + ret = -ENOMEM; + goto error_ret; + } + + for (i = 0; i < st->trigger_number; i++) { + if (st->trigger_list[i].is_external && !(st->use_external)) + continue; + + st->trig[i] = at91_adc_allocate_trigger(idev, + st->trigger_list + i); + if (st->trig[i] == NULL) { + dev_err(&idev->dev, + "Could not allocate trigger %d\n", i); + ret = -ENOMEM; + goto error_trigger; + } + } + + return 0; + +error_trigger: + for (i--; i >= 0; i--) { + iio_trigger_unregister(st->trig[i]); + iio_trigger_free(st->trig[i]); + } +error_ret: + return ret; +} + +static void at91_adc_trigger_remove(struct iio_dev *idev) +{ + struct at91_adc_state *st = iio_priv(idev); + int i; + + for (i = 0; i < st->trigger_number; i++) { + iio_trigger_unregister(st->trig[i]); + iio_trigger_free(st->trig[i]); + } +} + +static int at91_adc_buffer_init(struct iio_dev *idev) +{ + return iio_triggered_buffer_setup(idev, &iio_pollfunc_store_time, + &at91_adc_trigger_handler, NULL); +} + +static void at91_adc_buffer_remove(struct iio_dev *idev) +{ + iio_triggered_buffer_cleanup(idev); +} + +static int at91_adc_read_raw(struct iio_dev *idev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct at91_adc_state *st = iio_priv(idev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + + st->chnb = chan->channel; + at91_adc_writel(st, AT91_ADC_CHER, + AT91_ADC_CH(chan->channel)); + at91_adc_writel(st, AT91_ADC_IER, BIT(chan->channel)); + at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_START); + + ret = wait_event_interruptible_timeout(st->wq_data_avail, + st->done, + msecs_to_jiffies(1000)); + + /* 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)); + + 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 ret; + + case IIO_CHAN_INFO_SCALE: + *val = st->vref_mv; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + break; + } + return -EINVAL; +} + +static int at91_adc_of_get_resolution(struct iio_dev *idev, + struct platform_device *pdev) +{ + struct at91_adc_state *st = iio_priv(idev); + struct device_node *np = pdev->dev.of_node; + int count, i, ret = 0; + char *res_name, *s; + u32 *resolutions; + + count = of_property_count_strings(np, "atmel,adc-res-names"); + if (count < 2) { + dev_err(&idev->dev, "You must specified at least two resolution names for " + "adc-res-names property in the DT\n"); + return count; + } + + resolutions = kmalloc_array(count, sizeof(*resolutions), GFP_KERNEL); + if (!resolutions) + return -ENOMEM; + + if (of_property_read_u32_array(np, "atmel,adc-res", resolutions, count)) { + dev_err(&idev->dev, "Missing adc-res property in the DT.\n"); + ret = -ENODEV; + goto ret; + } + + if (of_property_read_string(np, "atmel,adc-use-res", (const char **)&res_name)) + res_name = "highres"; + + for (i = 0; i < count; i++) { + if (of_property_read_string_index(np, "atmel,adc-res-names", i, (const char **)&s)) + continue; + + if (strcmp(res_name, s)) + continue; + + st->res = resolutions[i]; + if (!strcmp(res_name, "lowres")) + st->low_res = true; + else + st->low_res = false; + + dev_info(&idev->dev, "Resolution used: %u bits\n", st->res); + goto ret; + } + + dev_err(&idev->dev, "There is no resolution for %s\n", res_name); + +ret: + kfree(resolutions); + return ret; +} + +static u32 calc_startup_ticks_9260(u32 startup_time, u32 adc_clk_khz) +{ + /* + * Number of ticks needed to cover the startup time of the ADC + * as defined in the electrical characteristics of the board, + * divided by 8. The formula thus is : + * Startup Time = (ticks + 1) * 8 / ADC Clock + */ + return round_up((startup_time * adc_clk_khz / 1000) - 1, 8) / 8; +} + +static u32 calc_startup_ticks_9x5(u32 startup_time, u32 adc_clk_khz) +{ + /* + * For sama5d3x and at91sam9x5, the formula changes to: + * Startup Time = <lookup_table_value> / ADC Clock + */ + static const int startup_lookup[] = { + 0, 8, 16, 24, + 64, 80, 96, 112, + 512, 576, 640, 704, + 768, 832, 896, 960 + }; + int i, size = ARRAY_SIZE(startup_lookup); + unsigned int ticks; + + ticks = startup_time * adc_clk_khz / 1000; + for (i = 0; i < size; i++) + if (ticks < startup_lookup[i]) + break; + + ticks = i; + if (ticks == size) + /* Reach the end of lookup table */ + ticks = size - 1; + + return ticks; +} + +static const struct of_device_id at91_adc_dt_ids[]; + +static int at91_adc_probe_dt_ts(struct device_node *node, + struct at91_adc_state *st, struct device *dev) +{ + int ret; + u32 prop; + + ret = of_property_read_u32(node, "atmel,adc-ts-wires", &prop); + if (ret) { + dev_info(dev, "ADC Touch screen is disabled.\n"); + return 0; + } + + switch (prop) { + case 4: + case 5: + st->touchscreen_type = prop; + break; + default: + dev_err(dev, "Unsupported number of touchscreen wires (%d). Should be 4 or 5.\n", prop); + return -EINVAL; + } + + if (!st->caps->has_tsmr) + return 0; + prop = 0; + of_property_read_u32(node, "atmel,adc-ts-pressure-threshold", &prop); + st->ts_pressure_threshold = prop; + if (st->ts_pressure_threshold) { + return 0; + } else { + dev_err(dev, "Invalid pressure threshold for the touchscreen\n"); + return -EINVAL; + } +} + +static int at91_adc_probe_dt(struct iio_dev *idev, + struct platform_device *pdev) +{ + struct at91_adc_state *st = iio_priv(idev); + struct device_node *node = pdev->dev.of_node; + struct device_node *trig_node; + int i = 0, ret; + u32 prop; + + if (!node) + return -EINVAL; + + st->caps = (struct at91_adc_caps *) + of_match_device(at91_adc_dt_ids, &pdev->dev)->data; + + st->use_external = of_property_read_bool(node, "atmel,adc-use-external-triggers"); + + if (of_property_read_u32(node, "atmel,adc-channels-used", &prop)) { + dev_err(&idev->dev, "Missing adc-channels-used property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + st->channels_mask = prop; + + st->sleep_mode = of_property_read_bool(node, "atmel,adc-sleep-mode"); + + if (of_property_read_u32(node, "atmel,adc-startup-time", &prop)) { + dev_err(&idev->dev, "Missing adc-startup-time property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + st->startup_time = prop; + + prop = 0; + of_property_read_u32(node, "atmel,adc-sample-hold-time", &prop); + st->sample_hold_time = prop; + + if (of_property_read_u32(node, "atmel,adc-vref", &prop)) { + dev_err(&idev->dev, "Missing adc-vref property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + st->vref_mv = prop; + + ret = at91_adc_of_get_resolution(idev, pdev); + if (ret) + goto error_ret; + + st->registers = &st->caps->registers; + st->num_channels = st->caps->num_channels; + st->trigger_number = of_get_child_count(node); + st->trigger_list = devm_kcalloc(&idev->dev, + st->trigger_number, + sizeof(struct at91_adc_trigger), + GFP_KERNEL); + if (!st->trigger_list) { + dev_err(&idev->dev, "Could not allocate trigger list memory.\n"); + ret = -ENOMEM; + goto error_ret; + } + + for_each_child_of_node(node, trig_node) { + struct at91_adc_trigger *trig = st->trigger_list + i; + const char *name; + + if (of_property_read_string(trig_node, "trigger-name", &name)) { + dev_err(&idev->dev, "Missing trigger-name property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + trig->name = name; + + if (of_property_read_u32(trig_node, "trigger-value", &prop)) { + dev_err(&idev->dev, "Missing trigger-value property in the DT.\n"); + ret = -EINVAL; + goto error_ret; + } + trig->value = prop; + trig->is_external = of_property_read_bool(trig_node, "trigger-external"); + i++; + } + + /* Check if touchscreen is supported. */ + if (st->caps->has_ts) + return at91_adc_probe_dt_ts(node, st, &idev->dev); + else + dev_info(&idev->dev, "not support touchscreen in the adc compatible string.\n"); + + return 0; + +error_ret: + return ret; +} + +static int at91_adc_probe_pdata(struct at91_adc_state *st, + struct platform_device *pdev) +{ + struct at91_adc_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return -EINVAL; + + st->caps = (struct at91_adc_caps *) + platform_get_device_id(pdev)->driver_data; + + st->use_external = pdata->use_external_triggers; + st->vref_mv = pdata->vref; + st->channels_mask = pdata->channels_used; + st->num_channels = st->caps->num_channels; + st->startup_time = pdata->startup_time; + st->trigger_number = pdata->trigger_number; + st->trigger_list = pdata->trigger_list; + st->registers = &st->caps->registers; + st->touchscreen_type = pdata->touchscreen_type; + + return 0; +} + +static const struct iio_info at91_adc_info = { + .read_raw = &at91_adc_read_raw, +}; + +/* Touchscreen related functions */ +static int atmel_ts_open(struct input_dev *dev) +{ + struct at91_adc_state *st = input_get_drvdata(dev); + + if (st->caps->has_tsmr) + at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN); + else + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN); + return 0; +} + +static void atmel_ts_close(struct input_dev *dev) +{ + struct at91_adc_state *st = input_get_drvdata(dev); + + if (st->caps->has_tsmr) + at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN); + else + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN); +} + +static int at91_ts_hw_init(struct iio_dev *idev, u32 adc_clk_khz) +{ + struct at91_adc_state *st = iio_priv(idev); + u32 reg = 0; + u32 tssctim = 0; + int i = 0; + + /* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid + * pen detect noise. + * The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock + */ + st->ts_pendbc = round_up(TOUCH_PEN_DETECT_DEBOUNCE_US * adc_clk_khz / + 1000, 1); + + while (st->ts_pendbc >> ++i) + ; /* Empty! Find the shift offset */ + if (abs(st->ts_pendbc - (1 << i)) < abs(st->ts_pendbc - (1 << (i - 1)))) + st->ts_pendbc = i; + else + st->ts_pendbc = i - 1; + + if (!st->caps->has_tsmr) { + reg = at91_adc_readl(st, AT91_ADC_MR); + reg |= AT91_ADC_TSAMOD_TS_ONLY_MODE | AT91_ADC_PENDET; + + reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + + reg = AT91_ADC_TSR_SHTIM_(TOUCH_SHTIM) & AT91_ADC_TSR_SHTIM; + at91_adc_writel(st, AT91_ADC_TSR, reg); + + st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US_RL * + adc_clk_khz / 1000) - 1, 1); + + return 0; + } + + /* Touchscreen Switches Closure time needed for allowing the value to + * stabilize. + * Switch Closure Time = (TSSCTIM * 4) ADCClock periods + */ + tssctim = DIV_ROUND_UP(TOUCH_SCTIM_US * adc_clk_khz / 1000, 4); + dev_dbg(&idev->dev, "adc_clk at: %d KHz, tssctim at: %d\n", + adc_clk_khz, tssctim); + + if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE) + reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS; + else + reg = AT91_ADC_TSMR_TSMODE_5WIRE; + + reg |= AT91_ADC_TSMR_SCTIM_(tssctim) & AT91_ADC_TSMR_SCTIM; + reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average) + & AT91_ADC_TSMR_TSAV; + reg |= AT91_ADC_TSMR_PENDBC_(st->ts_pendbc) & AT91_ADC_TSMR_PENDBC; + reg |= AT91_ADC_TSMR_NOTSDMA; + reg |= AT91_ADC_TSMR_PENDET_ENA; + reg |= 0x03 << 8; /* TSFREQ, needs to be bigger than TSAV */ + + at91_adc_writel(st, AT91_ADC_TSMR, reg); + + /* Change adc internal resistor value for better pen detection, + * default value is 100 kOhm. + * 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm + * option only available on ES2 and higher + */ + at91_adc_writel(st, AT91_ADC_ACR, st->caps->ts_pen_detect_sensitivity + & AT91_ADC_ACR_PENDETSENS); + + /* Sample Period Time = (TRGPER + 1) / ADCClock */ + st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US * + adc_clk_khz / 1000) - 1, 1); + + return 0; +} + +static int at91_ts_register(struct iio_dev *idev, + struct platform_device *pdev) +{ + struct at91_adc_state *st = iio_priv(idev); + struct input_dev *input; + int ret; + + input = input_allocate_device(); + if (!input) { + dev_err(&idev->dev, "Failed to allocate TS device!\n"); + return -ENOMEM; + } + + input->name = DRIVER_NAME; + input->id.bustype = BUS_HOST; + input->dev.parent = &pdev->dev; + input->open = atmel_ts_open; + input->close = atmel_ts_close; + + __set_bit(EV_ABS, input->evbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(BTN_TOUCH, input->keybit); + if (st->caps->has_tsmr) { + input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0); + } else { + if (st->touchscreen_type != ATMEL_ADC_TOUCHSCREEN_4WIRE) { + dev_err(&pdev->dev, + "This touchscreen controller only support 4 wires\n"); + ret = -EINVAL; + goto err; + } + + input_set_abs_params(input, ABS_X, 0, (1 << MAX_RLPOS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_Y, 0, (1 << MAX_RLPOS_BITS) - 1, + 0, 0); + } + + st->ts_input = input; + input_set_drvdata(input, st); + + ret = input_register_device(input); + if (ret) + goto err; + + return ret; + +err: + input_free_device(st->ts_input); + return ret; +} + +static void at91_ts_unregister(struct at91_adc_state *st) +{ + input_unregister_device(st->ts_input); +} + +static int at91_adc_probe(struct platform_device *pdev) +{ + unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim; + int ret; + struct iio_dev *idev; + struct at91_adc_state *st; + u32 reg; + + idev = devm_iio_device_alloc(&pdev->dev, sizeof(struct at91_adc_state)); + if (!idev) + return -ENOMEM; + + st = iio_priv(idev); + + if (pdev->dev.of_node) + ret = at91_adc_probe_dt(idev, pdev); + else + ret = at91_adc_probe_pdata(st, pdev); + + if (ret) { + dev_err(&pdev->dev, "No platform data available.\n"); + return -EINVAL; + } + + platform_set_drvdata(pdev, idev); + + idev->name = dev_name(&pdev->dev); + idev->modes = INDIO_DIRECT_MODE; + idev->info = &at91_adc_info; + + st->irq = platform_get_irq(pdev, 0); + if (st->irq < 0) + return -ENODEV; + + st->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(st->reg_base)) + return PTR_ERR(st->reg_base); + + + /* + * Disable all IRQs before setting up the handler + */ + at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST); + at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF); + + if (st->caps->has_tsmr) + ret = request_irq(st->irq, at91_adc_9x5_interrupt, 0, + pdev->dev.driver->name, idev); + else + ret = request_irq(st->irq, at91_adc_rl_interrupt, 0, + pdev->dev.driver->name, idev); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); + return ret; + } + + st->clk = devm_clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(st->clk)) { + dev_err(&pdev->dev, "Failed to get the clock.\n"); + ret = PTR_ERR(st->clk); + goto error_free_irq; + } + + ret = clk_prepare_enable(st->clk); + if (ret) { + dev_err(&pdev->dev, + "Could not prepare or enable the clock.\n"); + goto error_free_irq; + } + + st->adc_clk = devm_clk_get(&pdev->dev, "adc_op_clk"); + if (IS_ERR(st->adc_clk)) { + dev_err(&pdev->dev, "Failed to get the ADC clock.\n"); + ret = PTR_ERR(st->adc_clk); + goto error_disable_clk; + } + + ret = clk_prepare_enable(st->adc_clk); + if (ret) { + dev_err(&pdev->dev, + "Could not prepare or enable the ADC clock.\n"); + goto error_disable_clk; + } + + /* + * Prescaler rate computation using the formula from the Atmel's + * datasheet : ADC Clock = MCK / ((Prescaler + 1) * 2), ADC Clock being + * specified by the electrical characteristics of the board. + */ + mstrclk = clk_get_rate(st->clk); + adc_clk = clk_get_rate(st->adc_clk); + adc_clk_khz = adc_clk / 1000; + + dev_dbg(&pdev->dev, "Master clock is set as: %d Hz, adc_clk should set as: %d Hz\n", + mstrclk, adc_clk); + + prsc = (mstrclk / (2 * adc_clk)) - 1; + + if (!st->startup_time) { + dev_err(&pdev->dev, "No startup time available.\n"); + ret = -EINVAL; + goto error_disable_adc_clk; + } + ticks = (*st->caps->calc_startup_ticks)(st->startup_time, adc_clk_khz); + + /* + * a minimal Sample and Hold Time is necessary for the ADC to guarantee + * the best converted final value between two channels selection + * The formula thus is : Sample and Hold Time = (shtim + 1) / ADCClock + */ + if (st->sample_hold_time > 0) + shtim = round_up((st->sample_hold_time * adc_clk_khz / 1000) + - 1, 1); + else + shtim = 0; + + reg = AT91_ADC_PRESCAL_(prsc) & st->registers->mr_prescal_mask; + reg |= AT91_ADC_STARTUP_(ticks) & st->registers->mr_startup_mask; + if (st->low_res) + reg |= AT91_ADC_LOWRES; + if (st->sleep_mode) + reg |= AT91_ADC_SLEEP; + reg |= AT91_ADC_SHTIM_(shtim) & AT91_ADC_SHTIM; + at91_adc_writel(st, AT91_ADC_MR, reg); + + /* Setup the ADC channels available on the board */ + ret = at91_adc_channel_init(idev); + if (ret < 0) { + dev_err(&pdev->dev, "Couldn't initialize the channels.\n"); + goto error_disable_adc_clk; + } + + init_waitqueue_head(&st->wq_data_avail); + mutex_init(&st->lock); + + /* + * Since touch screen will set trigger register as period trigger. So + * when touch screen is enabled, then we have to disable hardware + * trigger for classic adc. + */ + if (!st->touchscreen_type) { + ret = at91_adc_buffer_init(idev); + if (ret < 0) { + dev_err(&pdev->dev, "Couldn't initialize the buffer.\n"); + goto error_disable_adc_clk; + } + + ret = at91_adc_trigger_init(idev); + if (ret < 0) { + dev_err(&pdev->dev, "Couldn't setup the triggers.\n"); + at91_adc_buffer_remove(idev); + goto error_disable_adc_clk; + } + } else { + ret = at91_ts_register(idev, pdev); + if (ret) + goto error_disable_adc_clk; + + at91_ts_hw_init(idev, adc_clk_khz); + } + + ret = iio_device_register(idev); + if (ret < 0) { + dev_err(&pdev->dev, "Couldn't register the device.\n"); + goto error_iio_device_register; + } + + return 0; + +error_iio_device_register: + if (!st->touchscreen_type) { + at91_adc_trigger_remove(idev); + at91_adc_buffer_remove(idev); + } else { + at91_ts_unregister(st); + } +error_disable_adc_clk: + clk_disable_unprepare(st->adc_clk); +error_disable_clk: + clk_disable_unprepare(st->clk); +error_free_irq: + free_irq(st->irq, idev); + return ret; +} + +static int at91_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *idev = platform_get_drvdata(pdev); + struct at91_adc_state *st = iio_priv(idev); + + iio_device_unregister(idev); + if (!st->touchscreen_type) { + at91_adc_trigger_remove(idev); + at91_adc_buffer_remove(idev); + } else { + at91_ts_unregister(st); + } + clk_disable_unprepare(st->adc_clk); + clk_disable_unprepare(st->clk); + free_irq(st->irq, idev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int at91_adc_suspend(struct device *dev) +{ + struct iio_dev *idev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(idev); + + pinctrl_pm_select_sleep_state(dev); + clk_disable_unprepare(st->clk); + + return 0; +} + +static int at91_adc_resume(struct device *dev) +{ + struct iio_dev *idev = dev_get_drvdata(dev); + struct at91_adc_state *st = iio_priv(idev); + + clk_prepare_enable(st->clk); + pinctrl_pm_select_default_state(dev); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume); + +static struct at91_adc_caps at91sam9260_caps = { + .calc_startup_ticks = calc_startup_ticks_9260, + .num_channels = 4, + .registers = { + .channel_base = AT91_ADC_CHR(0), + .drdy_mask = AT91_ADC_DRDY, + .status_register = AT91_ADC_SR, + .trigger_register = AT91_ADC_TRGR_9260, + .mr_prescal_mask = AT91_ADC_PRESCAL_9260, + .mr_startup_mask = AT91_ADC_STARTUP_9260, + }, +}; + +static struct at91_adc_caps at91sam9rl_caps = { + .has_ts = true, + .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */ + .num_channels = 6, + .registers = { + .channel_base = AT91_ADC_CHR(0), + .drdy_mask = AT91_ADC_DRDY, + .status_register = AT91_ADC_SR, + .trigger_register = AT91_ADC_TRGR_9G45, + .mr_prescal_mask = AT91_ADC_PRESCAL_9260, + .mr_startup_mask = AT91_ADC_STARTUP_9G45, + }, +}; + +static struct at91_adc_caps at91sam9g45_caps = { + .has_ts = true, + .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */ + .num_channels = 8, + .registers = { + .channel_base = AT91_ADC_CHR(0), + .drdy_mask = AT91_ADC_DRDY, + .status_register = AT91_ADC_SR, + .trigger_register = AT91_ADC_TRGR_9G45, + .mr_prescal_mask = AT91_ADC_PRESCAL_9G45, + .mr_startup_mask = AT91_ADC_STARTUP_9G45, + }, +}; + +static struct at91_adc_caps at91sam9x5_caps = { + .has_ts = true, + .has_tsmr = true, + .ts_filter_average = 3, + .ts_pen_detect_sensitivity = 2, + .calc_startup_ticks = calc_startup_ticks_9x5, + .num_channels = 12, + .registers = { + .channel_base = AT91_ADC_CDR0_9X5, + .drdy_mask = AT91_ADC_SR_DRDY_9X5, + .status_register = AT91_ADC_SR_9X5, + .trigger_register = AT91_ADC_TRGR_9X5, + /* prescal mask is same as 9G45 */ + .mr_prescal_mask = AT91_ADC_PRESCAL_9G45, + .mr_startup_mask = AT91_ADC_STARTUP_9X5, + }, +}; + +static const struct of_device_id at91_adc_dt_ids[] = { + { .compatible = "atmel,at91sam9260-adc", .data = &at91sam9260_caps }, + { .compatible = "atmel,at91sam9rl-adc", .data = &at91sam9rl_caps }, + { .compatible = "atmel,at91sam9g45-adc", .data = &at91sam9g45_caps }, + { .compatible = "atmel,at91sam9x5-adc", .data = &at91sam9x5_caps }, + {}, +}; +MODULE_DEVICE_TABLE(of, at91_adc_dt_ids); + +static const struct platform_device_id at91_adc_ids[] = { + { + .name = "at91sam9260-adc", + .driver_data = (unsigned long)&at91sam9260_caps, + }, { + .name = "at91sam9rl-adc", + .driver_data = (unsigned long)&at91sam9rl_caps, + }, { + .name = "at91sam9g45-adc", + .driver_data = (unsigned long)&at91sam9g45_caps, + }, { + .name = "at91sam9x5-adc", + .driver_data = (unsigned long)&at91sam9x5_caps, + }, { + /* terminator */ + } +}; +MODULE_DEVICE_TABLE(platform, at91_adc_ids); + +static struct platform_driver at91_adc_driver = { + .probe = at91_adc_probe, + .remove = at91_adc_remove, + .id_table = at91_adc_ids, + .driver = { + .name = DRIVER_NAME, + .of_match_table = at91_adc_dt_ids, + .pm = &at91_adc_pm_ops, + }, +}; + +module_platform_driver(at91_adc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Atmel AT91 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c new file mode 100644 index 000000000..df99f1365 --- /dev/null +++ b/drivers/iio/adc/axp20x_adc.c @@ -0,0 +1,744 @@ +// 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> + */ + +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/thermal.h> + +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> +#include <linux/iio/machine.h> +#include <linux/mfd/axp20x.h> + +#define AXP20X_ADC_EN1_MASK GENMASK(7, 0) + +#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7)) +#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0)) + +#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0) +#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1) +#define AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(x) ((x) & BIT(0)) +#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1) + +#define AXP20X_ADC_RATE_MASK GENMASK(7, 6) +#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4) +#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK) +#define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK) +#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK) +#define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x) +#define AXP813_V_I_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK) +#define AXP813_ADC_RATE_HZ(x) (AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x)) + +#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \ + { \ + .type = _type, \ + .indexed = 1, \ + .channel = _channel, \ + .address = _reg, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _name, \ + } + +#define AXP20X_ADC_CHANNEL_OFFSET(_channel, _name, _type, _reg) \ + { \ + .type = _type, \ + .indexed = 1, \ + .channel = _channel, \ + .address = _reg, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) |\ + BIT(IIO_CHAN_INFO_OFFSET),\ + .datasheet_name = _name, \ + } + +struct axp_data; + +struct axp20x_adc_iio { + struct regmap *regmap; + const struct axp_data *data; +}; + +enum axp20x_adc_channel_v { + AXP20X_ACIN_V = 0, + AXP20X_VBUS_V, + AXP20X_TS_IN, + AXP20X_GPIO0_V, + AXP20X_GPIO1_V, + AXP20X_IPSOUT_V, + AXP20X_BATT_V, +}; + +enum axp20x_adc_channel_i { + AXP20X_ACIN_I = 0, + AXP20X_VBUS_I, + AXP20X_BATT_CHRG_I, + AXP20X_BATT_DISCHRG_I, +}; + +enum axp22x_adc_channel_v { + AXP22X_TS_IN = 0, + AXP22X_BATT_V, +}; + +enum axp22x_adc_channel_i { + AXP22X_BATT_CHRG_I = 1, + AXP22X_BATT_DISCHRG_I, +}; + +enum axp813_adc_channel_v { + AXP813_TS_IN = 0, + AXP813_GPIO0_V, + AXP813_BATT_V, +}; + +static struct iio_map axp20x_maps[] = { + { + .consumer_dev_name = "axp20x-usb-power-supply", + .consumer_channel = "vbus_v", + .adc_channel_label = "vbus_v", + }, { + .consumer_dev_name = "axp20x-usb-power-supply", + .consumer_channel = "vbus_i", + .adc_channel_label = "vbus_i", + }, { + .consumer_dev_name = "axp20x-ac-power-supply", + .consumer_channel = "acin_v", + .adc_channel_label = "acin_v", + }, { + .consumer_dev_name = "axp20x-ac-power-supply", + .consumer_channel = "acin_i", + .adc_channel_label = "acin_i", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_v", + .adc_channel_label = "batt_v", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_chrg_i", + .adc_channel_label = "batt_chrg_i", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_dischrg_i", + .adc_channel_label = "batt_dischrg_i", + }, { /* sentinel */ } +}; + +static struct iio_map axp22x_maps[] = { + { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_v", + .adc_channel_label = "batt_v", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_chrg_i", + .adc_channel_label = "batt_chrg_i", + }, { + .consumer_dev_name = "axp20x-battery-power-supply", + .consumer_channel = "batt_dischrg_i", + .adc_channel_label = "batt_dischrg_i", + }, { /* sentinel */ } +}; + +/* + * Channels are mapped by physical system. Their channels share the same index. + * i.e. acin_i is in_current0_raw and acin_v is in_voltage0_raw. + * The only exception is for the battery. batt_v will be in_voltage6_raw and + * charge current in_current6_raw and discharge current will be in_current7_raw. + */ +static const struct iio_chan_spec axp20x_adc_channels[] = { + AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE, + AXP20X_ACIN_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_ACIN_I, "acin_i", IIO_CURRENT, + AXP20X_ACIN_I_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_VBUS_V, "vbus_v", IIO_VOLTAGE, + AXP20X_VBUS_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_VBUS_I, "vbus_i", IIO_CURRENT, + AXP20X_VBUS_I_ADC_H), + { + .type = IIO_TEMP, + .address = AXP20X_TEMP_ADC_H, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "pmic_temp", + }, + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO0_V, "gpio0_v", IIO_VOLTAGE, + AXP20X_GPIO0_V_ADC_H), + AXP20X_ADC_CHANNEL_OFFSET(AXP20X_GPIO1_V, "gpio1_v", IIO_VOLTAGE, + AXP20X_GPIO1_V_ADC_H), + AXP20X_ADC_CHANNEL(AXP20X_IPSOUT_V, "ipsout_v", IIO_VOLTAGE, + AXP20X_IPSOUT_V_HIGH_H), + AXP20X_ADC_CHANNEL(AXP20X_BATT_V, "batt_v", IIO_VOLTAGE, + AXP20X_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP20X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP20X_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP20X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, + AXP20X_BATT_DISCHRG_I_H), +}; + +static const struct iio_chan_spec axp22x_adc_channels[] = { + { + .type = IIO_TEMP, + .address = AXP22X_PMIC_TEMP_H, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "pmic_temp", + }, + AXP20X_ADC_CHANNEL(AXP22X_BATT_V, "batt_v", IIO_VOLTAGE, + AXP20X_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP20X_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, + AXP20X_BATT_DISCHRG_I_H), +}; + +static const struct iio_chan_spec axp813_adc_channels[] = { + { + .type = IIO_TEMP, + .address = AXP22X_PMIC_TEMP_H, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "pmic_temp", + }, + AXP20X_ADC_CHANNEL(AXP813_GPIO0_V, "gpio0_v", IIO_VOLTAGE, + AXP288_GP_ADC_H), + AXP20X_ADC_CHANNEL(AXP813_BATT_V, "batt_v", IIO_VOLTAGE, + AXP20X_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP20X_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, + AXP20X_BATT_DISCHRG_I_H), +}; + +static int axp20x_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + int size = 12; + + /* + * N.B.: Unlike the Chinese datasheets tell, the charging current is + * stored on 12 bits, not 13 bits. Only discharging current is on 13 + * bits. + */ + if (chan->type == IIO_CURRENT && chan->channel == AXP20X_BATT_DISCHRG_I) + size = 13; + else + size = 12; + + *val = axp20x_read_variable_width(info->regmap, chan->address, size); + if (*val < 0) + return *val; + + return IIO_VAL_INT; +} + +static int axp22x_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + + *val = axp20x_read_variable_width(info->regmap, chan->address, 12); + if (*val < 0) + return *val; + + return IIO_VAL_INT; +} + +static int axp813_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + + *val = axp20x_read_variable_width(info->regmap, chan->address, 12); + if (*val < 0) + return *val; + + return IIO_VAL_INT; +} + +static int axp20x_adc_scale_voltage(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP20X_ACIN_V: + case AXP20X_VBUS_V: + *val = 1; + *val2 = 700000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_GPIO0_V: + case AXP20X_GPIO1_V: + *val = 0; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_BATT_V: + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_IPSOUT_V: + *val = 1; + *val2 = 400000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int axp813_adc_scale_voltage(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP813_GPIO0_V: + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP813_BATT_V: + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int axp20x_adc_scale_current(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP20X_ACIN_I: + *val = 0; + *val2 = 625000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_VBUS_I: + *val = 0; + *val2 = 375000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP20X_BATT_DISCHRG_I: + case AXP20X_BATT_CHRG_I: + *val = 0; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int axp20x_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp20x_adc_scale_voltage(chan->channel, val, val2); + + case IIO_CURRENT: + return axp20x_adc_scale_current(chan->channel, val, val2); + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->channel != AXP22X_BATT_V) + return -EINVAL; + + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + case IIO_CURRENT: + *val = 1; + return IIO_VAL_INT; + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp813_adc_scale_voltage(chan->channel, val, val2); + + case IIO_CURRENT: + *val = 1; + return IIO_VAL_INT; + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel, + int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + int ret; + + ret = regmap_read(info->regmap, AXP20X_GPIO10_IN_RANGE, val); + if (ret < 0) + return ret; + + switch (channel) { + case AXP20X_GPIO0_V: + *val &= AXP20X_GPIO10_IN_RANGE_GPIO0; + break; + + case AXP20X_GPIO1_V: + *val &= AXP20X_GPIO10_IN_RANGE_GPIO1; + break; + + default: + return -EINVAL; + } + + *val = *val ? 700000 : 0; + + return IIO_VAL_INT; +} + +static int axp20x_adc_offset(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp20x_adc_offset_voltage(indio_dev, chan->channel, val); + + case IIO_TEMP: + *val = -1447; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int axp20x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + return axp20x_adc_offset(indio_dev, chan, val); + + case IIO_CHAN_INFO_SCALE: + return axp20x_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp20x_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + +static int axp22x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + *val = -2677; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return axp22x_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp22x_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + +static int axp813_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + *val = -2667; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return axp813_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp813_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + +static int axp20x_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, + long mask) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + unsigned int reg, regval; + + /* + * The AXP20X PMIC allows the user to choose between 0V and 0.7V offsets + * for (independently) GPIO0 and GPIO1 when in ADC mode. + */ + if (mask != IIO_CHAN_INFO_OFFSET) + return -EINVAL; + + if (val != 0 && val != 700000) + return -EINVAL; + + val = val ? 1 : 0; + + switch (chan->channel) { + case AXP20X_GPIO0_V: + reg = AXP20X_GPIO10_IN_RANGE_GPIO0; + regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(val); + break; + + case AXP20X_GPIO1_V: + reg = AXP20X_GPIO10_IN_RANGE_GPIO1; + regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(val); + break; + + default: + return -EINVAL; + } + + return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, reg, + regval); +} + +static const struct iio_info axp20x_adc_iio_info = { + .read_raw = axp20x_read_raw, + .write_raw = axp20x_write_raw, +}; + +static const struct iio_info axp22x_adc_iio_info = { + .read_raw = axp22x_read_raw, +}; + +static const struct iio_info axp813_adc_iio_info = { + .read_raw = axp813_read_raw, +}; + +static int axp20x_adc_rate(struct axp20x_adc_iio *info, int rate) +{ + return regmap_update_bits(info->regmap, AXP20X_ADC_RATE, + AXP20X_ADC_RATE_MASK, + AXP20X_ADC_RATE_HZ(rate)); +} + +static int axp22x_adc_rate(struct axp20x_adc_iio *info, int rate) +{ + return regmap_update_bits(info->regmap, AXP20X_ADC_RATE, + AXP20X_ADC_RATE_MASK, + AXP22X_ADC_RATE_HZ(rate)); +} + +static int axp813_adc_rate(struct axp20x_adc_iio *info, int rate) +{ + return regmap_update_bits(info->regmap, AXP813_ADC_RATE, + AXP813_ADC_RATE_MASK, + AXP813_ADC_RATE_HZ(rate)); +} + +struct axp_data { + const struct iio_info *iio_info; + int num_channels; + struct iio_chan_spec const *channels; + unsigned long adc_en1_mask; + int (*adc_rate)(struct axp20x_adc_iio *info, + int rate); + bool adc_en2; + struct iio_map *maps; +}; + +static const struct axp_data axp20x_data = { + .iio_info = &axp20x_adc_iio_info, + .num_channels = ARRAY_SIZE(axp20x_adc_channels), + .channels = axp20x_adc_channels, + .adc_en1_mask = AXP20X_ADC_EN1_MASK, + .adc_rate = axp20x_adc_rate, + .adc_en2 = true, + .maps = axp20x_maps, +}; + +static const struct axp_data axp22x_data = { + .iio_info = &axp22x_adc_iio_info, + .num_channels = ARRAY_SIZE(axp22x_adc_channels), + .channels = axp22x_adc_channels, + .adc_en1_mask = AXP22X_ADC_EN1_MASK, + .adc_rate = axp22x_adc_rate, + .adc_en2 = false, + .maps = axp22x_maps, +}; + +static const struct axp_data axp813_data = { + .iio_info = &axp813_adc_iio_info, + .num_channels = ARRAY_SIZE(axp813_adc_channels), + .channels = axp813_adc_channels, + .adc_en1_mask = AXP22X_ADC_EN1_MASK, + .adc_rate = axp813_adc_rate, + .adc_en2 = false, + .maps = axp22x_maps, +}; + +static const struct of_device_id axp20x_adc_of_match[] = { + { .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, }, + { .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, }, + { .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, axp20x_adc_of_match); + +static const struct platform_device_id axp20x_adc_id_match[] = { + { .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, }, + { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, }, + { .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match); + +static int axp20x_probe(struct platform_device *pdev) +{ + struct axp20x_adc_iio *info; + struct iio_dev *indio_dev; + struct axp20x_dev *axp20x_dev; + int ret; + + axp20x_dev = dev_get_drvdata(pdev->dev.parent); + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + platform_set_drvdata(pdev, indio_dev); + + info->regmap = axp20x_dev->regmap; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (!dev_fwnode(&pdev->dev)) { + const struct platform_device_id *id; + + id = platform_get_device_id(pdev); + info->data = (const struct axp_data *)id->driver_data; + } else { + struct device *dev = &pdev->dev; + + info->data = device_get_match_data(dev); + } + + indio_dev->name = platform_get_device_id(pdev)->name; + indio_dev->info = info->data->iio_info; + indio_dev->num_channels = info->data->num_channels; + indio_dev->channels = info->data->channels; + + /* Enable the ADCs on IP */ + regmap_write(info->regmap, AXP20X_ADC_EN1, info->data->adc_en1_mask); + + if (info->data->adc_en2) + /* Enable GPIO0/1 and internal temperature ADCs */ + regmap_update_bits(info->regmap, AXP20X_ADC_EN2, + AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK); + + /* Configure ADCs rate */ + info->data->adc_rate(info, 100); + + ret = iio_map_array_register(indio_dev, info->data->maps); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register IIO maps: %d\n", ret); + goto fail_map; + } + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "could not register the device\n"); + goto fail_register; + } + + return 0; + +fail_register: + iio_map_array_unregister(indio_dev); + +fail_map: + regmap_write(info->regmap, AXP20X_ADC_EN1, 0); + + if (info->data->adc_en2) + regmap_write(info->regmap, AXP20X_ADC_EN2, 0); + + return ret; +} + +static int axp20x_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct axp20x_adc_iio *info = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + + regmap_write(info->regmap, AXP20X_ADC_EN1, 0); + + if (info->data->adc_en2) + regmap_write(info->regmap, AXP20X_ADC_EN2, 0); + + return 0; +} + +static struct platform_driver axp20x_adc_driver = { + .driver = { + .name = "axp20x-adc", + .of_match_table = axp20x_adc_of_match, + }, + .id_table = axp20x_adc_id_match, + .probe = axp20x_probe, + .remove = axp20x_remove, +}; + +module_platform_driver(axp20x_adc_driver); + +MODULE_DESCRIPTION("ADC driver for AXP20X and AXP22X PMICs"); +MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c new file mode 100644 index 000000000..84dbe9e2f --- /dev/null +++ b/drivers/iio/adc/axp288_adc.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver + * + * Copyright (C) 2014 Intel Corporation + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/dmi.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/mfd/axp20x.h> +#include <linux/platform_device.h> + +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> + +/* + * 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, + AXP288_ADC_PMIC, + AXP288_ADC_GP, + AXP288_ADC_BATT_CHRG_I, + AXP288_ADC_BATT_DISCHRG_I, + AXP288_ADC_BATT_V, + AXP288_ADC_NR_CHAN, +}; + +struct axp288_adc_info { + int irq; + struct regmap *regmap; + bool ts_enabled; +}; + +static const struct iio_chan_spec axp288_adc_channels[] = { + { + .indexed = 1, + .type = IIO_TEMP, + .channel = 0, + .address = AXP288_TS_ADC_H, + .datasheet_name = "TS_PIN", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = 1, + .address = AXP288_PMIC_ADC_H, + .datasheet_name = "PMIC_TEMP", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = 2, + .address = AXP288_GP_ADC_H, + .datasheet_name = "GPADC", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_CURRENT, + .channel = 3, + .address = AXP20X_BATT_CHRG_I_H, + .datasheet_name = "BATT_CHG_I", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_CURRENT, + .channel = 4, + .address = AXP20X_BATT_DISCHRG_I_H, + .datasheet_name = "BATT_DISCHRG_I", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .indexed = 1, + .type = IIO_VOLTAGE, + .channel = 5, + .address = AXP20X_BATT_V_H, + .datasheet_name = "BATT_V", + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, +}; + +/* for consumer drivers */ +static struct iio_map axp288_adc_default_maps[] = { + IIO_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"), + IIO_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"), + IIO_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"), + IIO_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"), + IIO_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"), + IIO_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"), + {}, +}; + +static int axp288_adc_read_channel(int *val, unsigned long address, + struct regmap *regmap) +{ + u8 buf[2]; + + if (regmap_bulk_read(regmap, address, buf, 2)) + return -EIO; + *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F); + + return IIO_VAL_INT; +} + +/* + * 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; + + /* 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_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_CURRENT_ON_ONDEMAND) + usleep_range(6000, 10000); + + return 0; +} + +static int axp288_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct axp288_adc_info *info = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (axp288_adc_set_ts(info, 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, AXP288_ADC_TS_CURRENT_ON, + chan->address)) + dev_err(&indio_dev->dev, "TS pin restore\n"); + break; + default: + ret = -EINVAL; + } + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +/* + * 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, + }, + { + /* Nuvision Solo 10 Draw */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TMAX"), + DMI_MATCH(DMI_PRODUCT_NAME, "TM101W610L"), + }, + .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA, + }, + {} +}; + +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 = { + .read_raw = &axp288_adc_read_raw, +}; + +static int axp288_adc_probe(struct platform_device *pdev) +{ + int ret; + struct axp288_adc_info *info; + struct iio_dev *indio_dev; + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) + 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_initialize(info); + if (ret) { + dev_err(&pdev->dev, "unable to enable ADC device\n"); + return ret; + } + + indio_dev->name = pdev->name; + indio_dev->channels = axp288_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels); + indio_dev->info = &axp288_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + ret = iio_map_array_register(indio_dev, axp288_adc_default_maps); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register iio device\n"); + goto err_array_unregister; + } + return 0; + +err_array_unregister: + iio_map_array_unregister(indio_dev); + + return ret; +} + +static int axp288_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + + return 0; +} + +static const struct platform_device_id axp288_adc_id_table[] = { + { .name = "axp288_adc" }, + {}, +}; + +static struct platform_driver axp288_adc_driver = { + .probe = axp288_adc_probe, + .remove = axp288_adc_remove, + .id_table = axp288_adc_id_table, + .driver = { + .name = "axp288_adc", + }, +}; + +MODULE_DEVICE_TABLE(platform, axp288_adc_id_table); + +module_platform_driver(axp288_adc_driver); + +MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>"); +MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c new file mode 100644 index 000000000..44e1e53ad --- /dev/null +++ b/drivers/iio/adc/bcm_iproc_adc.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2016 Broadcom + */ + +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> + +#include <linux/iio/iio.h> + +/* Below Register's are common to IPROC ADC and Touchscreen IP */ +#define IPROC_REGCTL1 0x00 +#define IPROC_REGCTL2 0x04 +#define IPROC_INTERRUPT_THRES 0x08 +#define IPROC_INTERRUPT_MASK 0x0c +#define IPROC_INTERRUPT_STATUS 0x10 +#define IPROC_ANALOG_CONTROL 0x1c +#define IPROC_CONTROLLER_STATUS 0x14 +#define IPROC_AUX_DATA 0x20 +#define IPROC_SOFT_BYPASS_CONTROL 0x38 +#define IPROC_SOFT_BYPASS_DATA 0x3C + +/* IPROC ADC Channel register offsets */ +#define IPROC_ADC_CHANNEL_REGCTL1 0x800 +#define IPROC_ADC_CHANNEL_REGCTL2 0x804 +#define IPROC_ADC_CHANNEL_STATUS 0x808 +#define IPROC_ADC_CHANNEL_INTERRUPT_STATUS 0x80c +#define IPROC_ADC_CHANNEL_INTERRUPT_MASK 0x810 +#define IPROC_ADC_CHANNEL_DATA 0x814 +#define IPROC_ADC_CHANNEL_OFFSET 0x20 + +/* Bit definitions for IPROC_REGCTL2 */ +#define IPROC_ADC_AUXIN_SCAN_ENA BIT(0) +#define IPROC_ADC_PWR_LDO BIT(5) +#define IPROC_ADC_PWR_ADC BIT(4) +#define IPROC_ADC_PWR_BG BIT(3) +#define IPROC_ADC_CONTROLLER_EN BIT(17) + +/* Bit definitions for IPROC_INTERRUPT_MASK and IPROC_INTERRUPT_STATUS */ +#define IPROC_ADC_AUXDATA_RDY_INTR BIT(3) +#define IPROC_ADC_INTR 9 +#define IPROC_ADC_INTR_MASK (0xFF << IPROC_ADC_INTR) + +/* Bit definitions for IPROC_ANALOG_CONTROL */ +#define IPROC_ADC_CHANNEL_SEL 11 +#define IPROC_ADC_CHANNEL_SEL_MASK (0x7 << IPROC_ADC_CHANNEL_SEL) + +/* Bit definitions for IPROC_ADC_CHANNEL_REGCTL1 */ +#define IPROC_ADC_CHANNEL_ROUNDS 0x2 +#define IPROC_ADC_CHANNEL_ROUNDS_MASK (0x3F << IPROC_ADC_CHANNEL_ROUNDS) +#define IPROC_ADC_CHANNEL_MODE 0x1 +#define IPROC_ADC_CHANNEL_MODE_MASK (0x1 << IPROC_ADC_CHANNEL_MODE) +#define IPROC_ADC_CHANNEL_MODE_TDM 0x1 +#define IPROC_ADC_CHANNEL_MODE_SNAPSHOT 0x0 +#define IPROC_ADC_CHANNEL_ENABLE 0x0 +#define IPROC_ADC_CHANNEL_ENABLE_MASK 0x1 + +/* Bit definitions for IPROC_ADC_CHANNEL_REGCTL2 */ +#define IPROC_ADC_CHANNEL_WATERMARK 0x0 +#define IPROC_ADC_CHANNEL_WATERMARK_MASK \ + (0x3F << IPROC_ADC_CHANNEL_WATERMARK) + +#define IPROC_ADC_WATER_MARK_LEVEL 0x1 + +/* Bit definitions for IPROC_ADC_CHANNEL_STATUS */ +#define IPROC_ADC_CHANNEL_DATA_LOST 0x0 +#define IPROC_ADC_CHANNEL_DATA_LOST_MASK \ + (0x0 << IPROC_ADC_CHANNEL_DATA_LOST) +#define IPROC_ADC_CHANNEL_VALID_ENTERIES 0x1 +#define IPROC_ADC_CHANNEL_VALID_ENTERIES_MASK \ + (0xFF << IPROC_ADC_CHANNEL_VALID_ENTERIES) +#define IPROC_ADC_CHANNEL_TOTAL_ENTERIES 0x9 +#define IPROC_ADC_CHANNEL_TOTAL_ENTERIES_MASK \ + (0xFF << IPROC_ADC_CHANNEL_TOTAL_ENTERIES) + +/* Bit definitions for IPROC_ADC_CHANNEL_INTERRUPT_MASK */ +#define IPROC_ADC_CHANNEL_WTRMRK_INTR 0x0 +#define IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK \ + (0x1 << IPROC_ADC_CHANNEL_WTRMRK_INTR) +#define IPROC_ADC_CHANNEL_FULL_INTR 0x1 +#define IPROC_ADC_CHANNEL_FULL_INTR_MASK \ + (0x1 << IPROC_ADC_IPROC_ADC_CHANNEL_FULL_INTR) +#define IPROC_ADC_CHANNEL_EMPTY_INTR 0x2 +#define IPROC_ADC_CHANNEL_EMPTY_INTR_MASK \ + (0x1 << IPROC_ADC_CHANNEL_EMPTY_INTR) + +#define IPROC_ADC_WATER_MARK_INTR_ENABLE 0x1 + +/* Number of time to retry a set of the interrupt mask reg */ +#define IPROC_ADC_INTMASK_RETRY_ATTEMPTS 10 + +#define IPROC_ADC_READ_TIMEOUT (HZ*2) + +#define iproc_adc_dbg_reg(dev, priv, reg) \ +do { \ + u32 val; \ + regmap_read(priv->regmap, reg, &val); \ + dev_dbg(dev, "%20s= 0x%08x\n", #reg, val); \ +} while (0) + +struct iproc_adc_priv { + struct regmap *regmap; + struct clk *adc_clk; + struct mutex mutex; + int irqno; + int chan_val; + int chan_id; + struct completion completion; +}; + +static void iproc_adc_reg_dump(struct iio_dev *indio_dev) +{ + struct device *dev = &indio_dev->dev; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + iproc_adc_dbg_reg(dev, adc_priv, IPROC_REGCTL1); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_REGCTL2); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_THRES); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_MASK); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_INTERRUPT_STATUS); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_CONTROLLER_STATUS); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_ANALOG_CONTROL); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_AUX_DATA); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_SOFT_BYPASS_CONTROL); + iproc_adc_dbg_reg(dev, adc_priv, IPROC_SOFT_BYPASS_DATA); +} + +static irqreturn_t iproc_adc_interrupt_thread(int irq, void *data) +{ + u32 channel_intr_status; + u32 intr_status; + u32 intr_mask; + struct iio_dev *indio_dev = data; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + /* + * This interrupt is shared with the touchscreen driver. + * Make sure this interrupt is intended for us. + * Handle only ADC channel specific interrupts. + */ + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_STATUS, &intr_status); + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &intr_mask); + intr_status = intr_status & intr_mask; + channel_intr_status = (intr_status & IPROC_ADC_INTR_MASK) >> + IPROC_ADC_INTR; + if (channel_intr_status) + return IRQ_WAKE_THREAD; + + return IRQ_NONE; +} + +static irqreturn_t iproc_adc_interrupt_handler(int irq, void *data) +{ + irqreturn_t retval = IRQ_NONE; + struct iproc_adc_priv *adc_priv; + struct iio_dev *indio_dev = data; + unsigned int valid_entries; + u32 intr_status; + u32 intr_channels; + u32 channel_status; + u32 ch_intr_status; + + adc_priv = iio_priv(indio_dev); + + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_STATUS, &intr_status); + dev_dbg(&indio_dev->dev, "iproc_adc_interrupt_handler(),INTRPT_STS:%x\n", + intr_status); + + intr_channels = (intr_status & IPROC_ADC_INTR_MASK) >> IPROC_ADC_INTR; + if (intr_channels) { + regmap_read(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_STATUS + + IPROC_ADC_CHANNEL_OFFSET * adc_priv->chan_id, + &ch_intr_status); + + if (ch_intr_status & IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK) { + regmap_read(adc_priv->regmap, + IPROC_ADC_CHANNEL_STATUS + + IPROC_ADC_CHANNEL_OFFSET * + adc_priv->chan_id, + &channel_status); + + valid_entries = ((channel_status & + IPROC_ADC_CHANNEL_VALID_ENTERIES_MASK) >> + IPROC_ADC_CHANNEL_VALID_ENTERIES); + if (valid_entries >= 1) { + regmap_read(adc_priv->regmap, + IPROC_ADC_CHANNEL_DATA + + IPROC_ADC_CHANNEL_OFFSET * + adc_priv->chan_id, + &adc_priv->chan_val); + complete(&adc_priv->completion); + } else { + dev_err(&indio_dev->dev, + "No data rcvd on channel %d\n", + adc_priv->chan_id); + } + regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_MASK + + IPROC_ADC_CHANNEL_OFFSET * + adc_priv->chan_id, + (ch_intr_status & + ~(IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK))); + } + regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_STATUS + + IPROC_ADC_CHANNEL_OFFSET * adc_priv->chan_id, + ch_intr_status); + regmap_write(adc_priv->regmap, IPROC_INTERRUPT_STATUS, + intr_channels); + retval = IRQ_HANDLED; + } + + return retval; +} + +static int iproc_adc_do_read(struct iio_dev *indio_dev, + int channel, + u16 *p_adc_data) +{ + int read_len = 0; + u32 val; + u32 mask; + u32 val_check; + int failed_cnt = 0; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + mutex_lock(&adc_priv->mutex); + + /* + * After a read is complete the ADC interrupts will be disabled so + * we can assume this section of code is safe from interrupts. + */ + adc_priv->chan_val = -1; + adc_priv->chan_id = channel; + + reinit_completion(&adc_priv->completion); + /* Clear any pending interrupt */ + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_STATUS, + IPROC_ADC_INTR_MASK | IPROC_ADC_AUXDATA_RDY_INTR, + ((0x0 << channel) << IPROC_ADC_INTR) | + IPROC_ADC_AUXDATA_RDY_INTR); + + /* Configure channel for snapshot mode and enable */ + val = (BIT(IPROC_ADC_CHANNEL_ROUNDS) | + (IPROC_ADC_CHANNEL_MODE_SNAPSHOT << IPROC_ADC_CHANNEL_MODE) | + (0x1 << IPROC_ADC_CHANNEL_ENABLE)); + + mask = IPROC_ADC_CHANNEL_ROUNDS_MASK | IPROC_ADC_CHANNEL_MODE_MASK | + IPROC_ADC_CHANNEL_ENABLE_MASK; + regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_REGCTL1 + + IPROC_ADC_CHANNEL_OFFSET * channel), + mask, val); + + /* Set the Watermark for a channel */ + regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_REGCTL2 + + IPROC_ADC_CHANNEL_OFFSET * channel), + IPROC_ADC_CHANNEL_WATERMARK_MASK, + 0x1); + + /* Enable water mark interrupt */ + regmap_update_bits(adc_priv->regmap, (IPROC_ADC_CHANNEL_INTERRUPT_MASK + + IPROC_ADC_CHANNEL_OFFSET * + channel), + IPROC_ADC_CHANNEL_WTRMRK_INTR_MASK, + IPROC_ADC_WATER_MARK_INTR_ENABLE); + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val); + + /* Enable ADC interrupt for a channel */ + val |= (BIT(channel) << IPROC_ADC_INTR); + regmap_write(adc_priv->regmap, IPROC_INTERRUPT_MASK, val); + + /* + * There seems to be a very rare issue where writing to this register + * does not take effect. To work around the issue we will try multiple + * writes. In total we will spend about 10*10 = 100 us attempting this. + * Testing has shown that this may loop a few time, but we have never + * hit the full count. + */ + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check); + while (val_check != val) { + failed_cnt++; + + if (failed_cnt > IPROC_ADC_INTMASK_RETRY_ATTEMPTS) + break; + + udelay(10); + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_MASK, + IPROC_ADC_INTR_MASK, + ((0x1 << channel) << + IPROC_ADC_INTR)); + + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check); + } + + if (failed_cnt) { + dev_dbg(&indio_dev->dev, + "IntMask failed (%d times)", failed_cnt); + if (failed_cnt > IPROC_ADC_INTMASK_RETRY_ATTEMPTS) { + dev_err(&indio_dev->dev, + "IntMask set failed. Read will likely fail."); + read_len = -EIO; + goto adc_err; + } + } + regmap_read(adc_priv->regmap, IPROC_INTERRUPT_MASK, &val_check); + + if (wait_for_completion_timeout(&adc_priv->completion, + IPROC_ADC_READ_TIMEOUT) > 0) { + + /* Only the lower 16 bits are relevant */ + *p_adc_data = adc_priv->chan_val & 0xFFFF; + read_len = sizeof(*p_adc_data); + + } else { + /* + * We never got the interrupt, something went wrong. + * Perhaps the interrupt may still be coming, we do not want + * that now. Lets disable the ADC interrupt, and clear the + * status to put it back in to normal state. + */ + read_len = -ETIMEDOUT; + goto adc_err; + } + mutex_unlock(&adc_priv->mutex); + + return read_len; + +adc_err: + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_MASK, + IPROC_ADC_INTR_MASK, + ((0x0 << channel) << IPROC_ADC_INTR)); + + regmap_update_bits(adc_priv->regmap, IPROC_INTERRUPT_STATUS, + IPROC_ADC_INTR_MASK, + ((0x0 << channel) << IPROC_ADC_INTR)); + + dev_err(&indio_dev->dev, "Timed out waiting for ADC data!\n"); + iproc_adc_reg_dump(indio_dev); + mutex_unlock(&adc_priv->mutex); + + return read_len; +} + +static int iproc_adc_enable(struct iio_dev *indio_dev) +{ + u32 val; + u32 channel_id; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + int ret; + + /* Set i_amux = 3b'000, select channel 0 */ + ret = regmap_update_bits(adc_priv->regmap, IPROC_ANALOG_CONTROL, + IPROC_ADC_CHANNEL_SEL_MASK, 0); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_ANALOG_CONTROL %d\n", ret); + return ret; + } + adc_priv->chan_val = -1; + + /* + * PWR up LDO, ADC, and Band Gap (0 to enable) + * Also enable ADC controller (set high) + */ + ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to read IPROC_REGCTL2 %d\n", ret); + return ret; + } + + val &= ~(IPROC_ADC_PWR_LDO | IPROC_ADC_PWR_ADC | IPROC_ADC_PWR_BG); + + ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_REGCTL2 %d\n", ret); + return ret; + } + + ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to read IPROC_REGCTL2 %d\n", ret); + return ret; + } + + val |= IPROC_ADC_CONTROLLER_EN; + ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_REGCTL2 %d\n", ret); + return ret; + } + + for (channel_id = 0; channel_id < indio_dev->num_channels; + channel_id++) { + ret = regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_MASK + + IPROC_ADC_CHANNEL_OFFSET * channel_id, 0); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write ADC_CHANNEL_INTERRUPT_MASK %d\n", + ret); + return ret; + } + + ret = regmap_write(adc_priv->regmap, + IPROC_ADC_CHANNEL_INTERRUPT_STATUS + + IPROC_ADC_CHANNEL_OFFSET * channel_id, 0); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write ADC_CHANNEL_INTERRUPT_STATUS %d\n", + ret); + return ret; + } + } + + return 0; +} + +static void iproc_adc_disable(struct iio_dev *indio_dev) +{ + u32 val; + int ret; + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + ret = regmap_read(adc_priv->regmap, IPROC_REGCTL2, &val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to read IPROC_REGCTL2 %d\n", ret); + return; + } + + val &= ~IPROC_ADC_CONTROLLER_EN; + ret = regmap_write(adc_priv->regmap, IPROC_REGCTL2, val); + if (ret) { + dev_err(&indio_dev->dev, + "failed to write IPROC_REGCTL2 %d\n", ret); + return; + } +} + +static int iproc_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + u16 adc_data; + int err; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = iproc_adc_do_read(indio_dev, chan->channel, &adc_data); + if (err < 0) + return err; + *val = adc_data; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = 1800; + *val2 = 10; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_info iproc_adc_iio_info = { + .read_raw = &iproc_adc_read_raw, +}; + +#define IPROC_ADC_CHANNEL(_index, _id) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _id, \ +} + +static const struct iio_chan_spec iproc_adc_iio_channels[] = { + IPROC_ADC_CHANNEL(0, "adc0"), + IPROC_ADC_CHANNEL(1, "adc1"), + IPROC_ADC_CHANNEL(2, "adc2"), + IPROC_ADC_CHANNEL(3, "adc3"), + IPROC_ADC_CHANNEL(4, "adc4"), + IPROC_ADC_CHANNEL(5, "adc5"), + IPROC_ADC_CHANNEL(6, "adc6"), + IPROC_ADC_CHANNEL(7, "adc7"), +}; + +static int iproc_adc_probe(struct platform_device *pdev) +{ + struct iproc_adc_priv *adc_priv; + struct iio_dev *indio_dev = NULL; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, + sizeof(*adc_priv)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed to allocate iio device\n"); + return -ENOMEM; + } + + adc_priv = iio_priv(indio_dev); + platform_set_drvdata(pdev, indio_dev); + + mutex_init(&adc_priv->mutex); + + init_completion(&adc_priv->completion); + + adc_priv->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "adc-syscon"); + if (IS_ERR(adc_priv->regmap)) { + dev_err(&pdev->dev, "failed to get handle for tsc syscon\n"); + ret = PTR_ERR(adc_priv->regmap); + return ret; + } + + adc_priv->adc_clk = devm_clk_get(&pdev->dev, "tsc_clk"); + if (IS_ERR(adc_priv->adc_clk)) { + dev_err(&pdev->dev, + "failed getting clock tsc_clk\n"); + ret = PTR_ERR(adc_priv->adc_clk); + return ret; + } + + adc_priv->irqno = platform_get_irq(pdev, 0); + if (adc_priv->irqno <= 0) + return -ENODEV; + + ret = regmap_update_bits(adc_priv->regmap, IPROC_REGCTL2, + IPROC_ADC_AUXIN_SCAN_ENA, 0); + if (ret) { + dev_err(&pdev->dev, "failed to write IPROC_REGCTL2 %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, adc_priv->irqno, + iproc_adc_interrupt_handler, + iproc_adc_interrupt_thread, + IRQF_SHARED, "iproc-adc", indio_dev); + if (ret) { + dev_err(&pdev->dev, "request_irq error %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(adc_priv->adc_clk); + if (ret) { + dev_err(&pdev->dev, + "clk_prepare_enable failed %d\n", ret); + return ret; + } + + ret = iproc_adc_enable(indio_dev); + if (ret) { + dev_err(&pdev->dev, "failed to enable adc %d\n", ret); + goto err_adc_enable; + } + + indio_dev->name = "iproc-static-adc"; + indio_dev->info = &iproc_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = iproc_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(iproc_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "iio_device_register failed:err %d\n", ret); + goto err_clk; + } + + return 0; + +err_clk: + iproc_adc_disable(indio_dev); +err_adc_enable: + clk_disable_unprepare(adc_priv->adc_clk); + + return ret; +} + +static int iproc_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct iproc_adc_priv *adc_priv = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iproc_adc_disable(indio_dev); + clk_disable_unprepare(adc_priv->adc_clk); + + return 0; +} + +static const struct of_device_id iproc_adc_of_match[] = { + {.compatible = "brcm,iproc-static-adc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, iproc_adc_of_match); + +static struct platform_driver iproc_adc_driver = { + .probe = iproc_adc_probe, + .remove = iproc_adc_remove, + .driver = { + .name = "iproc-static-adc", + .of_match_table = iproc_adc_of_match, + }, +}; +module_platform_driver(iproc_adc_driver); + +MODULE_DESCRIPTION("Broadcom iProc ADC controller driver"); +MODULE_AUTHOR("Raveendra Padasalagi <raveendra.padasalagi@broadcom.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c new file mode 100644 index 000000000..fa2c87946 --- /dev/null +++ b/drivers/iio/adc/berlin2-adc.c @@ -0,0 +1,381 @@ +/* + * Marvell Berlin2 ADC driver + * + * Copyright (C) 2015 Marvell Technology Group Ltd. + * + * Antoine Tenart <antoine.tenart@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> +#include <linux/iio/machine.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/sched.h> +#include <linux/wait.h> + +#define BERLIN2_SM_CTRL 0x14 +#define BERLIN2_SM_CTRL_SM_SOC_INT BIT(1) +#define BERLIN2_SM_CTRL_SOC_SM_INT BIT(2) +#define BERLIN2_SM_CTRL_ADC_SEL(x) ((x) << 5) /* 0-15 */ +#define BERLIN2_SM_CTRL_ADC_SEL_MASK GENMASK(8, 5) +#define BERLIN2_SM_CTRL_ADC_POWER BIT(9) +#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2 (0x0 << 10) +#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV3 (0x1 << 10) +#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV4 (0x2 << 10) +#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV8 (0x3 << 10) +#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK GENMASK(11, 10) +#define BERLIN2_SM_CTRL_ADC_START BIT(12) +#define BERLIN2_SM_CTRL_ADC_RESET BIT(13) +#define BERLIN2_SM_CTRL_ADC_BANDGAP_RDY BIT(14) +#define BERLIN2_SM_CTRL_ADC_CONT_SINGLE (0x0 << 15) +#define BERLIN2_SM_CTRL_ADC_CONT_CONTINUOUS (0x1 << 15) +#define BERLIN2_SM_CTRL_ADC_BUFFER_EN BIT(16) +#define BERLIN2_SM_CTRL_ADC_VREF_EXT (0x0 << 17) +#define BERLIN2_SM_CTRL_ADC_VREF_INT (0x1 << 17) +#define BERLIN2_SM_CTRL_ADC_ROTATE BIT(19) +#define BERLIN2_SM_CTRL_TSEN_EN BIT(20) +#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_125 (0x0 << 21) /* 1.25 MHz */ +#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_250 (0x1 << 21) /* 2.5 MHz */ +#define BERLIN2_SM_CTRL_TSEN_MODE_0_125 (0x0 << 22) /* 0-125 C */ +#define BERLIN2_SM_CTRL_TSEN_MODE_10_50 (0x1 << 22) /* 10-50 C */ +#define BERLIN2_SM_CTRL_TSEN_RESET BIT(29) +#define BERLIN2_SM_ADC_DATA 0x20 +#define BERLIN2_SM_ADC_MASK GENMASK(9, 0) +#define BERLIN2_SM_ADC_STATUS 0x1c +#define BERLIN2_SM_ADC_STATUS_DATA_RDY(x) BIT(x) /* 0-15 */ +#define BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK GENMASK(15, 0) +#define BERLIN2_SM_ADC_STATUS_INT_EN(x) (BIT(x) << 16) /* 0-15 */ +#define BERLIN2_SM_ADC_STATUS_INT_EN_MASK GENMASK(31, 16) +#define BERLIN2_SM_TSEN_STATUS 0x24 +#define BERLIN2_SM_TSEN_STATUS_DATA_RDY BIT(0) +#define BERLIN2_SM_TSEN_STATUS_INT_EN BIT(1) +#define BERLIN2_SM_TSEN_DATA 0x28 +#define BERLIN2_SM_TSEN_MASK GENMASK(9, 0) +#define BERLIN2_SM_TSEN_CTRL 0x74 +#define BERLIN2_SM_TSEN_CTRL_START BIT(8) +#define BERLIN2_SM_TSEN_CTRL_SETTLING_4 (0x0 << 21) /* 4 us */ +#define BERLIN2_SM_TSEN_CTRL_SETTLING_12 (0x1 << 21) /* 12 us */ +#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK BIT(21) +#define BERLIN2_SM_TSEN_CTRL_TRIM(x) ((x) << 22) +#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK GENMASK(25, 22) + +struct berlin2_adc_priv { + struct regmap *regmap; + struct mutex lock; + wait_queue_head_t wq; + bool data_available; + int data; +}; + +#define BERLIN2_ADC_CHANNEL(n, t) \ + { \ + .channel = n, \ + .datasheet_name = "channel"#n, \ + .type = t, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + } + +static const struct iio_chan_spec berlin2_adc_channels[] = { + BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE), /* external input */ + BERLIN2_ADC_CHANNEL(1, IIO_VOLTAGE), /* external input */ + BERLIN2_ADC_CHANNEL(2, IIO_VOLTAGE), /* external input */ + BERLIN2_ADC_CHANNEL(3, IIO_VOLTAGE), /* external input */ + BERLIN2_ADC_CHANNEL(4, IIO_VOLTAGE), /* reserved */ + BERLIN2_ADC_CHANNEL(5, IIO_VOLTAGE), /* reserved */ + { /* temperature sensor */ + .channel = 6, + .datasheet_name = "channel6", + .type = IIO_TEMP, + .indexed = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, + BERLIN2_ADC_CHANNEL(7, IIO_VOLTAGE), /* reserved */ + IIO_CHAN_SOFT_TIMESTAMP(8), /* timestamp */ +}; + +static int berlin2_adc_read(struct iio_dev *indio_dev, int channel) +{ + struct berlin2_adc_priv *priv = iio_priv(indio_dev); + int data, ret; + + mutex_lock(&priv->lock); + + /* Enable the interrupts */ + regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS, + BERLIN2_SM_ADC_STATUS_INT_EN(channel)); + + /* Configure the ADC */ + regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_ADC_RESET | + BERLIN2_SM_CTRL_ADC_SEL_MASK | + BERLIN2_SM_CTRL_ADC_START, + BERLIN2_SM_CTRL_ADC_SEL(channel) | + BERLIN2_SM_CTRL_ADC_START); + + ret = wait_event_interruptible_timeout(priv->wq, priv->data_available, + msecs_to_jiffies(1000)); + + /* Disable the interrupts */ + regmap_update_bits(priv->regmap, BERLIN2_SM_ADC_STATUS, + BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0); + + if (ret == 0) + ret = -ETIMEDOUT; + if (ret < 0) { + mutex_unlock(&priv->lock); + return ret; + } + + regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_ADC_START, 0); + + data = priv->data; + priv->data_available = false; + + mutex_unlock(&priv->lock); + + return data; +} + +static int berlin2_adc_tsen_read(struct iio_dev *indio_dev) +{ + struct berlin2_adc_priv *priv = iio_priv(indio_dev); + int data, ret; + + mutex_lock(&priv->lock); + + /* Enable interrupts */ + regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS, + BERLIN2_SM_TSEN_STATUS_INT_EN); + + /* Configure the ADC */ + regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_TSEN_RESET | + BERLIN2_SM_CTRL_ADC_ROTATE, + BERLIN2_SM_CTRL_ADC_ROTATE); + + /* Configure the temperature sensor */ + regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL, + BERLIN2_SM_TSEN_CTRL_TRIM_MASK | + BERLIN2_SM_TSEN_CTRL_SETTLING_MASK | + BERLIN2_SM_TSEN_CTRL_START, + BERLIN2_SM_TSEN_CTRL_TRIM(3) | + BERLIN2_SM_TSEN_CTRL_SETTLING_12 | + BERLIN2_SM_TSEN_CTRL_START); + + ret = wait_event_interruptible_timeout(priv->wq, priv->data_available, + msecs_to_jiffies(1000)); + + /* Disable interrupts */ + regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_STATUS, + BERLIN2_SM_TSEN_STATUS_INT_EN, 0); + + if (ret == 0) + ret = -ETIMEDOUT; + if (ret < 0) { + mutex_unlock(&priv->lock); + return ret; + } + + regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL, + BERLIN2_SM_TSEN_CTRL_START, 0); + + data = priv->data; + priv->data_available = false; + + mutex_unlock(&priv->lock); + + return data; +} + +static int berlin2_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int temp; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_VOLTAGE) + return -EINVAL; + + *val = berlin2_adc_read(indio_dev, chan->channel); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_PROCESSED: + if (chan->type != IIO_TEMP) + return -EINVAL; + + temp = berlin2_adc_tsen_read(indio_dev); + if (temp < 0) + return temp; + + if (temp > 2047) + temp -= 4096; + + /* Convert to milli Celsius */ + *val = ((temp * 100000) / 264 - 270000); + return IIO_VAL_INT; + default: + break; + } + + return -EINVAL; +} + +static irqreturn_t berlin2_adc_irq(int irq, void *private) +{ + struct berlin2_adc_priv *priv = iio_priv(private); + unsigned val; + + regmap_read(priv->regmap, BERLIN2_SM_ADC_STATUS, &val); + if (val & BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK) { + regmap_read(priv->regmap, BERLIN2_SM_ADC_DATA, &priv->data); + priv->data &= BERLIN2_SM_ADC_MASK; + + val &= ~BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK; + regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS, val); + + priv->data_available = true; + wake_up_interruptible(&priv->wq); + } + + return IRQ_HANDLED; +} + +static irqreturn_t berlin2_adc_tsen_irq(int irq, void *private) +{ + struct berlin2_adc_priv *priv = iio_priv(private); + unsigned val; + + regmap_read(priv->regmap, BERLIN2_SM_TSEN_STATUS, &val); + if (val & BERLIN2_SM_TSEN_STATUS_DATA_RDY) { + regmap_read(priv->regmap, BERLIN2_SM_TSEN_DATA, &priv->data); + priv->data &= BERLIN2_SM_TSEN_MASK; + + val &= ~BERLIN2_SM_TSEN_STATUS_DATA_RDY; + regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS, val); + + priv->data_available = true; + wake_up_interruptible(&priv->wq); + } + + return IRQ_HANDLED; +} + +static const struct iio_info berlin2_adc_info = { + .read_raw = berlin2_adc_read_raw, +}; + +static int berlin2_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct berlin2_adc_priv *priv; + struct device_node *parent_np = of_get_parent(pdev->dev.of_node); + int irq, tsen_irq; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!indio_dev) { + of_node_put(parent_np); + return -ENOMEM; + } + + priv = iio_priv(indio_dev); + platform_set_drvdata(pdev, indio_dev); + + priv->regmap = syscon_node_to_regmap(parent_np); + of_node_put(parent_np); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + irq = platform_get_irq_byname(pdev, "adc"); + if (irq < 0) + return irq; + + tsen_irq = platform_get_irq_byname(pdev, "tsen"); + if (tsen_irq < 0) + return tsen_irq; + + ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0, + pdev->dev.driver->name, indio_dev); + if (ret) + return ret; + + ret = devm_request_irq(&pdev->dev, tsen_irq, berlin2_adc_tsen_irq, + 0, pdev->dev.driver->name, indio_dev); + if (ret) + return ret; + + init_waitqueue_head(&priv->wq); + mutex_init(&priv->lock); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &berlin2_adc_info; + + indio_dev->channels = berlin2_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(berlin2_adc_channels); + + /* Power up the ADC */ + regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_ADC_POWER, + BERLIN2_SM_CTRL_ADC_POWER); + + ret = iio_device_register(indio_dev); + if (ret) { + /* Power down the ADC */ + regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_ADC_POWER, 0); + return ret; + } + + return 0; +} + +static int berlin2_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct berlin2_adc_priv *priv = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + /* Power down the ADC */ + regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_ADC_POWER, 0); + + return 0; +} + +static const struct of_device_id berlin2_adc_match[] = { + { .compatible = "marvell,berlin2-adc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, berlin2_adc_match); + +static struct platform_driver berlin2_adc_driver = { + .driver = { + .name = "berlin2-adc", + .of_match_table = berlin2_adc_match, + }, + .probe = berlin2_adc_probe, + .remove = berlin2_adc_remove, +}; +module_platform_driver(berlin2_adc_driver); + +MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>"); +MODULE_DESCRIPTION("Marvell Berlin2 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c new file mode 100644 index 000000000..e16ac9356 --- /dev/null +++ b/drivers/iio/adc/cc10001_adc.c @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014-2015 Imagination Technologies Ltd. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +/* Registers */ +#define CC10001_ADC_CONFIG 0x00 +#define CC10001_ADC_START_CONV BIT(4) +#define CC10001_ADC_MODE_SINGLE_CONV BIT(5) + +#define CC10001_ADC_DDATA_OUT 0x04 +#define CC10001_ADC_EOC 0x08 +#define CC10001_ADC_EOC_SET BIT(0) + +#define CC10001_ADC_CHSEL_SAMPLED 0x0c +#define CC10001_ADC_POWER_DOWN 0x10 +#define CC10001_ADC_POWER_DOWN_SET BIT(0) + +#define CC10001_ADC_DEBUG 0x14 +#define CC10001_ADC_DATA_COUNT 0x20 + +#define CC10001_ADC_DATA_MASK GENMASK(9, 0) +#define CC10001_ADC_NUM_CHANNELS 8 +#define CC10001_ADC_CH_MASK GENMASK(2, 0) + +#define CC10001_INVALID_SAMPLED 0xffff +#define CC10001_MAX_POLL_COUNT 20 + +/* + * As per device specification, wait six clock cycles after power-up to + * activate START. Since adding two more clock cycles delay does not + * impact the performance too much, we are adding two additional cycles delay + * intentionally here. + */ +#define CC10001_WAIT_CYCLES 8 + +struct cc10001_adc_device { + void __iomem *reg_base; + struct clk *adc_clk; + struct regulator *reg; + u16 *buf; + + bool shared; + struct mutex lock; + unsigned int start_delay_ns; + unsigned int eoc_delay_ns; +}; + +static inline void cc10001_adc_write_reg(struct cc10001_adc_device *adc_dev, + u32 reg, u32 val) +{ + writel(val, adc_dev->reg_base + reg); +} + +static inline u32 cc10001_adc_read_reg(struct cc10001_adc_device *adc_dev, + u32 reg) +{ + return readl(adc_dev->reg_base + reg); +} + +static void cc10001_adc_power_up(struct cc10001_adc_device *adc_dev) +{ + cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_DOWN, 0); + ndelay(adc_dev->start_delay_ns); +} + +static void cc10001_adc_power_down(struct cc10001_adc_device *adc_dev) +{ + cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_DOWN, + CC10001_ADC_POWER_DOWN_SET); +} + +static void cc10001_adc_start(struct cc10001_adc_device *adc_dev, + unsigned int channel) +{ + u32 val; + + /* Channel selection and mode of operation */ + val = (channel & CC10001_ADC_CH_MASK) | CC10001_ADC_MODE_SINGLE_CONV; + cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val); + + udelay(1); + val = cc10001_adc_read_reg(adc_dev, CC10001_ADC_CONFIG); + val = val | CC10001_ADC_START_CONV; + cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val); +} + +static u16 cc10001_adc_poll_done(struct iio_dev *indio_dev, + unsigned int channel, + unsigned int delay) +{ + struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); + unsigned int poll_count = 0; + + while (!(cc10001_adc_read_reg(adc_dev, CC10001_ADC_EOC) & + CC10001_ADC_EOC_SET)) { + + ndelay(delay); + if (poll_count++ == CC10001_MAX_POLL_COUNT) + return CC10001_INVALID_SAMPLED; + } + + poll_count = 0; + while ((cc10001_adc_read_reg(adc_dev, CC10001_ADC_CHSEL_SAMPLED) & + CC10001_ADC_CH_MASK) != channel) { + + ndelay(delay); + if (poll_count++ == CC10001_MAX_POLL_COUNT) + return CC10001_INVALID_SAMPLED; + } + + /* Read the 10 bit output register */ + return cc10001_adc_read_reg(adc_dev, CC10001_ADC_DDATA_OUT) & + CC10001_ADC_DATA_MASK; +} + +static irqreturn_t cc10001_adc_trigger_h(int irq, void *p) +{ + struct cc10001_adc_device *adc_dev; + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev; + unsigned int delay_ns; + unsigned int channel; + unsigned int scan_idx; + bool sample_invalid; + u16 *data; + int i; + + indio_dev = pf->indio_dev; + adc_dev = iio_priv(indio_dev); + data = adc_dev->buf; + + mutex_lock(&adc_dev->lock); + + if (!adc_dev->shared) + cc10001_adc_power_up(adc_dev); + + /* Calculate delay step for eoc and sampled data */ + delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT; + + i = 0; + sample_invalid = false; + for_each_set_bit(scan_idx, indio_dev->active_scan_mask, + indio_dev->masklength) { + + channel = indio_dev->channels[scan_idx].channel; + cc10001_adc_start(adc_dev, channel); + + data[i] = cc10001_adc_poll_done(indio_dev, channel, delay_ns); + if (data[i] == CC10001_INVALID_SAMPLED) { + dev_warn(&indio_dev->dev, + "invalid sample on channel %d\n", channel); + sample_invalid = true; + goto done; + } + i++; + } + +done: + if (!adc_dev->shared) + cc10001_adc_power_down(adc_dev); + + mutex_unlock(&adc_dev->lock); + + if (!sample_invalid) + iio_push_to_buffers_with_timestamp(indio_dev, data, + iio_get_time_ns(indio_dev)); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); + unsigned int delay_ns; + u16 val; + + if (!adc_dev->shared) + cc10001_adc_power_up(adc_dev); + + /* Calculate delay step for eoc and sampled data */ + delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT; + + cc10001_adc_start(adc_dev, chan->channel); + + val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns); + + if (!adc_dev->shared) + cc10001_adc_power_down(adc_dev); + + return val; +} + +static int cc10001_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + mutex_lock(&adc_dev->lock); + *val = cc10001_adc_read_raw_voltage(indio_dev, chan); + mutex_unlock(&adc_dev->lock); + + if (*val == CC10001_INVALID_SAMPLED) + return -EIO; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(adc_dev->reg); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static int cc10001_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); + + kfree(adc_dev->buf); + adc_dev->buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); + if (!adc_dev->buf) + return -ENOMEM; + + return 0; +} + +static const struct iio_info cc10001_adc_info = { + .read_raw = &cc10001_adc_read_raw, + .update_scan_mode = &cc10001_update_scan_mode, +}; + +static int cc10001_adc_channel_init(struct iio_dev *indio_dev, + unsigned long channel_map) +{ + struct iio_chan_spec *chan_array, *timestamp; + unsigned int bit, idx = 0; + + indio_dev->num_channels = bitmap_weight(&channel_map, + CC10001_ADC_NUM_CHANNELS) + 1; + + chan_array = devm_kcalloc(&indio_dev->dev, indio_dev->num_channels, + sizeof(struct iio_chan_spec), + GFP_KERNEL); + if (!chan_array) + return -ENOMEM; + + for_each_set_bit(bit, &channel_map, CC10001_ADC_NUM_CHANNELS) { + struct iio_chan_spec *chan = &chan_array[idx]; + + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = bit; + chan->scan_index = idx; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 10; + chan->scan_type.storagebits = 16; + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + idx++; + } + + timestamp = &chan_array[idx]; + timestamp->type = IIO_TIMESTAMP; + timestamp->channel = -1; + timestamp->scan_index = idx; + timestamp->scan_type.sign = 's'; + timestamp->scan_type.realbits = 64; + timestamp->scan_type.storagebits = 64; + + indio_dev->channels = chan_array; + + return 0; +} + +static int cc10001_adc_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct cc10001_adc_device *adc_dev; + unsigned long adc_clk_rate; + struct iio_dev *indio_dev; + unsigned long channel_map; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev)); + if (indio_dev == NULL) + return -ENOMEM; + + adc_dev = iio_priv(indio_dev); + + channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0); + if (!of_property_read_u32(node, "adc-reserved-channels", &ret)) { + adc_dev->shared = true; + channel_map &= ~ret; + } + + adc_dev->reg = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(adc_dev->reg)) + return PTR_ERR(adc_dev->reg); + + ret = regulator_enable(adc_dev->reg); + if (ret) + return ret; + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &cc10001_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + adc_dev->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc_dev->reg_base)) { + ret = PTR_ERR(adc_dev->reg_base); + goto err_disable_reg; + } + + adc_dev->adc_clk = devm_clk_get(&pdev->dev, "adc"); + if (IS_ERR(adc_dev->adc_clk)) { + dev_err(&pdev->dev, "failed to get the clock\n"); + ret = PTR_ERR(adc_dev->adc_clk); + goto err_disable_reg; + } + + ret = clk_prepare_enable(adc_dev->adc_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable the clock\n"); + goto err_disable_reg; + } + + adc_clk_rate = clk_get_rate(adc_dev->adc_clk); + if (!adc_clk_rate) { + ret = -EINVAL; + dev_err(&pdev->dev, "null clock rate!\n"); + goto err_disable_clk; + } + + adc_dev->eoc_delay_ns = NSEC_PER_SEC / adc_clk_rate; + adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES; + + /* + * There is only one register to power-up/power-down the AUX ADC. + * If the ADC is shared among multiple CPUs, always power it up here. + * If the ADC is used only by the MIPS, power-up/power-down at runtime. + */ + if (adc_dev->shared) + cc10001_adc_power_up(adc_dev); + + /* Setup the ADC channels available on the device */ + ret = cc10001_adc_channel_init(indio_dev, channel_map); + if (ret < 0) + goto err_disable_clk; + + mutex_init(&adc_dev->lock); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &cc10001_adc_trigger_h, NULL); + if (ret < 0) + goto err_disable_clk; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto err_cleanup_buffer; + + platform_set_drvdata(pdev, indio_dev); + + return 0; + +err_cleanup_buffer: + iio_triggered_buffer_cleanup(indio_dev); +err_disable_clk: + clk_disable_unprepare(adc_dev->adc_clk); +err_disable_reg: + regulator_disable(adc_dev->reg); + return ret; +} + +static int cc10001_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct cc10001_adc_device *adc_dev = iio_priv(indio_dev); + + cc10001_adc_power_down(adc_dev); + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + clk_disable_unprepare(adc_dev->adc_clk); + regulator_disable(adc_dev->reg); + + return 0; +} + +static const struct of_device_id cc10001_adc_dt_ids[] = { + { .compatible = "cosmic,10001-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, cc10001_adc_dt_ids); + +static struct platform_driver cc10001_adc_driver = { + .driver = { + .name = "cc10001-adc", + .of_match_table = cc10001_adc_dt_ids, + }, + .probe = cc10001_adc_probe, + .remove = cc10001_adc_remove, +}; +module_platform_driver(cc10001_adc_driver); + +MODULE_AUTHOR("Phani Movva <Phani.Movva@imgtec.com>"); +MODULE_DESCRIPTION("Cosmic Circuits ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c new file mode 100644 index 000000000..64c3cc382 --- /dev/null +++ b/drivers/iio/adc/cpcap-adc.c @@ -0,0 +1,1030 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2017 Tony Lindgren <tony@atomide.com> + * + * Rewritten for Linux IIO framework with some code based on + * earlier driver found in the Motorola Linux kernel: + * + * Copyright (C) 2009-2010 Motorola, Inc. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/driver.h> +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/mfd/motorola-cpcap.h> + +/* Register CPCAP_REG_ADCC1 bits */ +#define CPCAP_BIT_ADEN_AUTO_CLR BIT(15) /* Currently unused */ +#define CPCAP_BIT_CAL_MODE BIT(14) /* Set with BIT_RAND0 */ +#define CPCAP_BIT_ADC_CLK_SEL1 BIT(13) /* Currently unused */ +#define CPCAP_BIT_ADC_CLK_SEL0 BIT(12) /* Currently unused */ +#define CPCAP_BIT_ATOX BIT(11) +#define CPCAP_BIT_ATO3 BIT(10) +#define CPCAP_BIT_ATO2 BIT(9) +#define CPCAP_BIT_ATO1 BIT(8) +#define CPCAP_BIT_ATO0 BIT(7) +#define CPCAP_BIT_ADA2 BIT(6) +#define CPCAP_BIT_ADA1 BIT(5) +#define CPCAP_BIT_ADA0 BIT(4) +#define CPCAP_BIT_AD_SEL1 BIT(3) /* Set for bank1 */ +#define CPCAP_BIT_RAND1 BIT(2) /* Set for channel 16 & 17 */ +#define CPCAP_BIT_RAND0 BIT(1) /* Set with CAL_MODE */ +#define CPCAP_BIT_ADEN BIT(0) /* Currently unused */ + +#define CPCAP_REG_ADCC1_DEFAULTS (CPCAP_BIT_ADEN_AUTO_CLR | \ + CPCAP_BIT_ADC_CLK_SEL0 | \ + CPCAP_BIT_RAND1) + +/* Register CPCAP_REG_ADCC2 bits */ +#define CPCAP_BIT_CAL_FACTOR_ENABLE BIT(15) /* Currently unused */ +#define CPCAP_BIT_BATDETB_EN BIT(14) /* Currently unused */ +#define CPCAP_BIT_ADTRIG_ONESHOT BIT(13) /* Set for !TIMING_IMM */ +#define CPCAP_BIT_ASC BIT(12) /* Set for TIMING_IMM */ +#define CPCAP_BIT_ATOX_PS_FACTOR BIT(11) +#define CPCAP_BIT_ADC_PS_FACTOR1 BIT(10) +#define CPCAP_BIT_ADC_PS_FACTOR0 BIT(9) +#define CPCAP_BIT_AD4_SELECT BIT(8) /* Currently unused */ +#define CPCAP_BIT_ADC_BUSY BIT(7) /* Currently unused */ +#define CPCAP_BIT_THERMBIAS_EN BIT(6) /* Bias for AD0_BATTDETB */ +#define CPCAP_BIT_ADTRIG_DIS BIT(5) /* Disable interrupt */ +#define CPCAP_BIT_LIADC BIT(4) /* Currently unused */ +#define CPCAP_BIT_TS_REFEN BIT(3) /* Currently unused */ +#define CPCAP_BIT_TS_M2 BIT(2) /* Currently unused */ +#define CPCAP_BIT_TS_M1 BIT(1) /* Currently unused */ +#define CPCAP_BIT_TS_M0 BIT(0) /* Currently unused */ + +#define CPCAP_REG_ADCC2_DEFAULTS (CPCAP_BIT_AD4_SELECT | \ + CPCAP_BIT_ADTRIG_DIS | \ + CPCAP_BIT_LIADC | \ + CPCAP_BIT_TS_M2 | \ + CPCAP_BIT_TS_M1) + +#define CPCAP_MAX_TEMP_LVL 27 +#define CPCAP_FOUR_POINT_TWO_ADC 801 +#define ST_ADC_CAL_CHRGI_HIGH_THRESHOLD 530 +#define ST_ADC_CAL_CHRGI_LOW_THRESHOLD 494 +#define ST_ADC_CAL_BATTI_HIGH_THRESHOLD 530 +#define ST_ADC_CAL_BATTI_LOW_THRESHOLD 494 +#define ST_ADC_CALIBRATE_DIFF_THRESHOLD 3 + +#define CPCAP_ADC_MAX_RETRIES 5 /* Calibration */ + +/* + * struct cpcap_adc_ato - timing settings for cpcap adc + * + * Unfortunately no cpcap documentation available, please document when + * using these. + */ +struct cpcap_adc_ato { + unsigned short ato_in; + unsigned short atox_in; + unsigned short adc_ps_factor_in; + unsigned short atox_ps_factor_in; + unsigned short ato_out; + unsigned short atox_out; + unsigned short adc_ps_factor_out; + unsigned short atox_ps_factor_out; +}; + +/** + * struct cpcap-adc - cpcap adc device driver data + * @reg: cpcap regmap + * @dev: struct device + * @vendor: cpcap vendor + * @irq: interrupt + * @lock: mutex + * @ato: request timings + * @wq_data_avail: work queue + * @done: work done + */ +struct cpcap_adc { + struct regmap *reg; + struct device *dev; + u16 vendor; + int irq; + struct mutex lock; /* ADC register access lock */ + const struct cpcap_adc_ato *ato; + wait_queue_head_t wq_data_avail; + bool done; +}; + +/* + * enum cpcap_adc_channel - cpcap adc channels + */ +enum cpcap_adc_channel { + /* Bank0 channels */ + CPCAP_ADC_AD0, /* Battery temperature */ + CPCAP_ADC_BATTP, /* Battery voltage */ + CPCAP_ADC_VBUS, /* USB VBUS voltage */ + CPCAP_ADC_AD3, /* Die temperature when charging */ + CPCAP_ADC_BPLUS_AD4, /* Another battery or system voltage */ + CPCAP_ADC_CHG_ISENSE, /* Calibrated charge current */ + CPCAP_ADC_BATTI, /* Calibrated system current */ + CPCAP_ADC_USB_ID, /* USB OTG ID, unused on droid 4? */ + + /* Bank1 channels */ + CPCAP_ADC_AD8, /* Seems unused */ + CPCAP_ADC_AD9, /* Seems unused */ + CPCAP_ADC_LICELL, /* Maybe system voltage? Always 3V */ + CPCAP_ADC_HV_BATTP, /* Another battery detection? */ + CPCAP_ADC_TSX1_AD12, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSX2_AD13, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSY1_AD14, /* Seems unused, for touchscreen? */ + CPCAP_ADC_TSY2_AD15, /* Seems unused, for touchscreen? */ + + /* Remuxed channels using bank0 entries */ + CPCAP_ADC_BATTP_PI16, /* Alternative mux mode for BATTP */ + CPCAP_ADC_BATTI_PI17, /* Alternative mux mode for BATTI */ + + CPCAP_ADC_CHANNEL_NUM, +}; + +/* + * enum cpcap_adc_timing - cpcap adc timing options + * + * CPCAP_ADC_TIMING_IMM seems to be immediate with no timings. + * Please document when using. + */ +enum cpcap_adc_timing { + CPCAP_ADC_TIMING_IMM, + CPCAP_ADC_TIMING_IN, + CPCAP_ADC_TIMING_OUT, +}; + +/** + * struct cpcap_adc_phasing_tbl - cpcap phasing table + * @offset: offset in the phasing table + * @multiplier: multiplier in the phasing table + * @divider: divider in the phasing table + * @min: minimum value + * @max: maximum value + */ +struct cpcap_adc_phasing_tbl { + short offset; + unsigned short multiplier; + unsigned short divider; + short min; + short max; +}; + +/** + * struct cpcap_adc_conversion_tbl - cpcap conversion table + * @conv_type: conversion type + * @align_offset: align offset + * @conv_offset: conversion offset + * @cal_offset: calibration offset + * @multiplier: conversion multiplier + * @divider: conversion divider + */ +struct cpcap_adc_conversion_tbl { + enum iio_chan_info_enum conv_type; + int align_offset; + int conv_offset; + int cal_offset; + int multiplier; + int divider; +}; + +/** + * struct cpcap_adc_request - cpcap adc request + * @channel: request channel + * @phase_tbl: channel phasing table + * @conv_tbl: channel conversion table + * @bank_index: channel index within the bank + * @timing: timing settings + * @result: result + */ +struct cpcap_adc_request { + int channel; + const struct cpcap_adc_phasing_tbl *phase_tbl; + const struct cpcap_adc_conversion_tbl *conv_tbl; + int bank_index; + enum cpcap_adc_timing timing; + int result; +}; + +/* Phasing table for channels. Note that channels 16 & 17 use BATTP and BATTI */ +static const struct cpcap_adc_phasing_tbl bank_phasing[] = { + /* Bank0 */ + [CPCAP_ADC_AD0] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_BATTP] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_VBUS] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_AD3] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_BPLUS_AD4] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_CHG_ISENSE] = {0, 0x80, 0x80, -512, 511}, + [CPCAP_ADC_BATTI] = {0, 0x80, 0x80, -512, 511}, + [CPCAP_ADC_USB_ID] = {0, 0x80, 0x80, 0, 1023}, + + /* Bank1 */ + [CPCAP_ADC_AD8] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_AD9] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_LICELL] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_HV_BATTP] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSX1_AD12] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSX2_AD13] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSY1_AD14] = {0, 0x80, 0x80, 0, 1023}, + [CPCAP_ADC_TSY2_AD15] = {0, 0x80, 0x80, 0, 1023}, +}; + +/* + * Conversion table for channels. Updated during init based on calibration. + * Here too channels 16 & 17 use BATTP and BATTI. + */ +static struct cpcap_adc_conversion_tbl bank_conversion[] = { + /* Bank0 */ + [CPCAP_ADC_AD0] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_BATTP] = { + IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023, + }, + [CPCAP_ADC_VBUS] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 10000, 1023, + }, + [CPCAP_ADC_AD3] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_BPLUS_AD4] = { + IIO_CHAN_INFO_PROCESSED, 0, 2400, 0, 2300, 1023, + }, + [CPCAP_ADC_CHG_ISENSE] = { + IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023, + }, + [CPCAP_ADC_BATTI] = { + IIO_CHAN_INFO_PROCESSED, -512, 2, 0, 5000, 1023, + }, + [CPCAP_ADC_USB_ID] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + + /* Bank1 */ + [CPCAP_ADC_AD8] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_AD9] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_LICELL] = { + IIO_CHAN_INFO_PROCESSED, 0, 0, 0, 3400, 1023, + }, + [CPCAP_ADC_HV_BATTP] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSX1_AD12] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSX2_AD13] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSY1_AD14] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, + [CPCAP_ADC_TSY2_AD15] = { + IIO_CHAN_INFO_RAW, 0, 0, 0, 1, 1, + }, +}; + +/* + * Temperature lookup table of register values to milliCelcius. + * REVISIT: Check the duplicate 0x3ff entry in a freezer + */ +static const int temp_map[CPCAP_MAX_TEMP_LVL][2] = { + { 0x03ff, -40000 }, + { 0x03ff, -35000 }, + { 0x03ef, -30000 }, + { 0x03b2, -25000 }, + { 0x036c, -20000 }, + { 0x0320, -15000 }, + { 0x02d0, -10000 }, + { 0x027f, -5000 }, + { 0x022f, 0 }, + { 0x01e4, 5000 }, + { 0x019f, 10000 }, + { 0x0161, 15000 }, + { 0x012b, 20000 }, + { 0x00fc, 25000 }, + { 0x00d4, 30000 }, + { 0x00b2, 35000 }, + { 0x0095, 40000 }, + { 0x007d, 45000 }, + { 0x0069, 50000 }, + { 0x0059, 55000 }, + { 0x004b, 60000 }, + { 0x003f, 65000 }, + { 0x0036, 70000 }, + { 0x002e, 75000 }, + { 0x0027, 80000 }, + { 0x0022, 85000 }, + { 0x001d, 90000 }, +}; + +#define CPCAP_CHAN(_type, _index, _address, _datasheet_name) { \ + .type = (_type), \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 10, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + .datasheet_name = (_datasheet_name), \ +} + +/* + * The datasheet names are from Motorola mapphone Linux kernel except + * for the last two which might be uncalibrated charge voltage and + * current. + */ +static const struct iio_chan_spec cpcap_adc_channels[] = { + /* Bank0 */ + CPCAP_CHAN(IIO_TEMP, 0, CPCAP_REG_ADCD0, "battdetb"), + CPCAP_CHAN(IIO_VOLTAGE, 1, CPCAP_REG_ADCD1, "battp"), + CPCAP_CHAN(IIO_VOLTAGE, 2, CPCAP_REG_ADCD2, "vbus"), + CPCAP_CHAN(IIO_TEMP, 3, CPCAP_REG_ADCD3, "ad3"), + CPCAP_CHAN(IIO_VOLTAGE, 4, CPCAP_REG_ADCD4, "ad4"), + CPCAP_CHAN(IIO_CURRENT, 5, CPCAP_REG_ADCD5, "chg_isense"), + CPCAP_CHAN(IIO_CURRENT, 6, CPCAP_REG_ADCD6, "batti"), + CPCAP_CHAN(IIO_VOLTAGE, 7, CPCAP_REG_ADCD7, "usb_id"), + + /* Bank1 */ + CPCAP_CHAN(IIO_CURRENT, 8, CPCAP_REG_ADCD0, "ad8"), + CPCAP_CHAN(IIO_VOLTAGE, 9, CPCAP_REG_ADCD1, "ad9"), + CPCAP_CHAN(IIO_VOLTAGE, 10, CPCAP_REG_ADCD2, "licell"), + CPCAP_CHAN(IIO_VOLTAGE, 11, CPCAP_REG_ADCD3, "hv_battp"), + CPCAP_CHAN(IIO_VOLTAGE, 12, CPCAP_REG_ADCD4, "tsx1_ad12"), + CPCAP_CHAN(IIO_VOLTAGE, 13, CPCAP_REG_ADCD5, "tsx2_ad13"), + CPCAP_CHAN(IIO_VOLTAGE, 14, CPCAP_REG_ADCD6, "tsy1_ad14"), + CPCAP_CHAN(IIO_VOLTAGE, 15, CPCAP_REG_ADCD7, "tsy2_ad15"), + + /* There are two registers with multiplexed functionality */ + CPCAP_CHAN(IIO_VOLTAGE, 16, CPCAP_REG_ADCD0, "chg_vsense"), + CPCAP_CHAN(IIO_CURRENT, 17, CPCAP_REG_ADCD1, "batti2"), +}; + +static irqreturn_t cpcap_adc_irq_thread(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct cpcap_adc *ddata = iio_priv(indio_dev); + int error; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return IRQ_NONE; + + ddata->done = true; + wake_up_interruptible(&ddata->wq_data_avail); + + return IRQ_HANDLED; +} + +/* ADC calibration functions */ +static void cpcap_adc_setup_calibrate(struct cpcap_adc *ddata, + enum cpcap_adc_channel chan) +{ + unsigned int value = 0; + unsigned long timeout = jiffies + msecs_to_jiffies(3000); + int error; + + if ((chan != CPCAP_ADC_CHG_ISENSE) && + (chan != CPCAP_ADC_BATTI)) + return; + + value |= CPCAP_BIT_CAL_MODE | CPCAP_BIT_RAND0; + value |= ((chan << 4) & + (CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | CPCAP_BIT_ADA0)); + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX | + CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 | + CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 | + CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | + CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 | + CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0, + value); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ATOX_PS_FACTOR | + CPCAP_BIT_ADC_PS_FACTOR1 | + CPCAP_BIT_ADC_PS_FACTOR0, + 0); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ASC, + CPCAP_BIT_ASC); + if (error) + return; + + do { + schedule_timeout_uninterruptible(1); + error = regmap_read(ddata->reg, CPCAP_REG_ADCC2, &value); + if (error) + return; + } while ((value & CPCAP_BIT_ASC) && time_before(jiffies, timeout)); + + if (value & CPCAP_BIT_ASC) + dev_err(ddata->dev, + "Timeout waiting for calibration to complete\n"); + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE, 0); + if (error) + return; +} + +static int cpcap_adc_calibrate_one(struct cpcap_adc *ddata, + int channel, + u16 calibration_register, + int lower_threshold, + int upper_threshold) +{ + unsigned int calibration_data[2]; + unsigned short cal_data_diff; + int i, error; + + for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) { + calibration_data[0] = 0; + calibration_data[1] = 0; + cal_data_diff = 0; + cpcap_adc_setup_calibrate(ddata, channel); + error = regmap_read(ddata->reg, calibration_register, + &calibration_data[0]); + if (error) + return error; + cpcap_adc_setup_calibrate(ddata, channel); + error = regmap_read(ddata->reg, calibration_register, + &calibration_data[1]); + if (error) + return error; + + if (calibration_data[0] > calibration_data[1]) + cal_data_diff = + calibration_data[0] - calibration_data[1]; + else + cal_data_diff = + calibration_data[1] - calibration_data[0]; + + if (((calibration_data[1] >= lower_threshold) && + (calibration_data[1] <= upper_threshold) && + (cal_data_diff <= ST_ADC_CALIBRATE_DIFF_THRESHOLD)) || + (ddata->vendor == CPCAP_VENDOR_TI)) { + bank_conversion[channel].cal_offset = + ((short)calibration_data[1] * -1) + 512; + dev_dbg(ddata->dev, "ch%i calibration complete: %i\n", + channel, bank_conversion[channel].cal_offset); + break; + } + usleep_range(5000, 10000); + } + + return 0; +} + +static int cpcap_adc_calibrate(struct cpcap_adc *ddata) +{ + int error; + + error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_CHG_ISENSE, + CPCAP_REG_ADCAL1, + ST_ADC_CAL_CHRGI_LOW_THRESHOLD, + ST_ADC_CAL_CHRGI_HIGH_THRESHOLD); + if (error) + return error; + + error = cpcap_adc_calibrate_one(ddata, CPCAP_ADC_BATTI, + CPCAP_REG_ADCAL2, + ST_ADC_CAL_BATTI_LOW_THRESHOLD, + ST_ADC_CAL_BATTI_HIGH_THRESHOLD); + if (error) + return error; + + return 0; +} + +/* ADC setup, read and scale functions */ +static void cpcap_adc_setup_bank(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + const struct cpcap_adc_ato *ato = ddata->ato; + unsigned short value1 = 0; + unsigned short value2 = 0; + int error; + + if (!ato) + return; + + switch (req->channel) { + case CPCAP_ADC_AD0: + value2 |= CPCAP_BIT_THERMBIAS_EN; + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_THERMBIAS_EN, + value2); + if (error) + return; + usleep_range(800, 1000); + break; + case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15: + value1 |= CPCAP_BIT_AD_SEL1; + break; + case CPCAP_ADC_BATTP_PI16 ... CPCAP_ADC_BATTI_PI17: + value1 |= CPCAP_BIT_RAND1; + default: + break; + } + + switch (req->timing) { + case CPCAP_ADC_TIMING_IN: + value1 |= ato->ato_in; + value1 |= ato->atox_in; + value2 |= ato->adc_ps_factor_in; + value2 |= ato->atox_ps_factor_in; + break; + case CPCAP_ADC_TIMING_OUT: + value1 |= ato->ato_out; + value1 |= ato->atox_out; + value2 |= ato->adc_ps_factor_out; + value2 |= ato->atox_ps_factor_out; + break; + + case CPCAP_ADC_TIMING_IMM: + default: + break; + } + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX | + CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 | + CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 | + CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | + CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 | + CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0, + value1); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ATOX_PS_FACTOR | + CPCAP_BIT_ADC_PS_FACTOR1 | + CPCAP_BIT_ADC_PS_FACTOR0 | + CPCAP_BIT_THERMBIAS_EN, + value2); + if (error) + return; + + if (req->timing == CPCAP_ADC_TIMING_IMM) { + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, + CPCAP_BIT_ADTRIG_DIS); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ASC, + CPCAP_BIT_ASC); + if (error) + return; + } else { + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_ONESHOT, + CPCAP_BIT_ADTRIG_ONESHOT); + if (error) + return; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + CPCAP_BIT_ADTRIG_DIS, 0); + if (error) + return; + } +} + +static int cpcap_adc_start_bank(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + int i, error; + + req->timing = CPCAP_ADC_TIMING_IMM; + ddata->done = false; + + for (i = 0; i < CPCAP_ADC_MAX_RETRIES; i++) { + cpcap_adc_setup_bank(ddata, req); + error = wait_event_interruptible_timeout(ddata->wq_data_avail, + ddata->done, + msecs_to_jiffies(50)); + if (error > 0) + return 0; + + if (error == 0) { + error = -ETIMEDOUT; + continue; + } + + if (error < 0) + return error; + } + + return error; +} + +static int cpcap_adc_stop_bank(struct cpcap_adc *ddata) +{ + int error; + + error = regmap_update_bits(ddata->reg, CPCAP_REG_ADCC1, + 0xffff, + CPCAP_REG_ADCC1_DEFAULTS); + if (error) + return error; + + return regmap_update_bits(ddata->reg, CPCAP_REG_ADCC2, + 0xffff, + CPCAP_REG_ADCC2_DEFAULTS); +} + +static void cpcap_adc_phase(struct cpcap_adc_request *req) +{ + const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl; + const struct cpcap_adc_phasing_tbl *phase_tbl = req->phase_tbl; + int index = req->channel; + + /* Remuxed channels 16 and 17 use BATTP and BATTI entries */ + switch (req->channel) { + case CPCAP_ADC_BATTP: + case CPCAP_ADC_BATTP_PI16: + index = req->bank_index; + req->result -= phase_tbl[index].offset; + req->result -= CPCAP_FOUR_POINT_TWO_ADC; + req->result *= phase_tbl[index].multiplier; + if (phase_tbl[index].divider == 0) + return; + req->result /= phase_tbl[index].divider; + req->result += CPCAP_FOUR_POINT_TWO_ADC; + break; + case CPCAP_ADC_BATTI_PI17: + index = req->bank_index; + fallthrough; + default: + req->result += conv_tbl[index].cal_offset; + req->result += conv_tbl[index].align_offset; + req->result *= phase_tbl[index].multiplier; + if (phase_tbl[index].divider == 0) + return; + req->result /= phase_tbl[index].divider; + req->result += phase_tbl[index].offset; + break; + } + + if (req->result < phase_tbl[index].min) + req->result = phase_tbl[index].min; + else if (req->result > phase_tbl[index].max) + req->result = phase_tbl[index].max; +} + +/* Looks up temperatures in a table and calculates averages if needed */ +static int cpcap_adc_table_to_millicelcius(unsigned short value) +{ + int i, result = 0, alpha; + + if (value <= temp_map[CPCAP_MAX_TEMP_LVL - 1][0]) + return temp_map[CPCAP_MAX_TEMP_LVL - 1][1]; + + if (value >= temp_map[0][0]) + return temp_map[0][1]; + + for (i = 0; i < CPCAP_MAX_TEMP_LVL - 1; i++) { + if ((value <= temp_map[i][0]) && + (value >= temp_map[i + 1][0])) { + if (value == temp_map[i][0]) { + result = temp_map[i][1]; + } else if (value == temp_map[i + 1][0]) { + result = temp_map[i + 1][1]; + } else { + alpha = ((value - temp_map[i][0]) * 1000) / + (temp_map[i + 1][0] - temp_map[i][0]); + + result = temp_map[i][1] + + ((alpha * (temp_map[i + 1][1] - + temp_map[i][1])) / 1000); + } + break; + } + } + + return result; +} + +static void cpcap_adc_convert(struct cpcap_adc_request *req) +{ + const struct cpcap_adc_conversion_tbl *conv_tbl = req->conv_tbl; + int index = req->channel; + + /* Remuxed channels 16 and 17 use BATTP and BATTI entries */ + switch (req->channel) { + case CPCAP_ADC_BATTP_PI16: + index = CPCAP_ADC_BATTP; + break; + case CPCAP_ADC_BATTI_PI17: + index = CPCAP_ADC_BATTI; + break; + default: + break; + } + + /* No conversion for raw channels */ + if (conv_tbl[index].conv_type == IIO_CHAN_INFO_RAW) + return; + + /* Temperatures use a lookup table instead of conversion table */ + if ((req->channel == CPCAP_ADC_AD0) || + (req->channel == CPCAP_ADC_AD3)) { + req->result = + cpcap_adc_table_to_millicelcius(req->result); + + return; + } + + /* All processed channels use a conversion table */ + req->result *= conv_tbl[index].multiplier; + if (conv_tbl[index].divider == 0) + return; + req->result /= conv_tbl[index].divider; + req->result += conv_tbl[index].conv_offset; +} + +/* + * REVISIT: Check if timed sampling can use multiple channels at the + * same time. If not, replace channel_mask with just channel. + */ +static int cpcap_adc_read_bank_scaled(struct cpcap_adc *ddata, + struct cpcap_adc_request *req) +{ + int calibration_data, error, addr; + + if (ddata->vendor == CPCAP_VENDOR_TI) { + error = regmap_read(ddata->reg, CPCAP_REG_ADCAL1, + &calibration_data); + if (error) + return error; + bank_conversion[CPCAP_ADC_CHG_ISENSE].cal_offset = + ((short)calibration_data * -1) + 512; + + error = regmap_read(ddata->reg, CPCAP_REG_ADCAL2, + &calibration_data); + if (error) + return error; + bank_conversion[CPCAP_ADC_BATTI].cal_offset = + ((short)calibration_data * -1) + 512; + } + + addr = CPCAP_REG_ADCD0 + req->bank_index * 4; + + error = regmap_read(ddata->reg, addr, &req->result); + if (error) + return error; + + req->result &= 0x3ff; + cpcap_adc_phase(req); + cpcap_adc_convert(req); + + return 0; +} + +static int cpcap_adc_init_request(struct cpcap_adc_request *req, + int channel) +{ + req->channel = channel; + req->phase_tbl = bank_phasing; + req->conv_tbl = bank_conversion; + + switch (channel) { + case CPCAP_ADC_AD0 ... CPCAP_ADC_USB_ID: + req->bank_index = channel; + break; + case CPCAP_ADC_AD8 ... CPCAP_ADC_TSY2_AD15: + req->bank_index = channel - 8; + break; + case CPCAP_ADC_BATTP_PI16: + req->bank_index = CPCAP_ADC_BATTP; + break; + case CPCAP_ADC_BATTI_PI17: + req->bank_index = CPCAP_ADC_BATTI; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cpcap_adc_read_st_die_temp(struct cpcap_adc *ddata, + int addr, int *val) +{ + int error; + + error = regmap_read(ddata->reg, addr, val); + if (error) + return error; + + *val -= 282; + *val *= 114; + *val += 25000; + + return 0; +} + +static int cpcap_adc_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct cpcap_adc *ddata = iio_priv(indio_dev); + struct cpcap_adc_request req; + int error; + + error = cpcap_adc_init_request(&req, chan->channel); + if (error) + return error; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&ddata->lock); + error = cpcap_adc_start_bank(ddata, &req); + if (error) + goto err_unlock; + error = regmap_read(ddata->reg, chan->address, val); + if (error) + goto err_unlock; + error = cpcap_adc_stop_bank(ddata); + if (error) + goto err_unlock; + mutex_unlock(&ddata->lock); + break; + case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&ddata->lock); + error = cpcap_adc_start_bank(ddata, &req); + if (error) + goto err_unlock; + if ((ddata->vendor == CPCAP_VENDOR_ST) && + (chan->channel == CPCAP_ADC_AD3)) { + error = cpcap_adc_read_st_die_temp(ddata, + chan->address, + &req.result); + if (error) + goto err_unlock; + } else { + error = cpcap_adc_read_bank_scaled(ddata, &req); + if (error) + goto err_unlock; + } + error = cpcap_adc_stop_bank(ddata); + if (error) + goto err_unlock; + mutex_unlock(&ddata->lock); + *val = req.result; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; + +err_unlock: + mutex_unlock(&ddata->lock); + dev_err(ddata->dev, "error reading ADC: %i\n", error); + + return error; +} + +static const struct iio_info cpcap_adc_info = { + .read_raw = &cpcap_adc_read, +}; + +/* + * Configuration for Motorola mapphone series such as droid 4. + * Copied from the Motorola mapphone kernel tree. + */ +static const struct cpcap_adc_ato mapphone_adc = { + .ato_in = 0x0480, + .atox_in = 0, + .adc_ps_factor_in = 0x0200, + .atox_ps_factor_in = 0, + .ato_out = 0, + .atox_out = 0, + .adc_ps_factor_out = 0, + .atox_ps_factor_out = 0, +}; + +static const struct of_device_id cpcap_adc_id_table[] = { + { + .compatible = "motorola,cpcap-adc", + }, + { + .compatible = "motorola,mapphone-cpcap-adc", + .data = &mapphone_adc, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, cpcap_adc_id_table); + +static int cpcap_adc_probe(struct platform_device *pdev) +{ + struct cpcap_adc *ddata; + struct iio_dev *indio_dev; + int error; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ddata)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed to allocate iio device\n"); + + return -ENOMEM; + } + ddata = iio_priv(indio_dev); + ddata->ato = device_get_match_data(&pdev->dev); + if (!ddata->ato) + return -ENODEV; + ddata->dev = &pdev->dev; + + mutex_init(&ddata->lock); + init_waitqueue_head(&ddata->wq_data_avail); + + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->channels = cpcap_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(cpcap_adc_channels); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &cpcap_adc_info; + + ddata->reg = dev_get_regmap(pdev->dev.parent, NULL); + if (!ddata->reg) + return -ENODEV; + + error = cpcap_get_vendor(ddata->dev, ddata->reg, &ddata->vendor); + if (error) + return error; + + platform_set_drvdata(pdev, indio_dev); + + ddata->irq = platform_get_irq_byname(pdev, "adcdone"); + if (ddata->irq < 0) + return -ENODEV; + + error = devm_request_threaded_irq(&pdev->dev, ddata->irq, NULL, + cpcap_adc_irq_thread, + IRQF_TRIGGER_NONE | IRQF_ONESHOT, + "cpcap-adc", indio_dev); + if (error) { + dev_err(&pdev->dev, "could not get irq: %i\n", + error); + + return error; + } + + error = cpcap_adc_calibrate(ddata); + if (error) + return error; + + dev_info(&pdev->dev, "CPCAP ADC device probed\n"); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static struct platform_driver cpcap_adc_driver = { + .driver = { + .name = "cpcap_adc", + .of_match_table = cpcap_adc_id_table, + }, + .probe = cpcap_adc_probe, +}; + +module_platform_driver(cpcap_adc_driver); + +MODULE_ALIAS("platform:cpcap_adc"); +MODULE_DESCRIPTION("CPCAP ADC driver"); +MODULE_AUTHOR("Tony Lindgren <tony@atomide.com"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c new file mode 100644 index 000000000..7a7a54a7e --- /dev/null +++ b/drivers/iio/adc/da9150-gpadc.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * DA9150 GPADC Driver + * + * Copyright (c) 2014 Dialog Semiconductor + * + * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> +#include <linux/mfd/da9150/core.h> +#include <linux/mfd/da9150/registers.h> + +/* Channels */ +enum da9150_gpadc_hw_channel { + DA9150_GPADC_HW_CHAN_GPIOA_2V = 0, + DA9150_GPADC_HW_CHAN_GPIOA_2V_, + DA9150_GPADC_HW_CHAN_GPIOB_2V, + DA9150_GPADC_HW_CHAN_GPIOB_2V_, + DA9150_GPADC_HW_CHAN_GPIOC_2V, + DA9150_GPADC_HW_CHAN_GPIOC_2V_, + DA9150_GPADC_HW_CHAN_GPIOD_2V, + DA9150_GPADC_HW_CHAN_GPIOD_2V_, + DA9150_GPADC_HW_CHAN_IBUS_SENSE, + DA9150_GPADC_HW_CHAN_IBUS_SENSE_, + DA9150_GPADC_HW_CHAN_VBUS_DIV, + DA9150_GPADC_HW_CHAN_VBUS_DIV_, + DA9150_GPADC_HW_CHAN_ID, + DA9150_GPADC_HW_CHAN_ID_, + DA9150_GPADC_HW_CHAN_VSYS, + DA9150_GPADC_HW_CHAN_VSYS_, + DA9150_GPADC_HW_CHAN_GPIOA_6V, + DA9150_GPADC_HW_CHAN_GPIOA_6V_, + DA9150_GPADC_HW_CHAN_GPIOB_6V, + DA9150_GPADC_HW_CHAN_GPIOB_6V_, + DA9150_GPADC_HW_CHAN_GPIOC_6V, + DA9150_GPADC_HW_CHAN_GPIOC_6V_, + DA9150_GPADC_HW_CHAN_GPIOD_6V, + DA9150_GPADC_HW_CHAN_GPIOD_6V_, + DA9150_GPADC_HW_CHAN_VBAT, + DA9150_GPADC_HW_CHAN_VBAT_, + DA9150_GPADC_HW_CHAN_TBAT, + DA9150_GPADC_HW_CHAN_TBAT_, + DA9150_GPADC_HW_CHAN_TJUNC_CORE, + DA9150_GPADC_HW_CHAN_TJUNC_CORE_, + DA9150_GPADC_HW_CHAN_TJUNC_OVP, + DA9150_GPADC_HW_CHAN_TJUNC_OVP_, +}; + +enum da9150_gpadc_channel { + DA9150_GPADC_CHAN_GPIOA = 0, + DA9150_GPADC_CHAN_GPIOB, + DA9150_GPADC_CHAN_GPIOC, + DA9150_GPADC_CHAN_GPIOD, + DA9150_GPADC_CHAN_IBUS, + DA9150_GPADC_CHAN_VBUS, + DA9150_GPADC_CHAN_VSYS, + DA9150_GPADC_CHAN_VBAT, + DA9150_GPADC_CHAN_TBAT, + DA9150_GPADC_CHAN_TJUNC_CORE, + DA9150_GPADC_CHAN_TJUNC_OVP, +}; + +/* Private data */ +struct da9150_gpadc { + struct da9150 *da9150; + struct device *dev; + + struct mutex lock; + struct completion complete; +}; + + +static irqreturn_t da9150_gpadc_irq(int irq, void *data) +{ + + struct da9150_gpadc *gpadc = data; + + complete(&gpadc->complete); + + return IRQ_HANDLED; +} + +static int da9150_gpadc_read_adc(struct da9150_gpadc *gpadc, int hw_chan) +{ + u8 result_regs[2]; + int result; + + mutex_lock(&gpadc->lock); + + /* Set channel & enable measurement */ + da9150_reg_write(gpadc->da9150, DA9150_GPADC_MAN, + (DA9150_GPADC_EN_MASK | + hw_chan << DA9150_GPADC_MUX_SHIFT)); + + /* Consume left-over completion from a previous timeout */ + try_wait_for_completion(&gpadc->complete); + + /* Check for actual completion */ + wait_for_completion_timeout(&gpadc->complete, msecs_to_jiffies(5)); + + /* Read result and status from device */ + da9150_bulk_read(gpadc->da9150, DA9150_GPADC_RES_A, 2, result_regs); + + mutex_unlock(&gpadc->lock); + + /* Check to make sure device really has completed reading */ + if (result_regs[1] & DA9150_GPADC_RUN_MASK) { + dev_err(gpadc->dev, "Timeout on channel %d of GPADC\n", + hw_chan); + return -ETIMEDOUT; + } + + /* LSBs - 2 bits */ + result = (result_regs[1] & DA9150_GPADC_RES_L_MASK) >> + DA9150_GPADC_RES_L_SHIFT; + /* MSBs - 8 bits */ + result |= result_regs[0] << DA9150_GPADC_RES_L_BITS; + + return result; +} + +static inline int da9150_gpadc_gpio_6v_voltage_now(int raw_val) +{ + /* Convert to mV */ + return (6 * ((raw_val * 1000) + 500)) / 1024; +} + +static inline int da9150_gpadc_ibus_current_avg(int raw_val) +{ + /* Convert to mA */ + return (4 * ((raw_val * 1000) + 500)) / 2048; +} + +static inline int da9150_gpadc_vbus_21v_voltage_now(int raw_val) +{ + /* Convert to mV */ + return (21 * ((raw_val * 1000) + 500)) / 1024; +} + +static inline int da9150_gpadc_vsys_6v_voltage_now(int raw_val) +{ + /* Convert to mV */ + return (3 * ((raw_val * 1000) + 500)) / 512; +} + +static int da9150_gpadc_read_processed(struct da9150_gpadc *gpadc, int channel, + int hw_chan, int *val) +{ + int raw_val; + + raw_val = da9150_gpadc_read_adc(gpadc, hw_chan); + if (raw_val < 0) + return raw_val; + + switch (channel) { + case DA9150_GPADC_CHAN_GPIOA: + case DA9150_GPADC_CHAN_GPIOB: + case DA9150_GPADC_CHAN_GPIOC: + case DA9150_GPADC_CHAN_GPIOD: + *val = da9150_gpadc_gpio_6v_voltage_now(raw_val); + break; + case DA9150_GPADC_CHAN_IBUS: + *val = da9150_gpadc_ibus_current_avg(raw_val); + break; + case DA9150_GPADC_CHAN_VBUS: + *val = da9150_gpadc_vbus_21v_voltage_now(raw_val); + break; + case DA9150_GPADC_CHAN_VSYS: + *val = da9150_gpadc_vsys_6v_voltage_now(raw_val); + break; + default: + /* No processing for other channels so return raw value */ + *val = raw_val; + break; + } + + return IIO_VAL_INT; +} + +static int da9150_gpadc_read_scale(int channel, int *val, int *val2) +{ + switch (channel) { + case DA9150_GPADC_CHAN_VBAT: + *val = 2932; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + case DA9150_GPADC_CHAN_TJUNC_CORE: + case DA9150_GPADC_CHAN_TJUNC_OVP: + *val = 1000000; + *val2 = 4420; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int da9150_gpadc_read_offset(int channel, int *val) +{ + switch (channel) { + case DA9150_GPADC_CHAN_VBAT: + *val = 1500000 / 2932; + return IIO_VAL_INT; + case DA9150_GPADC_CHAN_TJUNC_CORE: + case DA9150_GPADC_CHAN_TJUNC_OVP: + *val = -144; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int da9150_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct da9150_gpadc *gpadc = iio_priv(indio_dev); + + if ((chan->channel < DA9150_GPADC_CHAN_GPIOA) || + (chan->channel > DA9150_GPADC_CHAN_TJUNC_OVP)) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + return da9150_gpadc_read_processed(gpadc, chan->channel, + chan->address, val); + case IIO_CHAN_INFO_SCALE: + return da9150_gpadc_read_scale(chan->channel, val, val2); + case IIO_CHAN_INFO_OFFSET: + return da9150_gpadc_read_offset(chan->channel, val); + default: + return -EINVAL; + } +} + +static const struct iio_info da9150_gpadc_info = { + .read_raw = &da9150_gpadc_read_raw, +}; + +#define DA9150_GPADC_CHANNEL(_id, _hw_id, _type, chan_info, \ + _ext_name) { \ + .type = _type, \ + .indexed = 1, \ + .channel = DA9150_GPADC_CHAN_##_id, \ + .address = DA9150_GPADC_HW_CHAN_##_hw_id, \ + .info_mask_separate = chan_info, \ + .extend_name = _ext_name, \ + .datasheet_name = #_id, \ +} + +#define DA9150_GPADC_CHANNEL_RAW(_id, _hw_id, _type, _ext_name) \ + DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \ + BIT(IIO_CHAN_INFO_RAW), _ext_name) + +#define DA9150_GPADC_CHANNEL_SCALED(_id, _hw_id, _type, _ext_name) \ + DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \ + BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + _ext_name) + +#define DA9150_GPADC_CHANNEL_PROCESSED(_id, _hw_id, _type, _ext_name) \ + DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \ + BIT(IIO_CHAN_INFO_PROCESSED), _ext_name) + +/* Supported channels */ +static const struct iio_chan_spec da9150_gpadc_channels[] = { + DA9150_GPADC_CHANNEL_PROCESSED(GPIOA, GPIOA_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(GPIOB, GPIOB_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(GPIOC, GPIOC_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(GPIOD, GPIOD_6V, IIO_VOLTAGE, NULL), + DA9150_GPADC_CHANNEL_PROCESSED(IBUS, IBUS_SENSE, IIO_CURRENT, "ibus"), + DA9150_GPADC_CHANNEL_PROCESSED(VBUS, VBUS_DIV_, IIO_VOLTAGE, "vbus"), + DA9150_GPADC_CHANNEL_PROCESSED(VSYS, VSYS, IIO_VOLTAGE, "vsys"), + DA9150_GPADC_CHANNEL_SCALED(VBAT, VBAT, IIO_VOLTAGE, "vbat"), + DA9150_GPADC_CHANNEL_RAW(TBAT, TBAT, IIO_VOLTAGE, "tbat"), + DA9150_GPADC_CHANNEL_SCALED(TJUNC_CORE, TJUNC_CORE, IIO_TEMP, + "tjunc_core"), + DA9150_GPADC_CHANNEL_SCALED(TJUNC_OVP, TJUNC_OVP, IIO_TEMP, + "tjunc_ovp"), +}; + +/* Default maps used by da9150-charger */ +static struct iio_map da9150_gpadc_default_maps[] = { + { + .consumer_dev_name = "da9150-charger", + .consumer_channel = "CHAN_IBUS", + .adc_channel_label = "IBUS", + }, + { + .consumer_dev_name = "da9150-charger", + .consumer_channel = "CHAN_VBUS", + .adc_channel_label = "VBUS", + }, + { + .consumer_dev_name = "da9150-charger", + .consumer_channel = "CHAN_TJUNC", + .adc_channel_label = "TJUNC_CORE", + }, + { + .consumer_dev_name = "da9150-charger", + .consumer_channel = "CHAN_VBAT", + .adc_channel_label = "VBAT", + }, + {}, +}; + +static int da9150_gpadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct da9150 *da9150 = dev_get_drvdata(dev->parent); + struct da9150_gpadc *gpadc; + struct iio_dev *indio_dev; + int irq, ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc)); + if (!indio_dev) { + dev_err(&pdev->dev, "Failed to allocate IIO device\n"); + return -ENOMEM; + } + gpadc = iio_priv(indio_dev); + + platform_set_drvdata(pdev, indio_dev); + gpadc->da9150 = da9150; + gpadc->dev = dev; + mutex_init(&gpadc->lock); + init_completion(&gpadc->complete); + + irq = platform_get_irq_byname(pdev, "GPADC"); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, da9150_gpadc_irq, + IRQF_ONESHOT, "GPADC", gpadc); + if (ret) { + dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret); + return ret; + } + + ret = iio_map_array_register(indio_dev, da9150_gpadc_default_maps); + if (ret) { + dev_err(dev, "Failed to register IIO maps: %d\n", ret); + return ret; + } + + indio_dev->name = dev_name(dev); + indio_dev->info = &da9150_gpadc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = da9150_gpadc_channels; + indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "Failed to register IIO device: %d\n", ret); + goto iio_map_unreg; + } + + return 0; + +iio_map_unreg: + iio_map_array_unregister(indio_dev); + + return ret; +} + +static int da9150_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + + return 0; +} + +static struct platform_driver da9150_gpadc_driver = { + .driver = { + .name = "da9150-gpadc", + }, + .probe = da9150_gpadc_probe, + .remove = da9150_gpadc_remove, +}; + +module_platform_driver(da9150_gpadc_driver); + +MODULE_DESCRIPTION("GPADC Driver for DA9150"); +MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c new file mode 100644 index 000000000..58b3f6065 --- /dev/null +++ b/drivers/iio/adc/dln2-adc.c @@ -0,0 +1,713 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the Diolan DLN-2 USB-ADC adapter + * + * Copyright (c) 2017 Jack Andersen + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mfd/dln2.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/buffer.h> +#include <linux/iio/kfifo_buf.h> + +#define DLN2_ADC_MOD_NAME "dln2-adc" + +#define DLN2_ADC_ID 0x06 + +#define DLN2_ADC_GET_CHANNEL_COUNT DLN2_CMD(0x01, DLN2_ADC_ID) +#define DLN2_ADC_ENABLE DLN2_CMD(0x02, DLN2_ADC_ID) +#define DLN2_ADC_DISABLE DLN2_CMD(0x03, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_ENABLE DLN2_CMD(0x05, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_DISABLE DLN2_CMD(0x06, DLN2_ADC_ID) +#define DLN2_ADC_SET_RESOLUTION DLN2_CMD(0x08, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_GET_VAL DLN2_CMD(0x0A, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_GET_ALL_VAL DLN2_CMD(0x0B, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_SET_CFG DLN2_CMD(0x0C, DLN2_ADC_ID) +#define DLN2_ADC_CHANNEL_GET_CFG DLN2_CMD(0x0D, DLN2_ADC_ID) +#define DLN2_ADC_CONDITION_MET_EV DLN2_CMD(0x10, DLN2_ADC_ID) + +#define DLN2_ADC_EVENT_NONE 0 +#define DLN2_ADC_EVENT_BELOW 1 +#define DLN2_ADC_EVENT_LEVEL_ABOVE 2 +#define DLN2_ADC_EVENT_OUTSIDE 3 +#define DLN2_ADC_EVENT_INSIDE 4 +#define DLN2_ADC_EVENT_ALWAYS 5 + +#define DLN2_ADC_MAX_CHANNELS 8 +#define DLN2_ADC_DATA_BITS 10 + +/* + * Plays similar role to iio_demux_table in subsystem core; except allocated + * in a fixed 8-element array. + */ +struct dln2_adc_demux_table { + unsigned int from; + unsigned int to; + unsigned int length; +}; + +struct dln2_adc { + struct platform_device *pdev; + struct iio_chan_spec iio_channels[DLN2_ADC_MAX_CHANNELS + 1]; + int port, trigger_chan; + struct iio_trigger *trig; + struct mutex mutex; + /* Cached sample period in milliseconds */ + unsigned int sample_period; + /* Demux table */ + unsigned int demux_count; + struct dln2_adc_demux_table demux[DLN2_ADC_MAX_CHANNELS]; + /* Precomputed timestamp padding offset and length */ + unsigned int ts_pad_offset, ts_pad_length; +}; + +struct dln2_adc_port_chan { + u8 port; + u8 chan; +}; + +struct dln2_adc_get_all_vals { + __le16 channel_mask; + __le16 values[DLN2_ADC_MAX_CHANNELS]; +}; + +static void dln2_adc_add_demux(struct dln2_adc *dln2, + unsigned int in_loc, unsigned int out_loc, + unsigned int length) +{ + struct dln2_adc_demux_table *p = dln2->demux_count ? + &dln2->demux[dln2->demux_count - 1] : NULL; + + if (p && p->from + p->length == in_loc && + p->to + p->length == out_loc) { + p->length += length; + } else if (dln2->demux_count < DLN2_ADC_MAX_CHANNELS) { + p = &dln2->demux[dln2->demux_count++]; + p->from = in_loc; + p->to = out_loc; + p->length = length; + } +} + +static void dln2_adc_update_demux(struct dln2_adc *dln2) +{ + int in_ind = -1, out_ind; + unsigned int in_loc = 0, out_loc = 0; + struct iio_dev *indio_dev = platform_get_drvdata(dln2->pdev); + + /* Clear out any old demux */ + dln2->demux_count = 0; + + /* Optimize all 8-channels case */ + if (indio_dev->masklength && + (*indio_dev->active_scan_mask & 0xff) == 0xff) { + dln2_adc_add_demux(dln2, 0, 0, 16); + dln2->ts_pad_offset = 0; + dln2->ts_pad_length = 0; + return; + } + + /* Build demux table from fixed 8-channels to active_scan_mask */ + for_each_set_bit(out_ind, + indio_dev->active_scan_mask, + indio_dev->masklength) { + /* Handle timestamp separately */ + if (out_ind == DLN2_ADC_MAX_CHANNELS) + break; + for (++in_ind; in_ind != out_ind; ++in_ind) + in_loc += 2; + dln2_adc_add_demux(dln2, in_loc, out_loc, 2); + out_loc += 2; + in_loc += 2; + } + + if (indio_dev->scan_timestamp) { + size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1; + + dln2->ts_pad_offset = out_loc; + dln2->ts_pad_length = ts_offset * sizeof(int64_t) - out_loc; + } else { + dln2->ts_pad_offset = 0; + dln2->ts_pad_length = 0; + } +} + +static int dln2_adc_get_chan_count(struct dln2_adc *dln2) +{ + int ret; + u8 port = dln2->port; + u8 count; + int olen = sizeof(count); + + ret = dln2_transfer(dln2->pdev, DLN2_ADC_GET_CHANNEL_COUNT, + &port, sizeof(port), &count, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + return ret; + } + if (olen < sizeof(count)) + return -EPROTO; + + return count; +} + +static int dln2_adc_set_port_resolution(struct dln2_adc *dln2) +{ + int ret; + struct dln2_adc_port_chan port_chan = { + .port = dln2->port, + .chan = DLN2_ADC_DATA_BITS, + }; + + ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_SET_RESOLUTION, + &port_chan, sizeof(port_chan)); + if (ret < 0) + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + + return ret; +} + +static int dln2_adc_set_chan_enabled(struct dln2_adc *dln2, + int channel, bool enable) +{ + int ret; + struct dln2_adc_port_chan port_chan = { + .port = dln2->port, + .chan = channel, + }; + u16 cmd = enable ? DLN2_ADC_CHANNEL_ENABLE : DLN2_ADC_CHANNEL_DISABLE; + + ret = dln2_transfer_tx(dln2->pdev, cmd, &port_chan, sizeof(port_chan)); + if (ret < 0) + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + + return ret; +} + +static int dln2_adc_set_port_enabled(struct dln2_adc *dln2, bool enable, + u16 *conflict_out) +{ + int ret; + u8 port = dln2->port; + __le16 conflict; + int olen = sizeof(conflict); + u16 cmd = enable ? DLN2_ADC_ENABLE : DLN2_ADC_DISABLE; + + if (conflict_out) + *conflict_out = 0; + + ret = dln2_transfer(dln2->pdev, cmd, &port, sizeof(port), + &conflict, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s(%d)\n", + __func__, (int)enable); + if (conflict_out && enable && olen >= sizeof(conflict)) + *conflict_out = le16_to_cpu(conflict); + return ret; + } + if (enable && olen < sizeof(conflict)) + return -EPROTO; + + return ret; +} + +static int dln2_adc_set_chan_period(struct dln2_adc *dln2, + unsigned int channel, unsigned int period) +{ + int ret; + struct { + struct dln2_adc_port_chan port_chan; + __u8 type; + __le16 period; + __le16 low; + __le16 high; + } __packed set_cfg = { + .port_chan.port = dln2->port, + .port_chan.chan = channel, + .type = period ? DLN2_ADC_EVENT_ALWAYS : DLN2_ADC_EVENT_NONE, + .period = cpu_to_le16(period) + }; + + ret = dln2_transfer_tx(dln2->pdev, DLN2_ADC_CHANNEL_SET_CFG, + &set_cfg, sizeof(set_cfg)); + if (ret < 0) + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + + return ret; +} + +static int dln2_adc_read(struct dln2_adc *dln2, unsigned int channel) +{ + int ret, i; + u16 conflict; + __le16 value; + int olen = sizeof(value); + struct dln2_adc_port_chan port_chan = { + .port = dln2->port, + .chan = channel, + }; + + ret = dln2_adc_set_chan_enabled(dln2, channel, true); + if (ret < 0) + return ret; + + ret = dln2_adc_set_port_enabled(dln2, true, &conflict); + if (ret < 0) { + if (conflict) { + dev_err(&dln2->pdev->dev, + "ADC pins conflict with mask %04X\n", + (int)conflict); + ret = -EBUSY; + } + goto disable_chan; + } + + /* + * Call GET_VAL twice due to initial zero-return immediately after + * enabling channel. + */ + for (i = 0; i < 2; ++i) { + ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_VAL, + &port_chan, sizeof(port_chan), + &value, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + goto disable_port; + } + if (olen < sizeof(value)) { + ret = -EPROTO; + goto disable_port; + } + } + + ret = le16_to_cpu(value); + +disable_port: + dln2_adc_set_port_enabled(dln2, false, NULL); +disable_chan: + dln2_adc_set_chan_enabled(dln2, channel, false); + + return ret; +} + +static int dln2_adc_read_all(struct dln2_adc *dln2, + struct dln2_adc_get_all_vals *get_all_vals) +{ + int ret; + __u8 port = dln2->port; + int olen = sizeof(*get_all_vals); + + ret = dln2_transfer(dln2->pdev, DLN2_ADC_CHANNEL_GET_ALL_VAL, + &port, sizeof(port), get_all_vals, &olen); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + return ret; + } + if (olen < sizeof(*get_all_vals)) + return -EPROTO; + + return ret; +} + +static int dln2_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + int ret; + unsigned int microhertz; + struct dln2_adc *dln2 = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret < 0) + return ret; + + mutex_lock(&dln2->mutex); + ret = dln2_adc_read(dln2, chan->channel); + mutex_unlock(&dln2->mutex); + + iio_device_release_direct_mode(indio_dev); + + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + /* + * Voltage reference is fixed at 3.3v + * 3.3 / (1 << 10) * 1000000000 + */ + *val = 0; + *val2 = 3222656; + return IIO_VAL_INT_PLUS_NANO; + + case IIO_CHAN_INFO_SAMP_FREQ: + if (dln2->sample_period) { + microhertz = 1000000000 / dln2->sample_period; + *val = microhertz / 1000000; + *val2 = microhertz % 1000000; + } else { + *val = 0; + *val2 = 0; + } + + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int dln2_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + int ret; + unsigned int microhertz; + struct dln2_adc *dln2 = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + microhertz = 1000000 * val + val2; + + mutex_lock(&dln2->mutex); + + dln2->sample_period = + microhertz ? 1000000000 / microhertz : UINT_MAX; + if (dln2->sample_period > 65535) { + dln2->sample_period = 65535; + dev_warn(&dln2->pdev->dev, + "clamping period to 65535ms\n"); + } + + /* + * The first requested channel is arbitrated as a shared + * trigger source, so only one event is registered with the + * DLN. The event handler will then read all enabled channel + * values using DLN2_ADC_CHANNEL_GET_ALL_VAL to maintain + * synchronization between ADC readings. + */ + if (dln2->trigger_chan != -1) + ret = dln2_adc_set_chan_period(dln2, + dln2->trigger_chan, dln2->sample_period); + else + ret = 0; + + mutex_unlock(&dln2->mutex); + + return ret; + + default: + return -EINVAL; + } +} + +static int dln2_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct dln2_adc *dln2 = iio_priv(indio_dev); + int chan_count = indio_dev->num_channels - 1; + int ret, i, j; + + mutex_lock(&dln2->mutex); + + for (i = 0; i < chan_count; ++i) { + ret = dln2_adc_set_chan_enabled(dln2, i, + test_bit(i, scan_mask)); + if (ret < 0) { + for (j = 0; j < i; ++j) + dln2_adc_set_chan_enabled(dln2, j, false); + mutex_unlock(&dln2->mutex); + dev_err(&dln2->pdev->dev, + "Unable to enable ADC channel %d\n", i); + return -EBUSY; + } + } + + dln2_adc_update_demux(dln2); + + mutex_unlock(&dln2->mutex); + + return 0; +} + +#define DLN2_ADC_CHAN(lval, idx) { \ + lval.type = IIO_VOLTAGE; \ + lval.channel = idx; \ + lval.indexed = 1; \ + lval.info_mask_separate = BIT(IIO_CHAN_INFO_RAW); \ + lval.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ); \ + lval.scan_index = idx; \ + lval.scan_type.sign = 'u'; \ + lval.scan_type.realbits = DLN2_ADC_DATA_BITS; \ + lval.scan_type.storagebits = 16; \ + lval.scan_type.endianness = IIO_LE; \ +} + +/* Assignment version of IIO_CHAN_SOFT_TIMESTAMP */ +#define IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(lval, _si) { \ + lval.type = IIO_TIMESTAMP; \ + lval.channel = -1; \ + lval.scan_index = _si; \ + lval.scan_type.sign = 's'; \ + lval.scan_type.realbits = 64; \ + lval.scan_type.storagebits = 64; \ +} + +static const struct iio_info dln2_adc_info = { + .read_raw = dln2_adc_read_raw, + .write_raw = dln2_adc_write_raw, + .update_scan_mode = dln2_update_scan_mode, +}; + +static irqreturn_t dln2_adc_trigger_h(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct { + __le16 values[DLN2_ADC_MAX_CHANNELS]; + int64_t timestamp_space; + } data; + struct dln2_adc_get_all_vals dev_data; + struct dln2_adc *dln2 = iio_priv(indio_dev); + const struct dln2_adc_demux_table *t; + int ret, i; + + mutex_lock(&dln2->mutex); + ret = dln2_adc_read_all(dln2, &dev_data); + mutex_unlock(&dln2->mutex); + if (ret < 0) + goto done; + + /* Demux operation */ + for (i = 0; i < dln2->demux_count; ++i) { + t = &dln2->demux[i]; + memcpy((void *)data.values + t->to, + (void *)dev_data.values + t->from, t->length); + } + + /* Zero padding space between values and timestamp */ + if (dln2->ts_pad_length) + memset((void *)data.values + dln2->ts_pad_offset, + 0, dln2->ts_pad_length); + + iio_push_to_buffers_with_timestamp(indio_dev, &data, + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int dln2_adc_triggered_buffer_postenable(struct iio_dev *indio_dev) +{ + int ret; + struct dln2_adc *dln2 = iio_priv(indio_dev); + u16 conflict; + unsigned int trigger_chan; + + mutex_lock(&dln2->mutex); + + /* Enable ADC */ + ret = dln2_adc_set_port_enabled(dln2, true, &conflict); + if (ret < 0) { + mutex_unlock(&dln2->mutex); + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + if (conflict) { + dev_err(&dln2->pdev->dev, + "ADC pins conflict with mask %04X\n", + (int)conflict); + ret = -EBUSY; + } + return ret; + } + + /* Assign trigger channel based on first enabled channel */ + trigger_chan = find_first_bit(indio_dev->active_scan_mask, + indio_dev->masklength); + if (trigger_chan < DLN2_ADC_MAX_CHANNELS) { + dln2->trigger_chan = trigger_chan; + ret = dln2_adc_set_chan_period(dln2, dln2->trigger_chan, + dln2->sample_period); + mutex_unlock(&dln2->mutex); + if (ret < 0) { + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + return ret; + } + } else { + dln2->trigger_chan = -1; + mutex_unlock(&dln2->mutex); + } + + return 0; +} + +static int dln2_adc_triggered_buffer_predisable(struct iio_dev *indio_dev) +{ + int ret; + struct dln2_adc *dln2 = iio_priv(indio_dev); + + mutex_lock(&dln2->mutex); + + /* Disable trigger channel */ + if (dln2->trigger_chan != -1) { + dln2_adc_set_chan_period(dln2, dln2->trigger_chan, 0); + dln2->trigger_chan = -1; + } + + /* Disable ADC */ + ret = dln2_adc_set_port_enabled(dln2, false, NULL); + + mutex_unlock(&dln2->mutex); + if (ret < 0) + dev_dbg(&dln2->pdev->dev, "Problem in %s\n", __func__); + + return ret; +} + +static const struct iio_buffer_setup_ops dln2_adc_buffer_setup_ops = { + .postenable = dln2_adc_triggered_buffer_postenable, + .predisable = dln2_adc_triggered_buffer_predisable, +}; + +static void dln2_adc_event(struct platform_device *pdev, u16 echo, + const void *data, int len) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct dln2_adc *dln2 = iio_priv(indio_dev); + + /* Called via URB completion handler */ + iio_trigger_poll(dln2->trig); +} + +static int dln2_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct dln2_adc *dln2; + struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct iio_dev *indio_dev; + int i, ret, chans; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2)); + if (!indio_dev) { + dev_err(dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + dln2 = iio_priv(indio_dev); + dln2->pdev = pdev; + dln2->port = pdata->port; + dln2->trigger_chan = -1; + mutex_init(&dln2->mutex); + + platform_set_drvdata(pdev, indio_dev); + + ret = dln2_adc_set_port_resolution(dln2); + if (ret < 0) { + dev_err(dev, "failed to set ADC resolution to 10 bits\n"); + return ret; + } + + chans = dln2_adc_get_chan_count(dln2); + if (chans < 0) { + dev_err(dev, "failed to get channel count: %d\n", chans); + return chans; + } + if (chans > DLN2_ADC_MAX_CHANNELS) { + chans = DLN2_ADC_MAX_CHANNELS; + dev_warn(dev, "clamping channels to %d\n", + DLN2_ADC_MAX_CHANNELS); + } + + for (i = 0; i < chans; ++i) + DLN2_ADC_CHAN(dln2->iio_channels[i], i) + IIO_CHAN_SOFT_TIMESTAMP_ASSIGN(dln2->iio_channels[i], i); + + indio_dev->name = DLN2_ADC_MOD_NAME; + indio_dev->info = &dln2_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = dln2->iio_channels; + indio_dev->num_channels = chans + 1; + indio_dev->setup_ops = &dln2_adc_buffer_setup_ops; + + dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + if (!dln2->trig) { + dev_err(dev, "failed to allocate trigger\n"); + return -ENOMEM; + } + iio_trigger_set_drvdata(dln2->trig, dln2); + ret = devm_iio_trigger_register(dev, dln2->trig); + if (ret) { + dev_err(dev, "failed to register trigger: %d\n", ret); + return ret; + } + iio_trigger_set_immutable(indio_dev, dln2->trig); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + dln2_adc_trigger_h, + &dln2_adc_buffer_setup_ops); + if (ret) { + dev_err(dev, "failed to allocate triggered buffer: %d\n", ret); + return ret; + } + + ret = dln2_register_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV, + dln2_adc_event); + if (ret) { + dev_err(dev, "failed to setup DLN2 periodic event: %d\n", ret); + return ret; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "failed to register iio device: %d\n", ret); + goto unregister_event; + } + + return ret; + +unregister_event: + dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV); + + return ret; +} + +static int dln2_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + dln2_unregister_event_cb(pdev, DLN2_ADC_CONDITION_MET_EV); + return 0; +} + +static struct platform_driver dln2_adc_driver = { + .driver.name = DLN2_ADC_MOD_NAME, + .probe = dln2_adc_probe, + .remove = dln2_adc_remove, +}; + +module_platform_driver(dln2_adc_driver); + +MODULE_AUTHOR("Jack Andersen <jackoalan@gmail.com"); +MODULE_DESCRIPTION("Driver for the Diolan DLN2 ADC interface"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dln2-adc"); diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c new file mode 100644 index 000000000..d73eac361 --- /dev/null +++ b/drivers/iio/adc/envelope-detector.c @@ -0,0 +1,409 @@ +// 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> + */ + +/* + * The DAC is used to find the peak level of an alternating voltage input + * signal by a binary search using the output of a comparator wired to + * an interrupt pin. Like so: + * _ + * | \ + * input +------>-------|+ \ + * | \ + * .-------. | }---. + * | | | / | + * | dac|-->--|- / | + * | | |_/ | + * | | | + * | | | + * | irq|------<-------' + * | | + * '-------' + */ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/iio/consumer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> + +struct envelope { + spinlock_t comp_lock; /* protects comp */ + int comp; + + struct mutex read_lock; /* protects everything else */ + + int comp_irq; + u32 comp_irq_trigger; + u32 comp_irq_trigger_inv; + + struct iio_channel *dac; + struct delayed_work comp_timeout; + + unsigned int comp_interval; + bool invert; + u32 dac_max; + + int high; + int level; + int low; + + struct completion done; +}; + +/* + * The envelope_detector_comp_latch function works together with the compare + * interrupt service routine below (envelope_detector_comp_isr) as a latch + * (one-bit memory) for if the interrupt has triggered since last calling + * this function. + * The ..._comp_isr function disables the interrupt so that the cpu does not + * need to service a possible interrupt flood from the comparator when no-one + * cares anyway, and this ..._comp_latch function reenables them again if + * needed. + */ +static int envelope_detector_comp_latch(struct envelope *env) +{ + int comp; + + spin_lock_irq(&env->comp_lock); + comp = env->comp; + env->comp = 0; + spin_unlock_irq(&env->comp_lock); + + if (!comp) + return 0; + + /* + * The irq was disabled, and is reenabled just now. + * But there might have been a pending irq that + * happened while the irq was disabled that fires + * just as the irq is reenabled. That is not what + * is desired. + */ + enable_irq(env->comp_irq); + + /* So, synchronize this possibly pending irq... */ + synchronize_irq(env->comp_irq); + + /* ...and redo the whole dance. */ + spin_lock_irq(&env->comp_lock); + comp = env->comp; + env->comp = 0; + spin_unlock_irq(&env->comp_lock); + + if (comp) + enable_irq(env->comp_irq); + + return 1; +} + +static irqreturn_t envelope_detector_comp_isr(int irq, void *ctx) +{ + struct envelope *env = ctx; + + spin_lock(&env->comp_lock); + env->comp = 1; + disable_irq_nosync(env->comp_irq); + spin_unlock(&env->comp_lock); + + return IRQ_HANDLED; +} + +static void envelope_detector_setup_compare(struct envelope *env) +{ + int ret; + + /* + * Do a binary search for the peak input level, and stop + * when that level is "trapped" between two adjacent DAC + * values. + * When invert is active, use the midpoint floor so that + * env->level ends up as env->low when the termination + * criteria below is fulfilled, and use the midpoint + * ceiling when invert is not active so that env->level + * ends up as env->high in that case. + */ + env->level = (env->high + env->low + !env->invert) / 2; + + if (env->high == env->low + 1) { + complete(&env->done); + return; + } + + /* Set a "safe" DAC level (if there is such a thing)... */ + ret = iio_write_channel_raw(env->dac, env->invert ? 0 : env->dac_max); + if (ret < 0) + goto err; + + /* ...clear the comparison result... */ + envelope_detector_comp_latch(env); + + /* ...set the real DAC level... */ + ret = iio_write_channel_raw(env->dac, env->level); + if (ret < 0) + goto err; + + /* ...and wait for a bit to see if the latch catches anything. */ + schedule_delayed_work(&env->comp_timeout, + msecs_to_jiffies(env->comp_interval)); + return; + +err: + env->level = ret; + complete(&env->done); +} + +static void envelope_detector_timeout(struct work_struct *work) +{ + struct envelope *env = container_of(work, struct envelope, + comp_timeout.work); + + /* Adjust low/high depending on the latch content... */ + if (!envelope_detector_comp_latch(env) ^ !env->invert) + env->low = env->level; + else + env->high = env->level; + + /* ...and continue the search. */ + envelope_detector_setup_compare(env); +} + +static int envelope_detector_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct envelope *env = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* + * When invert is active, start with high=max+1 and low=0 + * since we will end up with the low value when the + * termination criteria is fulfilled (rounding down). And + * start with high=max and low=-1 when invert is not active + * since we will end up with the high value in that case. + * This ensures that the returned value in both cases are + * in the same range as the DAC and is a value that has not + * triggered the comparator. + */ + mutex_lock(&env->read_lock); + env->high = env->dac_max + env->invert; + env->low = -1 + env->invert; + envelope_detector_setup_compare(env); + wait_for_completion(&env->done); + if (env->level < 0) { + ret = env->level; + goto err_unlock; + } + *val = env->invert ? env->dac_max - env->level : env->level; + mutex_unlock(&env->read_lock); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return iio_read_channel_scale(env->dac, val, val2); + } + + return -EINVAL; + +err_unlock: + mutex_unlock(&env->read_lock); + return ret; +} + +static ssize_t envelope_show_invert(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, char *buf) +{ + struct envelope *env = iio_priv(indio_dev); + + return sprintf(buf, "%u\n", env->invert); +} + +static ssize_t envelope_store_invert(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, + const char *buf, size_t len) +{ + struct envelope *env = iio_priv(indio_dev); + unsigned long invert; + int ret; + u32 trigger; + + ret = kstrtoul(buf, 0, &invert); + if (ret < 0) + return ret; + if (invert > 1) + return -EINVAL; + + trigger = invert ? env->comp_irq_trigger_inv : env->comp_irq_trigger; + + mutex_lock(&env->read_lock); + if (invert != env->invert) + ret = irq_set_irq_type(env->comp_irq, trigger); + if (!ret) { + env->invert = invert; + ret = len; + } + mutex_unlock(&env->read_lock); + + return ret; +} + +static ssize_t envelope_show_comp_interval(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, + char *buf) +{ + struct envelope *env = iio_priv(indio_dev); + + return sprintf(buf, "%u\n", env->comp_interval); +} + +static ssize_t envelope_store_comp_interval(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, + const char *buf, size_t len) +{ + struct envelope *env = iio_priv(indio_dev); + unsigned long interval; + int ret; + + ret = kstrtoul(buf, 0, &interval); + if (ret < 0) + return ret; + if (interval > 1000) + return -EINVAL; + + mutex_lock(&env->read_lock); + env->comp_interval = interval; + mutex_unlock(&env->read_lock); + + return len; +} + +static const struct iio_chan_spec_ext_info envelope_detector_ext_info[] = { + { .name = "invert", + .read = envelope_show_invert, + .write = envelope_store_invert, }, + { .name = "compare_interval", + .read = envelope_show_comp_interval, + .write = envelope_store_comp_interval, }, + { /* sentinel */ } +}; + +static const struct iio_chan_spec envelope_detector_iio_channel = { + .type = IIO_ALTVOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + .ext_info = envelope_detector_ext_info, + .indexed = 1, +}; + +static const struct iio_info envelope_detector_info = { + .read_raw = &envelope_detector_read_raw, +}; + +static int envelope_detector_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct envelope *env; + enum iio_chan_type type; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*env)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + env = iio_priv(indio_dev); + env->comp_interval = 50; /* some sensible default? */ + + spin_lock_init(&env->comp_lock); + mutex_init(&env->read_lock); + init_completion(&env->done); + INIT_DELAYED_WORK(&env->comp_timeout, envelope_detector_timeout); + + indio_dev->name = dev_name(dev); + indio_dev->info = &envelope_detector_info; + indio_dev->channels = &envelope_detector_iio_channel; + indio_dev->num_channels = 1; + + env->dac = devm_iio_channel_get(dev, "dac"); + if (IS_ERR(env->dac)) + return dev_err_probe(dev, PTR_ERR(env->dac), + "failed to get dac input channel\n"); + + env->comp_irq = platform_get_irq_byname(pdev, "comp"); + 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); + if (ret) + return dev_err_probe(dev, ret, "failed to request interrupt\n"); + + env->comp_irq_trigger = irq_get_trigger_type(env->comp_irq); + if (env->comp_irq_trigger & IRQF_TRIGGER_RISING) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_FALLING; + if (env->comp_irq_trigger & IRQF_TRIGGER_FALLING) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_RISING; + if (env->comp_irq_trigger & IRQF_TRIGGER_HIGH) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_LOW; + if (env->comp_irq_trigger & IRQF_TRIGGER_LOW) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_HIGH; + + ret = iio_get_channel_type(env->dac, &type); + if (ret < 0) + return ret; + + if (type != IIO_VOLTAGE) { + dev_err(dev, "dac is of the wrong type\n"); + return -EINVAL; + } + + ret = iio_read_max_channel_raw(env->dac, &env->dac_max); + if (ret < 0) { + dev_err(dev, "dac does not indicate its raw maximum value\n"); + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id envelope_detector_match[] = { + { .compatible = "axentia,tse850-envelope-detector", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, envelope_detector_match); + +static struct platform_driver envelope_detector_driver = { + .probe = envelope_detector_probe, + .driver = { + .name = "iio-envelope-detector", + .of_match_table = envelope_detector_match, + }, +}; +module_platform_driver(envelope_detector_driver); + +MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator"); +MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c new file mode 100644 index 000000000..c08ab3c6d --- /dev/null +++ b/drivers/iio/adc/ep93xx_adc.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for ADC module on the Cirrus Logic EP93xx series of SoCs + * + * Copyright (C) 2015 Alexander Sverdlin + * + * 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 + * long enough so that maximum allowed conversion rate is not exceeded. This + * basically renders IRQ mode unusable. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/iio/iio.h> +#include <linux/io.h> +#include <linux/irqflags.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> + +/* + * This code could benefit from real HR Timers, but jiffy granularity would + * lower ADC conversion rate down to CONFIG_HZ, so we fallback to busy wait + * in such case. + * + * HR Timers-based version loads CPU only up to 10% during back to back ADC + * conversion, while busy wait-based version consumes whole CPU power. + */ +#ifdef CONFIG_HIGH_RES_TIMERS +#define ep93xx_adc_delay(usmin, usmax) usleep_range(usmin, usmax) +#else +#define ep93xx_adc_delay(usmin, usmax) udelay(usmin) +#endif + +#define EP93XX_ADC_RESULT 0x08 +#define EP93XX_ADC_SDR BIT(31) +#define EP93XX_ADC_SWITCH 0x18 +#define EP93XX_ADC_SW_LOCK 0x20 + +struct ep93xx_adc_priv { + struct clk *clk; + void __iomem *base; + int lastch; + struct mutex lock; +}; + +#define EP93XX_ADC_CH(index, dname, swcfg) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .address = swcfg, \ + .datasheet_name = dname, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +} + +/* + * Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets. + * EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is + * not defined. So the last three are numbered randomly, let's say. + */ +static const struct iio_chan_spec ep93xx_adc_channels[8] = { + EP93XX_ADC_CH(0, "YM", 0x608), + EP93XX_ADC_CH(1, "SXP", 0x680), + EP93XX_ADC_CH(2, "SXM", 0x640), + EP93XX_ADC_CH(3, "SYP", 0x620), + EP93XX_ADC_CH(4, "SYM", 0x610), + EP93XX_ADC_CH(5, "XP", 0x601), + EP93XX_ADC_CH(6, "XM", 0x602), + EP93XX_ADC_CH(7, "YP", 0x604), +}; + +static int ep93xx_read_raw(struct iio_dev *iiodev, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct ep93xx_adc_priv *priv = iio_priv(iiodev); + unsigned long timeout; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&priv->lock); + if (priv->lastch != channel->channel) { + priv->lastch = channel->channel; + /* + * Switch register is software-locked, unlocking must be + * immediately followed by write + */ + local_irq_disable(); + writel_relaxed(0xAA, priv->base + EP93XX_ADC_SW_LOCK); + writel_relaxed(channel->address, + priv->base + EP93XX_ADC_SWITCH); + local_irq_enable(); + /* + * Settling delay depends on module clock and could be + * 2ms or 500us + */ + ep93xx_adc_delay(2000, 2000); + } + /* Start the conversion, eventually discarding old result */ + readl_relaxed(priv->base + EP93XX_ADC_RESULT); + /* Ensure maximum conversion rate is not exceeded */ + ep93xx_adc_delay(DIV_ROUND_UP(1000000, 925), + DIV_ROUND_UP(1000000, 925)); + /* At this point conversion must be completed, but anyway... */ + ret = IIO_VAL_INT; + timeout = jiffies + msecs_to_jiffies(1) + 1; + while (1) { + u32 t; + + t = readl_relaxed(priv->base + EP93XX_ADC_RESULT); + if (t & EP93XX_ADC_SDR) { + *value = sign_extend32(t, 15); + break; + } + + if (time_after(jiffies, timeout)) { + dev_err(&iiodev->dev, "Conversion timeout\n"); + ret = -ETIMEDOUT; + break; + } + + cpu_relax(); + } + mutex_unlock(&priv->lock); + return ret; + + case IIO_CHAN_INFO_OFFSET: + /* According to datasheet, range is -25000..25000 */ + *value = 25000; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + /* Typical supply voltage is 3.3v */ + *value = (1ULL << 32) * 3300 / 50000; + *shift = 32; + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info ep93xx_adc_info = { + .read_raw = ep93xx_read_raw, +}; + +static int ep93xx_adc_probe(struct platform_device *pdev) +{ + int ret; + struct iio_dev *iiodev; + struct ep93xx_adc_priv *priv; + struct clk *pclk; + struct resource *res; + + iiodev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!iiodev) + return -ENOMEM; + priv = iio_priv(iiodev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) { + dev_err(&pdev->dev, "Cannot map memory resource\n"); + return PTR_ERR(priv->base); + } + + iiodev->name = dev_name(&pdev->dev); + iiodev->modes = INDIO_DIRECT_MODE; + iiodev->info = &ep93xx_adc_info; + iiodev->num_channels = ARRAY_SIZE(ep93xx_adc_channels); + iiodev->channels = ep93xx_adc_channels; + + priv->lastch = -1; + mutex_init(&priv->lock); + + platform_set_drvdata(pdev, iiodev); + + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(&pdev->dev, "Cannot obtain clock\n"); + return PTR_ERR(priv->clk); + } + + pclk = clk_get_parent(priv->clk); + if (!pclk) { + dev_warn(&pdev->dev, "Cannot obtain parent clock\n"); + } else { + /* + * This is actually a place for improvement: + * EP93xx ADC supports two clock divisors -- 4 and 16, + * resulting in conversion rates 3750 and 925 samples per second + * with 500us or 2ms settling time respectively. + * One might find this interesting enough to be configurable. + */ + ret = clk_set_rate(priv->clk, clk_get_rate(pclk) / 16); + if (ret) + dev_warn(&pdev->dev, "Cannot set clock rate\n"); + /* + * We can tolerate rate setting failure because the module should + * work in any case. + */ + } + + ret = clk_enable(priv->clk); + if (ret) { + dev_err(&pdev->dev, "Cannot enable clock\n"); + return ret; + } + + ret = iio_device_register(iiodev); + if (ret) + clk_disable(priv->clk); + + return ret; +} + +static int ep93xx_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *iiodev = platform_get_drvdata(pdev); + struct ep93xx_adc_priv *priv = iio_priv(iiodev); + + iio_device_unregister(iiodev); + clk_disable(priv->clk); + + return 0; +} + +static struct platform_driver ep93xx_adc_driver = { + .driver = { + .name = "ep93xx-adc", + }, + .probe = ep93xx_adc_probe, + .remove = ep93xx_adc_remove, +}; +module_platform_driver(ep93xx_adc_driver); + +MODULE_AUTHOR("Alexander Sverdlin <alexander.sverdlin@gmail.com>"); +MODULE_DESCRIPTION("Cirrus Logic EP93XX ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ep93xx-adc"); diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c new file mode 100644 index 000000000..b6f09e5ac --- /dev/null +++ b/drivers/iio/adc/exynos_adc.c @@ -0,0 +1,1023 @@ +// 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> + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/regulator/consumer.h> +#include <linux/of_platform.h> +#include <linux/err.h> +#include <linux/input.h> + +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#include <linux/platform_data/touchscreen-s3c2410.h> + +/* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */ +#define ADC_V1_CON(x) ((x) + 0x00) +#define ADC_V1_TSC(x) ((x) + 0x04) +#define ADC_V1_DLY(x) ((x) + 0x08) +#define ADC_V1_DATX(x) ((x) + 0x0C) +#define ADC_V1_DATY(x) ((x) + 0x10) +#define ADC_V1_UPDN(x) ((x) + 0x14) +#define ADC_V1_INTCLR(x) ((x) + 0x18) +#define ADC_V1_MUX(x) ((x) + 0x1c) +#define ADC_V1_CLRINTPNDNUP(x) ((x) + 0x20) + +/* S3C2410 ADC registers definitions */ +#define ADC_S3C2410_MUX(x) ((x) + 0x18) + +/* Future ADC_V2 registers definitions */ +#define ADC_V2_CON1(x) ((x) + 0x00) +#define ADC_V2_CON2(x) ((x) + 0x04) +#define ADC_V2_STAT(x) ((x) + 0x08) +#define ADC_V2_INT_EN(x) ((x) + 0x10) +#define ADC_V2_INT_ST(x) ((x) + 0x14) +#define ADC_V2_VER(x) ((x) + 0x20) + +/* Bit definitions for ADC_V1 */ +#define ADC_V1_CON_RES (1u << 16) +#define ADC_V1_CON_PRSCEN (1u << 14) +#define ADC_V1_CON_PRSCLV(x) (((x) & 0xFF) << 6) +#define ADC_V1_CON_STANDBY (1u << 2) + +/* Bit definitions for S3C2410 ADC */ +#define ADC_S3C2410_CON_SELMUX(x) (((x) & 7) << 3) +#define ADC_S3C2410_DATX_MASK 0x3FF +#define ADC_S3C2416_CON_RES_SEL (1u << 3) + +/* touch screen always uses channel 0 */ +#define ADC_S3C2410_MUX_TS 0 + +/* ADCTSC Register Bits */ +#define ADC_S3C2443_TSC_UD_SEN (1u << 8) +#define ADC_S3C2410_TSC_YM_SEN (1u << 7) +#define ADC_S3C2410_TSC_YP_SEN (1u << 6) +#define ADC_S3C2410_TSC_XM_SEN (1u << 5) +#define ADC_S3C2410_TSC_XP_SEN (1u << 4) +#define ADC_S3C2410_TSC_PULL_UP_DISABLE (1u << 3) +#define ADC_S3C2410_TSC_AUTO_PST (1u << 2) +#define ADC_S3C2410_TSC_XY_PST(x) (((x) & 0x3) << 0) + +#define ADC_TSC_WAIT4INT (ADC_S3C2410_TSC_YM_SEN | \ + ADC_S3C2410_TSC_YP_SEN | \ + ADC_S3C2410_TSC_XP_SEN | \ + ADC_S3C2410_TSC_XY_PST(3)) + +#define ADC_TSC_AUTOPST (ADC_S3C2410_TSC_YM_SEN | \ + ADC_S3C2410_TSC_YP_SEN | \ + ADC_S3C2410_TSC_XP_SEN | \ + ADC_S3C2410_TSC_AUTO_PST | \ + ADC_S3C2410_TSC_XY_PST(0)) + +/* Bit definitions for ADC_V2 */ +#define ADC_V2_CON1_SOFT_RESET (1u << 2) + +#define ADC_V2_CON2_OSEL (1u << 10) +#define ADC_V2_CON2_ESEL (1u << 9) +#define ADC_V2_CON2_HIGHF (1u << 8) +#define ADC_V2_CON2_C_TIME(x) (((x) & 7) << 4) +#define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0) +#define ADC_V2_CON2_ACH_MASK 0xF + +#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) +#define ADC_CON_EN_START_MASK (0x3 << 0) +#define ADC_DATX_PRESSED (1u << 15) +#define ADC_DATX_MASK 0xFFF +#define ADC_DATY_MASK 0xFFF + +#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100)) + +#define EXYNOS_ADCV1_PHY_OFFSET 0x0718 +#define EXYNOS_ADCV2_PHY_OFFSET 0x0720 + +struct exynos_adc { + struct exynos_adc_data *data; + struct device *dev; + struct input_dev *input; + void __iomem *regs; + struct regmap *pmu_map; + struct clk *clk; + struct clk *sclk; + unsigned int irq; + unsigned int tsirq; + unsigned int delay; + struct regulator *vdd; + + struct completion completion; + + u32 value; + unsigned int version; + + bool read_ts; + u32 ts_x; + u32 ts_y; + + /* + * Lock to protect from potential concurrent access to the + * completion callback during a manual conversion. For this driver + * a wait-callback is used to wait for the conversion result, + * so in the meantime no other read request (or conversion start) + * must be performed, otherwise it would interfere with the + * current conversion result. + */ + struct mutex lock; +}; + +struct exynos_adc_data { + int num_channels; + bool needs_sclk; + bool needs_adc_phy; + int phy_offset; + u32 mask; + + void (*init_hw)(struct exynos_adc *info); + void (*exit_hw)(struct exynos_adc *info); + void (*clear_irq)(struct exynos_adc *info); + void (*start_conv)(struct exynos_adc *info, unsigned long addr); +}; + +static void exynos_adc_unprepare_clk(struct exynos_adc *info) +{ + if (info->data->needs_sclk) + clk_unprepare(info->sclk); + clk_unprepare(info->clk); +} + +static int exynos_adc_prepare_clk(struct exynos_adc *info) +{ + int ret; + + ret = clk_prepare(info->clk); + if (ret) { + dev_err(info->dev, "failed preparing adc clock: %d\n", ret); + return ret; + } + + if (info->data->needs_sclk) { + ret = clk_prepare(info->sclk); + if (ret) { + clk_unprepare(info->clk); + dev_err(info->dev, + "failed preparing sclk_adc clock: %d\n", ret); + return ret; + } + } + + return 0; +} + +static void exynos_adc_disable_clk(struct exynos_adc *info) +{ + if (info->data->needs_sclk) + clk_disable(info->sclk); + clk_disable(info->clk); +} + +static int exynos_adc_enable_clk(struct exynos_adc *info) +{ + int ret; + + ret = clk_enable(info->clk); + if (ret) { + dev_err(info->dev, "failed enabling adc clock: %d\n", ret); + return ret; + } + + if (info->data->needs_sclk) { + ret = clk_enable(info->sclk); + if (ret) { + clk_disable(info->clk); + dev_err(info->dev, + "failed enabling sclk_adc clock: %d\n", ret); + return ret; + } + } + + return 0; +} + +static void exynos_adc_v1_init_hw(struct exynos_adc *info) +{ + u32 con1; + + if (info->data->needs_adc_phy) + regmap_write(info->pmu_map, info->data->phy_offset, 1); + + /* set default prescaler values and Enable prescaler */ + con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN; + + /* Enable 12-bit ADC resolution */ + con1 |= ADC_V1_CON_RES; + writel(con1, ADC_V1_CON(info->regs)); + + /* set touchscreen delay */ + writel(info->delay, ADC_V1_DLY(info->regs)); +} + +static void exynos_adc_v1_exit_hw(struct exynos_adc *info) +{ + u32 con; + + if (info->data->needs_adc_phy) + regmap_write(info->pmu_map, info->data->phy_offset, 0); + + con = readl(ADC_V1_CON(info->regs)); + con |= ADC_V1_CON_STANDBY; + writel(con, ADC_V1_CON(info->regs)); +} + +static void exynos_adc_v1_clear_irq(struct exynos_adc *info) +{ + writel(1, ADC_V1_INTCLR(info->regs)); +} + +static void exynos_adc_v1_start_conv(struct exynos_adc *info, + unsigned long addr) +{ + u32 con1; + + writel(addr, ADC_V1_MUX(info->regs)); + + con1 = readl(ADC_V1_CON(info->regs)); + 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 */ + .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_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) +{ + u32 con1; + + /* Enable 12 bit ADC resolution */ + con1 = readl(ADC_V1_CON(info->regs)); + con1 |= ADC_S3C2416_CON_RES_SEL; + writel(con1, ADC_V1_CON(info->regs)); + + /* Select channel for S3C2416 */ + writel(addr, ADC_S3C2410_MUX(info->regs)); + + con1 = readl(ADC_V1_CON(info->regs)); + writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); +} + +static struct exynos_adc_data const exynos_adc_s3c2416_data = { + .num_channels = MAX_ADC_V1_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + + .init_hw = exynos_adc_v1_init_hw, + .exit_hw = exynos_adc_v1_exit_hw, + .start_conv = exynos_adc_s3c2416_start_conv, +}; + +static void exynos_adc_s3c2443_start_conv(struct exynos_adc *info, + unsigned long addr) +{ + u32 con1; + + /* Select channel for S3C2433 */ + writel(addr, ADC_S3C2410_MUX(info->regs)); + + con1 = readl(ADC_V1_CON(info->regs)); + writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); +} + +static struct exynos_adc_data const exynos_adc_s3c2443_data = { + .num_channels = MAX_ADC_V1_CHANNELS, + .mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */ + + .init_hw = exynos_adc_v1_init_hw, + .exit_hw = exynos_adc_v1_exit_hw, + .start_conv = exynos_adc_s3c2443_start_conv, +}; + +static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info, + unsigned long addr) +{ + u32 con1; + + con1 = readl(ADC_V1_CON(info->regs)); + con1 &= ~ADC_S3C2410_CON_SELMUX(0x7); + con1 |= ADC_S3C2410_CON_SELMUX(addr); + writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs)); +} + +static struct exynos_adc_data const exynos_adc_s3c24xx_data = { + .num_channels = MAX_ADC_V1_CHANNELS, + .mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */ + + .init_hw = exynos_adc_v1_init_hw, + .exit_hw = exynos_adc_v1_exit_hw, + .start_conv = exynos_adc_s3c64xx_start_conv, +}; + +static struct exynos_adc_data const exynos_adc_s3c64xx_data = { + .num_channels = MAX_ADC_V1_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_s3c64xx_start_conv, +}; + +static void exynos_adc_v2_init_hw(struct exynos_adc *info) +{ + u32 con1, con2; + + if (info->data->needs_adc_phy) + regmap_write(info->pmu_map, info->data->phy_offset, 1); + + con1 = ADC_V2_CON1_SOFT_RESET; + writel(con1, ADC_V2_CON1(info->regs)); + + con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL | + ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0); + writel(con2, ADC_V2_CON2(info->regs)); + + /* Enable interrupts */ + writel(1, ADC_V2_INT_EN(info->regs)); +} + +static void exynos_adc_v2_exit_hw(struct exynos_adc *info) +{ + u32 con; + + if (info->data->needs_adc_phy) + regmap_write(info->pmu_map, info->data->phy_offset, 0); + + con = readl(ADC_V2_CON1(info->regs)); + con &= ~ADC_CON_EN_START; + writel(con, ADC_V2_CON1(info->regs)); +} + +static void exynos_adc_v2_clear_irq(struct exynos_adc *info) +{ + writel(1, ADC_V2_INT_ST(info->regs)); +} + +static void exynos_adc_v2_start_conv(struct exynos_adc *info, + unsigned long addr) +{ + u32 con1, con2; + + con2 = readl(ADC_V2_CON2(info->regs)); + con2 &= ~ADC_V2_CON2_ACH_MASK; + con2 |= ADC_V2_CON2_ACH_SEL(addr); + writel(con2, ADC_V2_CON2(info->regs)); + + con1 = readl(ADC_V2_CON1(info->regs)); + writel(con1 | ADC_CON_EN_START, ADC_V2_CON1(info->regs)); +} + +static const struct exynos_adc_data exynos_adc_v2_data = { + .num_channels = MAX_ADC_V2_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + .needs_adc_phy = true, + .phy_offset = EXYNOS_ADCV2_PHY_OFFSET, + + .init_hw = exynos_adc_v2_init_hw, + .exit_hw = exynos_adc_v2_exit_hw, + .clear_irq = exynos_adc_v2_clear_irq, + .start_conv = exynos_adc_v2_start_conv, +}; + +static const struct exynos_adc_data exynos3250_adc_data = { + .num_channels = MAX_EXYNOS3250_ADC_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + .needs_sclk = true, + .needs_adc_phy = true, + .phy_offset = EXYNOS_ADCV1_PHY_OFFSET, + + .init_hw = exynos_adc_v2_init_hw, + .exit_hw = exynos_adc_v2_exit_hw, + .clear_irq = exynos_adc_v2_clear_irq, + .start_conv = exynos_adc_v2_start_conv, +}; + +static void exynos_adc_exynos7_init_hw(struct exynos_adc *info) +{ + u32 con1, con2; + + con1 = ADC_V2_CON1_SOFT_RESET; + writel(con1, ADC_V2_CON1(info->regs)); + + con2 = readl(ADC_V2_CON2(info->regs)); + con2 &= ~ADC_V2_CON2_C_TIME(7); + con2 |= ADC_V2_CON2_C_TIME(0); + writel(con2, ADC_V2_CON2(info->regs)); + + /* Enable interrupts */ + writel(1, ADC_V2_INT_EN(info->regs)); +} + +static const struct exynos_adc_data exynos7_adc_data = { + .num_channels = MAX_ADC_V1_CHANNELS, + .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */ + + .init_hw = exynos_adc_exynos7_init_hw, + .exit_hw = exynos_adc_v2_exit_hw, + .clear_irq = exynos_adc_v2_clear_irq, + .start_conv = exynos_adc_v2_start_conv, +}; + +static const struct of_device_id exynos_adc_match[] = { + { + .compatible = "samsung,s3c2410-adc", + .data = &exynos_adc_s3c24xx_data, + }, { + .compatible = "samsung,s3c2416-adc", + .data = &exynos_adc_s3c2416_data, + }, { + .compatible = "samsung,s3c2440-adc", + .data = &exynos_adc_s3c24xx_data, + }, { + .compatible = "samsung,s3c2443-adc", + .data = &exynos_adc_s3c2443_data, + }, { + .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, + }, { + .compatible = "samsung,exynos-adc-v2", + .data = &exynos_adc_v2_data, + }, { + .compatible = "samsung,exynos3250-adc", + .data = &exynos3250_adc_data, + }, { + .compatible = "samsung,exynos7-adc", + .data = &exynos7_adc_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_adc_match); + +static struct exynos_adc_data *exynos_adc_get_data(struct platform_device *pdev) +{ + const struct of_device_id *match; + + match = of_match_node(exynos_adc_match, pdev->dev.of_node); + return (struct exynos_adc_data *)match->data; +} + +static int exynos_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct exynos_adc *info = iio_priv(indio_dev); + unsigned long timeout; + int ret; + + if (mask == IIO_CHAN_INFO_SCALE) { + ret = regulator_get_voltage(info->vdd); + if (ret < 0) + return ret; + + /* Regulator voltage is in uV, but need mV */ + *val = ret / 1000; + *val2 = info->data->mask; + + return IIO_VAL_FRACTIONAL; + } else if (mask != IIO_CHAN_INFO_RAW) { + return -EINVAL; + } + + mutex_lock(&info->lock); + reinit_completion(&info->completion); + + /* Select the channel to be used and Trigger conversion */ + if (info->data->start_conv) + info->data->start_conv(info, chan->address); + + timeout = wait_for_completion_timeout(&info->completion, + EXYNOS_ADC_TIMEOUT); + if (timeout == 0) { + dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n"); + if (info->data->init_hw) + info->data->init_hw(info); + ret = -ETIMEDOUT; + } else { + *val = info->value; + *val2 = 0; + ret = IIO_VAL_INT; + } + + mutex_unlock(&info->lock); + + return ret; +} + +static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y) +{ + struct exynos_adc *info = iio_priv(indio_dev); + unsigned long timeout; + int ret; + + mutex_lock(&info->lock); + info->read_ts = true; + + reinit_completion(&info->completion); + + writel(ADC_S3C2410_TSC_PULL_UP_DISABLE | ADC_TSC_AUTOPST, + ADC_V1_TSC(info->regs)); + + /* Select the ts channel to be used and Trigger conversion */ + info->data->start_conv(info, ADC_S3C2410_MUX_TS); + + timeout = wait_for_completion_timeout(&info->completion, + EXYNOS_ADC_TIMEOUT); + if (timeout == 0) { + dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n"); + if (info->data->init_hw) + info->data->init_hw(info); + ret = -ETIMEDOUT; + } else { + *x = info->ts_x; + *y = info->ts_y; + ret = 0; + } + + info->read_ts = false; + mutex_unlock(&info->lock); + + return ret; +} + +static irqreturn_t exynos_adc_isr(int irq, void *dev_id) +{ + struct exynos_adc *info = dev_id; + u32 mask = info->data->mask; + + /* Read value */ + if (info->read_ts) { + info->ts_x = readl(ADC_V1_DATX(info->regs)); + info->ts_y = readl(ADC_V1_DATY(info->regs)); + writel(ADC_TSC_WAIT4INT | ADC_S3C2443_TSC_UD_SEN, ADC_V1_TSC(info->regs)); + } else { + info->value = readl(ADC_V1_DATX(info->regs)) & mask; + } + + /* clear irq */ + if (info->data->clear_irq) + info->data->clear_irq(info); + + complete(&info->completion); + + return IRQ_HANDLED; +} + +/* + * Here we (ab)use a threaded interrupt handler to stay running + * for as long as the touchscreen remains pressed, we report + * a new event with the latest data and then sleep until the + * next timer tick. This mirrors the behavior of the old + * driver, with much less code. + */ +static irqreturn_t exynos_ts_isr(int irq, void *dev_id) +{ + struct exynos_adc *info = dev_id; + struct iio_dev *dev = dev_get_drvdata(info->dev); + u32 x, y; + bool pressed; + int ret; + + while (info->input->users) { + ret = exynos_read_s3c64xx_ts(dev, &x, &y); + if (ret == -ETIMEDOUT) + break; + + pressed = x & y & ADC_DATX_PRESSED; + if (!pressed) { + input_report_key(info->input, BTN_TOUCH, 0); + input_sync(info->input); + break; + } + + input_report_abs(info->input, ABS_X, x & ADC_DATX_MASK); + input_report_abs(info->input, ABS_Y, y & ADC_DATY_MASK); + input_report_key(info->input, BTN_TOUCH, 1); + input_sync(info->input); + + usleep_range(1000, 1100); + } + + writel(0, ADC_V1_CLRINTPNDNUP(info->regs)); + + return IRQ_HANDLED; +} + +static int exynos_adc_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct exynos_adc *info = iio_priv(indio_dev); + + if (readval == NULL) + return -EINVAL; + + *readval = readl(info->regs + reg); + + return 0; +} + +static const struct iio_info exynos_adc_iio_info = { + .read_raw = &exynos_read_raw, + .debugfs_reg_access = &exynos_adc_reg_access, +}; + +#define ADC_CHANNEL(_index, _id) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .address = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _id, \ +} + +static const struct iio_chan_spec exynos_adc_iio_channels[] = { + ADC_CHANNEL(0, "adc0"), + ADC_CHANNEL(1, "adc1"), + ADC_CHANNEL(2, "adc2"), + ADC_CHANNEL(3, "adc3"), + ADC_CHANNEL(4, "adc4"), + ADC_CHANNEL(5, "adc5"), + ADC_CHANNEL(6, "adc6"), + ADC_CHANNEL(7, "adc7"), + ADC_CHANNEL(8, "adc8"), + ADC_CHANNEL(9, "adc9"), +}; + +static int exynos_adc_remove_devices(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + +static int exynos_adc_ts_open(struct input_dev *dev) +{ + struct exynos_adc *info = input_get_drvdata(dev); + + enable_irq(info->tsirq); + + return 0; +} + +static void exynos_adc_ts_close(struct input_dev *dev) +{ + struct exynos_adc *info = input_get_drvdata(dev); + + disable_irq(info->tsirq); +} + +static int exynos_adc_ts_init(struct exynos_adc *info) +{ + int ret; + + if (info->tsirq <= 0) + return -ENODEV; + + info->input = input_allocate_device(); + if (!info->input) + return -ENOMEM; + + info->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + info->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(info->input, ABS_X, 0, 0x3FF, 0, 0); + input_set_abs_params(info->input, ABS_Y, 0, 0x3FF, 0, 0); + + info->input->name = "S3C24xx TouchScreen"; + info->input->id.bustype = BUS_HOST; + info->input->open = exynos_adc_ts_open; + info->input->close = exynos_adc_ts_close; + + input_set_drvdata(info->input, info); + + ret = input_register_device(info->input); + if (ret) { + input_free_device(info->input); + return ret; + } + + disable_irq(info->tsirq); + ret = request_threaded_irq(info->tsirq, NULL, exynos_ts_isr, + IRQF_ONESHOT, "touchscreen", info); + if (ret) + input_unregister_device(info->input); + + return ret; +} + +static int exynos_adc_probe(struct platform_device *pdev) +{ + struct exynos_adc *info = NULL; + struct device_node *np = pdev->dev.of_node; + struct s3c2410_ts_mach_info *pdata = dev_get_platdata(&pdev->dev); + struct iio_dev *indio_dev = NULL; + bool has_ts = false; + int ret = -ENODEV; + int irq; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + info = iio_priv(indio_dev); + + info->data = exynos_adc_get_data(pdev); + if (!info->data) { + dev_err(&pdev->dev, "failed getting exynos_adc_data\n"); + return -EINVAL; + } + + info->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + + if (info->data->needs_adc_phy) { + info->pmu_map = syscon_regmap_lookup_by_phandle( + pdev->dev.of_node, + "samsung,syscon-phandle"); + if (IS_ERR(info->pmu_map)) { + dev_err(&pdev->dev, "syscon regmap lookup failed.\n"); + return PTR_ERR(info->pmu_map); + } + } + + /* leave out any TS related code if unreachable */ + if (IS_REACHABLE(CONFIG_INPUT)) { + has_ts = of_property_read_bool(pdev->dev.of_node, + "has-touchscreen") || pdata; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + info->irq = irq; + + if (has_ts) { + irq = platform_get_irq(pdev, 1); + if (irq == -EPROBE_DEFER) + return irq; + + info->tsirq = irq; + } else { + info->tsirq = -1; + } + + info->dev = &pdev->dev; + + init_completion(&info->completion); + + info->clk = devm_clk_get(&pdev->dev, "adc"); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "failed getting clock, err = %ld\n", + PTR_ERR(info->clk)); + return PTR_ERR(info->clk); + } + + if (info->data->needs_sclk) { + info->sclk = devm_clk_get(&pdev->dev, "sclk"); + if (IS_ERR(info->sclk)) { + dev_err(&pdev->dev, + "failed getting sclk clock, err = %ld\n", + PTR_ERR(info->sclk)); + return PTR_ERR(info->sclk); + } + } + + info->vdd = devm_regulator_get(&pdev->dev, "vdd"); + if (IS_ERR(info->vdd)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->vdd), + "failed getting regulator"); + + ret = regulator_enable(info->vdd); + if (ret) + return ret; + + ret = exynos_adc_prepare_clk(info); + if (ret) + goto err_disable_reg; + + ret = exynos_adc_enable_clk(info); + if (ret) + goto err_unprepare_clk; + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &exynos_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = exynos_adc_iio_channels; + indio_dev->num_channels = info->data->num_channels; + + mutex_init(&info->lock); + + ret = request_irq(info->irq, exynos_adc_isr, + 0, dev_name(&pdev->dev), info); + if (ret < 0) { + dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", + info->irq); + goto err_disable_clk; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto err_irq; + + if (info->data->init_hw) + info->data->init_hw(info); + + if (pdata) + info->delay = pdata->delay; + else + info->delay = 10000; + + if (has_ts) + ret = exynos_adc_ts_init(info); + if (ret) + goto err_iio; + + ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed adding child nodes\n"); + goto err_of_populate; + } + + return 0; + +err_of_populate: + device_for_each_child(&indio_dev->dev, NULL, + exynos_adc_remove_devices); + if (has_ts) { + input_unregister_device(info->input); + free_irq(info->tsirq, info); + } +err_iio: + iio_device_unregister(indio_dev); +err_irq: + free_irq(info->irq, info); +err_disable_clk: + if (info->data->exit_hw) + info->data->exit_hw(info); + exynos_adc_disable_clk(info); +err_unprepare_clk: + exynos_adc_unprepare_clk(info); +err_disable_reg: + regulator_disable(info->vdd); + return ret; +} + +static int exynos_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct exynos_adc *info = iio_priv(indio_dev); + + if (IS_REACHABLE(CONFIG_INPUT) && info->input) { + free_irq(info->tsirq, info); + input_unregister_device(info->input); + } + device_for_each_child(&indio_dev->dev, NULL, + exynos_adc_remove_devices); + iio_device_unregister(indio_dev); + free_irq(info->irq, info); + if (info->data->exit_hw) + info->data->exit_hw(info); + exynos_adc_disable_clk(info); + exynos_adc_unprepare_clk(info); + regulator_disable(info->vdd); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int exynos_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct exynos_adc *info = iio_priv(indio_dev); + + if (info->data->exit_hw) + info->data->exit_hw(info); + exynos_adc_disable_clk(info); + regulator_disable(info->vdd); + + return 0; +} + +static int exynos_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct exynos_adc *info = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(info->vdd); + if (ret) + return ret; + + ret = exynos_adc_enable_clk(info); + if (ret) + return ret; + + if (info->data->init_hw) + info->data->init_hw(info); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops, + exynos_adc_suspend, + exynos_adc_resume); + +static struct platform_driver exynos_adc_driver = { + .probe = exynos_adc_probe, + .remove = exynos_adc_remove, + .driver = { + .name = "exynos-adc", + .of_match_table = exynos_adc_match, + .pm = &exynos_adc_pm_ops, + }, +}; + +module_platform_driver(exynos_adc_driver); + +MODULE_AUTHOR("Naveen Krishna Chatradhi <ch.naveen@samsung.com>"); +MODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c new file mode 100644 index 000000000..ab5139e91 --- /dev/null +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -0,0 +1,428 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de> + * + * This is the driver for the imx25 GCQ (Generic Conversion Queue) + * connected to the imx25 ADC. + */ + +#include <dt-bindings/iio/adc/fsl-imx25-gcq.h> +#include <linux/clk.h> +#include <linux/iio/iio.h> +#include <linux/interrupt.h> +#include <linux/mfd/imx25-tsadc.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#define MX25_GCQ_TIMEOUT (msecs_to_jiffies(2000)) + +static const char * const driver_name = "mx25-gcq"; + +enum mx25_gcq_cfgs { + MX25_CFG_XP = 0, + MX25_CFG_YP, + MX25_CFG_XN, + MX25_CFG_YN, + MX25_CFG_WIPER, + MX25_CFG_INAUX0, + MX25_CFG_INAUX1, + MX25_CFG_INAUX2, + MX25_NUM_CFGS, +}; + +struct mx25_gcq_priv { + struct regmap *regs; + struct completion completed; + struct clk *clk; + int irq; + struct regulator *vref[4]; + u32 channel_vref_mv[MX25_NUM_CFGS]; + /* + * Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a completion callback, + * and finally a register read, during which userspace could issue + * another read request. This lock protects a read access from + * ocurring before another one has finished. + */ + struct mutex lock; +}; + +#define MX25_CQG_CHAN(chan, id) {\ + .type = IIO_VOLTAGE,\ + .indexed = 1,\ + .channel = chan,\ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE),\ + .datasheet_name = id,\ +} + +static const struct iio_chan_spec mx25_gcq_channels[MX25_NUM_CFGS] = { + MX25_CQG_CHAN(MX25_CFG_XP, "xp"), + MX25_CQG_CHAN(MX25_CFG_YP, "yp"), + MX25_CQG_CHAN(MX25_CFG_XN, "xn"), + MX25_CQG_CHAN(MX25_CFG_YN, "yn"), + MX25_CQG_CHAN(MX25_CFG_WIPER, "wiper"), + MX25_CQG_CHAN(MX25_CFG_INAUX0, "inaux0"), + MX25_CQG_CHAN(MX25_CFG_INAUX1, "inaux1"), + MX25_CQG_CHAN(MX25_CFG_INAUX2, "inaux2"), +}; + +static const char * const mx25_gcq_refp_names[] = { + [MX25_ADC_REFP_YP] = "yp", + [MX25_ADC_REFP_XP] = "xp", + [MX25_ADC_REFP_INT] = "int", + [MX25_ADC_REFP_EXT] = "ext", +}; + +static irqreturn_t mx25_gcq_irq(int irq, void *data) +{ + struct mx25_gcq_priv *priv = data; + u32 stats; + + regmap_read(priv->regs, MX25_ADCQ_SR, &stats); + + if (stats & MX25_ADCQ_SR_EOQ) { + regmap_update_bits(priv->regs, MX25_ADCQ_MR, + MX25_ADCQ_MR_EOQ_IRQ, MX25_ADCQ_MR_EOQ_IRQ); + complete(&priv->completed); + } + + /* Disable conversion queue run */ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, 0); + + /* Acknowledge all possible irqs */ + regmap_write(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR | + MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR | + MX25_ADCQ_SR_EOQ | MX25_ADCQ_SR_PD); + + return IRQ_HANDLED; +} + +static int mx25_gcq_get_raw_value(struct device *dev, + struct iio_chan_spec const *chan, + struct mx25_gcq_priv *priv, + int *val) +{ + long timeout; + u32 data; + + /* Setup the configuration we want to use */ + regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0, + MX25_ADCQ_ITEM(0, chan->channel)); + + regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_EOQ_IRQ, 0); + + /* Trigger queue for one run */ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, + MX25_ADCQ_CR_FQS); + + timeout = wait_for_completion_interruptible_timeout( + &priv->completed, MX25_GCQ_TIMEOUT); + if (timeout < 0) { + dev_err(dev, "ADC wait for measurement failed\n"); + return timeout; + } else if (timeout == 0) { + dev_err(dev, "ADC timed out\n"); + return -ETIMEDOUT; + } + + regmap_read(priv->regs, MX25_ADCQ_FIFO, &data); + + *val = MX25_ADCQ_FIFO_DATA(data); + + return IIO_VAL_INT; +} + +static int mx25_gcq_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct mx25_gcq_priv *priv = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&priv->lock); + ret = mx25_gcq_get_raw_value(&indio_dev->dev, chan, priv, val); + mutex_unlock(&priv->lock); + return ret; + + case IIO_CHAN_INFO_SCALE: + *val = priv->channel_vref_mv[chan->channel]; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static const struct iio_info mx25_gcq_iio_info = { + .read_raw = mx25_gcq_read_raw, +}; + +static const struct regmap_config mx25_gcq_regconfig = { + .max_register = 0x5c, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static int mx25_gcq_setup_cfgs(struct platform_device *pdev, + struct mx25_gcq_priv *priv) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + struct device *dev = &pdev->dev; + unsigned int refp_used[4] = {}; + int ret, i; + + /* + * Setup all configurations registers with a default conversion + * configuration for each input + */ + for (i = 0; i < MX25_NUM_CFGS; ++i) + regmap_write(priv->regs, MX25_ADCQ_CFG(i), + MX25_ADCQ_CFG_YPLL_OFF | + MX25_ADCQ_CFG_XNUR_OFF | + MX25_ADCQ_CFG_XPUL_OFF | + MX25_ADCQ_CFG_REFP_INT | + MX25_ADCQ_CFG_IN(i) | + MX25_ADCQ_CFG_REFN_NGND2); + + /* + * First get all regulators to store them in channel_vref_mv if + * necessary. Later we use that information for proper IIO scale + * information. + */ + priv->vref[MX25_ADC_REFP_INT] = NULL; + priv->vref[MX25_ADC_REFP_EXT] = + devm_regulator_get_optional(&pdev->dev, "vref-ext"); + priv->vref[MX25_ADC_REFP_XP] = + devm_regulator_get_optional(&pdev->dev, "vref-xp"); + priv->vref[MX25_ADC_REFP_YP] = + devm_regulator_get_optional(&pdev->dev, "vref-yp"); + + for_each_child_of_node(np, child) { + u32 reg; + u32 refp = MX25_ADCQ_CFG_REFP_INT; + u32 refn = MX25_ADCQ_CFG_REFN_NGND2; + + ret = of_property_read_u32(child, "reg", ®); + if (ret) { + dev_err(dev, "Failed to get reg property\n"); + of_node_put(child); + return ret; + } + + if (reg >= MX25_NUM_CFGS) { + dev_err(dev, + "reg value is greater than the number of available configuration registers\n"); + of_node_put(child); + return -EINVAL; + } + + of_property_read_u32(child, "fsl,adc-refp", &refp); + of_property_read_u32(child, "fsl,adc-refn", &refn); + + switch (refp) { + case MX25_ADC_REFP_EXT: + case MX25_ADC_REFP_XP: + case MX25_ADC_REFP_YP: + if (IS_ERR(priv->vref[refp])) { + dev_err(dev, "Error, trying to use external voltage reference without a vref-%s regulator.", + mx25_gcq_refp_names[refp]); + of_node_put(child); + return PTR_ERR(priv->vref[refp]); + } + priv->channel_vref_mv[reg] = + regulator_get_voltage(priv->vref[refp]); + /* Conversion from uV to mV */ + priv->channel_vref_mv[reg] /= 1000; + break; + case MX25_ADC_REFP_INT: + priv->channel_vref_mv[reg] = 2500; + break; + default: + dev_err(dev, "Invalid positive reference %d\n", refp); + of_node_put(child); + return -EINVAL; + } + + ++refp_used[refp]; + + /* + * Shift the read values to the correct positions within the + * register. + */ + refp = MX25_ADCQ_CFG_REFP(refp); + refn = MX25_ADCQ_CFG_REFN(refn); + + if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) { + dev_err(dev, "Invalid fsl,adc-refp property value\n"); + of_node_put(child); + return -EINVAL; + } + if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) { + dev_err(dev, "Invalid fsl,adc-refn property value\n"); + of_node_put(child); + return -EINVAL; + } + + regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg), + MX25_ADCQ_CFG_REFP_MASK | + MX25_ADCQ_CFG_REFN_MASK, + refp | refn); + } + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST, + MX25_ADCQ_CR_FRST | MX25_ADCQ_CR_QRST); + + regmap_write(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS); + + /* Remove unused regulators */ + for (i = 0; i != 4; ++i) { + if (!refp_used[i]) { + if (!IS_ERR_OR_NULL(priv->vref[i])) + devm_regulator_put(priv->vref[i]); + priv->vref[i] = NULL; + } + } + + return 0; +} + +static int mx25_gcq_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct mx25_gcq_priv *priv; + struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + void __iomem *mem; + int ret; + int i; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + + mem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mem)) + return PTR_ERR(mem); + + priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig); + if (IS_ERR(priv->regs)) { + dev_err(dev, "Failed to initialize regmap\n"); + return PTR_ERR(priv->regs); + } + + mutex_init(&priv->lock); + + init_completion(&priv->completed); + + ret = mx25_gcq_setup_cfgs(pdev, priv); + if (ret) + return ret; + + for (i = 0; i != 4; ++i) { + if (!priv->vref[i]) + continue; + + ret = regulator_enable(priv->vref[i]); + if (ret) + goto err_regulator_disable; + } + + priv->clk = tsadc->clk; + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(dev, "Failed to enable clock\n"); + goto err_vref_disable; + } + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq <= 0) { + ret = priv->irq; + if (!ret) + ret = -ENXIO; + goto err_clk_unprepare; + } + + ret = request_irq(priv->irq, mx25_gcq_irq, 0, pdev->name, priv); + if (ret) { + dev_err(dev, "Failed requesting IRQ\n"); + goto err_clk_unprepare; + } + + indio_dev->channels = mx25_gcq_channels; + indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels); + indio_dev->info = &mx25_gcq_iio_info; + indio_dev->name = driver_name; + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "Failed to register iio device\n"); + goto err_irq_free; + } + + platform_set_drvdata(pdev, indio_dev); + + return 0; + +err_irq_free: + free_irq(priv->irq, priv); +err_clk_unprepare: + clk_disable_unprepare(priv->clk); +err_vref_disable: + i = 4; +err_regulator_disable: + for (; i-- > 0;) { + if (priv->vref[i]) + regulator_disable(priv->vref[i]); + } + return ret; +} + +static int mx25_gcq_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct mx25_gcq_priv *priv = iio_priv(indio_dev); + int i; + + iio_device_unregister(indio_dev); + free_irq(priv->irq, priv); + clk_disable_unprepare(priv->clk); + for (i = 4; i-- > 0;) { + if (priv->vref[i]) + regulator_disable(priv->vref[i]); + } + + return 0; +} + +static const struct of_device_id mx25_gcq_ids[] = { + { .compatible = "fsl,imx25-gcq", }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mx25_gcq_ids); + +static struct platform_driver mx25_gcq_driver = { + .driver = { + .name = "mx25-gcq", + .of_match_table = mx25_gcq_ids, + }, + .probe = mx25_gcq_probe, + .remove = mx25_gcq_remove, +}; +module_platform_driver(mx25_gcq_driver); + +MODULE_DESCRIPTION("ADC driver for Freescale mx25"); +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c new file mode 100644 index 000000000..074c30970 --- /dev/null +++ b/drivers/iio/adc/hi8435.c @@ -0,0 +1,550 @@ +// 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. + */ + +#include <linux/delay.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_event.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/spi/spi.h> +#include <linux/gpio/consumer.h> + +#define DRV_NAME "hi8435" + +/* Register offsets for HI-8435 */ +#define HI8435_CTRL_REG 0x02 +#define HI8435_PSEN_REG 0x04 +#define HI8435_TMDATA_REG 0x1E +#define HI8435_GOCENHYS_REG 0x3A +#define HI8435_SOCENHYS_REG 0x3C +#define HI8435_SO7_0_REG 0x10 +#define HI8435_SO15_8_REG 0x12 +#define HI8435_SO23_16_REG 0x14 +#define HI8435_SO31_24_REG 0x16 +#define HI8435_SO31_0_REG 0x78 + +#define HI8435_WRITE_OPCODE 0x00 +#define HI8435_READ_OPCODE 0x80 + +/* CTRL register bits */ +#define HI8435_CTRL_TEST 0x01 +#define HI8435_CTRL_SRST 0x02 + +struct hi8435_priv { + struct spi_device *spi; + struct mutex lock; + + unsigned long event_scan_mask; /* soft mask/unmask channels events */ + unsigned int event_prev_val; + + unsigned threshold_lo[2]; /* GND-Open and Supply-Open thresholds */ + unsigned threshold_hi[2]; /* GND-Open and Supply-Open thresholds */ + u8 reg_buffer[3] ____cacheline_aligned; +}; + +static int hi8435_readb(struct hi8435_priv *priv, u8 reg, u8 *val) +{ + reg |= HI8435_READ_OPCODE; + return spi_write_then_read(priv->spi, ®, 1, val, 1); +} + +static int hi8435_readw(struct hi8435_priv *priv, u8 reg, u16 *val) +{ + int ret; + __be16 be_val; + + reg |= HI8435_READ_OPCODE; + ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 2); + *val = be16_to_cpu(be_val); + + return ret; +} + +static int hi8435_readl(struct hi8435_priv *priv, u8 reg, u32 *val) +{ + int ret; + __be32 be_val; + + reg |= HI8435_READ_OPCODE; + ret = spi_write_then_read(priv->spi, ®, 1, &be_val, 4); + *val = be32_to_cpu(be_val); + + return ret; +} + +static int hi8435_writeb(struct hi8435_priv *priv, u8 reg, u8 val) +{ + priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE; + priv->reg_buffer[1] = val; + + return spi_write(priv->spi, priv->reg_buffer, 2); +} + +static int hi8435_writew(struct hi8435_priv *priv, u8 reg, u16 val) +{ + priv->reg_buffer[0] = reg | HI8435_WRITE_OPCODE; + priv->reg_buffer[1] = (val >> 8) & 0xff; + priv->reg_buffer[2] = val & 0xff; + + return spi_write(priv->spi, priv->reg_buffer, 3); +} + +static int hi8435_read_raw(struct iio_dev *idev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct hi8435_priv *priv = iio_priv(idev); + u32 tmp; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = hi8435_readl(priv, HI8435_SO31_0_REG, &tmp); + if (ret < 0) + return ret; + *val = !!(tmp & BIT(chan->channel)); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int hi8435_read_event_config(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct hi8435_priv *priv = iio_priv(idev); + + return !!(priv->event_scan_mask & BIT(chan->channel)); +} + +static int hi8435_write_event_config(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u32 tmp; + + if (state) { + ret = hi8435_readl(priv, HI8435_SO31_0_REG, &tmp); + if (ret < 0) + return ret; + if (tmp & BIT(chan->channel)) + priv->event_prev_val |= BIT(chan->channel); + else + priv->event_prev_val &= ~BIT(chan->channel); + + priv->event_scan_mask |= BIT(chan->channel); + } else + priv->event_scan_mask &= ~BIT(chan->channel); + + return 0; +} + +static int hi8435_read_event_value(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 mode, psen; + u16 reg; + + ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen); + if (ret < 0) + return ret; + + /* Supply-Open or GND-Open sensing mode */ + mode = !!(psen & BIT(chan->channel / 8)); + + ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, ®); + if (ret < 0) + return ret; + + if (dir == IIO_EV_DIR_FALLING) + *val = ((reg & 0xff) - (reg >> 8)) / 2; + else if (dir == IIO_EV_DIR_RISING) + *val = ((reg & 0xff) + (reg >> 8)) / 2; + + return IIO_VAL_INT; +} + +static int hi8435_write_event_value(struct iio_dev *idev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 mode, psen; + u16 reg; + + ret = hi8435_readb(priv, HI8435_PSEN_REG, &psen); + if (ret < 0) + return ret; + + /* Supply-Open or GND-Open sensing mode */ + mode = !!(psen & BIT(chan->channel / 8)); + + ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, ®); + if (ret < 0) + return ret; + + if (dir == IIO_EV_DIR_FALLING) { + /* falling threshold range 2..21V, hysteresis minimum 2V */ + if (val < 2 || val > 21 || (val + 2) > priv->threshold_hi[mode]) + return -EINVAL; + + if (val == priv->threshold_lo[mode]) + return 0; + + priv->threshold_lo[mode] = val; + + /* hysteresis must not be odd */ + if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2) + priv->threshold_hi[mode]--; + } else if (dir == IIO_EV_DIR_RISING) { + /* rising threshold range 3..22V, hysteresis minimum 2V */ + if (val < 3 || val > 22 || val < (priv->threshold_lo[mode] + 2)) + return -EINVAL; + + if (val == priv->threshold_hi[mode]) + return 0; + + priv->threshold_hi[mode] = val; + + /* hysteresis must not be odd */ + if ((priv->threshold_hi[mode] - priv->threshold_lo[mode]) % 2) + priv->threshold_lo[mode]++; + } + + /* program thresholds */ + mutex_lock(&priv->lock); + + ret = hi8435_readw(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, ®); + if (ret < 0) { + mutex_unlock(&priv->lock); + return ret; + } + + /* hysteresis */ + reg = priv->threshold_hi[mode] - priv->threshold_lo[mode]; + reg <<= 8; + /* threshold center */ + reg |= (priv->threshold_hi[mode] + priv->threshold_lo[mode]); + + ret = hi8435_writew(priv, mode ? HI8435_SOCENHYS_REG : + HI8435_GOCENHYS_REG, reg); + + mutex_unlock(&priv->lock); + + return ret; +} + +static int hi8435_debugfs_reg_access(struct iio_dev *idev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 val; + + if (readval != NULL) { + ret = hi8435_readb(priv, reg, &val); + *readval = val; + } else { + val = (u8)writeval; + ret = hi8435_writeb(priv, reg, val); + } + + return ret; +} + +static const struct iio_event_spec hi8435_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static int hi8435_get_sensing_mode(struct iio_dev *idev, + const struct iio_chan_spec *chan) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 reg; + + ret = hi8435_readb(priv, HI8435_PSEN_REG, ®); + if (ret < 0) + return ret; + + return !!(reg & BIT(chan->channel / 8)); +} + +static int hi8435_set_sensing_mode(struct iio_dev *idev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct hi8435_priv *priv = iio_priv(idev); + int ret; + u8 reg; + + mutex_lock(&priv->lock); + + ret = hi8435_readb(priv, HI8435_PSEN_REG, ®); + if (ret < 0) { + mutex_unlock(&priv->lock); + return ret; + } + + reg &= ~BIT(chan->channel / 8); + if (mode) + reg |= BIT(chan->channel / 8); + + ret = hi8435_writeb(priv, HI8435_PSEN_REG, reg); + + mutex_unlock(&priv->lock); + + return ret; +} + +static const char * const hi8435_sensing_modes[] = { "GND-Open", + "Supply-Open" }; + +static const struct iio_enum hi8435_sensing_mode = { + .items = hi8435_sensing_modes, + .num_items = ARRAY_SIZE(hi8435_sensing_modes), + .get = hi8435_get_sensing_mode, + .set = hi8435_set_sensing_mode, +}; + +static const struct iio_chan_spec_ext_info hi8435_ext_info[] = { + IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode), + IIO_ENUM_AVAILABLE("sensing_mode", &hi8435_sensing_mode), + {}, +}; + +#define HI8435_VOLTAGE_CHANNEL(num) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = num, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .event_spec = hi8435_events, \ + .num_event_specs = ARRAY_SIZE(hi8435_events), \ + .ext_info = hi8435_ext_info, \ +} + +static const struct iio_chan_spec hi8435_channels[] = { + HI8435_VOLTAGE_CHANNEL(0), + HI8435_VOLTAGE_CHANNEL(1), + HI8435_VOLTAGE_CHANNEL(2), + HI8435_VOLTAGE_CHANNEL(3), + HI8435_VOLTAGE_CHANNEL(4), + HI8435_VOLTAGE_CHANNEL(5), + HI8435_VOLTAGE_CHANNEL(6), + HI8435_VOLTAGE_CHANNEL(7), + HI8435_VOLTAGE_CHANNEL(8), + HI8435_VOLTAGE_CHANNEL(9), + HI8435_VOLTAGE_CHANNEL(10), + HI8435_VOLTAGE_CHANNEL(11), + HI8435_VOLTAGE_CHANNEL(12), + HI8435_VOLTAGE_CHANNEL(13), + HI8435_VOLTAGE_CHANNEL(14), + HI8435_VOLTAGE_CHANNEL(15), + HI8435_VOLTAGE_CHANNEL(16), + HI8435_VOLTAGE_CHANNEL(17), + HI8435_VOLTAGE_CHANNEL(18), + HI8435_VOLTAGE_CHANNEL(19), + HI8435_VOLTAGE_CHANNEL(20), + HI8435_VOLTAGE_CHANNEL(21), + HI8435_VOLTAGE_CHANNEL(22), + HI8435_VOLTAGE_CHANNEL(23), + HI8435_VOLTAGE_CHANNEL(24), + HI8435_VOLTAGE_CHANNEL(25), + HI8435_VOLTAGE_CHANNEL(26), + HI8435_VOLTAGE_CHANNEL(27), + HI8435_VOLTAGE_CHANNEL(28), + HI8435_VOLTAGE_CHANNEL(29), + HI8435_VOLTAGE_CHANNEL(30), + HI8435_VOLTAGE_CHANNEL(31), + IIO_CHAN_SOFT_TIMESTAMP(32), +}; + +static const struct iio_info hi8435_info = { + .read_raw = hi8435_read_raw, + .read_event_config = hi8435_read_event_config, + .write_event_config = hi8435_write_event_config, + .read_event_value = hi8435_read_event_value, + .write_event_value = hi8435_write_event_value, + .debugfs_reg_access = hi8435_debugfs_reg_access, +}; + +static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val) +{ + struct hi8435_priv *priv = iio_priv(idev); + enum iio_event_direction dir; + unsigned int i; + unsigned int status = priv->event_prev_val ^ val; + + if (!status) + return; + + for_each_set_bit(i, &priv->event_scan_mask, 32) { + if (status & BIT(i)) { + dir = val & BIT(i) ? IIO_EV_DIR_RISING : + IIO_EV_DIR_FALLING; + iio_push_event(idev, + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, + IIO_EV_TYPE_THRESH, dir), + iio_get_time_ns(idev)); + } + } + + priv->event_prev_val = val; +} + +static irqreturn_t hi8435_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *idev = pf->indio_dev; + struct hi8435_priv *priv = iio_priv(idev); + u32 val; + int ret; + + ret = hi8435_readl(priv, HI8435_SO31_0_REG, &val); + if (ret < 0) + goto err_read; + + hi8435_iio_push_event(idev, val); + +err_read: + iio_trigger_notify_done(idev->trig); + + 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; + struct hi8435_priv *priv; + struct gpio_desc *reset_gpio; + int ret; + + idev = devm_iio_device_alloc(&spi->dev, sizeof(*priv)); + if (!idev) + return -ENOMEM; + + priv = iio_priv(idev); + priv->spi = spi; + + reset_gpio = devm_gpiod_get(&spi->dev, NULL, GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) { + /* chip s/w reset if h/w reset failed */ + hi8435_writeb(priv, HI8435_CTRL_REG, HI8435_CTRL_SRST); + hi8435_writeb(priv, HI8435_CTRL_REG, 0); + } else { + udelay(5); + gpiod_set_value_cansleep(reset_gpio, 1); + } + + spi_set_drvdata(spi, idev); + mutex_init(&priv->lock); + + idev->name = spi_get_device_id(spi)->name; + idev->modes = INDIO_DIRECT_MODE; + idev->info = &hi8435_info; + idev->channels = hi8435_channels; + idev->num_channels = ARRAY_SIZE(hi8435_channels); + + /* unmask all events */ + priv->event_scan_mask = ~(0); + /* + * There is a restriction in the chip - the hysteresis can not be odd. + * If the hysteresis is set to odd value then chip gets into lock state + * and not functional anymore. + * After chip reset the thresholds are in undefined state, so we need to + * initialize thresholds to some initial values and then prevent + * userspace setting odd hysteresis. + * + * Set threshold low voltage to 2V, threshold high voltage to 4V + * for both GND-Open and Supply-Open sensing modes. + */ + priv->threshold_lo[0] = priv->threshold_lo[1] = 2; + priv->threshold_hi[0] = priv->threshold_hi[1] = 4; + hi8435_writew(priv, HI8435_GOCENHYS_REG, 0x206); + hi8435_writew(priv, HI8435_SOCENHYS_REG, 0x206); + + ret = iio_triggered_event_setup(idev, NULL, hi8435_trigger_handler); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, + hi8435_triggered_event_cleanup, + idev); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, idev); +} + +static const struct of_device_id hi8435_dt_ids[] = { + { .compatible = "holt,hi8435" }, + {}, +}; +MODULE_DEVICE_TABLE(of, hi8435_dt_ids); + +static const struct spi_device_id hi8435_id[] = { + { "hi8435", 0}, + { } +}; +MODULE_DEVICE_TABLE(spi, hi8435_id); + +static struct spi_driver hi8435_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = hi8435_dt_ids, + }, + .probe = hi8435_probe, + .id_table = hi8435_id, +}; +module_spi_driver(hi8435_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("HI-8435 threshold detector"); diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c new file mode 100644 index 000000000..f7ee856a6 --- /dev/null +++ b/drivers/iio/adc/hx711.c @@ -0,0 +1,622 @@ +// 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> + */ +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> + +/* gain to pulse and scale conversion */ +#define HX711_GAIN_MAX 3 +#define HX711_RESET_GAIN 128 + +struct hx711_gain_to_scale { + int gain; + int gain_pulse; + int scale; + int channel; +}; + +/* + * .scale depends on AVDD which in turn is known as soon as the regulator + * is available + * therefore we set .scale in hx711_probe() + * + * channel A in documentation is channel 0 in source code + * channel B in documentation is channel 1 in source code + */ +static struct hx711_gain_to_scale hx711_gain_to_scale[HX711_GAIN_MAX] = { + { 128, 1, 0, 0 }, + { 32, 2, 0, 1 }, + { 64, 3, 0, 0 } +}; + +static int hx711_get_gain_to_pulse(int gain) +{ + int i; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (hx711_gain_to_scale[i].gain == gain) + return hx711_gain_to_scale[i].gain_pulse; + return 1; +} + +static int hx711_get_gain_to_scale(int gain) +{ + int i; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (hx711_gain_to_scale[i].gain == gain) + return hx711_gain_to_scale[i].scale; + return 0; +} + +static int hx711_get_scale_to_gain(int scale) +{ + int i; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (hx711_gain_to_scale[i].scale == scale) + return hx711_gain_to_scale[i].gain; + return -EINVAL; +} + +struct hx711_data { + struct device *dev; + struct gpio_desc *gpiod_pd_sck; + struct gpio_desc *gpiod_dout; + struct regulator *reg_avdd; + int gain_set; /* gain set on device */ + int gain_chan_a; /* gain for channel A */ + struct mutex lock; + /* + * triggered buffer + * 2x32-bit channel + 64-bit naturally aligned timestamp + */ + u32 buffer[4] __aligned(8); + /* + * delay after a rising edge on SCK until the data is ready DOUT + * this is dependent on the hx711 where the datasheet tells a + * maximum value of 100 ns + * but also on potential parasitic capacities on the wiring + */ + u32 data_ready_delay_ns; + u32 clock_frequency; +}; + +static int hx711_cycle(struct hx711_data *hx711_data) +{ + unsigned long flags; + + /* + * if preempted for more then 60us while PD_SCK is high: + * hx711 is going in reset + * ==> measuring is false + */ + local_irq_save(flags); + gpiod_set_value(hx711_data->gpiod_pd_sck, 1); + + /* + * wait until DOUT is ready + * it turned out that parasitic capacities are extending the time + * until DOUT has reached it's value + */ + ndelay(hx711_data->data_ready_delay_ns); + + /* + * here we are not waiting for 0.2 us as suggested by the datasheet, + * because the oscilloscope showed in a test scenario + * at least 1.15 us for PD_SCK high (T3 in datasheet) + * and 0.56 us for PD_SCK low on TI Sitara with 800 MHz + */ + gpiod_set_value(hx711_data->gpiod_pd_sck, 0); + local_irq_restore(flags); + + /* + * make it a square wave for addressing cases with capacitance on + * PC_SCK + */ + ndelay(hx711_data->data_ready_delay_ns); + + /* sample as late as possible */ + return gpiod_get_value(hx711_data->gpiod_dout); +} + +static int hx711_read(struct hx711_data *hx711_data) +{ + int i, ret; + int value = 0; + int val = gpiod_get_value(hx711_data->gpiod_dout); + + /* we double check if it's really down */ + if (val) + return -EIO; + + for (i = 0; i < 24; i++) { + value <<= 1; + ret = hx711_cycle(hx711_data); + if (ret) + value++; + } + + value ^= 0x800000; + + for (i = 0; i < hx711_get_gain_to_pulse(hx711_data->gain_set); i++) + hx711_cycle(hx711_data); + + return value; +} + +static int hx711_wait_for_ready(struct hx711_data *hx711_data) +{ + int i, val; + + /* + * in some rare cases the reset takes quite a long time + * especially when the channel is changed. + * Allow up to one second for it + */ + for (i = 0; i < 100; i++) { + val = gpiod_get_value(hx711_data->gpiod_dout); + if (!val) + break; + /* sleep at least 10 ms */ + msleep(10); + } + if (val) + return -EIO; + + return 0; +} + +static int hx711_reset(struct hx711_data *hx711_data) +{ + int val = hx711_wait_for_ready(hx711_data); + + if (val) { + /* + * an examination with the oszilloscope indicated + * that the first value read after the reset is not stable + * if we reset too short; + * the shorter the reset cycle + * the less reliable the first value after reset is; + * there were no problems encountered with a value + * of 10 ms or higher + */ + gpiod_set_value(hx711_data->gpiod_pd_sck, 1); + msleep(10); + gpiod_set_value(hx711_data->gpiod_pd_sck, 0); + + val = hx711_wait_for_ready(hx711_data); + + /* after a reset the gain is 128 */ + hx711_data->gain_set = HX711_RESET_GAIN; + } + + return val; +} + +static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan) +{ + int ret; + + if (chan == 0) { + if (hx711_data->gain_set == 32) { + hx711_data->gain_set = hx711_data->gain_chan_a; + + ret = hx711_read(hx711_data); + if (ret < 0) + return ret; + + ret = hx711_wait_for_ready(hx711_data); + if (ret) + return ret; + } + } else { + if (hx711_data->gain_set != 32) { + hx711_data->gain_set = 32; + + ret = hx711_read(hx711_data); + if (ret < 0) + return ret; + + ret = hx711_wait_for_ready(hx711_data); + if (ret) + return ret; + } + } + + return 0; +} + +static int hx711_reset_read(struct hx711_data *hx711_data, int chan) +{ + int ret; + int val; + + /* + * hx711_reset() must be called from here + * because it could be calling hx711_read() by itself + */ + if (hx711_reset(hx711_data)) { + dev_err(hx711_data->dev, "reset failed!"); + return -EIO; + } + + ret = hx711_set_gain_for_channel(hx711_data, chan); + if (ret < 0) + return ret; + + val = hx711_read(hx711_data); + + return val; +} + +static int hx711_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct hx711_data *hx711_data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&hx711_data->lock); + + *val = hx711_reset_read(hx711_data, chan->channel); + + mutex_unlock(&hx711_data->lock); + + if (*val < 0) + return *val; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + mutex_lock(&hx711_data->lock); + + *val2 = hx711_get_gain_to_scale(hx711_data->gain_set); + + mutex_unlock(&hx711_data->lock); + + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int hx711_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct hx711_data *hx711_data = iio_priv(indio_dev); + int ret; + int gain; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + /* + * a scale greater than 1 mV per LSB is not possible + * with the HX711, therefore val must be 0 + */ + if (val != 0) + return -EINVAL; + + mutex_lock(&hx711_data->lock); + + gain = hx711_get_scale_to_gain(val2); + if (gain < 0) { + mutex_unlock(&hx711_data->lock); + return gain; + } + + if (gain != hx711_data->gain_set) { + hx711_data->gain_set = gain; + if (gain != 32) + hx711_data->gain_chan_a = gain; + + ret = hx711_read(hx711_data); + if (ret < 0) { + mutex_unlock(&hx711_data->lock); + return ret; + } + } + + mutex_unlock(&hx711_data->lock); + return 0; + default: + return -EINVAL; + } + + return 0; +} + +static int hx711_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static irqreturn_t hx711_trigger(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct hx711_data *hx711_data = iio_priv(indio_dev); + int i, j = 0; + + mutex_lock(&hx711_data->lock); + + memset(hx711_data->buffer, 0, sizeof(hx711_data->buffer)); + + for (i = 0; i < indio_dev->masklength; i++) { + if (!test_bit(i, indio_dev->active_scan_mask)) + continue; + + hx711_data->buffer[j] = hx711_reset_read(hx711_data, + indio_dev->channels[i].channel); + j++; + } + + iio_push_to_buffers_with_timestamp(indio_dev, hx711_data->buffer, + pf->timestamp); + + mutex_unlock(&hx711_data->lock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static ssize_t hx711_scale_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr); + int channel = iio_attr->address; + int i, len = 0; + + for (i = 0; i < HX711_GAIN_MAX; i++) + if (hx711_gain_to_scale[i].channel == channel) + len += sprintf(buf + len, "0.%09d ", + hx711_gain_to_scale[i].scale); + + len += sprintf(buf + len, "\n"); + + return len; +} + +static IIO_DEVICE_ATTR(in_voltage0_scale_available, S_IRUGO, + hx711_scale_available_show, NULL, 0); + +static IIO_DEVICE_ATTR(in_voltage1_scale_available, S_IRUGO, + hx711_scale_available_show, NULL, 1); + +static struct attribute *hx711_attributes[] = { + &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group hx711_attribute_group = { + .attrs = hx711_attributes, +}; + +static const struct iio_info hx711_iio_info = { + .read_raw = hx711_read_raw, + .write_raw = hx711_write_raw, + .write_raw_get_fmt = hx711_write_raw_get_fmt, + .attrs = &hx711_attribute_group, +}; + +static const struct iio_chan_spec hx711_chan_spec[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_VOLTAGE, + .channel = 1, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static int hx711_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct hx711_data *hx711_data; + struct iio_dev *indio_dev; + int ret; + int i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(struct hx711_data)); + if (!indio_dev) { + dev_err(dev, "failed to allocate IIO device\n"); + return -ENOMEM; + } + + hx711_data = iio_priv(indio_dev); + hx711_data->dev = dev; + + mutex_init(&hx711_data->lock); + + /* + * PD_SCK stands for power down and serial clock input of HX711 + * in the driver it is an output + */ + hx711_data->gpiod_pd_sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW); + if (IS_ERR(hx711_data->gpiod_pd_sck)) { + dev_err(dev, "failed to get sck-gpiod: err=%ld\n", + PTR_ERR(hx711_data->gpiod_pd_sck)); + return PTR_ERR(hx711_data->gpiod_pd_sck); + } + + /* + * DOUT stands for serial data output of HX711 + * for the driver it is an input + */ + hx711_data->gpiod_dout = devm_gpiod_get(dev, "dout", GPIOD_IN); + if (IS_ERR(hx711_data->gpiod_dout)) { + dev_err(dev, "failed to get dout-gpiod: err=%ld\n", + PTR_ERR(hx711_data->gpiod_dout)); + return PTR_ERR(hx711_data->gpiod_dout); + } + + hx711_data->reg_avdd = devm_regulator_get(dev, "avdd"); + if (IS_ERR(hx711_data->reg_avdd)) + return PTR_ERR(hx711_data->reg_avdd); + + ret = regulator_enable(hx711_data->reg_avdd); + if (ret < 0) + return ret; + + /* + * with + * full scale differential input range: AVDD / GAIN + * full scale output data: 2^24 + * we can say: + * AVDD / GAIN = 2^24 + * therefore: + * 1 LSB = AVDD / GAIN / 2^24 + * AVDD is in uV, but we need 10^-9 mV + * approximately to fit into a 32 bit number: + * 1 LSB = (AVDD * 100) / GAIN / 1678 [10^-9 mV] + */ + ret = regulator_get_voltage(hx711_data->reg_avdd); + if (ret < 0) + goto error_regulator; + + /* we need 10^-9 mV */ + ret *= 100; + + for (i = 0; i < HX711_GAIN_MAX; i++) + hx711_gain_to_scale[i].scale = + ret / hx711_gain_to_scale[i].gain / 1678; + + hx711_data->gain_set = 128; + hx711_data->gain_chan_a = 128; + + hx711_data->clock_frequency = 400000; + ret = of_property_read_u32(np, "clock-frequency", + &hx711_data->clock_frequency); + + /* + * datasheet says the high level of PD_SCK has a maximum duration + * of 50 microseconds + */ + if (hx711_data->clock_frequency < 20000) { + dev_warn(dev, "clock-frequency too low - assuming 400 kHz\n"); + hx711_data->clock_frequency = 400000; + } + + hx711_data->data_ready_delay_ns = + 1000000000 / hx711_data->clock_frequency; + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = "hx711"; + indio_dev->info = &hx711_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = hx711_chan_spec; + indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec); + + ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time, + hx711_trigger, NULL); + if (ret < 0) { + dev_err(dev, "setup of iio triggered buffer failed\n"); + goto error_regulator; + } + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(dev, "Couldn't register the device\n"); + goto error_buffer; + } + + return 0; + +error_buffer: + iio_triggered_buffer_cleanup(indio_dev); + +error_regulator: + regulator_disable(hx711_data->reg_avdd); + + return ret; +} + +static int hx711_remove(struct platform_device *pdev) +{ + struct hx711_data *hx711_data; + struct iio_dev *indio_dev; + + indio_dev = platform_get_drvdata(pdev); + hx711_data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + iio_triggered_buffer_cleanup(indio_dev); + + regulator_disable(hx711_data->reg_avdd); + + return 0; +} + +static const struct of_device_id of_hx711_match[] = { + { .compatible = "avia,hx711", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, of_hx711_match); + +static struct platform_driver hx711_driver = { + .probe = hx711_probe, + .remove = hx711_remove, + .driver = { + .name = "hx711-gpio", + .of_match_table = of_hx711_match, + }, +}; + +module_platform_driver(hx711_driver); + +MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>"); +MODULE_DESCRIPTION("HX711 bitbanging driver - ADC for weight cells"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:hx711-gpio"); + diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c new file mode 100644 index 000000000..4969a5f94 --- /dev/null +++ b/drivers/iio/adc/imx7d_adc.c @@ -0,0 +1,564 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Freescale i.MX7D ADC driver + * + * Copyright (C) 2015 Freescale Semiconductor, Inc. + */ + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> +#include <linux/iio/sysfs.h> + +/* ADC register */ +#define IMX7D_REG_ADC_CH_A_CFG1 0x00 +#define IMX7D_REG_ADC_CH_A_CFG2 0x10 +#define IMX7D_REG_ADC_CH_B_CFG1 0x20 +#define IMX7D_REG_ADC_CH_B_CFG2 0x30 +#define IMX7D_REG_ADC_CH_C_CFG1 0x40 +#define IMX7D_REG_ADC_CH_C_CFG2 0x50 +#define IMX7D_REG_ADC_CH_D_CFG1 0x60 +#define IMX7D_REG_ADC_CH_D_CFG2 0x70 +#define IMX7D_REG_ADC_CH_SW_CFG 0x80 +#define IMX7D_REG_ADC_TIMER_UNIT 0x90 +#define IMX7D_REG_ADC_DMA_FIFO 0xa0 +#define IMX7D_REG_ADC_FIFO_STATUS 0xb0 +#define IMX7D_REG_ADC_INT_SIG_EN 0xc0 +#define IMX7D_REG_ADC_INT_EN 0xd0 +#define IMX7D_REG_ADC_INT_STATUS 0xe0 +#define IMX7D_REG_ADC_CHA_B_CNV_RSLT 0xf0 +#define IMX7D_REG_ADC_CHC_D_CNV_RSLT 0x100 +#define IMX7D_REG_ADC_CH_SW_CNV_RSLT 0x110 +#define IMX7D_REG_ADC_DMA_FIFO_DAT 0x120 +#define IMX7D_REG_ADC_ADC_CFG 0x130 + +#define IMX7D_REG_ADC_CHANNEL_CFG2_BASE 0x10 +#define IMX7D_EACH_CHANNEL_REG_OFFSET 0x20 + +#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN (0x1 << 31) +#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE BIT(30) +#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN BIT(29) +#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x) ((x) << 24) + +#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4 (0x0 << 12) +#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8 (0x1 << 12) +#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16 (0x2 << 12) +#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32 (0x3 << 12) + +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4 (0x0 << 29) +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8 (0x1 << 29) +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16 (0x2 << 29) +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32 (0x3 << 29) +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64 (0x4 << 29) +#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128 (0x5 << 29) + +#define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN BIT(31) +#define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN BIT(1) +#define IMX7D_REG_ADC_ADC_CFG_ADC_EN BIT(0) + +#define IMX7D_REG_ADC_INT_CHA_COV_INT_EN BIT(8) +#define IMX7D_REG_ADC_INT_CHB_COV_INT_EN BIT(9) +#define IMX7D_REG_ADC_INT_CHC_COV_INT_EN BIT(10) +#define IMX7D_REG_ADC_INT_CHD_COV_INT_EN BIT(11) +#define IMX7D_REG_ADC_INT_CHANNEL_INT_EN \ + (IMX7D_REG_ADC_INT_CHA_COV_INT_EN | \ + IMX7D_REG_ADC_INT_CHB_COV_INT_EN | \ + IMX7D_REG_ADC_INT_CHC_COV_INT_EN | \ + IMX7D_REG_ADC_INT_CHD_COV_INT_EN) +#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS 0xf00 +#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, + IMX7D_ADC_ANALOG_CLK_PRE_DIV_8, + IMX7D_ADC_ANALOG_CLK_PRE_DIV_16, + IMX7D_ADC_ANALOG_CLK_PRE_DIV_32, + IMX7D_ADC_ANALOG_CLK_PRE_DIV_64, + IMX7D_ADC_ANALOG_CLK_PRE_DIV_128, +}; + +enum imx7d_adc_average_num { + IMX7D_ADC_AVERAGE_NUM_4, + IMX7D_ADC_AVERAGE_NUM_8, + IMX7D_ADC_AVERAGE_NUM_16, + IMX7D_ADC_AVERAGE_NUM_32, +}; + +struct imx7d_adc_feature { + enum imx7d_adc_clk_pre_div clk_pre_div; + enum imx7d_adc_average_num avg_num; + + u32 core_time_unit; /* impact the sample rate */ +}; + +struct imx7d_adc { + struct device *dev; + void __iomem *regs; + struct clk *clk; + + u32 vref_uv; + u32 value; + u32 channel; + u32 pre_div_num; + + struct regulator *vref; + struct imx7d_adc_feature adc_feature; + + struct completion completion; +}; + +struct imx7d_adc_analogue_core_clk { + u32 pre_div; + u32 reg_config; +}; + +#define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) { \ + .pre_div = (_pre_div), \ + .reg_config = (_reg_conf), \ +} + +static const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = { + IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4), + IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8), + IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16), + IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32), + IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64), + IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128), +}; + +#define IMX7D_ADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .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 imx7d_adc_iio_channels[] = { + IMX7D_ADC_CHAN(0), + IMX7D_ADC_CHAN(1), + IMX7D_ADC_CHAN(2), + IMX7D_ADC_CHAN(3), + IMX7D_ADC_CHAN(4), + IMX7D_ADC_CHAN(5), + IMX7D_ADC_CHAN(6), + IMX7D_ADC_CHAN(7), + IMX7D_ADC_CHAN(8), + IMX7D_ADC_CHAN(9), + IMX7D_ADC_CHAN(10), + IMX7D_ADC_CHAN(11), + IMX7D_ADC_CHAN(12), + IMX7D_ADC_CHAN(13), + IMX7D_ADC_CHAN(14), + IMX7D_ADC_CHAN(15), +}; + +static const u32 imx7d_adc_average_num[] = { + IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4, + IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8, + IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16, + IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32, +}; + +static void imx7d_adc_feature_config(struct imx7d_adc *info) +{ + 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; +} + +static void imx7d_adc_sample_rate_set(struct imx7d_adc *info) +{ + struct imx7d_adc_feature *adc_feature = &info->adc_feature; + struct imx7d_adc_analogue_core_clk adc_analogure_clk; + u32 i; + u32 tmp_cfg1; + u32 sample_rate = 0; + + /* + * Before sample set, disable channel A,B,C,D. Here we + * clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1. + */ + for (i = 0; i < 4; i++) { + tmp_cfg1 = + readl(info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET); + tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN; + writel(tmp_cfg1, + info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET); + } + + adc_analogure_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div]; + sample_rate |= adc_analogure_clk.reg_config; + info->pre_div_num = adc_analogure_clk.pre_div; + + sample_rate |= adc_feature->core_time_unit; + writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT); +} + +static void imx7d_adc_hw_init(struct imx7d_adc *info) +{ + u32 cfg; + + /* power up and enable adc analogue core */ + cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG); + cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN | + IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN); + cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN; + writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG); + + /* enable channel A,B,C,D interrupt */ + writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN, + info->regs + IMX7D_REG_ADC_INT_SIG_EN); + writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN, + info->regs + IMX7D_REG_ADC_INT_EN); + + imx7d_adc_sample_rate_set(info); +} + +static void imx7d_adc_channel_set(struct imx7d_adc *info) +{ + u32 cfg1 = 0; + u32 cfg2; + u32 channel; + + channel = info->channel; + + /* the channel choose single conversion, and enable average mode */ + cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN | + IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE | + IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN); + + /* + * physical channel 0 chose logical channel A + * physical channel 1 chose logical channel B + * physical channel 2 chose logical channel C + * physical channel 3 chose logical channel D + */ + cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel); + + /* + * read register REG_ADC_CH_A\B\C\D_CFG2, according to the + * channel chosen + */ + cfg2 = readl(info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel + + IMX7D_REG_ADC_CHANNEL_CFG2_BASE); + + cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num]; + + /* + * write the register REG_ADC_CH_A\B\C\D_CFG2, according to + * the channel chosen + */ + writel(cfg2, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel + + IMX7D_REG_ADC_CHANNEL_CFG2_BASE); + writel(cfg1, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel); +} + +static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info) +{ + u32 analogue_core_clk; + u32 core_time_unit = info->adc_feature.core_time_unit; + u32 tmp; + + analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num; + tmp = (core_time_unit + 1) * 6; + + return analogue_core_clk / tmp; +} + +static int imx7d_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct imx7d_adc *info = iio_priv(indio_dev); + + u32 channel; + long ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + reinit_completion(&info->completion); + + channel = chan->channel & 0x03; + info->channel = channel; + imx7d_adc_channel_set(info); + + ret = wait_for_completion_interruptible_timeout + (&info->completion, IMX7D_ADC_TIMEOUT); + if (ret == 0) { + mutex_unlock(&indio_dev->mlock); + return -ETIMEDOUT; + } + if (ret < 0) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + + *val = info->value; + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + info->vref_uv = regulator_get_voltage(info->vref); + *val = info->vref_uv / 1000; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = imx7d_adc_get_sample_rate(info); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int imx7d_adc_read_data(struct imx7d_adc *info) +{ + u32 channel; + u32 value; + + channel = info->channel & 0x03; + + /* + * channel A and B conversion result share one register, + * bit[27~16] is the channel B conversion result, + * bit[11~0] is the channel A conversion result. + * channel C and D is the same. + */ + if (channel < 2) + value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT); + else + value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT); + if (channel & 0x1) /* channel B or D */ + value = (value >> 16) & 0xFFF; + else /* channel A or C */ + value &= 0xFFF; + + return value; +} + +static irqreturn_t imx7d_adc_isr(int irq, void *dev_id) +{ + struct imx7d_adc *info = dev_id; + int status; + + status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS); + if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) { + info->value = imx7d_adc_read_data(info); + complete(&info->completion); + + /* + * The register IMX7D_REG_ADC_INT_STATUS can't clear + * itself after read operation, need software to write + * 0 to the related bit. Here we clear the channel A/B/C/D + * conversion finished flag. + */ + status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS; + writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS); + } + + /* + * If the channel A/B/C/D conversion timeout, report it and clear these + * timeout flags. + */ + if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) { + 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); + } + + return IRQ_HANDLED; +} + +static int imx7d_adc_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct imx7d_adc *info = iio_priv(indio_dev); + + if (!readval || reg % 4 || reg > IMX7D_REG_ADC_ADC_CFG) + return -EINVAL; + + *readval = readl(info->regs + reg); + + return 0; +} + +static const struct iio_info imx7d_adc_iio_info = { + .read_raw = &imx7d_adc_read_raw, + .debugfs_reg_access = &imx7d_adc_reg_access, +}; + +static const struct of_device_id imx7d_adc_match[] = { + { .compatible = "fsl,imx7d-adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx7d_adc_match); + +static void imx7d_adc_power_down(struct imx7d_adc *info) +{ + u32 adc_cfg; + + adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG); + adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN | + IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN; + adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN; + writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG); +} + +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); + int ret; + + ret = regulator_enable(info->vref); + if (ret) { + dev_err(info->dev, + "Can't enable adc reference top voltage, err = %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(info->clk); + if (ret) { + dev_err(info->dev, + "Could not prepare or enable clock.\n"); + regulator_disable(info->vref); + return ret; + } + + imx7d_adc_hw_init(info); + + return 0; +} + +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->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, + .driver = { + .name = "imx7d_adc", + .of_match_table = imx7d_adc_match, + .pm = &imx7d_adc_pm_ops, + }, +}; + +module_platform_driver(imx7d_adc_driver); + +MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>"); +MODULE_DESCRIPTION("Freescale IMX7D ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c new file mode 100644 index 000000000..b573ec60a --- /dev/null +++ b/drivers/iio/adc/ina2xx-adc.c @@ -0,0 +1,1102 @@ +/* + * INA2XX Current and Power Monitors + * + * Copyright 2015 Baylibre SAS. + * + * 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. + * + * Based on linux/drivers/iio/adc/ad7291.c + * Copyright 2010-2011 Analog Devices Inc. + * + * Based on linux/drivers/hwmon/ina2xx.c + * Copyright 2012 Lothar Felten <l-felten@ti.com> + * + * Licensed under the GPL-2 or later. + * + * IIO driver for INA219-220-226-230-231 + * + * Configurable 7-bit I2C slave address from 0x40 to 0x4F + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/sysfs.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/sched/task.h> +#include <linux/util_macros.h> + +#include <linux/platform_data/ina2xx.h> + +/* INA2XX registers definition */ +#define INA2XX_CONFIG 0x00 +#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */ +#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */ +#define INA2XX_POWER 0x03 /* readonly */ +#define INA2XX_CURRENT 0x04 /* readonly */ +#define INA2XX_CALIBRATION 0x05 + +#define INA226_MASK_ENABLE 0x06 +#define INA226_CVRF BIT(3) + +#define INA2XX_MAX_REGISTERS 8 + +/* settings - depend on use case */ +#define INA219_CONFIG_DEFAULT 0x399F /* PGA=1/8, BRNG=32V */ +#define INA219_DEFAULT_IT 532 +#define INA219_DEFAULT_BRNG 1 /* 32V */ +#define INA219_DEFAULT_PGA 125 /* 1000/8 */ +#define INA226_CONFIG_DEFAULT 0x4327 +#define INA226_DEFAULT_AVG 4 +#define INA226_DEFAULT_IT 1110 + +#define INA2XX_RSHUNT_DEFAULT 10000 + +/* + * bit masks for reading the settings in the configuration register + * FIXME: use regmap_fields. + */ +#define INA2XX_MODE_MASK GENMASK(3, 0) + +/* Gain for VShunt: 1/8 (default), 1/4, 1/2, 1 */ +#define INA219_PGA_MASK GENMASK(12, 11) +#define INA219_SHIFT_PGA(val) ((val) << 11) + +/* VBus range: 32V (default), 16V */ +#define INA219_BRNG_MASK BIT(13) +#define INA219_SHIFT_BRNG(val) ((val) << 13) + +/* Averaging for VBus/VShunt/Power */ +#define INA226_AVG_MASK GENMASK(11, 9) +#define INA226_SHIFT_AVG(val) ((val) << 9) + +/* Integration time for VBus */ +#define INA219_ITB_MASK GENMASK(10, 7) +#define INA219_SHIFT_ITB(val) ((val) << 7) +#define INA226_ITB_MASK GENMASK(8, 6) +#define INA226_SHIFT_ITB(val) ((val) << 6) + +/* Integration time for VShunt */ +#define INA219_ITS_MASK GENMASK(6, 3) +#define INA219_SHIFT_ITS(val) ((val) << 3) +#define INA226_ITS_MASK GENMASK(5, 3) +#define INA226_SHIFT_ITS(val) ((val) << 3) + +/* INA219 Bus voltage register, low bits are flags */ +#define INA219_OVF BIT(0) +#define INA219_CNVR BIT(1) +#define INA219_BUS_VOLTAGE_SHIFT 3 + +/* Cosmetic macro giving the sampling period for a full P=UxI cycle */ +#define SAMPLING_PERIOD(c) ((c->int_time_vbus + c->int_time_vshunt) \ + * c->avg) + +static bool ina2xx_is_writeable_reg(struct device *dev, unsigned int reg) +{ + return (reg == INA2XX_CONFIG) || (reg > INA2XX_CURRENT); +} + +static bool ina2xx_is_volatile_reg(struct device *dev, unsigned int reg) +{ + return (reg != INA2XX_CONFIG); +} + +static inline bool is_signed_reg(unsigned int reg) +{ + return (reg == INA2XX_SHUNT_VOLTAGE) || (reg == INA2XX_CURRENT); +} + +static const struct regmap_config ina2xx_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = INA2XX_MAX_REGISTERS, + .writeable_reg = ina2xx_is_writeable_reg, + .volatile_reg = ina2xx_is_volatile_reg, +}; + +enum ina2xx_ids { ina219, ina226 }; + +struct ina2xx_config { + u16 config_default; + int calibration_value; + int shunt_voltage_lsb; /* nV */ + int bus_voltage_shift; /* position of lsb */ + int bus_voltage_lsb; /* uV */ + /* fixed relation between current and power lsb, uW/uA */ + int power_lsb_factor; + enum ina2xx_ids chip_id; +}; + +struct ina2xx_chip_info { + struct regmap *regmap; + struct task_struct *task; + const struct ina2xx_config *config; + struct mutex state_lock; + unsigned int shunt_resistor_uohm; + int avg; + int int_time_vbus; /* Bus voltage integration time uS */ + int int_time_vshunt; /* Shunt voltage integration time uS */ + int range_vbus; /* Bus voltage maximum in V */ + int pga_gain_vshunt; /* Shunt voltage PGA gain */ + bool allow_async_readout; + /* data buffer needs space for channel data and timestamp */ + struct { + u16 chan[4]; + u64 ts __aligned(8); + } scan; +}; + +static const struct ina2xx_config ina2xx_config[] = { + [ina219] = { + .config_default = INA219_CONFIG_DEFAULT, + .calibration_value = 4096, + .shunt_voltage_lsb = 10000, + .bus_voltage_shift = INA219_BUS_VOLTAGE_SHIFT, + .bus_voltage_lsb = 4000, + .power_lsb_factor = 20, + .chip_id = ina219, + }, + [ina226] = { + .config_default = INA226_CONFIG_DEFAULT, + .calibration_value = 2048, + .shunt_voltage_lsb = 2500, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 25, + .chip_id = ina226, + }, +}; + +static int ina2xx_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + unsigned int regval; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(chip->regmap, chan->address, ®val); + if (ret) + return ret; + + if (is_signed_reg(chan->address)) + *val = (s16) regval; + else + *val = regval; + + if (chan->address == INA2XX_BUS_VOLTAGE) + *val >>= chip->config->bus_voltage_shift; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = chip->avg; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + if (chan->address == INA2XX_SHUNT_VOLTAGE) + *val2 = chip->int_time_vshunt; + else + *val2 = chip->int_time_vbus; + + return IIO_VAL_INT_PLUS_MICRO; + + case IIO_CHAN_INFO_SAMP_FREQ: + /* + * Sample freq is read only, it is a consequence of + * 1/AVG*(CT_bus+CT_shunt). + */ + *val = DIV_ROUND_CLOSEST(1000000, SAMPLING_PERIOD(chip)); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + /* processed (mV) = raw * lsb(nV) / 1000000 */ + *val = chip->config->shunt_voltage_lsb; + *val2 = 1000000; + return IIO_VAL_FRACTIONAL; + + case INA2XX_BUS_VOLTAGE: + /* processed (mV) = raw * lsb (uV) / 1000 */ + *val = chip->config->bus_voltage_lsb; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + + case INA2XX_CURRENT: + /* + * processed (mA) = raw * current_lsb (mA) + * current_lsb (mA) = shunt_voltage_lsb (nV) / + * shunt_resistor (uOhm) + */ + *val = chip->config->shunt_voltage_lsb; + *val2 = chip->shunt_resistor_uohm; + return IIO_VAL_FRACTIONAL; + + case INA2XX_POWER: + /* + * processed (mW) = raw * power_lsb (mW) + * power_lsb (mW) = power_lsb_factor (mW/mA) * + * current_lsb (mA) + */ + *val = chip->config->power_lsb_factor * + chip->config->shunt_voltage_lsb; + *val2 = chip->shunt_resistor_uohm; + return IIO_VAL_FRACTIONAL; + } + return -EINVAL; + + case IIO_CHAN_INFO_HARDWAREGAIN: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + *val = chip->pga_gain_vshunt; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + + case INA2XX_BUS_VOLTAGE: + *val = chip->range_vbus == 32 ? 1 : 2; + return IIO_VAL_INT; + } + return -EINVAL; + } + + return -EINVAL; +} + +/* + * Available averaging rates for ina226. The indices correspond with + * the bit values expected by the chip (according to the ina226 datasheet, + * table 3 AVG bit settings, found at + * https://www.ti.com/lit/ds/symlink/ina226.pdf. + */ +static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 }; + +static int ina226_set_average(struct ina2xx_chip_info *chip, unsigned int val, + unsigned int *config) +{ + int bits; + + if (val > 1024 || val < 1) + return -EINVAL; + + bits = find_closest(val, ina226_avg_tab, + ARRAY_SIZE(ina226_avg_tab)); + + chip->avg = ina226_avg_tab[bits]; + + *config &= ~INA226_AVG_MASK; + *config |= INA226_SHIFT_AVG(bits) & INA226_AVG_MASK; + + return 0; +} + +/* Conversion times in uS */ +static const int ina226_conv_time_tab[] = { 140, 204, 332, 588, 1100, + 2116, 4156, 8244 }; + +static int ina226_set_int_time_vbus(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits; + + if (val_us > 8244 || val_us < 140) + return -EINVAL; + + bits = find_closest(val_us, ina226_conv_time_tab, + ARRAY_SIZE(ina226_conv_time_tab)); + + chip->int_time_vbus = ina226_conv_time_tab[bits]; + + *config &= ~INA226_ITB_MASK; + *config |= INA226_SHIFT_ITB(bits) & INA226_ITB_MASK; + + return 0; +} + +static int ina226_set_int_time_vshunt(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits; + + if (val_us > 8244 || val_us < 140) + return -EINVAL; + + bits = find_closest(val_us, ina226_conv_time_tab, + ARRAY_SIZE(ina226_conv_time_tab)); + + chip->int_time_vshunt = ina226_conv_time_tab[bits]; + + *config &= ~INA226_ITS_MASK; + *config |= INA226_SHIFT_ITS(bits) & INA226_ITS_MASK; + + return 0; +} + +/* Conversion times in uS. */ +static const int ina219_conv_time_tab_subsample[] = { 84, 148, 276, 532 }; +static const int ina219_conv_time_tab_average[] = { 532, 1060, 2130, 4260, + 8510, 17020, 34050, 68100}; + +static int ina219_lookup_int_time(unsigned int *val_us, int *bits) +{ + if (*val_us > 68100 || *val_us < 84) + return -EINVAL; + + if (*val_us <= 532) { + *bits = find_closest(*val_us, ina219_conv_time_tab_subsample, + ARRAY_SIZE(ina219_conv_time_tab_subsample)); + *val_us = ina219_conv_time_tab_subsample[*bits]; + } else { + *bits = find_closest(*val_us, ina219_conv_time_tab_average, + ARRAY_SIZE(ina219_conv_time_tab_average)); + *val_us = ina219_conv_time_tab_average[*bits]; + *bits |= 0x8; + } + + return 0; +} + +static int ina219_set_int_time_vbus(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits, ret; + unsigned int val_us_best = val_us; + + ret = ina219_lookup_int_time(&val_us_best, &bits); + if (ret) + return ret; + + chip->int_time_vbus = val_us_best; + + *config &= ~INA219_ITB_MASK; + *config |= INA219_SHIFT_ITB(bits) & INA219_ITB_MASK; + + return 0; +} + +static int ina219_set_int_time_vshunt(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits, ret; + unsigned int val_us_best = val_us; + + ret = ina219_lookup_int_time(&val_us_best, &bits); + if (ret) + return ret; + + chip->int_time_vshunt = val_us_best; + + *config &= ~INA219_ITS_MASK; + *config |= INA219_SHIFT_ITS(bits) & INA219_ITS_MASK; + + return 0; +} + +static const int ina219_vbus_range_tab[] = { 1, 2 }; +static int ina219_set_vbus_range_denom(struct ina2xx_chip_info *chip, + unsigned int range, + unsigned int *config) +{ + if (range == 1) + chip->range_vbus = 32; + else if (range == 2) + chip->range_vbus = 16; + else + return -EINVAL; + + *config &= ~INA219_BRNG_MASK; + *config |= INA219_SHIFT_BRNG(range == 1 ? 1 : 0) & INA219_BRNG_MASK; + + return 0; +} + +static const int ina219_vshunt_gain_tab[] = { 125, 250, 500, 1000 }; +static const int ina219_vshunt_gain_frac[] = { + 125, 1000, 250, 1000, 500, 1000, 1000, 1000 }; + +static int ina219_set_vshunt_pga_gain(struct ina2xx_chip_info *chip, + unsigned int gain, + unsigned int *config) +{ + int bits; + + if (gain < 125 || gain > 1000) + return -EINVAL; + + bits = find_closest(gain, ina219_vshunt_gain_tab, + ARRAY_SIZE(ina219_vshunt_gain_tab)); + + chip->pga_gain_vshunt = ina219_vshunt_gain_tab[bits]; + bits = 3 - bits; + + *config &= ~INA219_PGA_MASK; + *config |= INA219_SHIFT_PGA(bits) & INA219_PGA_MASK; + + return 0; +} + +static int ina2xx_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + *type = IIO_VAL_FRACTIONAL; + *length = sizeof(ina219_vshunt_gain_frac) / sizeof(int); + *vals = ina219_vshunt_gain_frac; + return IIO_AVAIL_LIST; + + case INA2XX_BUS_VOLTAGE: + *type = IIO_VAL_INT; + *length = sizeof(ina219_vbus_range_tab) / sizeof(int); + *vals = ina219_vbus_range_tab; + return IIO_AVAIL_LIST; + } + } + + return -EINVAL; +} + +static int ina2xx_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + unsigned int config, tmp; + int ret; + + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + mutex_lock(&chip->state_lock); + + ret = regmap_read(chip->regmap, INA2XX_CONFIG, &config); + if (ret) + goto err; + + tmp = config; + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = ina226_set_average(chip, val, &tmp); + break; + + case IIO_CHAN_INFO_INT_TIME: + if (chip->config->chip_id == ina226) { + if (chan->address == INA2XX_SHUNT_VOLTAGE) + ret = ina226_set_int_time_vshunt(chip, val2, + &tmp); + else + ret = ina226_set_int_time_vbus(chip, val2, + &tmp); + } else { + if (chan->address == INA2XX_SHUNT_VOLTAGE) + ret = ina219_set_int_time_vshunt(chip, val2, + &tmp); + else + ret = ina219_set_int_time_vbus(chip, val2, + &tmp); + } + break; + + case IIO_CHAN_INFO_HARDWAREGAIN: + if (chan->address == INA2XX_SHUNT_VOLTAGE) + ret = ina219_set_vshunt_pga_gain(chip, val * 1000 + + val2 / 1000, &tmp); + else + ret = ina219_set_vbus_range_denom(chip, val, &tmp); + break; + + default: + ret = -EINVAL; + } + + if (!ret && (tmp != config)) + ret = regmap_write(chip->regmap, INA2XX_CONFIG, tmp); +err: + mutex_unlock(&chip->state_lock); + + return ret; +} + +static ssize_t ina2xx_allow_async_readout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + + return sprintf(buf, "%d\n", chip->allow_async_readout); +} + +static ssize_t ina2xx_allow_async_readout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + bool val; + int ret; + + ret = strtobool((const char *) buf, &val); + if (ret) + return ret; + + chip->allow_async_readout = val; + + return len; +} + +/* + * Calibration register is set to the best value, which eliminates + * truncation errors on calculating current register in hardware. + * According to datasheet (INA 226: eq. 3, INA219: eq. 4) the best values + * are 2048 for ina226 and 4096 for ina219. They are hardcoded as + * calibration_value. + */ +static int ina2xx_set_calibration(struct ina2xx_chip_info *chip) +{ + return regmap_write(chip->regmap, INA2XX_CALIBRATION, + chip->config->calibration_value); +} + +static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val) +{ + if (val == 0 || val > INT_MAX) + return -EINVAL; + + chip->shunt_resistor_uohm = val; + + return 0; +} + +static ssize_t ina2xx_shunt_resistor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + int vals[2] = { chip->shunt_resistor_uohm, 1000000 }; + + return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals); +} + +static ssize_t ina2xx_shunt_resistor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + int val, val_fract, ret; + + ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract); + if (ret) + return ret; + + ret = set_shunt_resistor(chip, val * 1000000 + val_fract); + if (ret) + return ret; + + return len; +} + +#define INA219_CHAN(_type, _index, _address) { \ + .type = (_type), \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +#define INA226_CHAN(_type, _index, _address) { \ + .type = (_type), \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +/* + * Sampling Freq is a consequence of the integration times of + * the Voltage channels. + */ +#define INA219_CHAN_VOLTAGE(_index, _address, _shift) { \ + .type = IIO_VOLTAGE, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME) | \ + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .shift = _shift, \ + .realbits = 16 - _shift, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + } \ +} + +#define INA226_CHAN_VOLTAGE(_index, _address) { \ + .type = IIO_VOLTAGE, \ + .address = (_address), \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + } \ +} + + +static const struct iio_chan_spec ina226_channels[] = { + INA226_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE), + INA226_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE), + INA226_CHAN(IIO_POWER, 2, INA2XX_POWER), + INA226_CHAN(IIO_CURRENT, 3, INA2XX_CURRENT), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_chan_spec ina219_channels[] = { + INA219_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE, 0), + INA219_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE, INA219_BUS_VOLTAGE_SHIFT), + INA219_CHAN(IIO_POWER, 2, INA2XX_POWER), + INA219_CHAN(IIO_CURRENT, 3, INA2XX_CURRENT), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static int ina2xx_conversion_ready(struct iio_dev *indio_dev) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + int ret; + unsigned int alert; + + /* + * Because the timer thread and the chip conversion clock + * are asynchronous, the period difference will eventually + * result in reading V[k-1] again, or skip V[k] at time Tk. + * In order to resync the timer with the conversion process + * we check the ConVersionReadyFlag. + * On hardware that supports using the ALERT pin to toggle a + * GPIO a triggered buffer could be used instead. + * For now, we do an extra read of the MASK_ENABLE register (INA226) + * resp. the BUS_VOLTAGE register (INA219). + */ + if (chip->config->chip_id == ina226) { + ret = regmap_read(chip->regmap, + INA226_MASK_ENABLE, &alert); + alert &= INA226_CVRF; + } else { + ret = regmap_read(chip->regmap, + INA2XX_BUS_VOLTAGE, &alert); + alert &= INA219_CNVR; + } + + if (ret < 0) + return ret; + + return !!alert; +} + +static int ina2xx_work_buffer(struct iio_dev *indio_dev) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + int bit, ret, i = 0; + s64 time; + + time = iio_get_time_ns(indio_dev); + + /* + * Single register reads: bulk_read will not work with ina226/219 + * as there is no auto-increment of the register pointer. + */ + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + unsigned int val; + + ret = regmap_read(chip->regmap, + INA2XX_SHUNT_VOLTAGE + bit, &val); + if (ret < 0) + return ret; + + chip->scan.chan[i++] = val; + } + + iio_push_to_buffers_with_timestamp(indio_dev, &chip->scan, time); + + return 0; +}; + +static int ina2xx_capture_thread(void *data) +{ + struct iio_dev *indio_dev = data; + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + int sampling_us = SAMPLING_PERIOD(chip); + int ret; + struct timespec64 next, now, delta; + s64 delay_us; + + /* + * Poll a bit faster than the chip internal Fs, in case + * we wish to sync with the conversion ready flag. + */ + if (!chip->allow_async_readout) + sampling_us -= 200; + + ktime_get_ts64(&next); + + do { + while (!chip->allow_async_readout) { + ret = ina2xx_conversion_ready(indio_dev); + if (ret < 0) + return ret; + + /* + * If the conversion was not yet finished, + * reset the reference timestamp. + */ + if (ret == 0) + ktime_get_ts64(&next); + else + break; + } + + ret = ina2xx_work_buffer(indio_dev); + if (ret < 0) + return ret; + + ktime_get_ts64(&now); + + /* + * Advance the timestamp for the next poll by one sampling + * interval, and sleep for the remainder (next - now) + * In case "next" has already passed, the interval is added + * multiple times, i.e. samples are dropped. + */ + do { + timespec64_add_ns(&next, 1000 * sampling_us); + delta = timespec64_sub(next, now); + delay_us = div_s64(timespec64_to_ns(&delta), 1000); + } while (delay_us <= 0); + + usleep_range(delay_us, (delay_us * 3) >> 1); + + } while (!kthread_should_stop()); + + return 0; +} + +static int ina2xx_buffer_enable(struct iio_dev *indio_dev) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + unsigned int sampling_us = SAMPLING_PERIOD(chip); + struct task_struct *task; + + dev_dbg(&indio_dev->dev, "Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n", + (unsigned int)(*indio_dev->active_scan_mask), + 1000000 / sampling_us, chip->avg); + + dev_dbg(&indio_dev->dev, "Expected work period: %u us\n", sampling_us); + dev_dbg(&indio_dev->dev, "Async readout mode: %d\n", + chip->allow_async_readout); + + task = kthread_create(ina2xx_capture_thread, (void *)indio_dev, + "%s:%d-%uus", indio_dev->name, indio_dev->id, + sampling_us); + if (IS_ERR(task)) + return PTR_ERR(task); + + get_task_struct(task); + wake_up_process(task); + chip->task = task; + + return 0; +} + +static int ina2xx_buffer_disable(struct iio_dev *indio_dev) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + + if (chip->task) { + kthread_stop(chip->task); + put_task_struct(chip->task); + chip->task = NULL; + } + + return 0; +} + +static const struct iio_buffer_setup_ops ina2xx_setup_ops = { + .postenable = &ina2xx_buffer_enable, + .predisable = &ina2xx_buffer_disable, +}; + +static int ina2xx_debug_reg(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, unsigned *readval) +{ + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + + if (!readval) + return regmap_write(chip->regmap, reg, writeval); + + return regmap_read(chip->regmap, reg, readval); +} + +/* Possible integration times for vshunt and vbus */ +static IIO_CONST_ATTR_NAMED(ina219_integration_time_available, + integration_time_available, + "0.000084 0.000148 0.000276 0.000532 0.001060 0.002130 0.004260 0.008510 0.017020 0.034050 0.068100"); + +static IIO_CONST_ATTR_NAMED(ina226_integration_time_available, + integration_time_available, + "0.000140 0.000204 0.000332 0.000588 0.001100 0.002116 0.004156 0.008244"); + +static IIO_DEVICE_ATTR(in_allow_async_readout, S_IRUGO | S_IWUSR, + ina2xx_allow_async_readout_show, + ina2xx_allow_async_readout_store, 0); + +static IIO_DEVICE_ATTR(in_shunt_resistor, S_IRUGO | S_IWUSR, + ina2xx_shunt_resistor_show, + ina2xx_shunt_resistor_store, 0); + +static struct attribute *ina219_attributes[] = { + &iio_dev_attr_in_allow_async_readout.dev_attr.attr, + &iio_const_attr_ina219_integration_time_available.dev_attr.attr, + &iio_dev_attr_in_shunt_resistor.dev_attr.attr, + NULL, +}; + +static struct attribute *ina226_attributes[] = { + &iio_dev_attr_in_allow_async_readout.dev_attr.attr, + &iio_const_attr_ina226_integration_time_available.dev_attr.attr, + &iio_dev_attr_in_shunt_resistor.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ina219_attribute_group = { + .attrs = ina219_attributes, +}; + +static const struct attribute_group ina226_attribute_group = { + .attrs = ina226_attributes, +}; + +static const struct iio_info ina219_info = { + .attrs = &ina219_attribute_group, + .read_raw = ina2xx_read_raw, + .read_avail = ina2xx_read_avail, + .write_raw = ina2xx_write_raw, + .debugfs_reg_access = ina2xx_debug_reg, +}; + +static const struct iio_info ina226_info = { + .attrs = &ina226_attribute_group, + .read_raw = ina2xx_read_raw, + .write_raw = ina2xx_write_raw, + .debugfs_reg_access = ina2xx_debug_reg, +}; + +/* Initialize the configuration and calibration registers. */ +static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config) +{ + int ret = regmap_write(chip->regmap, INA2XX_CONFIG, config); + if (ret) + return ret; + + return ina2xx_set_calibration(chip); +} + +static int ina2xx_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ina2xx_chip_info *chip; + struct iio_dev *indio_dev; + struct iio_buffer *buffer; + unsigned int val; + enum ina2xx_ids type; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + + /* This is only used for device removal purposes. */ + i2c_set_clientdata(client, indio_dev); + + chip->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config); + if (IS_ERR(chip->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(chip->regmap); + } + + if (client->dev.of_node) + type = (enum ina2xx_ids)of_device_get_match_data(&client->dev); + else + type = id->driver_data; + chip->config = &ina2xx_config[type]; + + mutex_init(&chip->state_lock); + + if (of_property_read_u32(client->dev.of_node, + "shunt-resistor", &val) < 0) { + struct ina2xx_platform_data *pdata = + dev_get_platdata(&client->dev); + + if (pdata) + val = pdata->shunt_uohms; + else + val = INA2XX_RSHUNT_DEFAULT; + } + + ret = set_shunt_resistor(chip, val); + if (ret) + return ret; + + /* Patch the current config register with default. */ + val = chip->config->config_default; + + if (id->driver_data == ina226) { + ina226_set_average(chip, INA226_DEFAULT_AVG, &val); + ina226_set_int_time_vbus(chip, INA226_DEFAULT_IT, &val); + ina226_set_int_time_vshunt(chip, INA226_DEFAULT_IT, &val); + } else { + chip->avg = 1; + ina219_set_int_time_vbus(chip, INA219_DEFAULT_IT, &val); + ina219_set_int_time_vshunt(chip, INA219_DEFAULT_IT, &val); + ina219_set_vbus_range_denom(chip, INA219_DEFAULT_BRNG, &val); + ina219_set_vshunt_pga_gain(chip, INA219_DEFAULT_PGA, &val); + } + + ret = ina2xx_init(chip, val); + if (ret) { + dev_err(&client->dev, "error configuring the device\n"); + return ret; + } + + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + if (id->driver_data == ina226) { + indio_dev->channels = ina226_channels; + indio_dev->num_channels = ARRAY_SIZE(ina226_channels); + indio_dev->info = &ina226_info; + } else { + indio_dev->channels = ina219_channels; + indio_dev->num_channels = ARRAY_SIZE(ina219_channels); + indio_dev->info = &ina219_info; + } + indio_dev->name = id->name; + indio_dev->setup_ops = &ina2xx_setup_ops; + + buffer = devm_iio_kfifo_allocate(&indio_dev->dev); + if (!buffer) + return -ENOMEM; + + iio_device_attach_buffer(indio_dev, buffer); + + return iio_device_register(indio_dev); +} + +static int ina2xx_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ina2xx_chip_info *chip = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + /* Powerdown */ + return regmap_update_bits(chip->regmap, INA2XX_CONFIG, + INA2XX_MODE_MASK, 0); +} + +static const struct i2c_device_id ina2xx_id[] = { + {"ina219", ina219}, + {"ina220", ina219}, + {"ina226", ina226}, + {"ina230", ina226}, + {"ina231", ina226}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ina2xx_id); + +static const struct of_device_id ina2xx_of_match[] = { + { + .compatible = "ti,ina219", + .data = (void *)ina219 + }, + { + .compatible = "ti,ina220", + .data = (void *)ina219 + }, + { + .compatible = "ti,ina226", + .data = (void *)ina226 + }, + { + .compatible = "ti,ina230", + .data = (void *)ina226 + }, + { + .compatible = "ti,ina231", + .data = (void *)ina226 + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ina2xx_of_match); + +static struct i2c_driver ina2xx_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ina2xx_of_match, + }, + .probe = ina2xx_probe, + .remove = ina2xx_remove, + .id_table = ina2xx_id, +}; +module_i2c_driver(ina2xx_driver); + +MODULE_AUTHOR("Marc Titinger <marc.titinger@baylibre.com>"); +MODULE_DESCRIPTION("Texas Instruments INA2XX ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c new file mode 100644 index 000000000..1aafbe2cf --- /dev/null +++ b/drivers/iio/adc/ingenic-adc.c @@ -0,0 +1,848 @@ +// 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/buffer.h> +#include <linux/iio/iio.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.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_ADSAME 0x10 +#define JZ_ADC_REG_ADWAIT 0x14 +#define JZ_ADC_REG_ADTCH 0x18 +#define JZ_ADC_REG_ADBDAT 0x1c +#define JZ_ADC_REG_ADSDAT 0x20 +#define JZ_ADC_REG_ADCMD 0x24 +#define JZ_ADC_REG_ADCLK 0x28 + +#define JZ_ADC_REG_ENABLE_PD BIT(7) +#define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1)) +#define JZ_ADC_REG_CFG_BAT_MD BIT(4) +#define JZ_ADC_REG_CFG_SAMPLE_NUM(n) ((n) << 10) +#define JZ_ADC_REG_CFG_PULL_UP(n) ((n) << 16) +#define JZ_ADC_REG_CFG_CMD_SEL BIT(22) +#define JZ_ADC_REG_CFG_TOUCH_OPS_MASK (BIT(31) | GENMASK(23, 10)) +#define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0 +#define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16 +#define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8 +#define JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB 16 + +#define JZ_ADC_REG_ADCMD_YNADC BIT(7) +#define JZ_ADC_REG_ADCMD_YPADC BIT(8) +#define JZ_ADC_REG_ADCMD_XNADC BIT(9) +#define JZ_ADC_REG_ADCMD_XPADC BIT(10) +#define JZ_ADC_REG_ADCMD_VREFPYP BIT(11) +#define JZ_ADC_REG_ADCMD_VREFPXP BIT(12) +#define JZ_ADC_REG_ADCMD_VREFPXN BIT(13) +#define JZ_ADC_REG_ADCMD_VREFPAUX BIT(14) +#define JZ_ADC_REG_ADCMD_VREFPVDD33 BIT(15) +#define JZ_ADC_REG_ADCMD_VREFNYN BIT(16) +#define JZ_ADC_REG_ADCMD_VREFNXP BIT(17) +#define JZ_ADC_REG_ADCMD_VREFNXN BIT(18) +#define JZ_ADC_REG_ADCMD_VREFAUX BIT(19) +#define JZ_ADC_REG_ADCMD_YNGRU BIT(20) +#define JZ_ADC_REG_ADCMD_XNGRU BIT(21) +#define JZ_ADC_REG_ADCMD_XPGRU BIT(22) +#define JZ_ADC_REG_ADCMD_YPSUP BIT(23) +#define JZ_ADC_REG_ADCMD_XNSUP BIT(24) +#define JZ_ADC_REG_ADCMD_XPSUP BIT(25) + +#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 +#define JZ4770_ADC_BATTERY_VREF 1200 +#define JZ4770_ADC_BATTERY_VREF_BITS 12 + +#define JZ_ADC_IRQ_AUX BIT(0) +#define JZ_ADC_IRQ_BATTERY BIT(1) +#define JZ_ADC_IRQ_TOUCH BIT(2) +#define JZ_ADC_IRQ_PEN_DOWN BIT(3) +#define JZ_ADC_IRQ_PEN_UP BIT(4) +#define JZ_ADC_IRQ_PEN_DOWN_SLEEP BIT(5) +#define JZ_ADC_IRQ_SLEEP BIT(7) + +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; + unsigned int battery_vref_mode: 1; + unsigned int has_aux2: 1; + const struct iio_chan_spec *channels; + unsigned int num_channels; + int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc); +}; + +struct ingenic_adc { + void __iomem *base; + struct clk *clk; + struct mutex lock; + struct mutex aux_lock; + const struct ingenic_adc_soc_data *soc_data; + bool low_vref_mode; +}; + +static void ingenic_adc_set_adcmd(struct iio_dev *iio_dev, unsigned long mask) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + + mutex_lock(&adc->lock); + + /* Init ADCMD */ + readl(adc->base + JZ_ADC_REG_ADCMD); + + if (mask & 0x3) { + /* Second channel (INGENIC_ADC_TOUCH_YP): sample YP vs. GND */ + writel(JZ_ADC_REG_ADCMD_XNGRU + | JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_YPADC, + adc->base + JZ_ADC_REG_ADCMD); + + /* First channel (INGENIC_ADC_TOUCH_XP): sample XP vs. GND */ + writel(JZ_ADC_REG_ADCMD_YNGRU + | JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_XPADC, + adc->base + JZ_ADC_REG_ADCMD); + } + + if (mask & 0xc) { + /* Fourth channel (INGENIC_ADC_TOUCH_YN): sample YN vs. GND */ + writel(JZ_ADC_REG_ADCMD_XNGRU + | JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_YNADC, + adc->base + JZ_ADC_REG_ADCMD); + + /* Third channel (INGENIC_ADC_TOUCH_XN): sample XN vs. GND */ + writel(JZ_ADC_REG_ADCMD_YNGRU + | JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_XNADC, + adc->base + JZ_ADC_REG_ADCMD); + } + + if (mask & 0x30) { + /* Sixth channel (INGENIC_ADC_TOUCH_YD): sample YP vs. YN */ + writel(JZ_ADC_REG_ADCMD_VREFNYN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_YPADC, + adc->base + JZ_ADC_REG_ADCMD); + + /* Fifth channel (INGENIC_ADC_TOUCH_XD): sample XP vs. XN */ + writel(JZ_ADC_REG_ADCMD_VREFNXN | JZ_ADC_REG_ADCMD_VREFPVDD33 + | JZ_ADC_REG_ADCMD_XPADC, + adc->base + JZ_ADC_REG_ADCMD); + } + + /* We're done */ + writel(0, adc->base + JZ_ADC_REG_ADCMD); + + mutex_unlock(&adc->lock); +} + +static void ingenic_adc_set_config(struct ingenic_adc *adc, + uint32_t mask, + uint32_t val) +{ + uint32_t cfg; + + 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); +} + +static void ingenic_adc_enable_unlocked(struct ingenic_adc *adc, + int engine, + bool enabled) +{ + u8 val; + + 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); +} + +static void ingenic_adc_enable(struct ingenic_adc *adc, + int engine, + bool enabled) +{ + mutex_lock(&adc->lock); + ingenic_adc_enable_unlocked(adc, engine, enabled); + mutex_unlock(&adc->lock); +} + +static int ingenic_adc_capture(struct ingenic_adc *adc, + int engine) +{ + u32 cfg; + u8 val; + int ret; + + /* + * Disable CMD_SEL temporarily, because it causes wrong VBAT readings, + * probably due to the switch of VREF. We must keep the lock here to + * avoid races with the buffer enable/disable functions. + */ + mutex_lock(&adc->lock); + cfg = readl(adc->base + JZ_ADC_REG_CFG); + writel(cfg & ~JZ_ADC_REG_CFG_CMD_SEL, adc->base + JZ_ADC_REG_CFG); + + ingenic_adc_enable_unlocked(adc, engine, true); + ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val, + !(val & BIT(engine)), 250, 1000); + if (ret) + ingenic_adc_enable_unlocked(adc, engine, false); + + writel(cfg, adc->base + JZ_ADC_REG_CFG); + mutex_unlock(&adc->lock); + + 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); + struct device *dev = iio_dev->dev.parent; + int ret; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case INGENIC_ADC_BATTERY: + if (!adc->soc_data->battery_vref_mode) + return -EINVAL; + + ret = clk_enable(adc->clk); + if (ret) { + dev_err(dev, "Failed to enable clock: %d\n", + ret); + return ret; + } + + 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; + } + + clk_disable(adc->clk); + + return 0; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const int jz4725b_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, +}; + +static const int jz4725b_adc_battery_scale_avail[] = { + JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS, + JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, +}; + +static const int jz4740_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1, +}; + +static const int jz4740_adc_battery_scale_avail[] = { + JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS, + JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS, +}; + +static const int jz4770_adc_battery_raw_avail[] = { + 0, 1, (1 << JZ4770_ADC_BATTERY_VREF_BITS) - 1, +}; + +static const int jz4770_adc_battery_scale_avail[] = { + JZ4770_ADC_BATTERY_VREF, JZ4770_ADC_BATTERY_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) << JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB) | + (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB, + adc->base + JZ_ADC_REG_ADCLK); + + return 0; +} + +static int jz4770_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_ms, 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 JZ4770 ADC works at 20 kHz to 200 kHz. + * We pick the highest rate possible. + */ + div_main = DIV_ROUND_UP(parent_rate, 200000); + div_main = clamp(div_main, 1u, 256u); + rate = parent_rate / div_main; + if (rate < 20000 || rate > 200000) { + 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, 10000); + /* And another, which produces a 1ms clock. */ + div_ms = DIV_ROUND_UP(rate, 1000); + + writel(((div_ms - 1) << JZ4770_ADC_REG_ADCLK_CLKDIVMS_LSB) | + ((div_10us - 1) << JZ4770_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 iio_chan_spec jz4740_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, + .scan_index = -1, + }, + { + .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, + .scan_index = -1, + }, +}; + +static const struct iio_chan_spec jz4770_channels[] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_XP, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_YP, + .scan_index = 1, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_XN, + .scan_index = 2, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_YN, + .scan_index = 3, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_XD, + .scan_index = 4, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = INGENIC_ADC_TOUCH_YD, + .scan_index = 5, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, + }, + { + .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, + .scan_index = -1, + }, + { + .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, + .scan_index = -1, + }, + { + .extend_name = "aux2", + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = INGENIC_ADC_AUX2, + .scan_index = -1, + }, +}; + +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), + .battery_vref_mode = true, + .has_aux2 = false, + .channels = jz4740_channels, + .num_channels = ARRAY_SIZE(jz4740_channels), + .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), + .battery_vref_mode = true, + .has_aux2 = false, + .channels = jz4740_channels, + .num_channels = ARRAY_SIZE(jz4740_channels), + .init_clk_div = NULL, /* no ADCLK register on JZ4740 */ +}; + +static const struct ingenic_adc_soc_data jz4770_adc_soc_data = { + .battery_high_vref = JZ4770_ADC_BATTERY_VREF, + .battery_high_vref_bits = JZ4770_ADC_BATTERY_VREF_BITS, + .battery_raw_avail = jz4770_adc_battery_raw_avail, + .battery_raw_avail_size = ARRAY_SIZE(jz4770_adc_battery_raw_avail), + .battery_scale_avail = jz4770_adc_battery_scale_avail, + .battery_scale_avail_size = ARRAY_SIZE(jz4770_adc_battery_scale_avail), + .battery_vref_mode = false, + .has_aux2 = true, + .channels = jz4770_channels, + .num_channels = ARRAY_SIZE(jz4770_channels), + .init_clk_div = jz4770_adc_init_clk_div, +}; + +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_chan_info_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + int bit, ret, engine = (chan->channel == INGENIC_ADC_BATTERY); + struct ingenic_adc *adc = iio_priv(iio_dev); + + ret = clk_enable(adc->clk); + if (ret) { + dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n", + ret); + return ret; + } + + /* We cannot sample AUX/AUX2 in parallel. */ + mutex_lock(&adc->aux_lock); + if (adc->soc_data->has_aux2 && engine == 0) { + bit = BIT(chan->channel == INGENIC_ADC_AUX2); + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_AUX_MD, bit); + } + + ret = ingenic_adc_capture(adc, engine); + if (ret) + goto out; + + switch (chan->channel) { + case INGENIC_ADC_AUX: + case INGENIC_ADC_AUX2: + *val = readw(adc->base + JZ_ADC_REG_ADSDAT); + break; + case INGENIC_ADC_BATTERY: + *val = readw(adc->base + JZ_ADC_REG_ADBDAT); + break; + } + + ret = IIO_VAL_INT; +out: + mutex_unlock(&adc->aux_lock); + clk_disable(adc->clk); + + return ret; +} + +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); + + switch (m) { + case IIO_CHAN_INFO_RAW: + return ingenic_adc_read_chan_info_raw(iio_dev, chan, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case INGENIC_ADC_AUX: + case INGENIC_ADC_AUX2: + *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 int ingenic_adc_of_xlate(struct iio_dev *iio_dev, + const struct of_phandle_args *iiospec) +{ + int i; + + if (!iiospec->args_count) + return -EINVAL; + + for (i = 0; i < iio_dev->num_channels; ++i) + if (iio_dev->channels[i].channel == iiospec->args[0]) + return i; + + 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, + .of_xlate = ingenic_adc_of_xlate, +}; + +static int ingenic_adc_buffer_enable(struct iio_dev *iio_dev) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + int ret; + + ret = clk_enable(adc->clk); + if (ret) { + dev_err(iio_dev->dev.parent, "Failed to enable clock: %d\n", + ret); + return ret; + } + + /* It takes significant time for the touchscreen hw to stabilize. */ + msleep(50); + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, + JZ_ADC_REG_CFG_SAMPLE_NUM(4) | + JZ_ADC_REG_CFG_PULL_UP(4)); + + writew(80, adc->base + JZ_ADC_REG_ADWAIT); + writew(2, adc->base + JZ_ADC_REG_ADSAME); + writeb((u8)~JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_CTRL); + writel(0, adc->base + JZ_ADC_REG_ADTCH); + + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL, + JZ_ADC_REG_CFG_CMD_SEL); + ingenic_adc_set_adcmd(iio_dev, iio_dev->active_scan_mask[0]); + + ingenic_adc_enable(adc, 2, true); + + return 0; +} + +static int ingenic_adc_buffer_disable(struct iio_dev *iio_dev) +{ + struct ingenic_adc *adc = iio_priv(iio_dev); + + ingenic_adc_enable(adc, 2, false); + + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_CMD_SEL, 0); + + writeb(0xff, adc->base + JZ_ADC_REG_CTRL); + writeb(0xff, adc->base + JZ_ADC_REG_STATUS); + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, 0); + writew(0, adc->base + JZ_ADC_REG_ADSAME); + writew(0, adc->base + JZ_ADC_REG_ADWAIT); + clk_disable(adc->clk); + + return 0; +} + +static const struct iio_buffer_setup_ops ingenic_buffer_setup_ops = { + .postenable = &ingenic_adc_buffer_enable, + .predisable = &ingenic_adc_buffer_disable +}; + +static irqreturn_t ingenic_adc_irq(int irq, void *data) +{ + struct iio_dev *iio_dev = data; + struct ingenic_adc *adc = iio_priv(iio_dev); + unsigned long mask = iio_dev->active_scan_mask[0]; + unsigned int i; + u32 tdat[3]; + + for (i = 0; i < ARRAY_SIZE(tdat); mask >>= 2, i++) { + if (mask & 0x3) + tdat[i] = readl(adc->base + JZ_ADC_REG_ADTCH); + else + tdat[i] = 0; + } + + iio_push_to_buffers(iio_dev, tdat); + writeb(JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_STATUS); + + return IRQ_HANDLED; +} + +static int ingenic_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *iio_dev; + struct ingenic_adc *adc; + const struct ingenic_adc_soc_data *soc_data; + int irq, 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); + mutex_init(&adc->aux_lock); + adc->soc_data = soc_data; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, ingenic_adc_irq, 0, + dev_name(dev), iio_dev); + if (ret < 0) { + dev_err(dev, "Failed to request irq: %d\n", ret); + return ret; + } + + adc->base = devm_platform_ioremap_resource(pdev, 0); + 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); + usleep_range(2000, 3000); /* Must wait at least 2ms. */ + 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->name = "jz-adc"; + iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + iio_dev->setup_ops = &ingenic_buffer_setup_ops; + iio_dev->channels = soc_data->channels; + iio_dev->num_channels = soc_data->num_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; +} + +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, }, + { .compatible = "ingenic,jz4770-adc", .data = &jz4770_adc_soc_data, }, + { }, +}; +MODULE_DEVICE_TABLE(of, ingenic_adc_of_match); + +static struct platform_driver ingenic_adc_driver = { + .driver = { + .name = "ingenic-adc", + .of_match_table = ingenic_adc_of_match, + }, + .probe = ingenic_adc_probe, +}; +module_platform_driver(ingenic_adc_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c new file mode 100644 index 000000000..75394350e --- /dev/null +++ b/drivers/iio/adc/intel_mrfld_adc.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ADC driver for Basin Cove PMIC + * + * Copyright (C) 2012 Intel Corporation + * Author: Bin Yang <bin.yang@intel.com> + * + * Rewritten for upstream by: + * Vincent Pelletier <plr.vincent@gmail.com> + * Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/bitops.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/mfd/intel_soc_pmic.h> +#include <linux/mfd/intel_soc_pmic_mrfld.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <linux/iio/driver.h> +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> + +#include <asm/unaligned.h> + +#define BCOVE_GPADCREQ 0xDC +#define BCOVE_GPADCREQ_BUSY BIT(0) +#define BCOVE_GPADCREQ_IRQEN BIT(1) + +#define BCOVE_ADCIRQ_ALL ( \ + BCOVE_ADCIRQ_BATTEMP | \ + BCOVE_ADCIRQ_SYSTEMP | \ + BCOVE_ADCIRQ_BATTID | \ + BCOVE_ADCIRQ_VIBATT | \ + BCOVE_ADCIRQ_CCTICK) + +#define BCOVE_ADC_TIMEOUT msecs_to_jiffies(1000) + +static const u8 mrfld_adc_requests[] = { + BCOVE_ADCIRQ_VIBATT, + BCOVE_ADCIRQ_BATTID, + BCOVE_ADCIRQ_VIBATT, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_BATTEMP, + BCOVE_ADCIRQ_BATTEMP, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_SYSTEMP, + BCOVE_ADCIRQ_SYSTEMP, +}; + +struct mrfld_adc { + struct regmap *regmap; + struct completion completion; + /* Lock to protect the IPC transfers */ + struct mutex lock; +}; + +static irqreturn_t mrfld_adc_thread_isr(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct mrfld_adc *adc = iio_priv(indio_dev); + + complete(&adc->completion); + return IRQ_HANDLED; +} + +static int mrfld_adc_single_conv(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *result) +{ + struct mrfld_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->regmap; + unsigned int req; + long timeout; + __be16 value; + int ret; + + reinit_completion(&adc->completion); + + regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0); + regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0); + + ret = regmap_read_poll_timeout(regmap, BCOVE_GPADCREQ, req, + !(req & BCOVE_GPADCREQ_BUSY), + 2000, 1000000); + if (ret) + goto done; + + req = mrfld_adc_requests[chan->channel]; + ret = regmap_write(regmap, BCOVE_GPADCREQ, BCOVE_GPADCREQ_IRQEN | req); + if (ret) + goto done; + + timeout = wait_for_completion_interruptible_timeout(&adc->completion, + BCOVE_ADC_TIMEOUT); + if (timeout < 0) { + ret = timeout; + goto done; + } + if (timeout == 0) { + ret = -ETIMEDOUT; + goto done; + } + + ret = regmap_bulk_read(regmap, chan->address, &value, sizeof(value)); + if (ret) + goto done; + + *result = be16_to_cpu(value); + ret = IIO_VAL_INT; + +done: + regmap_update_bits(regmap, BCOVE_MIRQLVL1, BCOVE_LVL1_ADC, 0xff); + regmap_update_bits(regmap, BCOVE_MADCIRQ, BCOVE_ADCIRQ_ALL, 0xff); + + return ret; +} + +static int mrfld_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mrfld_adc *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + ret = mrfld_adc_single_conv(indio_dev, chan, val); + mutex_unlock(&adc->lock); + return ret; + default: + return -EINVAL; + } +} + +static const struct iio_info mrfld_adc_iio_info = { + .read_raw = &mrfld_adc_read_raw, +}; + +#define BCOVE_ADC_CHANNEL(_type, _channel, _datasheet_name, _address) \ + { \ + .indexed = 1, \ + .type = _type, \ + .channel = _channel, \ + .address = _address, \ + .datasheet_name = _datasheet_name, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + } + +static const struct iio_chan_spec mrfld_adc_channels[] = { + BCOVE_ADC_CHANNEL(IIO_VOLTAGE, 0, "CH0", 0xE9), + BCOVE_ADC_CHANNEL(IIO_RESISTANCE, 1, "CH1", 0xEB), + BCOVE_ADC_CHANNEL(IIO_CURRENT, 2, "CH2", 0xED), + BCOVE_ADC_CHANNEL(IIO_TEMP, 3, "CH3", 0xCC), + BCOVE_ADC_CHANNEL(IIO_TEMP, 4, "CH4", 0xC8), + BCOVE_ADC_CHANNEL(IIO_TEMP, 5, "CH5", 0xCA), + BCOVE_ADC_CHANNEL(IIO_TEMP, 6, "CH6", 0xC2), + BCOVE_ADC_CHANNEL(IIO_TEMP, 7, "CH7", 0xC4), + BCOVE_ADC_CHANNEL(IIO_TEMP, 8, "CH8", 0xC6), +}; + +static struct iio_map iio_maps[] = { + IIO_MAP("CH0", "bcove-battery", "VBATRSLT"), + IIO_MAP("CH1", "bcove-battery", "BATTID"), + IIO_MAP("CH2", "bcove-battery", "IBATRSLT"), + IIO_MAP("CH3", "bcove-temp", "PMICTEMP"), + IIO_MAP("CH4", "bcove-temp", "BATTEMP0"), + IIO_MAP("CH5", "bcove-temp", "BATTEMP1"), + IIO_MAP("CH6", "bcove-temp", "SYSTEMP0"), + IIO_MAP("CH7", "bcove-temp", "SYSTEMP1"), + IIO_MAP("CH8", "bcove-temp", "SYSTEMP2"), + {} +}; + +static int mrfld_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent); + struct iio_dev *indio_dev; + struct mrfld_adc *adc; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(struct mrfld_adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + + mutex_init(&adc->lock); + init_completion(&adc->completion); + adc->regmap = pmic->regmap; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_adc_thread_isr, + IRQF_ONESHOT | IRQF_SHARED, pdev->name, + indio_dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = pdev->name; + + indio_dev->channels = mrfld_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(mrfld_adc_channels); + indio_dev->info = &mrfld_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_map_array_register(indio_dev, iio_maps); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret < 0) + goto err_array_unregister; + + return 0; + +err_array_unregister: + iio_map_array_unregister(indio_dev); + return ret; +} + +static int mrfld_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_map_array_unregister(indio_dev); + + return 0; +} + +static const struct platform_device_id mrfld_adc_id_table[] = { + { .name = "mrfld_bcove_adc" }, + {} +}; +MODULE_DEVICE_TABLE(platform, mrfld_adc_id_table); + +static struct platform_driver mrfld_adc_driver = { + .driver = { + .name = "mrfld_bcove_adc", + }, + .probe = mrfld_adc_probe, + .remove = mrfld_adc_remove, + .id_table = mrfld_adc_id_table, +}; +module_platform_driver(mrfld_adc_driver); + +MODULE_AUTHOR("Bin Yang <bin.yang@intel.com>"); +MODULE_AUTHOR("Vincent Pelletier <plr.vincent@gmail.com>"); +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("ADC driver for Basin Cove PMIC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c new file mode 100644 index 000000000..8fb57e375 --- /dev/null +++ b/drivers/iio/adc/lp8788_adc.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI LP8788 MFD - ADC driver + * + * Copyright 2012 Texas Instruments + * + * Author: Milo(Woogyom) Kim <milo.kim@ti.com> + */ + +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> +#include <linux/iio/machine.h> +#include <linux/mfd/lp8788.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +/* register address */ +#define LP8788_ADC_CONF 0x60 +#define LP8788_ADC_RAW 0x61 +#define LP8788_ADC_DONE 0x63 + +#define ADC_CONV_START 1 + +struct lp8788_adc { + struct lp8788 *lp; + struct iio_map *map; + struct mutex lock; +}; + +static const int lp8788_scale[LPADC_MAX] = { + [LPADC_VBATT_5P5] = 1343101, + [LPADC_VIN_CHG] = 3052503, + [LPADC_IBATT] = 610500, + [LPADC_IC_TEMP] = 61050, + [LPADC_VBATT_6P0] = 1465201, + [LPADC_VBATT_5P0] = 1221001, + [LPADC_ADC1] = 610500, + [LPADC_ADC2] = 610500, + [LPADC_VDD] = 1025641, + [LPADC_VCOIN] = 757020, + [LPADC_ADC3] = 610500, + [LPADC_ADC4] = 610500, +}; + +static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id, + int *val) +{ + unsigned int msb; + unsigned int lsb; + unsigned int result; + u8 data; + u8 rawdata[2]; + int size = ARRAY_SIZE(rawdata); + int retry = 5; + int ret; + + data = (id << 1) | ADC_CONV_START; + ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data); + if (ret) + goto err_io; + + /* retry until adc conversion is done */ + data = 0; + while (retry--) { + usleep_range(100, 200); + + ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data); + if (ret) + goto err_io; + + /* conversion done */ + if (data) + break; + } + + ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size); + if (ret) + goto err_io; + + msb = (rawdata[0] << 4) & 0x00000ff0; + lsb = (rawdata[1] >> 4) & 0x0000000f; + result = msb | lsb; + *val = result; + + return 0; + +err_io: + return ret; +} + +static int lp8788_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct lp8788_adc *adc = iio_priv(indio_dev); + enum lp8788_adc_id id = chan->channel; + int ret; + + mutex_lock(&adc->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = lp8788_scale[id] / 1000000; + *val2 = lp8788_scale[id] % 1000000; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&adc->lock); + + return ret; +} + +static const struct iio_info lp8788_adc_info = { + .read_raw = &lp8788_adc_read_raw, +}; + +#define LP8788_CHAN(_id, _type) { \ + .type = _type, \ + .indexed = 1, \ + .channel = LPADC_##_id, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = #_id, \ +} + +static const struct iio_chan_spec lp8788_adc_channels[] = { + [LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE), + [LPADC_VIN_CHG] = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE), + [LPADC_IBATT] = LP8788_CHAN(IBATT, IIO_CURRENT), + [LPADC_IC_TEMP] = LP8788_CHAN(IC_TEMP, IIO_TEMP), + [LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE), + [LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE), + [LPADC_ADC1] = LP8788_CHAN(ADC1, IIO_VOLTAGE), + [LPADC_ADC2] = LP8788_CHAN(ADC2, IIO_VOLTAGE), + [LPADC_VDD] = LP8788_CHAN(VDD, IIO_VOLTAGE), + [LPADC_VCOIN] = LP8788_CHAN(VCOIN, IIO_VOLTAGE), + [LPADC_ADC3] = LP8788_CHAN(ADC3, IIO_VOLTAGE), + [LPADC_ADC4] = LP8788_CHAN(ADC4, IIO_VOLTAGE), +}; + +/* default maps used by iio consumer (lp8788-charger driver) */ +static struct iio_map lp8788_default_iio_maps[] = { + { + .consumer_dev_name = "lp8788-charger", + .consumer_channel = "lp8788_vbatt_5p0", + .adc_channel_label = "VBATT_5P0", + }, + { + .consumer_dev_name = "lp8788-charger", + .consumer_channel = "lp8788_adc1", + .adc_channel_label = "ADC1", + }, + { } +}; + +static int lp8788_iio_map_register(struct iio_dev *indio_dev, + struct lp8788_platform_data *pdata, + struct lp8788_adc *adc) +{ + struct iio_map *map; + int ret; + + map = (!pdata || !pdata->adc_pdata) ? + lp8788_default_iio_maps : pdata->adc_pdata; + + ret = iio_map_array_register(indio_dev, map); + if (ret) { + dev_err(&indio_dev->dev, "iio map err: %d\n", ret); + return ret; + } + + adc->map = map; + return 0; +} + +static int lp8788_adc_probe(struct platform_device *pdev) +{ + struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent); + struct iio_dev *indio_dev; + struct lp8788_adc *adc; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->lp = lp; + platform_set_drvdata(pdev, indio_dev); + + ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc); + if (ret) + return ret; + + mutex_init(&adc->lock); + + indio_dev->name = pdev->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &lp8788_adc_info; + indio_dev->channels = lp8788_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "iio dev register err: %d\n", ret); + goto err_iio_device; + } + + return 0; + +err_iio_device: + iio_map_array_unregister(indio_dev); + return ret; +} + +static int lp8788_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + + return 0; +} + +static struct platform_driver lp8788_adc_driver = { + .probe = lp8788_adc_probe, + .remove = lp8788_adc_remove, + .driver = { + .name = LP8788_DEV_ADC, + }, +}; +module_platform_driver(lp8788_adc_driver); + +MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:lp8788-adc"); diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c new file mode 100644 index 000000000..3566990ae --- /dev/null +++ b/drivers/iio/adc/lpc18xx_adc.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IIO ADC driver for NXP LPC18xx ADC + * + * Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com> + * + * UNSUPPORTED hardware features: + * - Hardware triggers + * - Burst mode + * - Interrupts + * - DMA + */ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +/* LPC18XX ADC registers and bits */ +#define LPC18XX_ADC_CR 0x000 +#define LPC18XX_ADC_CR_CLKDIV_SHIFT 8 +#define LPC18XX_ADC_CR_PDN BIT(21) +#define LPC18XX_ADC_CR_START_NOW (0x1 << 24) +#define LPC18XX_ADC_GDR 0x004 + +/* Data register bits */ +#define LPC18XX_ADC_SAMPLE_SHIFT 6 +#define LPC18XX_ADC_SAMPLE_MASK 0x3ff +#define LPC18XX_ADC_CONV_DONE BIT(31) + +/* Clock should be 4.5 MHz or less */ +#define LPC18XX_ADC_CLK_TARGET 4500000 + +struct lpc18xx_adc { + struct regulator *vref; + void __iomem *base; + struct device *dev; + struct mutex lock; + struct clk *clk; + u32 cr_reg; +}; + +#define LPC18XX_ADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _idx, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec lpc18xx_adc_iio_channels[] = { + LPC18XX_ADC_CHAN(0), + LPC18XX_ADC_CHAN(1), + LPC18XX_ADC_CHAN(2), + LPC18XX_ADC_CHAN(3), + LPC18XX_ADC_CHAN(4), + LPC18XX_ADC_CHAN(5), + LPC18XX_ADC_CHAN(6), + LPC18XX_ADC_CHAN(7), +}; + +static int lpc18xx_adc_read_chan(struct lpc18xx_adc *adc, unsigned int ch) +{ + int ret; + u32 reg; + + reg = adc->cr_reg | BIT(ch) | LPC18XX_ADC_CR_START_NOW; + writel(reg, adc->base + LPC18XX_ADC_CR); + + ret = readl_poll_timeout(adc->base + LPC18XX_ADC_GDR, reg, + reg & LPC18XX_ADC_CONV_DONE, 3, 9); + if (ret) { + dev_warn(adc->dev, "adc read timed out\n"); + return ret; + } + + return (reg >> LPC18XX_ADC_SAMPLE_SHIFT) & LPC18XX_ADC_SAMPLE_MASK; +} + +static int lpc18xx_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct lpc18xx_adc *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + *val = lpc18xx_adc_read_chan(adc, chan->channel); + mutex_unlock(&adc->lock); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = regulator_get_voltage(adc->vref) / 1000; + *val2 = 10; + + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info lpc18xx_adc_info = { + .read_raw = lpc18xx_adc_read_raw, +}; + +static int lpc18xx_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct lpc18xx_adc *adc; + unsigned int clkdiv; + unsigned long rate; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + adc = iio_priv(indio_dev); + adc->dev = &pdev->dev; + mutex_init(&adc->lock); + + adc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc->base)) + return PTR_ERR(adc->base); + + adc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(adc->clk)) { + dev_err(&pdev->dev, "error getting clock\n"); + return PTR_ERR(adc->clk); + } + + rate = clk_get_rate(adc->clk); + clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET); + + adc->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(adc->vref)) { + dev_err(&pdev->dev, "error getting regulator\n"); + return PTR_ERR(adc->vref); + } + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &lpc18xx_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = lpc18xx_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(lpc18xx_adc_iio_channels); + + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(&pdev->dev, "unable to enable regulator\n"); + return ret; + } + + ret = clk_prepare_enable(adc->clk); + if (ret) { + dev_err(&pdev->dev, "unable to enable clock\n"); + goto dis_reg; + } + + adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) | + LPC18XX_ADC_CR_PDN; + writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "unable to register device\n"); + goto dis_clk; + } + + return 0; + +dis_clk: + writel(0, adc->base + LPC18XX_ADC_CR); + clk_disable_unprepare(adc->clk); +dis_reg: + regulator_disable(adc->vref); + return ret; +} + +static int lpc18xx_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct lpc18xx_adc *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + writel(0, adc->base + LPC18XX_ADC_CR); + clk_disable_unprepare(adc->clk); + regulator_disable(adc->vref); + + return 0; +} + +static const struct of_device_id lpc18xx_adc_match[] = { + { .compatible = "nxp,lpc1850-adc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, lpc18xx_adc_match); + +static struct platform_driver lpc18xx_adc_driver = { + .probe = lpc18xx_adc_probe, + .remove = lpc18xx_adc_remove, + .driver = { + .name = "lpc18xx-adc", + .of_match_table = lpc18xx_adc_match, + }, +}; +module_platform_driver(lpc18xx_adc_driver); + +MODULE_DESCRIPTION("LPC18xx ADC driver"); +MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c new file mode 100644 index 000000000..b56ce1525 --- /dev/null +++ b/drivers/iio/adc/lpc32xx_adc.c @@ -0,0 +1,231 @@ +// 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> + */ + +#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/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> + +/* + * LPC32XX registers definitions + */ +#define LPC32XXAD_SELECT(x) ((x) + 0x04) +#define LPC32XXAD_CTRL(x) ((x) + 0x08) +#define LPC32XXAD_VALUE(x) ((x) + 0x48) + +/* Bit definitions for LPC32XXAD_SELECT: */ +/* constant, always write this value! */ +#define LPC32XXAD_REFm 0x00000200 +/* constant, always write this value! */ +#define LPC32XXAD_REFp 0x00000080 + /* multiple of this is the channel number: 0, 1, 2 */ +#define LPC32XXAD_IN 0x00000010 +/* constant, always write this value! */ +#define LPC32XXAD_INTERNAL 0x00000004 + +/* Bit definitions for LPC32XXAD_CTRL: */ +#define LPC32XXAD_STROBE 0x00000002 +#define LPC32XXAD_PDN_CTRL 0x00000004 + +/* Bit definitions for LPC32XXAD_VALUE: */ +#define LPC32XXAD_VALUE_MASK 0x000003FF + +#define LPC32XXAD_NAME "lpc32xx-adc" + +struct lpc32xx_adc_state { + void __iomem *adc_base; + struct clk *clk; + struct completion completion; + struct regulator *vref; + + u32 value; +}; + +static int lpc32xx_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct lpc32xx_adc_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = clk_prepare_enable(st->clk); + if (ret) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + /* Measurement setup */ + __raw_writel(LPC32XXAD_INTERNAL | (chan->address) | + LPC32XXAD_REFp | LPC32XXAD_REFm, + LPC32XXAD_SELECT(st->adc_base)); + /* Trigger conversion */ + __raw_writel(LPC32XXAD_PDN_CTRL | LPC32XXAD_STROBE, + LPC32XXAD_CTRL(st->adc_base)); + wait_for_completion(&st->completion); /* set by ISR */ + clk_disable_unprepare(st->clk); + *val = st->value; + mutex_unlock(&indio_dev->mlock); + + return IIO_VAL_INT; + + 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_BASE(_index) \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .address = LPC32XXAD_IN * _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[] = { + LPC32XX_ADC_CHANNEL(0), + LPC32XX_ADC_CHANNEL(1), + 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; + + /* Read value and clear irq */ + st->value = __raw_readl(LPC32XXAD_VALUE(st->adc_base)) & + LPC32XXAD_VALUE_MASK; + complete(&st->completion); + + return IRQ_HANDLED; +} + +static int lpc32xx_adc_probe(struct platform_device *pdev) +{ + struct lpc32xx_adc_state *st = NULL; + struct resource *res; + int retval = -ENODEV; + struct iio_dev *iodev = NULL; + int irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get platform I/O memory\n"); + return -ENXIO; + } + + iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); + if (!iodev) + return -ENOMEM; + + st = iio_priv(iodev); + + st->adc_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!st->adc_base) { + dev_err(&pdev->dev, "failed mapping memory\n"); + return -EBUSY; + } + + st->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(st->clk)) { + dev_err(&pdev->dev, "failed getting clock\n"); + return PTR_ERR(st->clk); + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -ENXIO; + + retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, + LPC32XXAD_NAME, st); + if (retval < 0) { + dev_err(&pdev->dev, "failed requesting interrupt\n"); + return retval; + } + + 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); + + iodev->name = LPC32XXAD_NAME; + iodev->info = &lpc32xx_adc_iio_info; + iodev->modes = INDIO_DIRECT_MODE; + iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels); + + retval = devm_iio_device_register(&pdev->dev, iodev); + if (retval) + return retval; + + dev_info(&pdev->dev, "LPC32XX ADC driver loaded, IRQ %d\n", irq); + + return 0; +} + +static const struct of_device_id lpc32xx_adc_match[] = { + { .compatible = "nxp,lpc3220-adc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, lpc32xx_adc_match); + +static struct platform_driver lpc32xx_adc_driver = { + .probe = lpc32xx_adc_probe, + .driver = { + .name = LPC32XXAD_NAME, + .of_match_table = lpc32xx_adc_match, + }, +}; + +module_platform_driver(lpc32xx_adc_driver); + +MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); +MODULE_DESCRIPTION("LPC32XX ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ltc2471.c b/drivers/iio/adc/ltc2471.c new file mode 100644 index 000000000..0e0fe881a --- /dev/null +++ b/drivers/iio/adc/ltc2471.c @@ -0,0 +1,157 @@ +// 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> + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +enum ltc2471_chips { + ltc2471, + ltc2473, +}; + +struct ltc2471_data { + struct i2c_client *client; +}; + +/* Reference voltage is 1.25V */ +#define LTC2471_VREF 1250 + +/* Read two bytes from the I2C bus to obtain the ADC result */ +static int ltc2471_get_value(struct i2c_client *client) +{ + int ret; + __be16 buf; + + ret = i2c_master_recv(client, (char *)&buf, sizeof(buf)); + if (ret < 0) + return ret; + if (ret != sizeof(buf)) + return -EIO; + + /* MSB first */ + return be16_to_cpu(buf); +} + +static int ltc2471_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ltc2471_data *data = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = ltc2471_get_value(data->client); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + if (chan->differential) + /* Output ranges from -VREF to +VREF */ + *val = 2 * LTC2471_VREF; + else + /* Output ranges from 0 to VREF */ + *val = LTC2471_VREF; + *val2 = 16; /* 16 data bits */ + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_OFFSET: + /* Only differential chip has this property */ + *val = -LTC2471_VREF; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec ltc2471_channel[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct iio_chan_spec ltc2473_channel[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .differential = 1, + }, +}; + +static const struct iio_info ltc2471_info = { + .read_raw = ltc2471_read_raw, +}; + +static int ltc2471_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ltc2471_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + + indio_dev->name = id->name; + indio_dev->info = <c2471_info; + indio_dev->modes = INDIO_DIRECT_MODE; + if (id->driver_data == ltc2473) + indio_dev->channels = ltc2473_channel; + else + indio_dev->channels = ltc2471_channel; + indio_dev->num_channels = 1; + + /* Trigger once to start conversion and check if chip is there */ + ret = ltc2471_get_value(client); + if (ret < 0) { + dev_err(&client->dev, "Cannot read from device.\n"); + return ret; + } + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id ltc2471_i2c_id[] = { + { "ltc2471", ltc2471 }, + { "ltc2473", ltc2473 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ltc2471_i2c_id); + +static struct i2c_driver ltc2471_i2c_driver = { + .driver = { + .name = "ltc2471", + }, + .probe = ltc2471_i2c_probe, + .id_table = ltc2471_i2c_id, +}; + +module_i2c_driver(ltc2471_i2c_driver); + +MODULE_DESCRIPTION("LTC2471/LTC2473 ADC driver"); +MODULE_AUTHOR("Topic Embedded Products"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2485.c b/drivers/iio/adc/ltc2485.c new file mode 100644 index 000000000..37c762f82 --- /dev/null +++ b/drivers/iio/adc/ltc2485.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ltc2485.c - Driver for Linear Technology LTC2485 ADC + * + * Copyright (C) 2016 Alison Schofield <amsfield22@gmail.com> + * + * Datasheet: http://cds.linear.com/docs/en/datasheet/2485fd.pdf + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* Power-on configuration: rejects both 50/60Hz, operates at 1x speed */ +#define LTC2485_CONFIG_DEFAULT 0 + +struct ltc2485_data { + struct i2c_client *client; + ktime_t time_prev; /* last conversion */ +}; + +static void ltc2485_wait_conv(struct ltc2485_data *data) +{ + const unsigned int conv_time = 147; /* conversion time ms */ + unsigned int time_elapsed; + + /* delay if conversion time not passed since last read or write */ + time_elapsed = ktime_ms_delta(ktime_get(), data->time_prev); + + if (time_elapsed < conv_time) + msleep(conv_time - time_elapsed); +} + +static int ltc2485_read(struct ltc2485_data *data, int *val) +{ + struct i2c_client *client = data->client; + __be32 buf = 0; + int ret; + + ltc2485_wait_conv(data); + + ret = i2c_master_recv(client, (char *)&buf, 4); + if (ret < 0) { + dev_err(&client->dev, "i2c_master_recv failed\n"); + return ret; + } + data->time_prev = ktime_get(); + *val = sign_extend32(be32_to_cpu(buf) >> 6, 24); + + return ret; +} + +static int ltc2485_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ltc2485_data *data = iio_priv(indio_dev); + int ret; + + if (mask == IIO_CHAN_INFO_RAW) { + ret = ltc2485_read(data, val); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + } else if (mask == IIO_CHAN_INFO_SCALE) { + *val = 5000; /* on board vref millivolts */ + *val2 = 25; /* 25 (24 + sign) data bits */ + return IIO_VAL_FRACTIONAL_LOG2; + + } else { + return -EINVAL; + } +} + +static const struct iio_chan_spec ltc2485_channel[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) + }, +}; + +static const struct iio_info ltc2485_info = { + .read_raw = ltc2485_read_raw, +}; + +static int ltc2485_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ltc2485_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->name = id->name; + indio_dev->info = <c2485_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ltc2485_channel; + indio_dev->num_channels = ARRAY_SIZE(ltc2485_channel); + + ret = i2c_smbus_write_byte(data->client, LTC2485_CONFIG_DEFAULT); + if (ret < 0) + return ret; + + data->time_prev = ktime_get(); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id ltc2485_id[] = { + { "ltc2485", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc2485_id); + +static struct i2c_driver ltc2485_driver = { + .driver = { + .name = "ltc2485", + }, + .probe = ltc2485_probe, + .id_table = ltc2485_id, +}; +module_i2c_driver(ltc2485_driver); + +MODULE_AUTHOR("Alison Schofield <amsfield22@gmail.com>"); +MODULE_DESCRIPTION("Linear Technology LTC2485 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2496.c b/drivers/iio/adc/ltc2496.c new file mode 100644 index 000000000..dd956a7c2 --- /dev/null +++ b/drivers/iio/adc/ltc2496.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ltc2496.c - Driver for Analog Devices/Linear Technology LTC2496 ADC + * + * Based on ltc2497.c which has + * Copyright (C) 2017 Analog Devices Inc. + * + * Licensed under the GPL-2. + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/2496fc.pdf + */ + +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> + +#include "ltc2497.h" + +struct ltc2496_driverdata { + /* this must be the first member */ + struct ltc2497core_driverdata common_ddata; + struct spi_device *spi; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + unsigned char rxbuf[3] ____cacheline_aligned; + unsigned char txbuf[3]; +}; + +static int ltc2496_result_and_measure(struct ltc2497core_driverdata *ddata, + u8 address, int *val) +{ + struct ltc2496_driverdata *st = + container_of(ddata, struct ltc2496_driverdata, common_ddata); + struct spi_transfer t = { + .tx_buf = st->txbuf, + .rx_buf = st->rxbuf, + .len = sizeof(st->txbuf), + }; + int ret; + + st->txbuf[0] = LTC2497_ENABLE | address; + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret < 0) { + dev_err(&st->spi->dev, "spi_sync_transfer failed: %pe\n", + ERR_PTR(ret)); + return ret; + } + + if (val) + *val = ((st->rxbuf[0] & 0x3f) << 12 | + st->rxbuf[1] << 4 | st->rxbuf[2] >> 4) - + (1 << 17); + + return 0; +} + +static int ltc2496_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ltc2496_driverdata *st; + struct device *dev = &spi->dev; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + st->common_ddata.result_and_measure = ltc2496_result_and_measure; + + return ltc2497core_probe(dev, indio_dev); +} + +static int ltc2496_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + ltc2497core_remove(indio_dev); + + return 0; +} + +static const struct of_device_id ltc2496_of_match[] = { + { .compatible = "lltc,ltc2496", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ltc2496_of_match); + +static struct spi_driver ltc2496_driver = { + .driver = { + .name = "ltc2496", + .of_match_table = ltc2496_of_match, + }, + .probe = ltc2496_probe, + .remove = ltc2496_remove, +}; +module_spi_driver(ltc2496_driver); + +MODULE_AUTHOR("Uwe Kleine-König <u.kleine-könig@pengutronix.de>"); +MODULE_DESCRIPTION("Linear Technology LTC2496 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c new file mode 100644 index 000000000..2a485c8a1 --- /dev/null +++ b/drivers/iio/adc/ltc2497-core.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ltc2497-core.c - Common code for Analog Devices/Linear Technology + * LTC2496 and LTC2497 ADCs + * + * Copyright (C) 2017 Analog Devices Inc. + */ + +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> + +#include "ltc2497.h" + +#define LTC2497_SGL BIT(4) +#define LTC2497_DIFF 0 +#define LTC2497_SIGN BIT(3) + +static int ltc2497core_wait_conv(struct ltc2497core_driverdata *ddata) +{ + s64 time_elapsed; + + time_elapsed = ktime_ms_delta(ktime_get(), ddata->time_prev); + + if (time_elapsed < LTC2497_CONVERSION_TIME_MS) { + /* delay if conversion time not passed + * since last read or write + */ + if (msleep_interruptible( + LTC2497_CONVERSION_TIME_MS - time_elapsed)) + return -ERESTARTSYS; + + return 0; + } + + if (time_elapsed - LTC2497_CONVERSION_TIME_MS <= 0) { + /* We're in automatic mode - + * so the last reading is still not outdated + */ + return 0; + } + + return 1; +} + +static int ltc2497core_read(struct ltc2497core_driverdata *ddata, u8 address, int *val) +{ + int ret; + + ret = ltc2497core_wait_conv(ddata); + if (ret < 0) + return ret; + + if (ret || ddata->addr_prev != address) { + ret = ddata->result_and_measure(ddata, address, NULL); + if (ret < 0) + return ret; + ddata->addr_prev = address; + + if (msleep_interruptible(LTC2497_CONVERSION_TIME_MS)) + return -ERESTARTSYS; + } + + ret = ddata->result_and_measure(ddata, address, val); + if (ret < 0) + return ret; + + ddata->time_prev = ktime_get(); + + return ret; +} + +static int ltc2497core_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = ltc2497core_read(ddata, chan->address, val); + mutex_unlock(&indio_dev->mlock); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(ddata->ref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = 17; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +#define LTC2497_CHAN(_chan, _addr, _ds_name) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_addr | (_chan / 2) | ((_chan & 1) ? LTC2497_SIGN : 0)), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = (_ds_name), \ +} + +#define LTC2497_CHAN_DIFF(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 1 : 0), \ + .channel2 = (_chan) * 2 + ((_addr) & LTC2497_SIGN ? 0 : 1),\ + .address = (_addr | _chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .differential = 1, \ +} + +static const struct iio_chan_spec ltc2497core_channel[] = { + LTC2497_CHAN(0, LTC2497_SGL, "CH0"), + LTC2497_CHAN(1, LTC2497_SGL, "CH1"), + LTC2497_CHAN(2, LTC2497_SGL, "CH2"), + LTC2497_CHAN(3, LTC2497_SGL, "CH3"), + LTC2497_CHAN(4, LTC2497_SGL, "CH4"), + LTC2497_CHAN(5, LTC2497_SGL, "CH5"), + LTC2497_CHAN(6, LTC2497_SGL, "CH6"), + LTC2497_CHAN(7, LTC2497_SGL, "CH7"), + LTC2497_CHAN(8, LTC2497_SGL, "CH8"), + LTC2497_CHAN(9, LTC2497_SGL, "CH9"), + LTC2497_CHAN(10, LTC2497_SGL, "CH10"), + LTC2497_CHAN(11, LTC2497_SGL, "CH11"), + LTC2497_CHAN(12, LTC2497_SGL, "CH12"), + LTC2497_CHAN(13, LTC2497_SGL, "CH13"), + LTC2497_CHAN(14, LTC2497_SGL, "CH14"), + LTC2497_CHAN(15, LTC2497_SGL, "CH15"), + LTC2497_CHAN_DIFF(0, LTC2497_DIFF), + LTC2497_CHAN_DIFF(1, LTC2497_DIFF), + LTC2497_CHAN_DIFF(2, LTC2497_DIFF), + LTC2497_CHAN_DIFF(3, LTC2497_DIFF), + LTC2497_CHAN_DIFF(4, LTC2497_DIFF), + LTC2497_CHAN_DIFF(5, LTC2497_DIFF), + LTC2497_CHAN_DIFF(6, LTC2497_DIFF), + LTC2497_CHAN_DIFF(7, LTC2497_DIFF), + LTC2497_CHAN_DIFF(0, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(1, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(2, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(3, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(4, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(5, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(6, LTC2497_DIFF | LTC2497_SIGN), + LTC2497_CHAN_DIFF(7, LTC2497_DIFF | LTC2497_SIGN), +}; + +static const struct iio_info ltc2497core_info = { + .read_raw = ltc2497core_read_raw, +}; + +int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev) +{ + struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); + int ret; + + indio_dev->name = dev_name(dev); + indio_dev->info = <c2497core_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ltc2497core_channel; + indio_dev->num_channels = ARRAY_SIZE(ltc2497core_channel); + + ret = ddata->result_and_measure(ddata, LTC2497_CONFIG_DEFAULT, NULL); + if (ret < 0) + return ret; + + ddata->ref = devm_regulator_get(dev, "vref"); + if (IS_ERR(ddata->ref)) + return dev_err_probe(dev, PTR_ERR(ddata->ref), + "Failed to get vref regulator\n"); + + ret = regulator_enable(ddata->ref); + if (ret < 0) { + dev_err(dev, "Failed to enable vref regulator: %pe\n", + ERR_PTR(ret)); + return ret; + } + + if (dev->platform_data) { + struct iio_map *plat_data; + + plat_data = (struct iio_map *)dev->platform_data; + + ret = iio_map_array_register(indio_dev, plat_data); + if (ret) { + dev_err(&indio_dev->dev, "iio map err: %d\n", ret); + goto err_regulator_disable; + } + } + + ddata->addr_prev = LTC2497_CONFIG_DEFAULT; + ddata->time_prev = ktime_get(); + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto err_array_unregister; + + return 0; + +err_array_unregister: + iio_map_array_unregister(indio_dev); + +err_regulator_disable: + regulator_disable(ddata->ref); + + return ret; +} +EXPORT_SYMBOL_NS(ltc2497core_probe, LTC2497); + +void ltc2497core_remove(struct iio_dev *indio_dev) +{ + struct ltc2497core_driverdata *ddata = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + iio_map_array_unregister(indio_dev); + + regulator_disable(ddata->ref); +} +EXPORT_SYMBOL_NS(ltc2497core_remove, LTC2497); + +MODULE_DESCRIPTION("common code for LTC2496/LTC2497 drivers"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c new file mode 100644 index 000000000..61f373fab --- /dev/null +++ b/drivers/iio/adc/ltc2497.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ltc2497.c - Driver for Analog Devices/Linear Technology LTC2497 ADC + * + * Copyright (C) 2017 Analog Devices Inc. + * + * Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf + */ + +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> + +#include "ltc2497.h" + +struct ltc2497_driverdata { + /* this must be the first member */ + struct ltc2497core_driverdata common_ddata; + struct i2c_client *client; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + __be32 buf ____cacheline_aligned; +}; + +static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata, + u8 address, int *val) +{ + struct ltc2497_driverdata *st = + container_of(ddata, struct ltc2497_driverdata, common_ddata); + int ret; + + if (val) { + ret = i2c_master_recv(st->client, (char *)&st->buf, 3); + if (ret < 0) { + dev_err(&st->client->dev, "i2c_master_recv failed\n"); + return ret; + } + + *val = (be32_to_cpu(st->buf) >> 14) - (1 << 17); + + /* + * The part started a new conversion at the end of the above i2c + * transfer, so if the address didn't change since the last call + * everything is fine and we can return early. + * If not (which should only happen when some sort of bulk + * conversion is implemented) we have to program the new + * address. Note that this probably fails as the conversion that + * was triggered above is like not complete yet and the two + * operations have to be done in a single transfer. + */ + if (ddata->addr_prev == address) + return 0; + } + + ret = i2c_smbus_write_byte(st->client, + LTC2497_ENABLE | address); + if (ret) + dev_err(&st->client->dev, "i2c transfer failed: %pe\n", + ERR_PTR(ret)); + return ret; +} + +static int ltc2497_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ltc2497_driverdata *st; + struct device *dev = &client->dev; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WRITE_BYTE)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + st->client = client; + st->common_ddata.result_and_measure = ltc2497_result_and_measure; + + return ltc2497core_probe(dev, indio_dev); +} + +static int ltc2497_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + ltc2497core_remove(indio_dev); + + return 0; +} + +static const struct i2c_device_id ltc2497_id[] = { + { "ltc2497", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc2497_id); + +static const struct of_device_id ltc2497_of_match[] = { + { .compatible = "lltc,ltc2497", }, + {}, +}; +MODULE_DEVICE_TABLE(of, ltc2497_of_match); + +static struct i2c_driver ltc2497_driver = { + .driver = { + .name = "ltc2497", + .of_match_table = ltc2497_of_match, + }, + .probe = ltc2497_probe, + .remove = ltc2497_remove, + .id_table = ltc2497_id, +}; +module_i2c_driver(ltc2497_driver); + +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_DESCRIPTION("Linear Technology LTC2497 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h new file mode 100644 index 000000000..d0b42dd6b --- /dev/null +++ b/drivers/iio/adc/ltc2497.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#define LTC2497_ENABLE 0xA0 +#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE +#define LTC2497_CONVERSION_TIME_MS 150ULL + +struct ltc2497core_driverdata { + struct regulator *ref; + ktime_t time_prev; + u8 addr_prev; + int (*result_and_measure)(struct ltc2497core_driverdata *ddata, + u8 address, int *val); +}; + +int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev); +void ltc2497core_remove(struct iio_dev *indio_dev); + +MODULE_IMPORT_NS(LTC2497); diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c new file mode 100644 index 000000000..a08efaaf1 --- /dev/null +++ b/drivers/iio/adc/max1027.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0-only + /* + * iio/adc/max1027.c + * Copyright (C) 2014 Philippe Reynes + * + * based on linux/drivers/iio/ad7923.c + * Copyright 2011 Analog Devices Inc (from AD7923 Driver) + * Copyright 2012 CS Systemes d'Information + * + * max1027.c + * + * Partial support for max1027 and similar chips. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/spi/spi.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define MAX1027_CONV_REG BIT(7) +#define MAX1027_SETUP_REG BIT(6) +#define MAX1027_AVG_REG BIT(5) +#define MAX1027_RST_REG BIT(4) + +/* conversion register */ +#define MAX1027_TEMP BIT(0) +#define MAX1027_SCAN_0_N (0x00 << 1) +#define MAX1027_SCAN_N_M (0x01 << 1) +#define MAX1027_SCAN_N (0x02 << 1) +#define MAX1027_NOSCAN (0x03 << 1) +#define MAX1027_CHAN(n) ((n) << 3) + +/* setup register */ +#define MAX1027_UNIPOLAR 0x02 +#define MAX1027_BIPOLAR 0x03 +#define MAX1027_REF_MODE0 (0x00 << 2) +#define MAX1027_REF_MODE1 (0x01 << 2) +#define MAX1027_REF_MODE2 (0x02 << 2) +#define MAX1027_REF_MODE3 (0x03 << 2) +#define MAX1027_CKS_MODE0 (0x00 << 4) +#define MAX1027_CKS_MODE1 (0x01 << 4) +#define MAX1027_CKS_MODE2 (0x02 << 4) +#define MAX1027_CKS_MODE3 (0x03 << 4) + +/* averaging register */ +#define MAX1027_NSCAN_4 0x00 +#define MAX1027_NSCAN_8 0x01 +#define MAX1027_NSCAN_12 0x02 +#define MAX1027_NSCAN_16 0x03 +#define MAX1027_NAVG_4 (0x00 << 2) +#define MAX1027_NAVG_8 (0x01 << 2) +#define MAX1027_NAVG_16 (0x02 << 2) +#define MAX1027_NAVG_32 (0x03 << 2) +#define MAX1027_AVG_EN BIT(4) + +enum max1027_id { + max1027, + max1029, + max1031, + max1227, + max1229, + max1231, +}; + +static const struct spi_device_id max1027_id[] = { + {"max1027", max1027}, + {"max1029", max1029}, + {"max1031", max1031}, + {"max1227", max1227}, + {"max1229", max1229}, + {"max1231", max1231}, + {} +}; +MODULE_DEVICE_TABLE(spi, max1027_id); + +static const struct of_device_id max1027_adc_dt_ids[] = { + { .compatible = "maxim,max1027" }, + { .compatible = "maxim,max1029" }, + { .compatible = "maxim,max1031" }, + { .compatible = "maxim,max1227" }, + { .compatible = "maxim,max1229" }, + { .compatible = "maxim,max1231" }, + {}, +}; +MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids); + +#define MAX1027_V_CHAN(index, depth) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = index + 1, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = depth, \ + .storagebits = 16, \ + .shift = (depth == 10) ? 2 : 0, \ + .endianness = IIO_BE, \ + }, \ + } + +#define MAX1027_T_CHAN \ + { \ + .type = IIO_TEMP, \ + .channel = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = 0, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +#define MAX1X27_CHANNELS(depth) \ + MAX1027_T_CHAN, \ + MAX1027_V_CHAN(0, depth), \ + MAX1027_V_CHAN(1, depth), \ + MAX1027_V_CHAN(2, depth), \ + MAX1027_V_CHAN(3, depth), \ + MAX1027_V_CHAN(4, depth), \ + MAX1027_V_CHAN(5, depth), \ + MAX1027_V_CHAN(6, depth), \ + MAX1027_V_CHAN(7, depth) + +#define MAX1X29_CHANNELS(depth) \ + MAX1X27_CHANNELS(depth), \ + MAX1027_V_CHAN(8, depth), \ + MAX1027_V_CHAN(9, depth), \ + MAX1027_V_CHAN(10, depth), \ + MAX1027_V_CHAN(11, depth) + +#define MAX1X31_CHANNELS(depth) \ + MAX1X29_CHANNELS(depth), \ + MAX1027_V_CHAN(12, depth), \ + MAX1027_V_CHAN(13, depth), \ + MAX1027_V_CHAN(14, depth), \ + MAX1027_V_CHAN(15, depth) + +static const struct iio_chan_spec max1027_channels[] = { + MAX1X27_CHANNELS(10), +}; + +static const struct iio_chan_spec max1029_channels[] = { + MAX1X29_CHANNELS(10), +}; + +static const struct iio_chan_spec max1031_channels[] = { + MAX1X31_CHANNELS(10), +}; + +static const struct iio_chan_spec max1227_channels[] = { + MAX1X27_CHANNELS(12), +}; + +static const struct iio_chan_spec max1229_channels[] = { + MAX1X29_CHANNELS(12), +}; + +static const struct iio_chan_spec max1231_channels[] = { + MAX1X31_CHANNELS(12), +}; + +static const unsigned long max1027_available_scan_masks[] = { + 0x000001ff, + 0x00000000, +}; + +static const unsigned long max1029_available_scan_masks[] = { + 0x00001fff, + 0x00000000, +}; + +static const unsigned long max1031_available_scan_masks[] = { + 0x0001ffff, + 0x00000000, +}; + +struct max1027_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + const unsigned long *available_scan_masks; +}; + +static const struct max1027_chip_info max1027_chip_info_tbl[] = { + [max1027] = { + .channels = max1027_channels, + .num_channels = ARRAY_SIZE(max1027_channels), + .available_scan_masks = max1027_available_scan_masks, + }, + [max1029] = { + .channels = max1029_channels, + .num_channels = ARRAY_SIZE(max1029_channels), + .available_scan_masks = max1029_available_scan_masks, + }, + [max1031] = { + .channels = max1031_channels, + .num_channels = ARRAY_SIZE(max1031_channels), + .available_scan_masks = max1031_available_scan_masks, + }, + [max1227] = { + .channels = max1227_channels, + .num_channels = ARRAY_SIZE(max1227_channels), + .available_scan_masks = max1027_available_scan_masks, + }, + [max1229] = { + .channels = max1229_channels, + .num_channels = ARRAY_SIZE(max1229_channels), + .available_scan_masks = max1029_available_scan_masks, + }, + [max1231] = { + .channels = max1231_channels, + .num_channels = ARRAY_SIZE(max1231_channels), + .available_scan_masks = max1031_available_scan_masks, + }, +}; + +struct max1027_state { + const struct max1027_chip_info *info; + struct spi_device *spi; + struct iio_trigger *trig; + __be16 *buffer; + struct mutex lock; + + u8 reg ____cacheline_aligned; +}; + +static int max1027_read_single_value(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + int ret; + struct max1027_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) { + dev_warn(&indio_dev->dev, "trigger mode already enabled"); + return -EBUSY; + } + + /* Start acquisition on conversion register write */ + st->reg = MAX1027_SETUP_REG | MAX1027_REF_MODE2 | MAX1027_CKS_MODE2; + ret = spi_write(st->spi, &st->reg, 1); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Failed to configure setup register\n"); + return ret; + } + + /* Configure conversion register with the requested chan */ + st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) | + MAX1027_NOSCAN; + if (chan->type == IIO_TEMP) + st->reg |= MAX1027_TEMP; + ret = spi_write(st->spi, &st->reg, 1); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Failed to configure conversion register\n"); + return ret; + } + + /* + * For an unknown reason, when we use the mode "10" (write + * conversion register), the interrupt doesn't occur every time. + * So we just wait 1 ms. + */ + mdelay(1); + + /* Read result */ + ret = spi_read(st->spi, st->buffer, (chan->type == IIO_TEMP) ? 4 : 2); + if (ret < 0) + return ret; + + *val = be16_to_cpu(st->buffer[0]); + + return IIO_VAL_INT; +} + +static int max1027_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret = 0; + struct max1027_state *st = iio_priv(indio_dev); + + mutex_lock(&st->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = max1027_read_single_value(indio_dev, chan, val); + break; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + *val = 1; + *val2 = 8; + ret = IIO_VAL_FRACTIONAL; + break; + case IIO_VOLTAGE: + *val = 2500; + *val2 = chan->scan_type.realbits; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + default: + ret = -EINVAL; + break; + } + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&st->lock); + + return ret; +} + +static int max1027_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct max1027_state *st = iio_priv(indio_dev); + u8 *val = (u8 *)st->buffer; + + if (readval) { + int ret = spi_read(st->spi, val, 2); + *readval = be16_to_cpu(st->buffer[0]); + return ret; + } + + *val = (u8)writeval; + return spi_write(st->spi, val, 1); +} + +static int max1027_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct max1027_state *st = iio_priv(indio_dev); + + if (st->trig != trig) + return -EINVAL; + + return 0; +} + +static int max1027_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct max1027_state *st = iio_priv(indio_dev); + int ret; + + if (state) { + /* Start acquisition on cnvst */ + st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE0 | + MAX1027_REF_MODE2; + ret = spi_write(st->spi, &st->reg, 1); + if (ret < 0) + return ret; + + /* Scan from 0 to max */ + st->reg = MAX1027_CONV_REG | MAX1027_CHAN(0) | + MAX1027_SCAN_N_M | MAX1027_TEMP; + ret = spi_write(st->spi, &st->reg, 1); + if (ret < 0) + return ret; + } else { + /* Start acquisition on conversion register write */ + st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE2 | + MAX1027_REF_MODE2; + ret = spi_write(st->spi, &st->reg, 1); + if (ret < 0) + return ret; + } + + return 0; +} + +static irqreturn_t max1027_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct max1027_state *st = iio_priv(indio_dev); + + pr_debug("%s(irq=%d, private=0x%p)\n", __func__, irq, private); + + /* fill buffer with all channel */ + spi_read(st->spi, st->buffer, indio_dev->masklength * 2); + + iio_push_to_buffers(indio_dev, st->buffer); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_trigger_ops max1027_trigger_ops = { + .validate_device = &iio_trigger_validate_own_device, + .set_trigger_state = &max1027_set_trigger_state, +}; + +static const struct iio_info max1027_info = { + .read_raw = &max1027_read_raw, + .validate_trigger = &max1027_validate_trigger, + .debugfs_reg_access = &max1027_debugfs_reg_access, +}; + +static int max1027_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct max1027_state *st; + + pr_debug("%s: probe(spi = 0x%p)\n", __func__, spi); + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) { + pr_err("Can't allocate iio device\n"); + return -ENOMEM; + } + + spi_set_drvdata(spi, indio_dev); + + st = iio_priv(indio_dev); + st->spi = spi; + st->info = &max1027_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + mutex_init(&st->lock); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &max1027_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->info->channels; + indio_dev->num_channels = st->info->num_channels; + indio_dev->available_scan_masks = st->info->available_scan_masks; + + st->buffer = devm_kmalloc_array(&indio_dev->dev, + indio_dev->num_channels, 2, + GFP_KERNEL); + if (st->buffer == NULL) { + dev_err(&indio_dev->dev, "Can't allocate buffer\n"); + return -ENOMEM; + } + + if (spi->irq) { + 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; + } + + st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger", + indio_dev->name); + if (st->trig == NULL) { + ret = -ENOMEM; + dev_err(&indio_dev->dev, + "Failed to allocate iio trigger\n"); + return ret; + } + + st->trig->ops = &max1027_trigger_ops; + st->trig->dev.parent = &spi->dev; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(&indio_dev->dev, + st->trig); + if (ret < 0) { + dev_err(&indio_dev->dev, + "Failed to register iio trigger\n"); + return ret; + } + + ret = devm_request_threaded_irq(&spi->dev, spi->irq, + iio_trigger_generic_data_rdy_poll, + NULL, + IRQF_TRIGGER_FALLING, + spi->dev.driver->name, + st->trig); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n"); + return ret; + } + } + + /* Internal reset */ + st->reg = MAX1027_RST_REG; + ret = spi_write(st->spi, &st->reg, 1); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to reset the ADC\n"); + return ret; + } + + /* Disable averaging */ + st->reg = MAX1027_AVG_REG; + ret = spi_write(st->spi, &st->reg, 1); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to configure averaging register\n"); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static struct spi_driver max1027_driver = { + .driver = { + .name = "max1027", + .of_match_table = max1027_adc_dt_ids, + }, + .probe = max1027_probe, + .id_table = max1027_id, +}; +module_spi_driver(max1027_driver); + +MODULE_AUTHOR("Philippe Reynes <tremyfr@yahoo.fr>"); +MODULE_DESCRIPTION("MAX1X27/MAX1X29/MAX1X31 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c new file mode 100644 index 000000000..6cf21758c --- /dev/null +++ b/drivers/iio/adc/max11100.c @@ -0,0 +1,175 @@ +// 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 + */ +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> + +/* + * LSB is the ADC single digital step + * 1 LSB = (vref_mv / 2 ^ 16) + * + * LSB is used to calculate analog voltage value + * from the number of ADC steps count + * + * Ain = (count * LSB) + */ +#define MAX11100_LSB_DIV (1 << 16) + +struct max11100_state { + struct regulator *vref_reg; + struct spi_device *spi; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + u8 buffer[3] ____cacheline_aligned; +}; + +static const struct iio_chan_spec max11100_channels[] = { + { /* [0] */ + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int max11100_read_single(struct iio_dev *indio_dev, int *val) +{ + int ret; + struct max11100_state *state = iio_priv(indio_dev); + + ret = spi_read(state->spi, state->buffer, sizeof(state->buffer)); + if (ret) { + dev_err(&indio_dev->dev, "SPI transfer failed\n"); + return ret; + } + + /* the first 8 bits sent out from ADC must be 0s */ + if (state->buffer[0]) { + dev_err(&indio_dev->dev, "Invalid value: buffer[0] != 0\n"); + return -EINVAL; + } + + *val = (state->buffer[1] << 8) | state->buffer[2]; + + return 0; +} + +static int max11100_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + int ret, vref_uv; + struct max11100_state *state = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = max11100_read_single(indio_dev, val); + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + vref_uv = regulator_get_voltage(state->vref_reg); + if (vref_uv < 0) + /* dummy regulator "get_voltage" returns -EINVAL */ + return -EINVAL; + + *val = vref_uv / 1000; + *val2 = MAX11100_LSB_DIV; + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static const struct iio_info max11100_info = { + .read_raw = max11100_read_raw, +}; + +static int max11100_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct max11100_state *state; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + spi_set_drvdata(spi, indio_dev); + + state = iio_priv(indio_dev); + state->spi = spi; + + indio_dev->name = "max11100"; + indio_dev->info = &max11100_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max11100_channels; + indio_dev->num_channels = ARRAY_SIZE(max11100_channels); + + state->vref_reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(state->vref_reg)) + return PTR_ERR(state->vref_reg); + + ret = regulator_enable(state->vref_reg); + if (ret) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) + goto disable_regulator; + + return 0; + +disable_regulator: + regulator_disable(state->vref_reg); + + return ret; +} + +static int max11100_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct max11100_state *state = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(state->vref_reg); + + return 0; +} + +static const struct of_device_id max11100_ids[] = { + {.compatible = "maxim,max11100"}, + { }, +}; +MODULE_DEVICE_TABLE(of, max11100_ids); + +static struct spi_driver max11100_driver = { + .driver = { + .name = "max11100", + .of_match_table = max11100_ids, + }, + .probe = max11100_probe, + .remove = max11100_remove, +}; + +module_spi_driver(max11100_driver); + +MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>"); +MODULE_DESCRIPTION("Maxim max11100 ADC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c new file mode 100644 index 000000000..6efb0b43d --- /dev/null +++ b/drivers/iio/adc/max1118.c @@ -0,0 +1,309 @@ +// 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> + * + * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1117-MAX1119.pdf + * + * SPI interface connections + * + * SPI MAXIM + * Master Direction MAX1117/8/9 + * ------ --------- ----------- + * nCS --> CNVST + * SCK --> SCLK + * MISO <-- DOUT + * ------ --------- ----------- + */ + +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/regulator/consumer.h> + +enum max1118_id { + max1117, + max1118, + max1119, +}; + +struct max1118 { + struct spi_device *spi; + struct mutex lock; + struct regulator *reg; + /* Ensure natural alignment of buffer elements */ + struct { + u8 channels[2]; + s64 ts __aligned(8); + } scan; + + u8 data ____cacheline_aligned; +}; + +#define MAX1118_CHANNEL(ch) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = ch, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ + } + +static const struct iio_chan_spec max1118_channels[] = { + MAX1118_CHANNEL(0), + MAX1118_CHANNEL(1), + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static int max1118_read(struct spi_device *spi, int channel) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct max1118 *adc = iio_priv(indio_dev); + struct spi_transfer xfers[] = { + /* + * To select CH1 for conversion, CNVST pin must be brought high + * and low for a second time. + */ + { + .len = 0, + .delay = { /* > CNVST Low Time 100 ns */ + .value = 1, + .unit = SPI_DELAY_UNIT_USECS + }, + .cs_change = 1, + }, + /* + * The acquisition interval begins with the falling edge of + * CNVST. The total acquisition and conversion process takes + * <7.5us. + */ + { + .len = 0, + .delay = { + .value = 8, + .unit = SPI_DELAY_UNIT_USECS + }, + }, + { + .rx_buf = &adc->data, + .len = 1, + }, + }; + int ret; + + if (channel == 0) + ret = spi_sync_transfer(spi, xfers + 1, 2); + else + ret = spi_sync_transfer(spi, xfers, 3); + + if (ret) + return ret; + + return adc->data; +} + +static int max1118_get_vref_mV(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct max1118 *adc = iio_priv(indio_dev); + const struct spi_device_id *id = spi_get_device_id(spi); + int vref_uV; + + switch (id->driver_data) { + case max1117: + return 2048; + case max1119: + return 4096; + case max1118: + vref_uV = regulator_get_voltage(adc->reg); + if (vref_uV < 0) + return vref_uV; + return vref_uV / 1000; + } + + return -ENODEV; +} + +static int max1118_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max1118 *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + *val = max1118_read(adc->spi, chan->channel); + mutex_unlock(&adc->lock); + if (*val < 0) + return *val; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = max1118_get_vref_mV(adc->spi); + if (*val < 0) + return *val; + *val2 = 8; + + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info max1118_info = { + .read_raw = max1118_read_raw, +}; + +static irqreturn_t max1118_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct max1118 *adc = iio_priv(indio_dev); + int scan_index; + int i = 0; + + mutex_lock(&adc->lock); + + for_each_set_bit(scan_index, indio_dev->active_scan_mask, + indio_dev->masklength) { + const struct iio_chan_spec *scan_chan = + &indio_dev->channels[scan_index]; + int ret = max1118_read(adc->spi, scan_chan->channel); + + if (ret < 0) { + dev_warn(&adc->spi->dev, + "failed to get conversion data\n"); + goto out; + } + + adc->scan.channels[i] = ret; + i++; + } + iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan, + iio_get_time_ns(indio_dev)); +out: + mutex_unlock(&adc->lock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int max1118_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct max1118 *adc; + const struct spi_device_id *id = spi_get_device_id(spi); + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + mutex_init(&adc->lock); + + if (id->driver_data == max1118) { + adc->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) { + dev_err(&spi->dev, "failed to get vref regulator\n"); + return PTR_ERR(adc->reg); + } + ret = regulator_enable(adc->reg); + if (ret) + return ret; + } + + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &max1118_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max1118_channels; + indio_dev->num_channels = ARRAY_SIZE(max1118_channels); + + /* + * To reinitiate a conversion on CH0, it is necessary to allow for a + * conversion to be complete and all of the data to be read out. Once + * a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go + * into AutoShutdown mode until the next conversion is initiated. + */ + max1118_read(spi, 0); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + max1118_trigger_handler, NULL); + if (ret) + goto err_reg_disable; + + ret = iio_device_register(indio_dev); + if (ret) + goto err_buffer_cleanup; + + return 0; + +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); +err_reg_disable: + if (id->driver_data == max1118) + regulator_disable(adc->reg); + + return ret; +} + +static int max1118_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct max1118 *adc = iio_priv(indio_dev); + const struct spi_device_id *id = spi_get_device_id(spi); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (id->driver_data == max1118) + return regulator_disable(adc->reg); + + return 0; +} + +static const struct spi_device_id max1118_id[] = { + { "max1117", max1117 }, + { "max1118", max1118 }, + { "max1119", max1119 }, + {} +}; +MODULE_DEVICE_TABLE(spi, max1118_id); + +static const struct of_device_id max1118_dt_ids[] = { + { .compatible = "maxim,max1117" }, + { .compatible = "maxim,max1118" }, + { .compatible = "maxim,max1119" }, + {}, +}; +MODULE_DEVICE_TABLE(of, max1118_dt_ids); + +static struct spi_driver max1118_spi_driver = { + .driver = { + .name = "max1118", + .of_match_table = max1118_dt_ids, + }, + .probe = max1118_probe, + .remove = max1118_remove, + .id_table = max1118_id, +}; +module_spi_driver(max1118_spi_driver); + +MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>"); +MODULE_DESCRIPTION("MAXIM MAX1117/MAX1118/MAX1119 ADCs driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c new file mode 100644 index 000000000..0cbbb3c56 --- /dev/null +++ b/drivers/iio/adc/max1241.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MAX1241 low-power, 12-bit serial ADC + * + * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1240-MAX1241.pdf + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#define MAX1241_VAL_MASK GENMASK(11, 0) +#define MAX1241_SHUTDOWN_DELAY_USEC 4 + +enum max1241_id { + max1241, +}; + +struct max1241 { + struct spi_device *spi; + struct mutex lock; + struct regulator *vdd; + struct regulator *vref; + struct gpio_desc *shutdown; + + __be16 data ____cacheline_aligned; +}; + +static const struct iio_chan_spec max1241_channels[] = { + { + .type = IIO_VOLTAGE, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int max1241_read(struct max1241 *adc) +{ + struct spi_transfer xfers[] = { + /* + * Begin conversion by bringing /CS low for at least + * tconv us. + */ + { + .len = 0, + .delay.value = 8, + .delay.unit = SPI_DELAY_UNIT_USECS, + }, + /* + * Then read two bytes of data in our RX buffer. + */ + { + .rx_buf = &adc->data, + .len = 2, + }, + }; + + return spi_sync_transfer(adc->spi, xfers, ARRAY_SIZE(xfers)); +} + +static int max1241_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret, vref_uV; + struct max1241 *adc = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + + if (adc->shutdown) { + gpiod_set_value(adc->shutdown, 0); + udelay(MAX1241_SHUTDOWN_DELAY_USEC); + ret = max1241_read(adc); + gpiod_set_value(adc->shutdown, 1); + } else + ret = max1241_read(adc); + + if (ret) { + mutex_unlock(&adc->lock); + return ret; + } + + *val = (be16_to_cpu(adc->data) >> 3) & MAX1241_VAL_MASK; + + mutex_unlock(&adc->lock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + vref_uV = regulator_get_voltage(adc->vref); + + if (vref_uV < 0) + return vref_uV; + + *val = vref_uV / 1000; + *val2 = 12; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info max1241_info = { + .read_raw = max1241_read_raw, +}; + +static void max1241_disable_vdd_action(void *data) +{ + struct max1241 *adc = data; + struct device *dev = &adc->spi->dev; + int err; + + err = regulator_disable(adc->vdd); + if (err) + dev_err(dev, "could not disable vdd regulator.\n"); +} + +static void max1241_disable_vref_action(void *data) +{ + struct max1241 *adc = data; + struct device *dev = &adc->spi->dev; + int err; + + err = regulator_disable(adc->vref); + if (err) + dev_err(dev, "could not disable vref regulator.\n"); +} + +static int max1241_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct max1241 *adc; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + mutex_init(&adc->lock); + + spi_set_drvdata(spi, indio_dev); + + adc->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(adc->vdd)) { + dev_err(dev, "failed to get vdd regulator\n"); + return PTR_ERR(adc->vdd); + } + + ret = regulator_enable(adc->vdd); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, max1241_disable_vdd_action, adc); + if (ret) { + dev_err(dev, "could not set up vdd regulator cleanup action\n"); + return ret; + } + + adc->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(adc->vref)) { + dev_err(dev, "failed to get vref regulator\n"); + return PTR_ERR(adc->vref); + } + + ret = regulator_enable(adc->vref); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, max1241_disable_vref_action, adc); + if (ret) { + dev_err(dev, "could not set up vref regulator cleanup action\n"); + return ret; + } + + adc->shutdown = devm_gpiod_get_optional(dev, "shutdown", + GPIOD_OUT_HIGH); + if (IS_ERR(adc->shutdown)) + return PTR_ERR(adc->shutdown); + + if (adc->shutdown) + dev_dbg(dev, "shutdown pin passed, low-power mode enabled"); + else + dev_dbg(dev, "no shutdown pin passed, low-power mode disabled"); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &max1241_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max1241_channels; + indio_dev->num_channels = ARRAY_SIZE(max1241_channels); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id max1241_id[] = { + { "max1241", max1241 }, + {} +}; + +static const struct of_device_id max1241_dt_ids[] = { + { .compatible = "maxim,max1241" }, + {} +}; +MODULE_DEVICE_TABLE(of, max1241_dt_ids); + +static struct spi_driver max1241_spi_driver = { + .driver = { + .name = "max1241", + .of_match_table = max1241_dt_ids, + }, + .probe = max1241_probe, + .id_table = max1241_id, +}; +module_spi_driver(max1241_spi_driver); + +MODULE_AUTHOR("Alexandru Lazar <alazar@startmail.com>"); +MODULE_DESCRIPTION("MAX1241 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c new file mode 100644 index 000000000..f2b576c69 --- /dev/null +++ b/drivers/iio/adc/max1363.c @@ -0,0 +1,1766 @@ +// SPDX-License-Identifier: GPL-2.0-only + /* + * iio/adc/max1363.c + * Copyright (C) 2008-2010 Jonathan Cameron + * + * based on linux/drivers/i2c/chips/max123x + * Copyright (C) 2002-2004 Stefan Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * Driver for max1363 and similar chips. + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/list.h> +#include <linux/i2c.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/buffer.h> +#include <linux/iio/driver.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define MAX1363_SETUP_BYTE(a) ((a) | 0x80) + +/* There is a fair bit more defined here than currently + * used, but the intention is to support everything these + * chips do in the long run */ + +/* see data sheets */ +/* max1363 and max1236, max1237, max1238, max1239 */ +#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_VDD 0x00 +#define MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF 0x20 +#define MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT 0x40 +#define MAX1363_SETUP_AIN3_IS_REF_REF_IS_INT 0x60 +#define MAX1363_SETUP_POWER_UP_INT_REF 0x10 +#define MAX1363_SETUP_POWER_DOWN_INT_REF 0x00 + +/* think about including max11600 etc - more settings */ +#define MAX1363_SETUP_EXT_CLOCK 0x08 +#define MAX1363_SETUP_INT_CLOCK 0x00 +#define MAX1363_SETUP_UNIPOLAR 0x00 +#define MAX1363_SETUP_BIPOLAR 0x04 +#define MAX1363_SETUP_RESET 0x00 +#define MAX1363_SETUP_NORESET 0x02 +/* max1363 only - though don't care on others. + * For now monitor modes are not implemented as the relevant + * line is not connected on my test board. + * The definitions are here as I intend to add this soon. + */ +#define MAX1363_SETUP_MONITOR_SETUP 0x01 + +/* Specific to the max1363 */ +#define MAX1363_MON_RESET_CHAN(a) (1 << ((a) + 4)) +#define MAX1363_MON_INT_ENABLE 0x01 + +/* defined for readability reasons */ +/* All chips */ +#define MAX1363_CONFIG_BYTE(a) ((a)) + +#define MAX1363_CONFIG_SE 0x01 +#define MAX1363_CONFIG_DE 0x00 +#define MAX1363_CONFIG_SCAN_TO_CS 0x00 +#define MAX1363_CONFIG_SCAN_SINGLE_8 0x20 +#define MAX1363_CONFIG_SCAN_MONITOR_MODE 0x40 +#define MAX1363_CONFIG_SCAN_SINGLE_1 0x60 +/* max123{6-9} only */ +#define MAX1236_SCAN_MID_TO_CHANNEL 0x40 + +/* max1363 only - merely part of channel selects or don't care for others */ +#define MAX1363_CONFIG_EN_MON_MODE_READ 0x18 + +#define MAX1363_CHANNEL_SEL(a) ((a) << 1) + +/* max1363 strictly 0x06 - but doesn't matter */ +#define MAX1363_CHANNEL_SEL_MASK 0x1E +#define MAX1363_SCAN_MASK 0x60 +#define MAX1363_SE_DE_MASK 0x01 + +#define MAX1363_MAX_CHANNELS 25 +/** + * struct max1363_mode - scan mode information + * @conf: The corresponding value of the configuration register + * @modemask: Bit mask corresponding to channels enabled in this mode + */ +struct max1363_mode { + int8_t conf; + DECLARE_BITMAP(modemask, MAX1363_MAX_CHANNELS); +}; + +/* This must be maintained along side the max1363_mode_table in max1363_core */ +enum max1363_modes { + /* Single read of a single channel */ + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, + /* Differential single read */ + d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, + d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, + /* Scan to channel and mid to channel where overlapping */ + s0to1, s0to2, s2to3, s0to3, s0to4, s0to5, s0to6, + s6to7, s0to7, s6to8, s0to8, s6to9, + s0to9, s6to10, s0to10, s6to11, s0to11, + /* Differential scan to channel and mid to channel where overlapping */ + d0m1to2m3, d0m1to4m5, d0m1to6m7, d6m7to8m9, + d0m1to8m9, d6m7to10m11, d0m1to10m11, d1m0to3m2, + d1m0to5m4, d1m0to7m6, d7m6to9m8, d1m0to9m8, + d7m6to11m10, d1m0to11m10, +}; + +/** + * struct max1363_chip_info - chip specifc information + * @info: iio core function callbacks structure + * @channels: channel specification + * @num_channels: number of channels + * @mode_list: array of available scan modes + * @default_mode: the scan mode in which the chip starts up + * @int_vref_mv: the internal reference voltage + * @num_modes: number of modes + * @bits: accuracy of the adc in bits + */ +struct max1363_chip_info { + const struct iio_info *info; + const struct iio_chan_spec *channels; + int num_channels; + const enum max1363_modes *mode_list; + enum max1363_modes default_mode; + u16 int_vref_mv; + u8 num_modes; + u8 bits; +}; + +/** + * struct max1363_state - driver instance specific data + * @client: i2c_client + * @setupbyte: cache of current device setup byte + * @configbyte: cache of current device config byte + * @chip_info: chip model specific constants, available modes, etc. + * @current_mode: the scan mode of this chip + * @requestedmask: a valid requested set of channels + * @reg: supply regulator + * @lock: lock to ensure state is consistent + * @monitor_on: whether monitor mode is enabled + * @monitor_speed: parameter corresponding to device monitor speed setting + * @mask_high: bitmask for enabled high thresholds + * @mask_low: bitmask for enabled low thresholds + * @thresh_high: high threshold values + * @thresh_low: low threshold values + * @vref: Reference voltage regulator + * @vref_uv: Actual (external or internal) reference voltage + * @send: function used to send data to the chip + * @recv: function used to receive data from the chip + */ +struct max1363_state { + struct i2c_client *client; + u8 setupbyte; + u8 configbyte; + const struct max1363_chip_info *chip_info; + const struct max1363_mode *current_mode; + u32 requestedmask; + struct regulator *reg; + struct mutex lock; + + /* Using monitor modes and buffer at the same time is + currently not supported */ + bool monitor_on; + unsigned int monitor_speed:3; + u8 mask_high; + u8 mask_low; + /* 4x unipolar first then the fours bipolar ones */ + s16 thresh_high[8]; + s16 thresh_low[8]; + struct regulator *vref; + u32 vref_uv; + int (*send)(const struct i2c_client *client, + const char *buf, int count); + int (*recv)(const struct i2c_client *client, + char *buf, int count); +}; + +#define MAX1363_MODE_SINGLE(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_SINGLE_1 \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask, \ + } + +#define MAX1363_MODE_SCAN_TO_CHANNEL(_num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_TO_CS \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask, \ + } + +/* note not available for max1363 hence naming */ +#define MAX1236_MODE_SCAN_MID_TO_CHANNEL(_mid, _num, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1236_SCAN_MID_TO_CHANNEL \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask \ +} + +#define MAX1363_MODE_DIFF_SINGLE(_nump, _numm, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_nump) \ + | MAX1363_CONFIG_SCAN_SINGLE_1 \ + | MAX1363_CONFIG_DE, \ + .modemask[0] = _mask \ + } + +/* Can't think how to automate naming so specify for now */ +#define MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1363_CONFIG_SCAN_TO_CS \ + | MAX1363_CONFIG_DE, \ + .modemask[0] = _mask \ + } + +/* note only available for max1363 hence naming */ +#define MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(_num, _numvals, _mask) { \ + .conf = MAX1363_CHANNEL_SEL(_num) \ + | MAX1236_SCAN_MID_TO_CHANNEL \ + | MAX1363_CONFIG_SE, \ + .modemask[0] = _mask \ +} + +static const struct max1363_mode max1363_mode_table[] = { + /* All of the single channel options first */ + MAX1363_MODE_SINGLE(0, 1 << 0), + MAX1363_MODE_SINGLE(1, 1 << 1), + MAX1363_MODE_SINGLE(2, 1 << 2), + MAX1363_MODE_SINGLE(3, 1 << 3), + MAX1363_MODE_SINGLE(4, 1 << 4), + MAX1363_MODE_SINGLE(5, 1 << 5), + MAX1363_MODE_SINGLE(6, 1 << 6), + MAX1363_MODE_SINGLE(7, 1 << 7), + MAX1363_MODE_SINGLE(8, 1 << 8), + MAX1363_MODE_SINGLE(9, 1 << 9), + MAX1363_MODE_SINGLE(10, 1 << 10), + MAX1363_MODE_SINGLE(11, 1 << 11), + + MAX1363_MODE_DIFF_SINGLE(0, 1, 1 << 12), + MAX1363_MODE_DIFF_SINGLE(2, 3, 1 << 13), + MAX1363_MODE_DIFF_SINGLE(4, 5, 1 << 14), + MAX1363_MODE_DIFF_SINGLE(6, 7, 1 << 15), + MAX1363_MODE_DIFF_SINGLE(8, 9, 1 << 16), + MAX1363_MODE_DIFF_SINGLE(10, 11, 1 << 17), + MAX1363_MODE_DIFF_SINGLE(1, 0, 1 << 18), + MAX1363_MODE_DIFF_SINGLE(3, 2, 1 << 19), + MAX1363_MODE_DIFF_SINGLE(5, 4, 1 << 20), + MAX1363_MODE_DIFF_SINGLE(7, 6, 1 << 21), + MAX1363_MODE_DIFF_SINGLE(9, 8, 1 << 22), + MAX1363_MODE_DIFF_SINGLE(11, 10, 1 << 23), + + /* The multichannel scans next */ + MAX1363_MODE_SCAN_TO_CHANNEL(1, 0x003), + MAX1363_MODE_SCAN_TO_CHANNEL(2, 0x007), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(2, 3, 0x00C), + MAX1363_MODE_SCAN_TO_CHANNEL(3, 0x00F), + MAX1363_MODE_SCAN_TO_CHANNEL(4, 0x01F), + MAX1363_MODE_SCAN_TO_CHANNEL(5, 0x03F), + MAX1363_MODE_SCAN_TO_CHANNEL(6, 0x07F), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 7, 0x0C0), + MAX1363_MODE_SCAN_TO_CHANNEL(7, 0x0FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 8, 0x1C0), + MAX1363_MODE_SCAN_TO_CHANNEL(8, 0x1FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 9, 0x3C0), + MAX1363_MODE_SCAN_TO_CHANNEL(9, 0x3FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 10, 0x7C0), + MAX1363_MODE_SCAN_TO_CHANNEL(10, 0x7FF), + MAX1236_MODE_SCAN_MID_TO_CHANNEL(6, 11, 0xFC0), + MAX1363_MODE_SCAN_TO_CHANNEL(11, 0xFFF), + + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(2, 2, 0x003000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(4, 3, 0x007000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(6, 4, 0x00F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(8, 2, 0x018000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(8, 5, 0x01F000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(10, 3, 0x038000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(10, 6, 0x3F000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(3, 2, 0x0C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(5, 3, 0x1C0000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(7, 4, 0x3C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(9, 2, 0x600000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(9, 5, 0x7C0000), + MAX1236_MODE_DIFF_SCAN_MID_TO_CHANNEL(11, 3, 0xE00000), + MAX1363_MODE_DIFF_SCAN_TO_CHANNEL(11, 6, 0xFC0000), +}; + +static const struct max1363_mode +*max1363_match_mode(const unsigned long *mask, + const struct max1363_chip_info *ci) +{ + int i; + if (mask) + for (i = 0; i < ci->num_modes; i++) + if (bitmap_subset(mask, + max1363_mode_table[ci->mode_list[i]]. + modemask, + MAX1363_MAX_CHANNELS)) + return &max1363_mode_table[ci->mode_list[i]]; + return NULL; +} + +static int max1363_smbus_send(const struct i2c_client *client, const char *buf, + int count) +{ + int i, err; + + for (i = err = 0; err == 0 && i < count; ++i) + err = i2c_smbus_write_byte(client, buf[i]); + + return err ? err : count; +} + +static int max1363_smbus_recv(const struct i2c_client *client, char *buf, + int count) +{ + int i, ret; + + for (i = 0; i < count; ++i) { + ret = i2c_smbus_read_byte(client); + if (ret < 0) + return ret; + buf[i] = ret; + } + + return count; +} + +static int max1363_write_basic_config(struct max1363_state *st) +{ + u8 tx_buf[2] = { st->setupbyte, st->configbyte }; + + return st->send(st->client, tx_buf, 2); +} + +static int max1363_set_scan_mode(struct max1363_state *st) +{ + st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK + | MAX1363_SCAN_MASK + | MAX1363_SE_DE_MASK); + st->configbyte |= st->current_mode->conf; + + return max1363_write_basic_config(st); +} + +static int max1363_read_single_chan(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + long m) +{ + int ret = 0; + s32 data; + u8 rxbuf[2]; + struct max1363_state *st = iio_priv(indio_dev); + struct i2c_client *client = st->client; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&st->lock); + + /* + * If monitor mode is enabled, the method for reading a single + * channel will have to be rather different and has not yet + * been implemented. + * + * Also, cannot read directly if buffered capture enabled. + */ + if (st->monitor_on) { + ret = -EBUSY; + goto error_ret; + } + + /* Check to see if current scan mode is correct */ + if (st->current_mode != &max1363_mode_table[chan->address]) { + /* Update scan mode if needed */ + st->current_mode = &max1363_mode_table[chan->address]; + ret = max1363_set_scan_mode(st); + if (ret < 0) + goto error_ret; + } + if (st->chip_info->bits != 8) { + /* Get reading */ + data = st->recv(client, rxbuf, 2); + if (data < 0) { + ret = data; + goto error_ret; + } + data = (rxbuf[1] | rxbuf[0] << 8) & + ((1 << st->chip_info->bits) - 1); + } else { + /* Get reading */ + data = st->recv(client, rxbuf, 1); + if (data < 0) { + ret = data; + goto error_ret; + } + data = rxbuf[0]; + } + *val = data; + +error_ret: + mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); + return ret; + +} + +static int max1363_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct max1363_state *st = iio_priv(indio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = max1363_read_single_chan(indio_dev, chan, val, m); + if (ret < 0) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = st->vref_uv / 1000; + *val2 = st->chip_info->bits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + return 0; +} + +/* Applies to max1363 */ +static const enum max1363_modes max1363_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, +}; + +static const struct iio_event_spec max1363_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define MAX1363_CHAN_U(num, addr, si, bits, ev_spec, num_ev_spec) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = num, \ + .address = addr, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = "AIN"#num, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = bits, \ + .storagebits = (bits > 8) ? 16 : 8, \ + .endianness = IIO_BE, \ + }, \ + .scan_index = si, \ + .event_spec = ev_spec, \ + .num_event_specs = num_ev_spec, \ + } + +/* bipolar channel */ +#define MAX1363_CHAN_B(num, num2, addr, si, bits, ev_spec, num_ev_spec) \ + { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .channel = num, \ + .channel2 = num2, \ + .address = addr, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = "AIN"#num"-AIN"#num2, \ + .scan_type = { \ + .sign = 's', \ + .realbits = bits, \ + .storagebits = (bits > 8) ? 16 : 8, \ + .endianness = IIO_BE, \ + }, \ + .scan_index = si, \ + .event_spec = ev_spec, \ + .num_event_specs = num_ev_spec, \ + } + +#define MAX1363_4X_CHANS(bits, ev_spec, num_ev_spec) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_U(1, _s1, 1, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_U(2, _s2, 2, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_U(3, _s3, 3, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(0, 1, d0m1, 4, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(2, 3, d2m3, 5, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(1, 0, d1m0, 6, bits, ev_spec, num_ev_spec), \ + MAX1363_CHAN_B(3, 2, d3m2, 7, bits, ev_spec, num_ev_spec), \ + IIO_CHAN_SOFT_TIMESTAMP(8) \ + } + +static const struct iio_chan_spec max1036_channels[] = + MAX1363_4X_CHANS(8, NULL, 0); +static const struct iio_chan_spec max1136_channels[] = + MAX1363_4X_CHANS(10, NULL, 0); +static const struct iio_chan_spec max1236_channels[] = + MAX1363_4X_CHANS(12, NULL, 0); +static const struct iio_chan_spec max1361_channels[] = + MAX1363_4X_CHANS(10, max1363_events, ARRAY_SIZE(max1363_events)); +static const struct iio_chan_spec max1363_channels[] = + MAX1363_4X_CHANS(12, max1363_events, ARRAY_SIZE(max1363_events)); + +/* Applies to max1236, max1237 */ +static const enum max1363_modes max1236_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, + s2to3, +}; + +/* Applies to max1238, max1239 */ +static const enum max1363_modes max1238_mode_list[] = { + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, _s8, _s9, _s10, _s11, + s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, + s0to7, s0to8, s0to9, s0to10, s0to11, + d0m1, d2m3, d4m5, d6m7, d8m9, d10m11, + d1m0, d3m2, d5m4, d7m6, d9m8, d11m10, + d0m1to2m3, d0m1to4m5, d0m1to6m7, d0m1to8m9, d0m1to10m11, + d1m0to3m2, d1m0to5m4, d1m0to7m6, d1m0to9m8, d1m0to11m10, + s6to7, s6to8, s6to9, s6to10, s6to11, + d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10, +}; + +#define MAX1363_12X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \ + MAX1363_CHAN_U(2, _s2, 2, bits, NULL, 0), \ + MAX1363_CHAN_U(3, _s3, 3, bits, NULL, 0), \ + MAX1363_CHAN_U(4, _s4, 4, bits, NULL, 0), \ + MAX1363_CHAN_U(5, _s5, 5, bits, NULL, 0), \ + MAX1363_CHAN_U(6, _s6, 6, bits, NULL, 0), \ + MAX1363_CHAN_U(7, _s7, 7, bits, NULL, 0), \ + MAX1363_CHAN_U(8, _s8, 8, bits, NULL, 0), \ + MAX1363_CHAN_U(9, _s9, 9, bits, NULL, 0), \ + MAX1363_CHAN_U(10, _s10, 10, bits, NULL, 0), \ + MAX1363_CHAN_U(11, _s11, 11, bits, NULL, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 12, bits, NULL, 0), \ + MAX1363_CHAN_B(2, 3, d2m3, 13, bits, NULL, 0), \ + MAX1363_CHAN_B(4, 5, d4m5, 14, bits, NULL, 0), \ + MAX1363_CHAN_B(6, 7, d6m7, 15, bits, NULL, 0), \ + MAX1363_CHAN_B(8, 9, d8m9, 16, bits, NULL, 0), \ + MAX1363_CHAN_B(10, 11, d10m11, 17, bits, NULL, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 18, bits, NULL, 0), \ + MAX1363_CHAN_B(3, 2, d3m2, 19, bits, NULL, 0), \ + MAX1363_CHAN_B(5, 4, d5m4, 20, bits, NULL, 0), \ + MAX1363_CHAN_B(7, 6, d7m6, 21, bits, NULL, 0), \ + MAX1363_CHAN_B(9, 8, d9m8, 22, bits, NULL, 0), \ + MAX1363_CHAN_B(11, 10, d11m10, 23, bits, NULL, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(24) \ + } +static const struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8); +static const struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10); +static const struct iio_chan_spec max1238_channels[] = MAX1363_12X_CHANS(12); + +static const enum max1363_modes max11607_mode_list[] = { + _s0, _s1, _s2, _s3, + s0to1, s0to2, s0to3, + s2to3, + d0m1, d2m3, d1m0, d3m2, + d0m1to2m3, d1m0to3m2, +}; + +static const enum max1363_modes max11608_mode_list[] = { + _s0, _s1, _s2, _s3, _s4, _s5, _s6, _s7, + s0to1, s0to2, s0to3, s0to4, s0to5, s0to6, s0to7, + s6to7, + d0m1, d2m3, d4m5, d6m7, + d1m0, d3m2, d5m4, d7m6, + d0m1to2m3, d0m1to4m5, d0m1to6m7, + d1m0to3m2, d1m0to5m4, d1m0to7m6, +}; + +#define MAX1363_8X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \ + MAX1363_CHAN_U(2, _s2, 2, bits, NULL, 0), \ + MAX1363_CHAN_U(3, _s3, 3, bits, NULL, 0), \ + MAX1363_CHAN_U(4, _s4, 4, bits, NULL, 0), \ + MAX1363_CHAN_U(5, _s5, 5, bits, NULL, 0), \ + MAX1363_CHAN_U(6, _s6, 6, bits, NULL, 0), \ + MAX1363_CHAN_U(7, _s7, 7, bits, NULL, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 8, bits, NULL, 0), \ + MAX1363_CHAN_B(2, 3, d2m3, 9, bits, NULL, 0), \ + MAX1363_CHAN_B(4, 5, d4m5, 10, bits, NULL, 0), \ + MAX1363_CHAN_B(6, 7, d6m7, 11, bits, NULL, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 12, bits, NULL, 0), \ + MAX1363_CHAN_B(3, 2, d3m2, 13, bits, NULL, 0), \ + MAX1363_CHAN_B(5, 4, d5m4, 14, bits, NULL, 0), \ + MAX1363_CHAN_B(7, 6, d7m6, 15, bits, NULL, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(16) \ +} +static const struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8); +static const struct iio_chan_spec max11608_channels[] = MAX1363_8X_CHANS(10); +static const struct iio_chan_spec max11614_channels[] = MAX1363_8X_CHANS(12); + +static const enum max1363_modes max11644_mode_list[] = { + _s0, _s1, s0to1, d0m1, d1m0, +}; + +#define MAX1363_2X_CHANS(bits) { \ + MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \ + MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \ + MAX1363_CHAN_B(0, 1, d0m1, 2, bits, NULL, 0), \ + MAX1363_CHAN_B(1, 0, d1m0, 3, bits, NULL, 0), \ + IIO_CHAN_SOFT_TIMESTAMP(4) \ + } + +static const struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10); +static const struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12); + +enum { max1361, + max1362, + max1363, + max1364, + max1036, + max1037, + max1038, + max1039, + max1136, + max1137, + max1138, + max1139, + max1236, + max1237, + max1238, + max1239, + max11600, + max11601, + max11602, + max11603, + max11604, + max11605, + max11606, + max11607, + max11608, + max11609, + max11610, + max11611, + max11612, + max11613, + max11614, + max11615, + max11616, + max11617, + max11644, + max11645, + max11646, + max11647 +}; + +static const int max1363_monitor_speeds[] = { 133000, 665000, 33300, 16600, + 8300, 4200, 2000, 1000 }; + +static ssize_t max1363_monitor_show_freq(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max1363_state *st = iio_priv(dev_to_iio_dev(dev)); + return sprintf(buf, "%d\n", max1363_monitor_speeds[st->monitor_speed]); +} + +static ssize_t max1363_monitor_store_freq(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max1363_state *st = iio_priv(indio_dev); + int i, ret; + unsigned long val; + bool found = false; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(max1363_monitor_speeds); i++) + if (val == max1363_monitor_speeds[i]) { + found = true; + break; + } + if (!found) + return -EINVAL; + + mutex_lock(&st->lock); + st->monitor_speed = i; + mutex_unlock(&st->lock); + + return 0; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, + max1363_monitor_show_freq, + max1363_monitor_store_freq); + +static IIO_CONST_ATTR(sampling_frequency_available, + "133000 665000 33300 16600 8300 4200 2000 1000"); + +static int max1363_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, int *val, + int *val2) +{ + struct max1363_state *st = iio_priv(indio_dev); + if (dir == IIO_EV_DIR_FALLING) + *val = st->thresh_low[chan->channel]; + else + *val = st->thresh_high[chan->channel]; + return IIO_VAL_INT; +} + +static int max1363_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, int val, + int val2) +{ + struct max1363_state *st = iio_priv(indio_dev); + /* make it handle signed correctly as well */ + switch (st->chip_info->bits) { + case 10: + if (val > 0x3FF) + return -EINVAL; + break; + case 12: + if (val > 0xFFF) + return -EINVAL; + break; + } + + switch (dir) { + case IIO_EV_DIR_FALLING: + st->thresh_low[chan->channel] = val; + break; + case IIO_EV_DIR_RISING: + st->thresh_high[chan->channel] = val; + break; + default: + return -EINVAL; + } + + return 0; +} + +static const u64 max1363_event_codes[] = { + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 2, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 3, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), +}; + +static irqreturn_t max1363_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct max1363_state *st = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(indio_dev); + unsigned long mask, loc; + u8 rx; + u8 tx[2] = { st->setupbyte, + MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0 }; + + st->recv(st->client, &rx, 1); + mask = rx; + for_each_set_bit(loc, &mask, 8) + iio_push_event(indio_dev, max1363_event_codes[loc], timestamp); + st->send(st->client, tx, 2); + + return IRQ_HANDLED; +} + +static int max1363_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir) +{ + struct max1363_state *st = iio_priv(indio_dev); + int val; + int number = chan->channel; + + mutex_lock(&st->lock); + if (dir == IIO_EV_DIR_FALLING) + val = (1 << number) & st->mask_low; + else + val = (1 << number) & st->mask_high; + mutex_unlock(&st->lock); + + return val; +} + +static int max1363_monitor_mode_update(struct max1363_state *st, int enabled) +{ + u8 *tx_buf; + int ret, i = 3, j; + unsigned long numelements; + int len; + const long *modemask; + + if (!enabled) { + /* transition to buffered capture is not currently supported */ + st->setupbyte &= ~MAX1363_SETUP_MONITOR_SETUP; + st->configbyte &= ~MAX1363_SCAN_MASK; + st->monitor_on = false; + return max1363_write_basic_config(st); + } + + /* Ensure we are in the relevant mode */ + st->setupbyte |= MAX1363_SETUP_MONITOR_SETUP; + st->configbyte &= ~(MAX1363_CHANNEL_SEL_MASK + | MAX1363_SCAN_MASK + | MAX1363_SE_DE_MASK); + st->configbyte |= MAX1363_CONFIG_SCAN_MONITOR_MODE; + if ((st->mask_low | st->mask_high) & 0x0F) { + st->configbyte |= max1363_mode_table[s0to3].conf; + modemask = max1363_mode_table[s0to3].modemask; + } else if ((st->mask_low | st->mask_high) & 0x30) { + st->configbyte |= max1363_mode_table[d0m1to2m3].conf; + modemask = max1363_mode_table[d0m1to2m3].modemask; + } else { + st->configbyte |= max1363_mode_table[d1m0to3m2].conf; + modemask = max1363_mode_table[d1m0to3m2].modemask; + } + numelements = bitmap_weight(modemask, MAX1363_MAX_CHANNELS); + len = 3 * numelements + 3; + tx_buf = kmalloc(len, GFP_KERNEL); + if (!tx_buf) { + ret = -ENOMEM; + goto error_ret; + } + tx_buf[0] = st->configbyte; + tx_buf[1] = st->setupbyte; + tx_buf[2] = (st->monitor_speed << 1); + + /* + * So we need to do yet another bit of nefarious scan mode + * setup to match what we need. + */ + for (j = 0; j < 8; j++) + if (test_bit(j, modemask)) { + /* Establish the mode is in the scan */ + if (st->mask_low & (1 << j)) { + tx_buf[i] = (st->thresh_low[j] >> 4) & 0xFF; + tx_buf[i + 1] = (st->thresh_low[j] << 4) & 0xF0; + } else if (j < 4) { + tx_buf[i] = 0; + tx_buf[i + 1] = 0; + } else { + tx_buf[i] = 0x80; + tx_buf[i + 1] = 0; + } + if (st->mask_high & (1 << j)) { + tx_buf[i + 1] |= + (st->thresh_high[j] >> 8) & 0x0F; + tx_buf[i + 2] = st->thresh_high[j] & 0xFF; + } else if (j < 4) { + tx_buf[i + 1] |= 0x0F; + tx_buf[i + 2] = 0xFF; + } else { + tx_buf[i + 1] |= 0x07; + tx_buf[i + 2] = 0xFF; + } + i += 3; + } + + + ret = st->send(st->client, tx_buf, len); + if (ret < 0) + goto error_ret; + if (ret != len) { + ret = -EIO; + goto error_ret; + } + + /* + * Now that we hopefully have sensible thresholds in place it is + * time to turn the interrupts on. + * It is unclear from the data sheet if this should be necessary + * (i.e. whether monitor mode setup is atomic) but it appears to + * be in practice. + */ + tx_buf[0] = st->setupbyte; + tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0; + ret = st->send(st->client, tx_buf, 2); + if (ret < 0) + goto error_ret; + if (ret != 2) { + ret = -EIO; + goto error_ret; + } + ret = 0; + st->monitor_on = true; +error_ret: + + kfree(tx_buf); + + return ret; +} + +/* + * To keep this manageable we always use one of 3 scan modes. + * Scan 0...3, 0-1,2-3 and 1-0,3-2 + */ + +static inline int __max1363_check_event_mask(int thismask, int checkmask) +{ + int ret = 0; + /* Is it unipolar */ + if (thismask < 4) { + if (checkmask & ~0x0F) { + ret = -EBUSY; + goto error_ret; + } + } else if (thismask < 6) { + if (checkmask & ~0x30) { + ret = -EBUSY; + goto error_ret; + } + } else if (checkmask & ~0xC0) + ret = -EBUSY; +error_ret: + return ret; +} + +static int max1363_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + int ret = 0; + struct max1363_state *st = iio_priv(indio_dev); + u16 unifiedmask; + int number = chan->channel; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&st->lock); + + unifiedmask = st->mask_low | st->mask_high; + if (dir == IIO_EV_DIR_FALLING) { + + if (state == 0) + st->mask_low &= ~(1 << number); + else { + ret = __max1363_check_event_mask((1 << number), + unifiedmask); + if (ret) + goto error_ret; + st->mask_low |= (1 << number); + } + } else { + if (state == 0) + st->mask_high &= ~(1 << number); + else { + ret = __max1363_check_event_mask((1 << number), + unifiedmask); + if (ret) + goto error_ret; + st->mask_high |= (1 << number); + } + } + + max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low)); +error_ret: + mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +/* + * As with scan_elements, only certain sets of these can + * be combined. + */ +static struct attribute *max1363_event_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group max1363_event_attribute_group = { + .attrs = max1363_event_attributes, +}; + +static int max1363_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct max1363_state *st = iio_priv(indio_dev); + + /* + * Need to figure out the current mode based upon the requested + * scan mask in iio_dev + */ + st->current_mode = max1363_match_mode(scan_mask, st->chip_info); + if (!st->current_mode) + return -EINVAL; + max1363_set_scan_mode(st); + return 0; +} + +static const struct iio_info max1238_info = { + .read_raw = &max1363_read_raw, + .update_scan_mode = &max1363_update_scan_mode, +}; + +static const struct iio_info max1363_info = { + .read_event_value = &max1363_read_thresh, + .write_event_value = &max1363_write_thresh, + .read_event_config = &max1363_read_event_config, + .write_event_config = &max1363_write_event_config, + .read_raw = &max1363_read_raw, + .update_scan_mode = &max1363_update_scan_mode, + .event_attrs = &max1363_event_attribute_group, +}; + +/* max1363 and max1368 tested - rest from data sheet */ +static const struct max1363_chip_info max1363_chip_info_tbl[] = { + [max1361] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1361_channels, + .num_channels = ARRAY_SIZE(max1361_channels), + .info = &max1363_info, + }, + [max1362] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1361_channels, + .num_channels = ARRAY_SIZE(max1361_channels), + .info = &max1363_info, + }, + [max1363] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + .info = &max1363_info, + }, + [max1364] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1363_mode_list, + .num_modes = ARRAY_SIZE(max1363_mode_list), + .default_mode = s0to3, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + .info = &max1363_info, + }, + [max1036] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max1037] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max1038] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max1039] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max1136] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max1137] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max1138] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max1139] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max1236] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1236_channels, + .num_channels = ARRAY_SIZE(max1236_channels), + }, + [max1237] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1236_mode_list, + .num_modes = ARRAY_SIZE(max1236_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1236_channels, + .num_channels = ARRAY_SIZE(max1236_channels), + }, + [max1238] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max1239] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11600] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max11601] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1036_channels, + .num_channels = ARRAY_SIZE(max1036_channels), + }, + [max11602] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11602_channels, + .num_channels = ARRAY_SIZE(max11602_channels), + }, + [max11603] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11602_channels, + .num_channels = ARRAY_SIZE(max11602_channels), + }, + [max11604] = { + .bits = 8, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max11605] = { + .bits = 8, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), + }, + [max11606] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max11607] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1136_channels, + .num_channels = ARRAY_SIZE(max1136_channels), + }, + [max11608] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11608_channels, + .num_channels = ARRAY_SIZE(max11608_channels), + }, + [max11609] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11608_channels, + .num_channels = ARRAY_SIZE(max11608_channels), + }, + [max11610] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max11611] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), + }, + [max11612] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + }, + [max11613] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11607_mode_list, + .num_modes = ARRAY_SIZE(max11607_mode_list), + .default_mode = s0to3, + .info = &max1238_info, + .channels = max1363_channels, + .num_channels = ARRAY_SIZE(max1363_channels), + }, + [max11614] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11614_channels, + .num_channels = ARRAY_SIZE(max11614_channels), + }, + [max11615] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11608_mode_list, + .num_modes = ARRAY_SIZE(max11608_mode_list), + .default_mode = s0to7, + .info = &max1238_info, + .channels = max11614_channels, + .num_channels = ARRAY_SIZE(max11614_channels), + }, + [max11616] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11617] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max1238_mode_list, + .num_modes = ARRAY_SIZE(max1238_mode_list), + .default_mode = s0to11, + .info = &max1238_info, + .channels = max1238_channels, + .num_channels = ARRAY_SIZE(max1238_channels), + }, + [max11644] = { + .bits = 12, + .int_vref_mv = 4096, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11644_channels, + .num_channels = ARRAY_SIZE(max11644_channels), + }, + [max11645] = { + .bits = 12, + .int_vref_mv = 2048, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11644_channels, + .num_channels = ARRAY_SIZE(max11644_channels), + }, + [max11646] = { + .bits = 10, + .int_vref_mv = 4096, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11646_channels, + .num_channels = ARRAY_SIZE(max11646_channels), + }, + [max11647] = { + .bits = 10, + .int_vref_mv = 2048, + .mode_list = max11644_mode_list, + .num_modes = ARRAY_SIZE(max11644_mode_list), + .default_mode = s0to1, + .info = &max1238_info, + .channels = max11646_channels, + .num_channels = ARRAY_SIZE(max11646_channels), + }, +}; + +static int max1363_initial_setup(struct max1363_state *st) +{ + st->setupbyte = MAX1363_SETUP_INT_CLOCK + | MAX1363_SETUP_UNIPOLAR + | MAX1363_SETUP_NORESET; + + if (st->vref) + st->setupbyte |= MAX1363_SETUP_AIN3_IS_REF_EXT_TO_REF; + else + st->setupbyte |= MAX1363_SETUP_POWER_UP_INT_REF + | MAX1363_SETUP_AIN3_IS_AIN3_REF_IS_INT; + + /* Set scan mode writes the config anyway so wait until then */ + st->setupbyte = MAX1363_SETUP_BYTE(st->setupbyte); + st->current_mode = &max1363_mode_table[st->chip_info->default_mode]; + st->configbyte = MAX1363_CONFIG_BYTE(st->configbyte); + + return max1363_set_scan_mode(st); +} + +static int max1363_alloc_scan_masks(struct iio_dev *indio_dev) +{ + struct max1363_state *st = iio_priv(indio_dev); + unsigned long *masks; + int i; + + masks = devm_kzalloc(&indio_dev->dev, + array3_size(BITS_TO_LONGS(MAX1363_MAX_CHANNELS), + sizeof(long), + st->chip_info->num_modes + 1), + GFP_KERNEL); + if (!masks) + return -ENOMEM; + + for (i = 0; i < st->chip_info->num_modes; i++) + bitmap_copy(masks + BITS_TO_LONGS(MAX1363_MAX_CHANNELS)*i, + max1363_mode_table[st->chip_info->mode_list[i]] + .modemask, MAX1363_MAX_CHANNELS); + + indio_dev->available_scan_masks = masks; + + return 0; +} + +static irqreturn_t max1363_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct max1363_state *st = iio_priv(indio_dev); + __u8 *rxbuf; + int b_sent; + size_t d_size; + unsigned long numvals = bitmap_weight(st->current_mode->modemask, + MAX1363_MAX_CHANNELS); + + /* Ensure the timestamp is 8 byte aligned */ + if (st->chip_info->bits != 8) + d_size = numvals*2; + else + d_size = numvals; + if (indio_dev->scan_timestamp) { + d_size += sizeof(s64); + if (d_size % sizeof(s64)) + d_size += sizeof(s64) - (d_size % sizeof(s64)); + } + /* Monitor mode prevents reading. Whilst not currently implemented + * might as well have this test in here in the meantime as it does + * no harm. + */ + if (numvals == 0) + goto done; + + rxbuf = kmalloc(d_size, GFP_KERNEL); + if (rxbuf == NULL) + goto done; + if (st->chip_info->bits != 8) + b_sent = st->recv(st->client, rxbuf, numvals * 2); + else + b_sent = st->recv(st->client, rxbuf, numvals); + if (b_sent < 0) + goto done_free; + + iio_push_to_buffers_with_timestamp(indio_dev, rxbuf, + iio_get_time_ns(indio_dev)); + +done_free: + kfree(rxbuf); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +#define MAX1363_COMPATIBLE(of_compatible, cfg) { \ + .compatible = of_compatible, \ + .data = &max1363_chip_info_tbl[cfg], \ +} + +static const struct of_device_id max1363_of_match[] = { + MAX1363_COMPATIBLE("maxim,max1361", max1361), + MAX1363_COMPATIBLE("maxim,max1362", max1362), + MAX1363_COMPATIBLE("maxim,max1363", max1363), + MAX1363_COMPATIBLE("maxim,max1364", max1364), + MAX1363_COMPATIBLE("maxim,max1036", max1036), + MAX1363_COMPATIBLE("maxim,max1037", max1037), + MAX1363_COMPATIBLE("maxim,max1038", max1038), + MAX1363_COMPATIBLE("maxim,max1039", max1039), + MAX1363_COMPATIBLE("maxim,max1136", max1136), + MAX1363_COMPATIBLE("maxim,max1137", max1137), + MAX1363_COMPATIBLE("maxim,max1138", max1138), + MAX1363_COMPATIBLE("maxim,max1139", max1139), + MAX1363_COMPATIBLE("maxim,max1236", max1236), + MAX1363_COMPATIBLE("maxim,max1237", max1237), + MAX1363_COMPATIBLE("maxim,max1238", max1238), + MAX1363_COMPATIBLE("maxim,max1239", max1239), + MAX1363_COMPATIBLE("maxim,max11600", max11600), + MAX1363_COMPATIBLE("maxim,max11601", max11601), + MAX1363_COMPATIBLE("maxim,max11602", max11602), + MAX1363_COMPATIBLE("maxim,max11603", max11603), + MAX1363_COMPATIBLE("maxim,max11604", max11604), + MAX1363_COMPATIBLE("maxim,max11605", max11605), + MAX1363_COMPATIBLE("maxim,max11606", max11606), + MAX1363_COMPATIBLE("maxim,max11607", max11607), + MAX1363_COMPATIBLE("maxim,max11608", max11608), + MAX1363_COMPATIBLE("maxim,max11609", max11609), + MAX1363_COMPATIBLE("maxim,max11610", max11610), + MAX1363_COMPATIBLE("maxim,max11611", max11611), + MAX1363_COMPATIBLE("maxim,max11612", max11612), + MAX1363_COMPATIBLE("maxim,max11613", max11613), + MAX1363_COMPATIBLE("maxim,max11614", max11614), + MAX1363_COMPATIBLE("maxim,max11615", max11615), + MAX1363_COMPATIBLE("maxim,max11616", max11616), + MAX1363_COMPATIBLE("maxim,max11617", max11617), + MAX1363_COMPATIBLE("maxim,max11644", max11644), + MAX1363_COMPATIBLE("maxim,max11645", max11645), + MAX1363_COMPATIBLE("maxim,max11646", max11646), + MAX1363_COMPATIBLE("maxim,max11647", max11647), + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, max1363_of_match); + +static int max1363_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct max1363_state *st; + struct iio_dev *indio_dev; + struct regulator *vref; + + indio_dev = devm_iio_device_alloc(&client->dev, + sizeof(struct max1363_state)); + if (!indio_dev) + return -ENOMEM; + + ret = iio_map_array_register(indio_dev, client->dev.platform_data); + if (ret < 0) + return ret; + + st = iio_priv(indio_dev); + + mutex_init(&st->lock); + st->reg = devm_regulator_get(&client->dev, "vcc"); + if (IS_ERR(st->reg)) { + ret = PTR_ERR(st->reg); + goto error_unregister_map; + } + + ret = regulator_enable(st->reg); + if (ret) + goto error_unregister_map; + + /* this is only used for device removal purposes */ + i2c_set_clientdata(client, indio_dev); + + st->chip_info = device_get_match_data(&client->dev); + if (!st->chip_info) + st->chip_info = &max1363_chip_info_tbl[id->driver_data]; + st->client = client; + + st->vref_uv = st->chip_info->int_vref_mv * 1000; + vref = devm_regulator_get_optional(&client->dev, "vref"); + if (!IS_ERR(vref)) { + int vref_uv; + + ret = regulator_enable(vref); + if (ret) + goto error_disable_reg; + st->vref = vref; + vref_uv = regulator_get_voltage(vref); + if (vref_uv <= 0) { + ret = -EINVAL; + goto error_disable_reg; + } + st->vref_uv = vref_uv; + } + + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + st->send = i2c_master_send; + st->recv = i2c_master_recv; + } else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE) + && st->chip_info->bits == 8) { + st->send = max1363_smbus_send; + st->recv = max1363_smbus_recv; + } else { + ret = -EOPNOTSUPP; + goto error_disable_reg; + } + + ret = max1363_alloc_scan_masks(indio_dev); + if (ret) + goto error_disable_reg; + + indio_dev->name = id->name; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = st->chip_info->info; + indio_dev->modes = INDIO_DIRECT_MODE; + ret = max1363_initial_setup(st); + if (ret < 0) + goto error_disable_reg; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &max1363_trigger_handler, NULL); + if (ret) + goto error_disable_reg; + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, st->client->irq, + NULL, + &max1363_event_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "max1363_event", + indio_dev); + + if (ret) + goto error_uninit_buffer; + } + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto error_uninit_buffer; + + return 0; + +error_uninit_buffer: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + if (st->vref) + regulator_disable(st->vref); + regulator_disable(st->reg); +error_unregister_map: + iio_map_array_unregister(indio_dev); + return ret; +} + +static int max1363_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct max1363_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (st->vref) + regulator_disable(st->vref); + regulator_disable(st->reg); + iio_map_array_unregister(indio_dev); + + return 0; +} + +static const struct i2c_device_id max1363_id[] = { + { "max1361", max1361 }, + { "max1362", max1362 }, + { "max1363", max1363 }, + { "max1364", max1364 }, + { "max1036", max1036 }, + { "max1037", max1037 }, + { "max1038", max1038 }, + { "max1039", max1039 }, + { "max1136", max1136 }, + { "max1137", max1137 }, + { "max1138", max1138 }, + { "max1139", max1139 }, + { "max1236", max1236 }, + { "max1237", max1237 }, + { "max1238", max1238 }, + { "max1239", max1239 }, + { "max11600", max11600 }, + { "max11601", max11601 }, + { "max11602", max11602 }, + { "max11603", max11603 }, + { "max11604", max11604 }, + { "max11605", max11605 }, + { "max11606", max11606 }, + { "max11607", max11607 }, + { "max11608", max11608 }, + { "max11609", max11609 }, + { "max11610", max11610 }, + { "max11611", max11611 }, + { "max11612", max11612 }, + { "max11613", max11613 }, + { "max11614", max11614 }, + { "max11615", max11615 }, + { "max11616", max11616 }, + { "max11617", max11617 }, + { "max11644", max11644 }, + { "max11645", max11645 }, + { "max11646", max11646 }, + { "max11647", max11647 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, max1363_id); + +static struct i2c_driver max1363_driver = { + .driver = { + .name = "max1363", + .of_match_table = max1363_of_match, + }, + .probe = max1363_probe, + .remove = max1363_remove, + .id_table = max1363_id, +}; +module_i2c_driver(max1363_driver); + +MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); +MODULE_DESCRIPTION("Maxim 1363 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c new file mode 100644 index 000000000..052ab23f1 --- /dev/null +++ b/drivers/iio/adc/max9611.c @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * iio/adc/max9611.c + * + * Maxim max9611/max9612 high side current sense amplifier with + * 12-bit ADC interface. + * + * Copyright (C) 2017 Jacopo Mondi + */ + +/* + * This driver supports input common-mode voltage, current-sense + * amplifier with programmable gains and die temperature reading from + * Maxim max9611/max9612. + * + * Op-amp, analog comparator, and watchdog functionalities are not + * supported by this driver. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/module.h> +#include <linux/of_device.h> + +#define DRIVER_NAME "max9611" + +/* max9611 register addresses */ +#define MAX9611_REG_CSA_DATA 0x00 +#define MAX9611_REG_RS_DATA 0x02 +#define MAX9611_REG_TEMP_DATA 0x08 +#define MAX9611_REG_CTRL1 0x0a +#define MAX9611_REG_CTRL2 0x0b + +/* max9611 REG1 mux configuration options */ +#define MAX9611_MUX_MASK GENMASK(3, 0) +#define MAX9611_MUX_SENSE_1x 0x00 +#define MAX9611_MUX_SENSE_4x 0x01 +#define MAX9611_MUX_SENSE_8x 0x02 +#define MAX9611_INPUT_VOLT 0x03 +#define MAX9611_MUX_TEMP 0x06 + +/* max9611 voltage (both csa and input) helper macros */ +#define MAX9611_VOLTAGE_SHIFT 0x04 +#define MAX9611_VOLTAGE_RAW(_r) ((_r) >> MAX9611_VOLTAGE_SHIFT) + +/* + * max9611 current sense amplifier voltage output: + * LSB and offset values depends on selected gain (1x, 4x, 8x) + * + * GAIN LSB (nV) OFFSET (LSB steps) + * 1x 107500 1 + * 4x 26880 1 + * 8x 13440 3 + * + * The complete formula to calculate current sense voltage is: + * (((adc_read >> 4) - offset) / ((1 / LSB) * 10^-3) + */ +#define MAX9611_CSA_1X_LSB_nV 107500 +#define MAX9611_CSA_4X_LSB_nV 26880 +#define MAX9611_CSA_8X_LSB_nV 13440 + +#define MAX9611_CSA_1X_OFFS_RAW 1 +#define MAX9611_CSA_4X_OFFS_RAW 1 +#define MAX9611_CSA_8X_OFFS_RAW 3 + +/* + * max9611 common input mode (CIM): LSB is 14mV, with 14mV offset at 25 C + * + * The complete formula to calculate input common voltage is: + * (((adc_read >> 4) * 1000) - offset) / (1 / 14 * 1000) + */ +#define MAX9611_CIM_LSB_mV 14 +#define MAX9611_CIM_OFFSET_RAW 1 + +/* + * max9611 temperature reading: LSB is 480 milli degrees Celsius + * + * The complete formula to calculate temperature is: + * ((adc_read >> 7) * 1000) / (1 / 480 * 1000) + */ +#define MAX9611_TEMP_MAX_POS 0x7f80 +#define MAX9611_TEMP_MAX_NEG 0xff80 +#define MAX9611_TEMP_MIN_NEG 0xd980 +#define MAX9611_TEMP_MASK GENMASK(15, 7) +#define MAX9611_TEMP_SHIFT 0x07 +#define MAX9611_TEMP_RAW(_r) ((_r) >> MAX9611_TEMP_SHIFT) +#define MAX9611_TEMP_SCALE_NUM 1000000 +#define MAX9611_TEMP_SCALE_DIV 2083 + +/* + * Conversion time is 2 ms (typically) at Ta=25 degreeC + * No maximum value is known, so play it safe. + */ +#define MAX9611_CONV_TIME_US_RANGE 3000, 3300 + +struct max9611_dev { + struct device *dev; + struct i2c_client *i2c_client; + struct mutex lock; + unsigned int shunt_resistor_uohm; +}; + +enum max9611_conf_ids { + CONF_SENSE_1x, + CONF_SENSE_4x, + CONF_SENSE_8x, + CONF_IN_VOLT, + CONF_TEMP, +}; + +/* + * max9611_mux_conf - associate ADC mux configuration with register address + * where data shall be read from + */ +static const unsigned int max9611_mux_conf[][2] = { + [CONF_SENSE_1x] = { MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA }, + [CONF_SENSE_4x] = { MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA }, + [CONF_SENSE_8x] = { MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA }, + [CONF_IN_VOLT] = { MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA }, + [CONF_TEMP] = { MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA }, +}; + +enum max9611_csa_gain { + CSA_GAIN_1x = CONF_SENSE_1x, + CSA_GAIN_4x = CONF_SENSE_4x, + CSA_GAIN_8x = CONF_SENSE_8x, +}; + +enum max9611_csa_gain_params { + CSA_GAIN_LSB_nV, + CSA_GAIN_OFFS_RAW, +}; + +/* + * max9611_csa_gain_conf - associate gain multiplier with LSB and + * offset values. + * + * Group together parameters associated with configurable gain + * on current sense amplifier path to ADC interface. + * Current sense read routine adjusts gain until it gets a meaningful + * value; use this structure to retrieve the correct LSB and offset values. + */ +static const unsigned int max9611_gain_conf[][2] = { + [CSA_GAIN_1x] = { MAX9611_CSA_1X_LSB_nV, MAX9611_CSA_1X_OFFS_RAW, }, + [CSA_GAIN_4x] = { MAX9611_CSA_4X_LSB_nV, MAX9611_CSA_4X_OFFS_RAW, }, + [CSA_GAIN_8x] = { MAX9611_CSA_8X_LSB_nV, MAX9611_CSA_8X_OFFS_RAW, }, +}; + +enum max9611_chan_addrs { + MAX9611_CHAN_VOLTAGE_INPUT, + MAX9611_CHAN_VOLTAGE_SENSE, + MAX9611_CHAN_TEMPERATURE, + MAX9611_CHAN_CURRENT_LOAD, + MAX9611_CHAN_POWER_LOAD, +}; + +static const struct iio_chan_spec max9611_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .address = MAX9611_CHAN_TEMPERATURE, + }, + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .address = MAX9611_CHAN_VOLTAGE_SENSE, + .indexed = 1, + .channel = 0, + }, + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .address = MAX9611_CHAN_VOLTAGE_INPUT, + .indexed = 1, + .channel = 1, + }, + { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .address = MAX9611_CHAN_CURRENT_LOAD, + }, + { + .type = IIO_POWER, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .address = MAX9611_CHAN_POWER_LOAD + }, +}; + +/** + * max9611_read_single() - read a single value from ADC interface + * + * Data registers are 16 bit long, spread between two 8 bit registers + * with consecutive addresses. + * Configure ADC mux first, then read register at address "reg_addr". + * The smbus_read_word routine asks for 16 bits and the ADC is kind enough + * to return values from "reg_addr" and "reg_addr + 1" consecutively. + * Data are transmitted with big-endian ordering: MSB arrives first. + * + * @max9611: max9611 device + * @selector: index for mux and register configuration + * @raw_val: the value returned from ADC + */ +static int max9611_read_single(struct max9611_dev *max9611, + enum max9611_conf_ids selector, + u16 *raw_val) +{ + int ret; + + u8 mux_conf = max9611_mux_conf[selector][0] & MAX9611_MUX_MASK; + u8 reg_addr = max9611_mux_conf[selector][1]; + + /* + * Keep mutex lock held during read-write to avoid mux register + * (CTRL1) re-configuration. + */ + mutex_lock(&max9611->lock); + ret = i2c_smbus_write_byte_data(max9611->i2c_client, + MAX9611_REG_CTRL1, mux_conf); + if (ret) { + dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n", + MAX9611_REG_CTRL1, mux_conf); + mutex_unlock(&max9611->lock); + return ret; + } + + /* need a delay here to make register configuration stabilize. */ + + usleep_range(MAX9611_CONV_TIME_US_RANGE); + + ret = i2c_smbus_read_word_swapped(max9611->i2c_client, reg_addr); + if (ret < 0) { + dev_err(max9611->dev, "i2c read word from 0x%2x failed\n", + reg_addr); + mutex_unlock(&max9611->lock); + return ret; + } + + *raw_val = ret; + mutex_unlock(&max9611->lock); + + return 0; +} + +/** + * max9611_read_csa_voltage() - read current sense amplifier output voltage + * + * Current sense amplifier output voltage is read through a configurable + * 1x, 4x or 8x gain. + * Start with plain 1x gain, and adjust gain control properly until a + * meaningful value is read from ADC output. + * + * @max9611: max9611 device + * @adc_raw: raw value read from ADC output + * @csa_gain: gain configuration option selector + */ +static int max9611_read_csa_voltage(struct max9611_dev *max9611, + u16 *adc_raw, + enum max9611_csa_gain *csa_gain) +{ + enum max9611_conf_ids gain_selectors[] = { + CONF_SENSE_1x, + CONF_SENSE_4x, + CONF_SENSE_8x + }; + unsigned int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(gain_selectors); ++i) { + ret = max9611_read_single(max9611, gain_selectors[i], adc_raw); + if (ret) + return ret; + + if (*adc_raw > 0) { + *csa_gain = (enum max9611_csa_gain)gain_selectors[i]; + return 0; + } + } + + return -EIO; +} + +static int max9611_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max9611_dev *dev = iio_priv(indio_dev); + enum max9611_csa_gain gain_selector; + const unsigned int *csa_gain; + u16 adc_data; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + + switch (chan->address) { + case MAX9611_CHAN_TEMPERATURE: + ret = max9611_read_single(dev, CONF_TEMP, + &adc_data); + if (ret) + return -EINVAL; + + *val = MAX9611_TEMP_RAW(adc_data); + return IIO_VAL_INT; + + case MAX9611_CHAN_VOLTAGE_INPUT: + ret = max9611_read_single(dev, CONF_IN_VOLT, + &adc_data); + if (ret) + return -EINVAL; + + *val = MAX9611_VOLTAGE_RAW(adc_data); + return IIO_VAL_INT; + } + + break; + + case IIO_CHAN_INFO_OFFSET: + /* MAX9611_CHAN_VOLTAGE_INPUT */ + *val = MAX9611_CIM_OFFSET_RAW; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + + switch (chan->address) { + case MAX9611_CHAN_TEMPERATURE: + *val = MAX9611_TEMP_SCALE_NUM; + *val2 = MAX9611_TEMP_SCALE_DIV; + + return IIO_VAL_FRACTIONAL; + + case MAX9611_CHAN_VOLTAGE_INPUT: + *val = MAX9611_CIM_LSB_mV; + + return IIO_VAL_INT; + } + + break; + + case IIO_CHAN_INFO_PROCESSED: + + switch (chan->address) { + case MAX9611_CHAN_VOLTAGE_SENSE: + /* + * processed (mV): (raw - offset) * LSB (nV) / 10^6 + * + * Even if max9611 can output raw csa voltage readings, + * use a produced value as scale depends on gain. + */ + ret = max9611_read_csa_voltage(dev, &adc_data, + &gain_selector); + if (ret) + return -EINVAL; + + csa_gain = max9611_gain_conf[gain_selector]; + + adc_data -= csa_gain[CSA_GAIN_OFFS_RAW]; + *val = MAX9611_VOLTAGE_RAW(adc_data) * + csa_gain[CSA_GAIN_LSB_nV]; + *val2 = 1000000; + + return IIO_VAL_FRACTIONAL; + + case MAX9611_CHAN_CURRENT_LOAD: + /* processed (mA): Vcsa (nV) / Rshunt (uOhm) */ + ret = max9611_read_csa_voltage(dev, &adc_data, + &gain_selector); + if (ret) + return -EINVAL; + + csa_gain = max9611_gain_conf[gain_selector]; + + adc_data -= csa_gain[CSA_GAIN_OFFS_RAW]; + *val = MAX9611_VOLTAGE_RAW(adc_data) * + csa_gain[CSA_GAIN_LSB_nV]; + *val2 = dev->shunt_resistor_uohm; + + return IIO_VAL_FRACTIONAL; + + case MAX9611_CHAN_POWER_LOAD: + /* + * processed (mW): Vin (mV) * Vcsa (uV) / + * Rshunt (uOhm) + */ + ret = max9611_read_single(dev, CONF_IN_VOLT, + &adc_data); + if (ret) + return -EINVAL; + + adc_data -= MAX9611_CIM_OFFSET_RAW; + *val = MAX9611_VOLTAGE_RAW(adc_data) * + MAX9611_CIM_LSB_mV; + + ret = max9611_read_csa_voltage(dev, &adc_data, + &gain_selector); + if (ret) + return -EINVAL; + + csa_gain = max9611_gain_conf[gain_selector]; + + /* divide by 10^3 here to avoid 32bit overflow */ + adc_data -= csa_gain[CSA_GAIN_OFFS_RAW]; + *val *= MAX9611_VOLTAGE_RAW(adc_data) * + csa_gain[CSA_GAIN_LSB_nV] / 1000; + *val2 = dev->shunt_resistor_uohm; + + return IIO_VAL_FRACTIONAL; + } + + break; + } + + return -EINVAL; +} + +static ssize_t max9611_shunt_resistor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max9611_dev *max9611 = iio_priv(dev_to_iio_dev(dev)); + unsigned int i, r; + + i = max9611->shunt_resistor_uohm / 1000000; + r = max9611->shunt_resistor_uohm % 1000000; + + return sprintf(buf, "%u.%06u\n", i, r); +} + +static IIO_DEVICE_ATTR(in_power_shunt_resistor, 0444, + max9611_shunt_resistor_show, NULL, 0); +static IIO_DEVICE_ATTR(in_current_shunt_resistor, 0444, + max9611_shunt_resistor_show, NULL, 0); + +static struct attribute *max9611_attributes[] = { + &iio_dev_attr_in_power_shunt_resistor.dev_attr.attr, + &iio_dev_attr_in_current_shunt_resistor.dev_attr.attr, + NULL, +}; + +static const struct attribute_group max9611_attribute_group = { + .attrs = max9611_attributes, +}; + +static const struct iio_info indio_info = { + .read_raw = max9611_read_raw, + .attrs = &max9611_attribute_group, +}; + +static int max9611_init(struct max9611_dev *max9611) +{ + struct i2c_client *client = max9611->i2c_client; + u16 regval; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + dev_err(max9611->dev, + "I2c adapter does not support smbus write_byte or read_word functionalities: aborting probe.\n"); + return -EINVAL; + } + + /* Make sure die temperature is in range to test communications. */ + ret = max9611_read_single(max9611, CONF_TEMP, ®val); + if (ret) + return ret; + + regval &= MAX9611_TEMP_MASK; + + if ((regval > MAX9611_TEMP_MAX_POS && + regval < MAX9611_TEMP_MIN_NEG) || + regval > MAX9611_TEMP_MAX_NEG) { + dev_err(max9611->dev, + "Invalid value received from ADC 0x%4x: aborting\n", + regval); + return -EIO; + } + + /* Mux shall be zeroed back before applying other configurations */ + ret = i2c_smbus_write_byte_data(max9611->i2c_client, + MAX9611_REG_CTRL1, 0); + if (ret) { + dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n", + MAX9611_REG_CTRL1, 0); + return ret; + } + + ret = i2c_smbus_write_byte_data(max9611->i2c_client, + MAX9611_REG_CTRL2, 0); + if (ret) { + dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n", + MAX9611_REG_CTRL2, 0); + return ret; + } + usleep_range(MAX9611_CONV_TIME_US_RANGE); + + return 0; +} + +static const struct of_device_id max9611_of_table[] = { + {.compatible = "maxim,max9611", .data = "max9611"}, + {.compatible = "maxim,max9612", .data = "max9612"}, + { }, +}; + +MODULE_DEVICE_TABLE(of, max9611_of_table); +static int max9611_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const char * const shunt_res_prop = "shunt-resistor-micro-ohms"; + const struct device_node *of_node = client->dev.of_node; + const struct of_device_id *of_id = + of_match_device(max9611_of_table, &client->dev); + struct max9611_dev *max9611; + struct iio_dev *indio_dev; + unsigned int of_shunt; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*max9611)); + if (!indio_dev) + return -ENOMEM; + + i2c_set_clientdata(client, indio_dev); + + max9611 = iio_priv(indio_dev); + max9611->dev = &client->dev; + max9611->i2c_client = client; + mutex_init(&max9611->lock); + + ret = of_property_read_u32(of_node, shunt_res_prop, &of_shunt); + if (ret) { + dev_err(&client->dev, + "Missing %s property for %pOF node\n", + shunt_res_prop, of_node); + return ret; + } + max9611->shunt_resistor_uohm = of_shunt; + + ret = max9611_init(max9611); + if (ret) + return ret; + + indio_dev->name = of_id->data; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &indio_info; + indio_dev->channels = max9611_channels; + indio_dev->num_channels = ARRAY_SIZE(max9611_channels); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static struct i2c_driver max9611_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = max9611_of_table, + }, + .probe = max9611_probe, +}; +module_i2c_driver(max9611_driver); + +MODULE_AUTHOR("Jacopo Mondi <jacopo+renesas@jmondi.org>"); +MODULE_DESCRIPTION("Maxim max9611/12 current sense amplifier with 12bit ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c new file mode 100644 index 000000000..8d1cff28c --- /dev/null +++ b/drivers/iio/adc/mcp320x.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com> + * Copyright (C) 2014 Rose Technology + * Allan Bendorff Jensen <abj@rosetechnology.dk> + * Soren Andersen <san@rosetechnology.dk> + * + * Driver for following ADC chips from Microchip Technology's: + * 10 Bit converter + * MCP3001 + * MCP3002 + * MCP3004 + * MCP3008 + * ------------ + * 12 bit converter + * MCP3201 + * MCP3202 + * MCP3204 + * MCP3208 + * ------------ + * 13 bit converter + * MCP3301 + * ------------ + * 22 bit converter + * MCP3550 + * MCP3551 + * MCP3553 + * + * Datasheet can be found here: + * https://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf mcp3001 + * https://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf mcp3002 + * https://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf mcp3004/08 + * http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf mcp3201 + * http://ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf mcp3202 + * http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08 + * https://ww1.microchip.com/downloads/en/DeviceDoc/21700E.pdf mcp3301 + * http://ww1.microchip.com/downloads/en/DeviceDoc/21950D.pdf mcp3550/1/3 + */ + +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/iio/iio.h> +#include <linux/regulator/consumer.h> + +enum { + mcp3001, + mcp3002, + mcp3004, + mcp3008, + mcp3201, + mcp3202, + mcp3204, + mcp3208, + mcp3301, + mcp3550_50, + mcp3550_60, + mcp3551, + mcp3553, +}; + +struct mcp320x_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; + unsigned int resolution; + unsigned int conv_time; /* usec */ +}; + +/** + * struct mcp320x - Microchip SPI ADC instance + * @spi: SPI slave (parent of the IIO device) + * @msg: SPI message to select a channel and receive a value from the ADC + * @transfer: SPI transfers used by @msg + * @start_conv_msg: SPI message to start a conversion by briefly asserting CS + * @start_conv_transfer: SPI transfer used by @start_conv_msg + * @reg: regulator generating Vref + * @lock: protects read sequences + * @chip_info: ADC properties + * @tx_buf: buffer for @transfer[0] (not used on single-channel converters) + * @rx_buf: buffer for @transfer[1] + */ +struct mcp320x { + struct spi_device *spi; + struct spi_message msg; + struct spi_transfer transfer[2]; + struct spi_message start_conv_msg; + struct spi_transfer start_conv_transfer; + + struct regulator *reg; + struct mutex lock; + const struct mcp320x_chip_info *chip_info; + + u8 tx_buf ____cacheline_aligned; + u8 rx_buf[4]; +}; + +static int mcp320x_channel_to_tx_data(int device_index, + const unsigned int channel, bool differential) +{ + int start_bit = 1; + + switch (device_index) { + case mcp3002: + case mcp3202: + return ((start_bit << 4) | (!differential << 3) | + (channel << 2)); + case mcp3004: + case mcp3204: + case mcp3008: + case mcp3208: + return ((start_bit << 6) | (!differential << 5) | + (channel << 2)); + default: + return -EINVAL; + } +} + +static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel, + bool differential, int device_index, int *val) +{ + int ret; + + if (adc->chip_info->conv_time) { + ret = spi_sync(adc->spi, &adc->start_conv_msg); + if (ret < 0) + return ret; + + usleep_range(adc->chip_info->conv_time, + adc->chip_info->conv_time + 100); + } + + memset(&adc->rx_buf, 0, sizeof(adc->rx_buf)); + if (adc->chip_info->num_channels > 1) + adc->tx_buf = mcp320x_channel_to_tx_data(device_index, channel, + differential); + + ret = spi_sync(adc->spi, &adc->msg); + if (ret < 0) + return ret; + + switch (device_index) { + case mcp3001: + *val = (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3); + return 0; + case mcp3002: + case mcp3004: + case mcp3008: + *val = (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6); + return 0; + case mcp3201: + *val = (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1); + return 0; + case mcp3202: + case mcp3204: + case mcp3208: + *val = (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4); + return 0; + case mcp3301: + *val = sign_extend32((adc->rx_buf[0] & 0x1f) << 8 + | adc->rx_buf[1], 12); + return 0; + case mcp3550_50: + case mcp3550_60: + case mcp3551: + case mcp3553: { + u32 raw = be32_to_cpup((__be32 *)adc->rx_buf); + + if (!(adc->spi->mode & SPI_CPOL)) + raw <<= 1; /* strip Data Ready bit in SPI mode 0,0 */ + + /* + * If the input is within -vref and vref, bit 21 is the sign. + * Up to 12% overrange or underrange are allowed, in which case + * bit 23 is the sign and bit 0 to 21 is the value. + */ + raw >>= 8; + if (raw & BIT(22) && raw & BIT(23)) + return -EIO; /* cannot have overrange AND underrange */ + else if (raw & BIT(22)) + raw &= ~BIT(22); /* overrange */ + else if (raw & BIT(23) || raw & BIT(21)) + raw |= GENMASK(31, 22); /* underrange or negative */ + + *val = (s32)raw; + return 0; + } + default: + return -EINVAL; + } +} + +static int mcp320x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct mcp320x *adc = iio_priv(indio_dev); + int ret = -EINVAL; + int device_index = 0; + + mutex_lock(&adc->lock); + + device_index = spi_get_device_id(adc->spi)->driver_data; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = mcp320x_adc_conversion(adc, channel->address, + channel->differential, device_index, val); + if (ret < 0) + goto out; + + ret = IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(adc->reg); + if (ret < 0) + goto out; + + /* convert regulator output voltage to mV */ + *val = ret / 1000; + *val2 = adc->chip_info->resolution; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + } + +out: + mutex_unlock(&adc->lock); + + return ret; +} + +#define MCP320X_VOLTAGE_CHANNEL(num) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (num), \ + .address = (num), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + } + +#define MCP320X_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan1), \ + .channel2 = (chan2), \ + .address = (chan1), \ + .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 mcp3201_channels[] = { + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), +}; + +static const struct iio_chan_spec mcp3202_channels[] = { + MCP320X_VOLTAGE_CHANNEL(0), + MCP320X_VOLTAGE_CHANNEL(1), + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), + MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0), +}; + +static const struct iio_chan_spec mcp3204_channels[] = { + MCP320X_VOLTAGE_CHANNEL(0), + MCP320X_VOLTAGE_CHANNEL(1), + MCP320X_VOLTAGE_CHANNEL(2), + MCP320X_VOLTAGE_CHANNEL(3), + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), + MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0), + MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3), + MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2), +}; + +static const struct iio_chan_spec mcp3208_channels[] = { + MCP320X_VOLTAGE_CHANNEL(0), + MCP320X_VOLTAGE_CHANNEL(1), + MCP320X_VOLTAGE_CHANNEL(2), + MCP320X_VOLTAGE_CHANNEL(3), + MCP320X_VOLTAGE_CHANNEL(4), + MCP320X_VOLTAGE_CHANNEL(5), + MCP320X_VOLTAGE_CHANNEL(6), + MCP320X_VOLTAGE_CHANNEL(7), + MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1), + MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0), + MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3), + MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2), + MCP320X_VOLTAGE_CHANNEL_DIFF(4, 5), + MCP320X_VOLTAGE_CHANNEL_DIFF(5, 4), + MCP320X_VOLTAGE_CHANNEL_DIFF(6, 7), + MCP320X_VOLTAGE_CHANNEL_DIFF(7, 6), +}; + +static const struct iio_info mcp320x_info = { + .read_raw = mcp320x_read_raw, +}; + +static const struct mcp320x_chip_info mcp320x_chip_infos[] = { + [mcp3001] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 10 + }, + [mcp3002] = { + .channels = mcp3202_channels, + .num_channels = ARRAY_SIZE(mcp3202_channels), + .resolution = 10 + }, + [mcp3004] = { + .channels = mcp3204_channels, + .num_channels = ARRAY_SIZE(mcp3204_channels), + .resolution = 10 + }, + [mcp3008] = { + .channels = mcp3208_channels, + .num_channels = ARRAY_SIZE(mcp3208_channels), + .resolution = 10 + }, + [mcp3201] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 12 + }, + [mcp3202] = { + .channels = mcp3202_channels, + .num_channels = ARRAY_SIZE(mcp3202_channels), + .resolution = 12 + }, + [mcp3204] = { + .channels = mcp3204_channels, + .num_channels = ARRAY_SIZE(mcp3204_channels), + .resolution = 12 + }, + [mcp3208] = { + .channels = mcp3208_channels, + .num_channels = ARRAY_SIZE(mcp3208_channels), + .resolution = 12 + }, + [mcp3301] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 13 + }, + [mcp3550_50] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 21, + /* 2% max deviation + 144 clock periods to exit shutdown */ + .conv_time = 80000 * 1.02 + 144000 / 102.4, + }, + [mcp3550_60] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 21, + .conv_time = 66670 * 1.02 + 144000 / 122.88, + }, + [mcp3551] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 21, + .conv_time = 73100 * 1.02 + 144000 / 112.64, + }, + [mcp3553] = { + .channels = mcp3201_channels, + .num_channels = ARRAY_SIZE(mcp3201_channels), + .resolution = 21, + .conv_time = 16670 * 1.02 + 144000 / 122.88, + }, +}; + +static int mcp320x_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct mcp320x *adc; + const struct mcp320x_chip_info *chip_info; + int ret, device_index; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp320x_info; + spi_set_drvdata(spi, indio_dev); + + device_index = spi_get_device_id(spi)->driver_data; + chip_info = &mcp320x_chip_infos[device_index]; + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = chip_info->num_channels; + + adc->chip_info = chip_info; + + adc->transfer[0].tx_buf = &adc->tx_buf; + adc->transfer[0].len = sizeof(adc->tx_buf); + adc->transfer[1].rx_buf = adc->rx_buf; + adc->transfer[1].len = DIV_ROUND_UP(chip_info->resolution, 8); + + if (chip_info->num_channels == 1) + /* single-channel converters are rx only (no MOSI pin) */ + spi_message_init_with_transfers(&adc->msg, + &adc->transfer[1], 1); + else + spi_message_init_with_transfers(&adc->msg, adc->transfer, + ARRAY_SIZE(adc->transfer)); + + switch (device_index) { + case mcp3550_50: + case mcp3550_60: + case mcp3551: + case mcp3553: + /* rx len increases from 24 to 25 bit in SPI mode 0,0 */ + if (!(spi->mode & SPI_CPOL)) + adc->transfer[1].len++; + + /* conversions are started by asserting CS pin for 8 usec */ + adc->start_conv_transfer.delay.value = 8; + adc->start_conv_transfer.delay.unit = SPI_DELAY_UNIT_USECS; + spi_message_init_with_transfers(&adc->start_conv_msg, + &adc->start_conv_transfer, 1); + + /* + * If CS was previously kept low (continuous conversion mode) + * and then changed to high, the chip is in shutdown. + * Sometimes it fails to wake from shutdown and clocks out + * only 0xffffff. The magic sequence of performing two + * conversions without delay between them resets the chip + * and ensures all subsequent conversions succeed. + */ + mcp320x_adc_conversion(adc, 0, 1, device_index, &ret); + mcp320x_adc_conversion(adc, 0, 1, device_index, &ret); + } + + 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 < 0) + return ret; + + mutex_init(&adc->lock); + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto reg_disable; + + return 0; + +reg_disable: + regulator_disable(adc->reg); + + return ret; +} + +static int mcp320x_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct mcp320x *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(adc->reg); + + return 0; +} + +static const struct of_device_id mcp320x_dt_ids[] = { + /* NOTE: The use of compatibles with no vendor prefix is deprecated. */ + { .compatible = "mcp3001" }, + { .compatible = "mcp3002" }, + { .compatible = "mcp3004" }, + { .compatible = "mcp3008" }, + { .compatible = "mcp3201" }, + { .compatible = "mcp3202" }, + { .compatible = "mcp3204" }, + { .compatible = "mcp3208" }, + { .compatible = "mcp3301" }, + { .compatible = "microchip,mcp3001" }, + { .compatible = "microchip,mcp3002" }, + { .compatible = "microchip,mcp3004" }, + { .compatible = "microchip,mcp3008" }, + { .compatible = "microchip,mcp3201" }, + { .compatible = "microchip,mcp3202" }, + { .compatible = "microchip,mcp3204" }, + { .compatible = "microchip,mcp3208" }, + { .compatible = "microchip,mcp3301" }, + { .compatible = "microchip,mcp3550-50" }, + { .compatible = "microchip,mcp3550-60" }, + { .compatible = "microchip,mcp3551" }, + { .compatible = "microchip,mcp3553" }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp320x_dt_ids); + +static const struct spi_device_id mcp320x_id[] = { + { "mcp3001", mcp3001 }, + { "mcp3002", mcp3002 }, + { "mcp3004", mcp3004 }, + { "mcp3008", mcp3008 }, + { "mcp3201", mcp3201 }, + { "mcp3202", mcp3202 }, + { "mcp3204", mcp3204 }, + { "mcp3208", mcp3208 }, + { "mcp3301", mcp3301 }, + { "mcp3550-50", mcp3550_50 }, + { "mcp3550-60", mcp3550_60 }, + { "mcp3551", mcp3551 }, + { "mcp3553", mcp3553 }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp320x_id); + +static struct spi_driver mcp320x_driver = { + .driver = { + .name = "mcp320x", + .of_match_table = mcp320x_dt_ids, + }, + .probe = mcp320x_probe, + .remove = mcp320x_remove, + .id_table = mcp320x_id, +}; +module_spi_driver(mcp320x_driver); + +MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>"); +MODULE_DESCRIPTION("Microchip Technology MCP3x01/02/04/08 and MCP3550/1/3"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c new file mode 100644 index 000000000..da353dcb1 --- /dev/null +++ b/drivers/iio/adc/mcp3422.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * mcp3422.c - driver for the Microchip mcp3421/2/3/4/5/6/7/8 chip family + * + * Copyright (C) 2013, Angelo Compagnucci + * Author: Angelo Compagnucci <angelo.compagnucci@gmail.com> + * + * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf + * https://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf + * https://ww1.microchip.com/downloads/en/DeviceDoc/22072b.pdf + * + * This driver exports the value of analog input voltage to sysfs, the + * voltage unit is nV. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <asm/unaligned.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* Masks */ +#define MCP3422_CHANNEL_MASK 0x60 +#define MCP3422_PGA_MASK 0x03 +#define MCP3422_SRATE_MASK 0x0C +#define MCP3422_SRATE_240 0x0 +#define MCP3422_SRATE_60 0x1 +#define MCP3422_SRATE_15 0x2 +#define MCP3422_SRATE_3 0x3 +#define MCP3422_PGA_1 0 +#define MCP3422_PGA_2 1 +#define MCP3422_PGA_4 2 +#define MCP3422_PGA_8 3 +#define MCP3422_CONT_SAMPLING 0x10 + +#define MCP3422_CHANNEL(config) (((config) & MCP3422_CHANNEL_MASK) >> 5) +#define MCP3422_PGA(config) ((config) & MCP3422_PGA_MASK) +#define MCP3422_SAMPLE_RATE(config) (((config) & MCP3422_SRATE_MASK) >> 2) + +#define MCP3422_CHANNEL_VALUE(value) (((value) << 5) & MCP3422_CHANNEL_MASK) +#define MCP3422_PGA_VALUE(value) ((value) & MCP3422_PGA_MASK) +#define MCP3422_SAMPLE_RATE_VALUE(value) ((value << 2) & MCP3422_SRATE_MASK) + +#define MCP3422_CHAN(_index) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + } + +static const int mcp3422_scales[4][4] = { + { 1000000, 500000, 250000, 125000 }, + { 250000, 125000, 62500, 31250 }, + { 62500, 31250, 15625, 7812 }, + { 15625, 7812, 3906, 1953 } }; + +/* Constant msleep times for data acquisitions */ +static const int mcp3422_read_times[4] = { + [MCP3422_SRATE_240] = 1000 / 240, + [MCP3422_SRATE_60] = 1000 / 60, + [MCP3422_SRATE_15] = 1000 / 15, + [MCP3422_SRATE_3] = 1000 / 3 }; + +/* sample rates to integer conversion table */ +static const int mcp3422_sample_rates[4] = { + [MCP3422_SRATE_240] = 240, + [MCP3422_SRATE_60] = 60, + [MCP3422_SRATE_15] = 15, + [MCP3422_SRATE_3] = 3 }; + +/* sample rates to sign extension table */ +static const int mcp3422_sign_extend[4] = { + [MCP3422_SRATE_240] = 11, + [MCP3422_SRATE_60] = 13, + [MCP3422_SRATE_15] = 15, + [MCP3422_SRATE_3] = 17 }; + +/* Client data (each client gets its own) */ +struct mcp3422 { + struct i2c_client *i2c; + u8 id; + u8 config; + u8 pga[4]; + struct mutex lock; +}; + +static int mcp3422_update_config(struct mcp3422 *adc, u8 newconfig) +{ + int ret; + + ret = i2c_master_send(adc->i2c, &newconfig, 1); + if (ret > 0) { + adc->config = newconfig; + ret = 0; + } + + return ret; +} + +static int mcp3422_read(struct mcp3422 *adc, int *value, u8 *config) +{ + int ret = 0; + u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); + u8 buf[4] = {0, 0, 0, 0}; + u32 temp; + + if (sample_rate == MCP3422_SRATE_3) { + ret = i2c_master_recv(adc->i2c, buf, 4); + temp = get_unaligned_be24(&buf[0]); + *config = buf[3]; + } else { + ret = i2c_master_recv(adc->i2c, buf, 3); + temp = get_unaligned_be16(&buf[0]); + *config = buf[2]; + } + + *value = sign_extend32(temp, mcp3422_sign_extend[sample_rate]); + + return ret; +} + +static int mcp3422_read_channel(struct mcp3422 *adc, + struct iio_chan_spec const *channel, int *value) +{ + int ret; + u8 config; + u8 req_channel = channel->channel; + + mutex_lock(&adc->lock); + + if (req_channel != MCP3422_CHANNEL(adc->config)) { + config = adc->config; + config &= ~MCP3422_CHANNEL_MASK; + config |= MCP3422_CHANNEL_VALUE(req_channel); + config &= ~MCP3422_PGA_MASK; + config |= MCP3422_PGA_VALUE(adc->pga[req_channel]); + ret = mcp3422_update_config(adc, config); + if (ret < 0) { + mutex_unlock(&adc->lock); + return ret; + } + msleep(mcp3422_read_times[MCP3422_SAMPLE_RATE(adc->config)]); + } + + ret = mcp3422_read(adc, value, &config); + + mutex_unlock(&adc->lock); + + return ret; +} + +static int mcp3422_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *val1, + int *val2, long mask) +{ + struct mcp3422 *adc = iio_priv(iio); + int err; + + u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); + u8 pga = MCP3422_PGA(adc->config); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = mcp3422_read_channel(adc, channel, val1); + if (err < 0) + return -EINVAL; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + + *val1 = 0; + *val2 = mcp3422_scales[sample_rate][pga]; + return IIO_VAL_INT_PLUS_NANO; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val1 = mcp3422_sample_rates[MCP3422_SAMPLE_RATE(adc->config)]; + return IIO_VAL_INT; + + default: + break; + } + + return -EINVAL; +} + +static int mcp3422_write_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int val1, + int val2, long mask) +{ + struct mcp3422 *adc = iio_priv(iio); + u8 temp; + u8 config = adc->config; + u8 req_channel = channel->channel; + u8 sample_rate = MCP3422_SAMPLE_RATE(config); + u8 i; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (val1 != 0) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(mcp3422_scales[0]); i++) { + if (val2 == mcp3422_scales[sample_rate][i]) { + adc->pga[req_channel] = i; + + config &= ~MCP3422_CHANNEL_MASK; + config |= MCP3422_CHANNEL_VALUE(req_channel); + config &= ~MCP3422_PGA_MASK; + config |= MCP3422_PGA_VALUE(adc->pga[req_channel]); + + return mcp3422_update_config(adc, config); + } + } + return -EINVAL; + + case IIO_CHAN_INFO_SAMP_FREQ: + switch (val1) { + case 240: + temp = MCP3422_SRATE_240; + break; + case 60: + temp = MCP3422_SRATE_60; + break; + case 15: + temp = MCP3422_SRATE_15; + break; + case 3: + if (adc->id > 4) + return -EINVAL; + temp = MCP3422_SRATE_3; + break; + default: + return -EINVAL; + } + + config &= ~MCP3422_CHANNEL_MASK; + config |= MCP3422_CHANNEL_VALUE(req_channel); + config &= ~MCP3422_SRATE_MASK; + config |= MCP3422_SAMPLE_RATE_VALUE(temp); + + return mcp3422_update_config(adc, config); + + default: + break; + } + + return -EINVAL; +} + +static int mcp3422_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static ssize_t mcp3422_show_samp_freqs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev)); + + if (adc->id > 4) + return sprintf(buf, "240 60 15\n"); + + return sprintf(buf, "240 60 15 3\n"); +} + +static ssize_t mcp3422_show_scales(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev)); + u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); + + return sprintf(buf, "0.%09u 0.%09u 0.%09u 0.%09u\n", + mcp3422_scales[sample_rate][0], + mcp3422_scales[sample_rate][1], + mcp3422_scales[sample_rate][2], + mcp3422_scales[sample_rate][3]); +} + +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO, + mcp3422_show_samp_freqs, NULL, 0); +static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, + mcp3422_show_scales, NULL, 0); + +static struct attribute *mcp3422_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group mcp3422_attribute_group = { + .attrs = mcp3422_attributes, +}; + +static const struct iio_chan_spec mcp3421_channels[] = { + MCP3422_CHAN(0), +}; + +static const struct iio_chan_spec mcp3422_channels[] = { + MCP3422_CHAN(0), + MCP3422_CHAN(1), +}; + +static const struct iio_chan_spec mcp3424_channels[] = { + MCP3422_CHAN(0), + MCP3422_CHAN(1), + MCP3422_CHAN(2), + MCP3422_CHAN(3), +}; + +static const struct iio_info mcp3422_info = { + .read_raw = mcp3422_read_raw, + .write_raw = mcp3422_write_raw, + .write_raw_get_fmt = mcp3422_write_raw_get_fmt, + .attrs = &mcp3422_attribute_group, +}; + +static int mcp3422_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct mcp3422 *adc; + int err; + u8 config; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->i2c = client; + adc->id = (u8)(id->driver_data); + + mutex_init(&adc->lock); + + indio_dev->name = dev_name(&client->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp3422_info; + + switch (adc->id) { + case 1: + case 5: + indio_dev->channels = mcp3421_channels; + indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels); + break; + case 2: + case 3: + case 6: + case 7: + indio_dev->channels = mcp3422_channels; + indio_dev->num_channels = ARRAY_SIZE(mcp3422_channels); + break; + case 4: + case 8: + indio_dev->channels = mcp3424_channels; + indio_dev->num_channels = ARRAY_SIZE(mcp3424_channels); + break; + } + + /* meaningful default configuration */ + config = (MCP3422_CONT_SAMPLING + | MCP3422_CHANNEL_VALUE(0) + | MCP3422_PGA_VALUE(MCP3422_PGA_1) + | MCP3422_SAMPLE_RATE_VALUE(MCP3422_SRATE_240)); + err = mcp3422_update_config(adc, config); + if (err < 0) + return err; + + err = devm_iio_device_register(&client->dev, indio_dev); + if (err < 0) + return err; + + i2c_set_clientdata(client, indio_dev); + + return 0; +} + +static const struct i2c_device_id mcp3422_id[] = { + { "mcp3421", 1 }, + { "mcp3422", 2 }, + { "mcp3423", 3 }, + { "mcp3424", 4 }, + { "mcp3425", 5 }, + { "mcp3426", 6 }, + { "mcp3427", 7 }, + { "mcp3428", 8 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcp3422_id); + +static const struct of_device_id mcp3422_of_match[] = { + { .compatible = "mcp3422" }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp3422_of_match); + +static struct i2c_driver mcp3422_driver = { + .driver = { + .name = "mcp3422", + .of_match_table = mcp3422_of_match, + }, + .probe = mcp3422_probe, + .id_table = mcp3422_id, +}; +module_i2c_driver(mcp3422_driver); + +MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>"); +MODULE_DESCRIPTION("Microchip mcp3421/2/3/4/5/6/7/8 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c new file mode 100644 index 000000000..65278270a --- /dev/null +++ b/drivers/iio/adc/mcp3911.c @@ -0,0 +1,370 @@ +// 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 mV */ +#define MCP3911_INT_VREF_MV 1200 + +#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; + + *val = sign_extend32(*val, 23); + + 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_MV; + } + + /* + * For 24bit Conversion + * Raw = ((Voltage)/(Vref) * 2^23 * Gain * 1.5 + * Voltage = Raw * (Vref)/(2^23 * Gain * 1.5) + */ + + /* val2 = (2^23 * 1.5) */ + *val2 = 12582912; + ret = IIO_VAL_FRACTIONAL; + 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->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 new file mode 100644 index 000000000..adc5ceaef --- /dev/null +++ b/drivers/iio/adc/men_z188_adc.c @@ -0,0 +1,176 @@ +// 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> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mcb.h> +#include <linux/io.h> +#include <linux/iio/iio.h> + +#define Z188_ADC_MAX_CHAN 8 +#define Z188_ADC_GAIN 0x0700000 +#define Z188_MODE_VOLTAGE BIT(27) +#define Z188_CFG_AUTO 0x1 +#define Z188_CTRL_REG 0x40 + +#define ADC_DATA(x) (((x) >> 2) & 0x7ffffc) +#define ADC_OVR(x) ((x) & 0x1) + +struct z188_adc { + struct resource *mem; + void __iomem *base; +}; + +#define Z188_ADC_CHANNEL(idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +} + +static const struct iio_chan_spec z188_adc_iio_channels[] = { + Z188_ADC_CHANNEL(0), + Z188_ADC_CHANNEL(1), + Z188_ADC_CHANNEL(2), + Z188_ADC_CHANNEL(3), + Z188_ADC_CHANNEL(4), + Z188_ADC_CHANNEL(5), + Z188_ADC_CHANNEL(6), + Z188_ADC_CHANNEL(7), +}; + +static int z188_iio_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long info) +{ + struct z188_adc *adc = iio_priv(iio_dev); + int ret; + u16 tmp; + + switch (info) { + case IIO_CHAN_INFO_RAW: + tmp = readw(adc->base + chan->channel * 4); + + if (ADC_OVR(tmp)) { + dev_info(&iio_dev->dev, + "Oversampling error on ADC channel %d\n", + chan->channel); + return -EIO; + } + *val = ADC_DATA(tmp); + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct iio_info z188_adc_info = { + .read_raw = &z188_iio_read_raw, +}; + +static void men_z188_config_channels(void __iomem *addr) +{ + int i; + u32 cfg; + u32 ctl; + + ctl = readl(addr + Z188_CTRL_REG); + ctl |= Z188_CFG_AUTO; + writel(ctl, addr + Z188_CTRL_REG); + + for (i = 0; i < Z188_ADC_MAX_CHAN; i++) { + cfg = readl(addr + i); + cfg &= ~Z188_ADC_GAIN; + cfg |= Z188_MODE_VOLTAGE; + writel(cfg, addr + i); + } +} + +static int men_z188_probe(struct mcb_device *dev, + const struct mcb_device_id *id) +{ + struct z188_adc *adc; + struct iio_dev *indio_dev; + struct resource *mem; + int ret; + + indio_dev = devm_iio_device_alloc(&dev->dev, sizeof(struct z188_adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + indio_dev->name = "z188-adc"; + indio_dev->info = &z188_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = z188_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(z188_adc_iio_channels); + + mem = mcb_request_mem(dev, "z188-adc"); + if (IS_ERR(mem)) + return PTR_ERR(mem); + + adc->base = ioremap(mem->start, resource_size(mem)); + if (adc->base == NULL) + goto err; + + men_z188_config_channels(adc->base); + + adc->mem = mem; + mcb_set_drvdata(dev, indio_dev); + + ret = iio_device_register(indio_dev); + if (ret) + goto err_unmap; + + return 0; + +err_unmap: + iounmap(adc->base); +err: + mcb_release_mem(mem); + return -ENXIO; +} + +static void men_z188_remove(struct mcb_device *dev) +{ + struct iio_dev *indio_dev = mcb_get_drvdata(dev); + struct z188_adc *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iounmap(adc->base); + mcb_release_mem(adc->mem); +} + +static const struct mcb_device_id men_z188_ids[] = { + { .device = 0xbc }, + { } +}; +MODULE_DEVICE_TABLE(mcb, men_z188_ids); + +static struct mcb_driver men_z188_driver = { + .driver = { + .name = "z188-adc", + .owner = THIS_MODULE, + }, + .probe = men_z188_probe, + .remove = men_z188_remove, + .id_table = men_z188_ids, +}; +module_mcb_driver(men_z188_driver); + +MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core"); +MODULE_ALIAS("mcb:16z188"); +MODULE_IMPORT_NS(MCB); diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c new file mode 100644 index 000000000..e771299fa --- /dev/null +++ b/drivers/iio/adc/meson_saradc.c @@ -0,0 +1,1355 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Amlogic Meson Successive Approximation Register (SAR) A/D Converter + * + * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#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> +#include <linux/of_device.h> +#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) + #define MESON_SAR_ADC_REG0_BUSY_MASK GENMASK(30, 28) + #define MESON_SAR_ADC_REG0_DELTA_BUSY BIT(30) + #define MESON_SAR_ADC_REG0_AVG_BUSY BIT(29) + #define MESON_SAR_ADC_REG0_SAMPLE_BUSY BIT(28) + #define MESON_SAR_ADC_REG0_FIFO_FULL BIT(27) + #define MESON_SAR_ADC_REG0_FIFO_EMPTY BIT(26) + #define MESON_SAR_ADC_REG0_FIFO_COUNT_MASK GENMASK(25, 21) + #define MESON_SAR_ADC_REG0_ADC_BIAS_CTRL_MASK GENMASK(20, 19) + #define MESON_SAR_ADC_REG0_CURR_CHAN_ID_MASK GENMASK(18, 16) + #define MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL BIT(15) + #define MESON_SAR_ADC_REG0_SAMPLING_STOP BIT(14) + #define MESON_SAR_ADC_REG0_CHAN_DELTA_EN_MASK GENMASK(13, 12) + #define MESON_SAR_ADC_REG0_DETECT_IRQ_POL BIT(10) + #define MESON_SAR_ADC_REG0_DETECT_IRQ_EN BIT(9) + #define MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK GENMASK(8, 4) + #define MESON_SAR_ADC_REG0_FIFO_IRQ_EN BIT(3) + #define MESON_SAR_ADC_REG0_SAMPLING_START BIT(2) + #define MESON_SAR_ADC_REG0_CONTINUOUS_EN BIT(1) + #define MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE BIT(0) + +#define MESON_SAR_ADC_CHAN_LIST 0x04 + #define MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK GENMASK(26, 24) + #define MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(_chan) \ + (GENMASK(2, 0) << ((_chan) * 3)) + +#define MESON_SAR_ADC_AVG_CNTL 0x08 + #define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(_chan) \ + (16 + ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(_chan) \ + (GENMASK(17, 16) << ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(_chan) \ + (0 + ((_chan) * 2)) + #define MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(_chan) \ + (GENMASK(1, 0) << ((_chan) * 2)) + +#define MESON_SAR_ADC_REG3 0x0c + #define MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY BIT(31) + #define MESON_SAR_ADC_REG3_CLK_EN BIT(30) + #define MESON_SAR_ADC_REG3_BL30_INITIALIZED BIT(28) + #define MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN BIT(27) + #define MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE BIT(26) + #define MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_REG3_DETECT_EN BIT(22) + #define MESON_SAR_ADC_REG3_ADC_EN BIT(21) + #define MESON_SAR_ADC_REG3_PANEL_DETECT_COUNT_MASK GENMASK(20, 18) + #define MESON_SAR_ADC_REG3_PANEL_DETECT_FILTER_TB_MASK GENMASK(17, 16) + #define MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT 10 + #define MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH 6 + #define MESON_SAR_ADC_REG3_BLOCK_DLY_SEL_MASK GENMASK(9, 8) + #define MESON_SAR_ADC_REG3_BLOCK_DLY_MASK GENMASK(7, 0) + +#define MESON_SAR_ADC_DELAY 0x10 + #define MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK GENMASK(25, 24) + #define MESON_SAR_ADC_DELAY_BL30_BUSY BIT(15) + #define MESON_SAR_ADC_DELAY_KERNEL_BUSY BIT(14) + #define MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK GENMASK(23, 16) + #define MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK GENMASK(9, 8) + #define MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK GENMASK(7, 0) + +#define MESON_SAR_ADC_LAST_RD 0x14 + #define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL1_MASK GENMASK(23, 16) + #define MESON_SAR_ADC_LAST_RD_LAST_CHANNEL0_MASK GENMASK(9, 0) + +#define MESON_SAR_ADC_FIFO_RD 0x18 + #define MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK GENMASK(14, 12) + #define MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK GENMASK(11, 0) + +#define MESON_SAR_ADC_AUX_SW 0x1c + #define MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(_chan) \ + (8 + (((_chan) - 2) * 3)) + #define MESON_SAR_ADC_AUX_SW_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_AUX_SW_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_AUX_SW_MODE_SEL BIT(4) + #define MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_AUX_SW_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_AUX_SW_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_CHAN_10_SW 0x20 + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_P_MUX BIT(22) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_VREF_N_MUX BIT(21) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_MODE_SEL BIT(20) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW BIT(19) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW BIT(18) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_YM_DRIVE_SW BIT(17) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN1_XM_DRIVE_SW BIT(16) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK GENMASK(9, 7) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_MODE_SEL BIT(4) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_CHAN_10_SW_CHAN0_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_DETECT_IDLE_SW 0x24 + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_SW_EN BIT(26) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK GENMASK(25, 23) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_P_MUX BIT(22) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_VREF_N_MUX BIT(21) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MODE_SEL BIT(20) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YP_DRIVE_SW BIT(19) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XP_DRIVE_SW BIT(18) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_YM_DRIVE_SW BIT(17) + #define MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_XM_DRIVE_SW BIT(16) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK GENMASK(9, 7) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_P_MUX BIT(6) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_VREF_N_MUX BIT(5) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MODE_SEL BIT(4) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YP_DRIVE_SW BIT(3) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XP_DRIVE_SW BIT(2) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_YM_DRIVE_SW BIT(1) + #define MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_XM_DRIVE_SW BIT(0) + +#define MESON_SAR_ADC_DELTA_10 0x28 + #define MESON_SAR_ADC_DELTA_10_TEMP_SEL BIT(27) + #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_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) + +/* + * NOTE: registers from here are undocumented (the vendor Linux kernel driver + * and u-boot source served as reference). These only seem to be relevant on + * GXBB and newer. + */ +#define MESON_SAR_ADC_REG11 0x2c + #define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13) + +#define MESON_SAR_ADC_REG13 0x34 + #define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8) + +#define MESON_SAR_ADC_MAX_FIFO_SIZE 32 +#define MESON_SAR_ADC_TIMEOUT 100 /* ms */ +#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 + +#define MESON_SAR_ADC_CHAN(_chan) { \ + .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), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .datasheet_name = "SAR_ADC_CH"#_chan, \ +} + +#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), + 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), + 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, + MEDIAN_AVERAGING = 0x2, +}; + +enum meson_sar_adc_num_samples { + ONE_SAMPLE = 0x0, + TWO_SAMPLES = 0x1, + FOUR_SAMPLES = 0x2, + EIGHT_SAMPLES = 0x3, +}; + +enum meson_sar_adc_chan7_mux_sel { + CHAN7_MUX_VSS = 0x0, + CHAN7_MUX_VDD_DIV4 = 0x1, + CHAN7_MUX_VDD_DIV2 = 0x2, + CHAN7_MUX_VDD_MUL3_DIV4 = 0x3, + CHAN7_MUX_VDD = 0x4, + CHAN7_MUX_CH7_INPUT = 0x7, +}; + +struct meson_sar_adc_param { + bool has_bl30_integration; + unsigned long clock_rate; + 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 { + const struct meson_sar_adc_param *param; + const char *name; +}; + +struct meson_sar_adc_priv { + struct regmap *regmap; + struct regulator *vref; + const struct meson_sar_adc_param *param; + struct clk *clkin; + struct clk *core_clk; + struct clk *adc_sel_clk; + struct clk *adc_clk; + struct clk_gate clk_gate; + struct clk *adc_div_clk; + struct clk_divider clk_div; + 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 = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = MESON_SAR_ADC_REG13, +}; + +static const struct regmap_config meson_sar_adc_regmap_config_meson8 = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = MESON_SAR_ADC_DELTA_10, +}; + +static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u32 regval; + + regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val); + + return FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval); +} + +static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int tmp; + + /* use val_calib = scale * val_raw + offset calibration function */ + tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias; + + return clamp(tmp, 0, (1 << priv->param->resolution) - 1); +} + +static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int regval, timeout = 10000; + + /* + * NOTE: we need a small delay before reading the status, otherwise + * the sample engine may not have started internally (which would + * seem to us that sampling is already finished). + */ + do { + udelay(1); + regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val); + } while (FIELD_GET(MESON_SAR_ADC_REG0_BUSY_MASK, regval) && timeout--); + + if (timeout < 0) + return -ETIMEDOUT; + + return 0; +} + +static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int regval, fifo_chan, fifo_val, count; + + if(!wait_for_completion_timeout(&priv->done, + msecs_to_jiffies(MESON_SAR_ADC_TIMEOUT))) + return -ETIMEDOUT; + + count = meson_sar_adc_get_fifo_count(indio_dev); + if (count != 1) { + dev_err(&indio_dev->dev, + "ADC FIFO has %d element(s) instead of one\n", count); + return -EINVAL; + } + + 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->address) { + dev_err(&indio_dev->dev, + "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->param->resolution - 1, 0); + *val = meson_sar_adc_calib_val(indio_dev, fifo_val); + + return 0; +} + +static void meson_sar_adc_set_averaging(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum meson_sar_adc_avg_mode mode, + enum meson_sar_adc_num_samples samples) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int val, address = chan->address; + + 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(address), + val); + + 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(address), val); +} + +static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u32 regval; + + /* + * the SAR ADC engine allows sampling multiple channels at the same + * time. to keep it simple we're only working with one *internal* + * channel, which starts counting at index 0 (which means: count = 1). + */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST, + MESON_SAR_ADC_CHAN_LIST_MAX_INDEX_MASK, regval); + + /* map channel index 0 to the channel which we want to read */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), + 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->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->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->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, + enum meson_sar_adc_chan7_mux_sel sel) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u32 regval; + + regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval); + + usleep_range(10, 20); +} + +static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + reinit_completion(&priv->done); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, + MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLING_START, + MESON_SAR_ADC_REG0_SAMPLING_START); +} + +static void meson_sar_adc_stop_sample_engine(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_IRQ_EN, 0); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLING_STOP, + MESON_SAR_ADC_REG0_SAMPLING_STOP); + + /* wait until all modules are stopped */ + meson_sar_adc_wait_busy_clear(indio_dev); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_SAMPLE_ENGINE_ENABLE, 0); +} + +static int meson_sar_adc_lock(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int val, timeout = 10000; + + mutex_lock(&indio_dev->mlock); + + 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, + MESON_SAR_ADC_DELAY_KERNEL_BUSY); + + /* + * wait until BL30 releases it's lock (so we can use the SAR + * ADC) + */ + do { + udelay(1); + regmap_read(priv->regmap, MESON_SAR_ADC_DELAY, &val); + } while (val & MESON_SAR_ADC_DELAY_BL30_BUSY && timeout--); + + if (timeout < 0) { + mutex_unlock(&indio_dev->mlock); + return -ETIMEDOUT; + } + } + + return 0; +} + +static void meson_sar_adc_unlock(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + + 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); + + mutex_unlock(&indio_dev->mlock); +} + +static void meson_sar_adc_clear_fifo(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + unsigned int count, tmp; + + for (count = 0; count < MESON_SAR_ADC_MAX_FIFO_SIZE; count++) { + if (!meson_sar_adc_get_fifo_count(indio_dev)) + break; + + regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, &tmp); + } +} + +static int meson_sar_adc_get_sample(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum meson_sar_adc_avg_mode avg_mode, + 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; + + /* clear the FIFO to make sure we're not reading old values */ + meson_sar_adc_clear_fifo(indio_dev); + + meson_sar_adc_set_averaging(indio_dev, chan, avg_mode, avg_samples); + + meson_sar_adc_enable_channel(indio_dev, chan); + + meson_sar_adc_start_sample_engine(indio_dev); + ret = meson_sar_adc_read_raw_sample(indio_dev, chan, val); + meson_sar_adc_stop_sample_engine(indio_dev); + + meson_sar_adc_unlock(indio_dev); + + if (ret) { + dev_warn(indio_dev->dev.parent, + "failed to read sample for channel %lu: %d\n", + chan->address, ret); + return ret; + } + + return IIO_VAL_INT; +} + +static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return meson_sar_adc_get_sample(indio_dev, chan, NO_AVERAGING, + ONE_SAMPLE, val); + break; + + case IIO_CHAN_INFO_AVERAGE_RAW: + return meson_sar_adc_get_sample(indio_dev, chan, + MEAN_AVERAGING, EIGHT_SAMPLES, + val); + break; + + case IIO_CHAN_INFO_SCALE: + 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->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; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_CALIBSCALE: + *val = priv->calibscale / MILLION; + *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; + } +} + +static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, + void __iomem *base) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + struct clk_init_data init; + const char *clk_parents[1]; + + 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); + init.parent_names = clk_parents; + init.num_parents = 1; + + priv->clk_div.reg = base + MESON_SAR_ADC_REG3; + priv->clk_div.shift = MESON_SAR_ADC_REG3_ADC_CLK_DIV_SHIFT; + priv->clk_div.width = MESON_SAR_ADC_REG3_ADC_CLK_DIV_WIDTH; + priv->clk_div.hw.init = &init; + priv->clk_div.flags = 0; + + priv->adc_div_clk = devm_clk_register(&indio_dev->dev, + &priv->clk_div.hw); + 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, "%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); + init.parent_names = clk_parents; + init.num_parents = 1; + + priv->clk_gate.reg = base + MESON_SAR_ADC_REG3; + priv->clk_gate.bit_idx = __ffs(MESON_SAR_ADC_REG3_CLK_EN); + priv->clk_gate.hw.init = &init; + + priv->adc_clk = devm_clk_register(&indio_dev->dev, &priv->clk_gate.hw); + if (WARN_ON(IS_ERR(priv->adc_clk))) + return PTR_ERR(priv->adc_clk); + + 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.parent, + "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; + + return dev_err_probe(indio_dev->dev.parent, ret, + "failed to get temperature_calib cell\n"); + } + + 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); + int regval, i, ret; + + /* + * make sure we start at CH7 input since the other muxes are only used + * for internal calibration. + */ + meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT); + + 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 + * reading the temperature sensor. + */ + regmap_read(priv->regmap, MESON_SAR_ADC_REG3, ®val); + if (regval & MESON_SAR_ADC_REG3_BL30_INITIALIZED) + return 0; + } + + meson_sar_adc_stop_sample_engine(indio_dev); + + /* + * 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, 0); + + /* disable all channels by default */ + regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CTRL_SAMPLING_CLOCK_PHASE, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY, + MESON_SAR_ADC_REG3_CNTL_USE_SC_DLY); + + /* delay between two samples = (10+1) * 1uS */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_CNT_MASK, + 10)); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_SAMPLE_DLY_SEL_MASK, + 0)); + + /* delay between two samples = (10+1) * 1uS */ + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_CNT_MASK, + 10)); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, + MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK, + FIELD_PREP(MESON_SAR_ADC_DELAY_INPUT_DLY_SEL_MASK, + 1)); + + /* + * set up the input channel muxes in MESON_SAR_ADC_CHAN_10_SW + * (0 = SAR_ADC_CH0, 1 = SAR_ADC_CH1) + */ + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN0_MUX_SEL_MASK, + regval); + regval = FIELD_PREP(MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK, 1); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK, + regval); + + /* + * set up the input channel muxes in MESON_SAR_ADC_AUX_SW + * (2 = SAR_ADC_CH2, 3 = SAR_ADC_CH3, ...) and enable + * MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW and + * MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW like the vendor driver. + */ + regval = 0; + for (i = 2; i <= 7; i++) + regval |= i << MESON_SAR_ADC_AUX_SW_MUX_SEL_CHAN_SHIFT(i); + regval |= MESON_SAR_ADC_AUX_SW_YP_DRIVE_SW; + 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, + "failed to set adc parent to clkin\n"); + return ret; + } + + 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"); + return ret; + } + + return 0; +} + +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->param; + u32 enable_mask; + + if (param->bandgap_reg == MESON_SAR_ADC_REG11) + enable_mask = MESON_SAR_ADC_REG11_BANDGAP_EN; + else + enable_mask = MESON_SAR_ADC_DELTA_10_TS_VBG_EN; + + regmap_update_bits(priv->regmap, param->bandgap_reg, enable_mask, + on_off ? enable_mask : 0); +} + +static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret; + u32 regval; + + ret = meson_sar_adc_lock(indio_dev); + if (ret) + goto err_lock; + + ret = regulator_enable(priv->vref); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "failed to enable vref regulator\n"); + goto err_vref; + } + + ret = clk_prepare_enable(priv->core_clk); + if (ret) { + dev_err(indio_dev->dev.parent, "failed to enable core clk\n"); + goto err_core_clk; + } + + regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, + MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval); + + meson_sar_adc_set_bandgap(indio_dev, true); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_ADC_EN, + MESON_SAR_ADC_REG3_ADC_EN); + + udelay(5); + + ret = clk_prepare_enable(priv->adc_clk); + if (ret) { + dev_err(indio_dev->dev.parent, "failed to enable adc clk\n"); + goto err_adc_clk; + } + + meson_sar_adc_unlock(indio_dev); + + return 0; + +err_adc_clk: + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_ADC_EN, 0); + meson_sar_adc_set_bandgap(indio_dev, false); + clk_disable_unprepare(priv->core_clk); +err_core_clk: + regulator_disable(priv->vref); +err_vref: + meson_sar_adc_unlock(indio_dev); +err_lock: + return ret; +} + +static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret; + + ret = meson_sar_adc_lock(indio_dev); + if (ret) + return ret; + + clk_disable_unprepare(priv->adc_clk); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_ADC_EN, 0); + + meson_sar_adc_set_bandgap(indio_dev, false); + + clk_disable_unprepare(priv->core_clk); + + regulator_disable(priv->vref); + + meson_sar_adc_unlock(indio_dev); + + return 0; +} + +static irqreturn_t meson_sar_adc_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + unsigned int cnt, threshold; + u32 regval; + + regmap_read(priv->regmap, MESON_SAR_ADC_REG0, ®val); + cnt = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_COUNT_MASK, regval); + threshold = FIELD_GET(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval); + + if (cnt < threshold) + return IRQ_NONE; + + complete(&priv->done); + + return IRQ_HANDLED; +} + +static int meson_sar_adc_calib(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + int ret, nominal0, nominal1, value0, value1; + + /* use points 25% and 75% for calibration */ + nominal0 = (1 << priv->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, + &indio_dev->channels[7], + MEAN_AVERAGING, EIGHT_SAMPLES, &value0); + if (ret < 0) + goto out; + + meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4); + usleep_range(10, 20); + ret = meson_sar_adc_get_sample(indio_dev, + &indio_dev->channels[7], + MEAN_AVERAGING, EIGHT_SAMPLES, &value1); + if (ret < 0) + goto out; + + if (value1 <= value0) { + ret = -EINVAL; + goto out; + } + + priv->calibscale = div_s64((nominal1 - nominal0) * (s64)MILLION, + value1 - value0); + priv->calibbias = nominal0 - div_s64((s64)value0 * priv->calibscale, + MILLION); + ret = 0; +out: + meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT); + + return ret; +} + +static const struct iio_info meson_sar_adc_iio_info = { + .read_raw = meson_sar_adc_iio_info_read_raw, +}; + +static const struct meson_sar_adc_param meson_sar_adc_meson8_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 = 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 = { + .has_bl30_integration = true, + .clock_rate = 1200000, + .bandgap_reg = MESON_SAR_ADC_REG11, + .regmap_config = &meson_sar_adc_regmap_config_gxbb, + .resolution = 10, +}; + +static const struct meson_sar_adc_param meson_sar_adc_gxl_param = { + .has_bl30_integration = true, + .clock_rate = 1200000, + .bandgap_reg = MESON_SAR_ADC_REG11, + .regmap_config = &meson_sar_adc_regmap_config_gxbb, + .resolution = 12, +}; + +static const struct meson_sar_adc_data meson_sar_adc_meson8_data = { + .param = &meson_sar_adc_meson8_param, + .name = "meson-meson8-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = { + .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_meson8b_param, + .name = "meson-meson8m2-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = { + .param = &meson_sar_adc_gxbb_param, + .name = "meson-gxbb-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_gxl_data = { + .param = &meson_sar_adc_gxl_param, + .name = "meson-gxl-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_gxm_data = { + .param = &meson_sar_adc_gxl_param, + .name = "meson-gxm-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_axg_data = { + .param = &meson_sar_adc_gxl_param, + .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", + .data = &meson_sar_adc_meson8_data, + }, { + .compatible = "amlogic,meson8b-saradc", + .data = &meson_sar_adc_meson8b_data, + }, { + .compatible = "amlogic,meson8m2-saradc", + .data = &meson_sar_adc_meson8m2_data, + }, { + .compatible = "amlogic,meson-gxbb-saradc", + .data = &meson_sar_adc_gxbb_data, + }, { + .compatible = "amlogic,meson-gxl-saradc", + .data = &meson_sar_adc_gxl_data, + }, { + .compatible = "amlogic,meson-gxm-saradc", + .data = &meson_sar_adc_gxm_data, + }, { + .compatible = "amlogic,meson-axg-saradc", + .data = &meson_sar_adc_axg_data, + }, { + .compatible = "amlogic,meson-g12a-saradc", + .data = &meson_sar_adc_g12a_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, meson_sar_adc_of_match); + +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; + void __iomem *base; + int irq, ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + priv = iio_priv(indio_dev); + init_completion(&priv->done); + + 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->param = match_data->param; + + indio_dev->name = match_data->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &meson_sar_adc_iio_info; + + base = devm_platform_ioremap_resource(pdev, 0); + 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; + + ret = devm_request_irq(&pdev->dev, irq, meson_sar_adc_irq, IRQF_SHARED, + dev_name(&pdev->dev), indio_dev); + if (ret) + return ret; + + priv->clkin = devm_clk_get(&pdev->dev, "clkin"); + if (IS_ERR(priv->clkin)) { + dev_err(&pdev->dev, "failed to get clkin\n"); + return PTR_ERR(priv->clkin); + } + + priv->core_clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(priv->core_clk)) { + dev_err(&pdev->dev, "failed to get core clk\n"); + return PTR_ERR(priv->core_clk); + } + + priv->adc_clk = devm_clk_get(&pdev->dev, "adc_clk"); + if (IS_ERR(priv->adc_clk)) { + if (PTR_ERR(priv->adc_clk) == -ENOENT) { + priv->adc_clk = NULL; + } else { + dev_err(&pdev->dev, "failed to get adc clk\n"); + return PTR_ERR(priv->adc_clk); + } + } + + priv->adc_sel_clk = devm_clk_get(&pdev->dev, "adc_sel"); + if (IS_ERR(priv->adc_sel_clk)) { + if (PTR_ERR(priv->adc_sel_clk) == -ENOENT) { + priv->adc_sel_clk = NULL; + } else { + dev_err(&pdev->dev, "failed to get adc_sel clk\n"); + return PTR_ERR(priv->adc_sel_clk); + } + } + + /* on pre-GXBB SoCs the SAR ADC itself provides the ADC clock: */ + if (!priv->adc_clk) { + ret = meson_sar_adc_clk_init(indio_dev, base); + if (ret) + return ret; + } + + priv->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(priv->vref)) { + dev_err(&pdev->dev, "failed to get vref regulator\n"); + return PTR_ERR(priv->vref); + } + + 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; + + ret = meson_sar_adc_hw_enable(indio_dev); + if (ret) + goto err; + + ret = meson_sar_adc_calib(indio_dev); + if (ret) + dev_warn(&pdev->dev, "calibration failed\n"); + + platform_set_drvdata(pdev, indio_dev); + + ret = iio_device_register(indio_dev); + if (ret) + goto err_hw; + + return 0; + +err_hw: + meson_sar_adc_hw_disable(indio_dev); +err: + return ret; +} + +static int meson_sar_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + + return meson_sar_adc_hw_disable(indio_dev); +} + +static int __maybe_unused meson_sar_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return meson_sar_adc_hw_disable(indio_dev); +} + +static int __maybe_unused meson_sar_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return meson_sar_adc_hw_enable(indio_dev); +} + +static SIMPLE_DEV_PM_OPS(meson_sar_adc_pm_ops, + meson_sar_adc_suspend, meson_sar_adc_resume); + +static struct platform_driver meson_sar_adc_driver = { + .probe = meson_sar_adc_probe, + .remove = meson_sar_adc_remove, + .driver = { + .name = "meson-saradc", + .of_match_table = meson_sar_adc_of_match, + .pm = &meson_sar_adc_pm_ops, + }, +}; + +module_platform_driver(meson_sar_adc_driver); + +MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>"); +MODULE_DESCRIPTION("Amlogic Meson SAR ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mp2629_adc.c b/drivers/iio/adc/mp2629_adc.c new file mode 100644 index 000000000..acd9420c0 --- /dev/null +++ b/drivers/iio/adc/mp2629_adc.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MP2629 Driver for ADC + * + * Copyright 2020 Monolithic Power Systems, Inc + * + * Author: Saravanan Sekar <sravanhome@gmail.com> + */ + +#include <linux/iio/driver.h> +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> +#include <linux/mfd/mp2629.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define MP2629_REG_ADC_CTRL 0x03 +#define MP2629_REG_BATT_VOLT 0x0e +#define MP2629_REG_SYSTEM_VOLT 0x0f +#define MP2629_REG_INPUT_VOLT 0x11 +#define MP2629_REG_BATT_CURRENT 0x12 +#define MP2629_REG_INPUT_CURRENT 0x13 + +#define MP2629_ADC_START BIT(7) +#define MP2629_ADC_CONTINUOUS BIT(6) + +#define MP2629_MAP(_mp, _mpc) IIO_MAP(#_mp, "mp2629_charger", "mp2629-"_mpc) + +#define MP2629_ADC_CHAN(_ch, _type) { \ + .type = _type, \ + .indexed = 1, \ + .datasheet_name = #_ch, \ + .channel = MP2629_ ## _ch, \ + .address = MP2629_REG_ ## _ch, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +struct mp2629_adc { + struct regmap *regmap; + struct device *dev; +}; + +static struct iio_chan_spec mp2629_channels[] = { + MP2629_ADC_CHAN(BATT_VOLT, IIO_VOLTAGE), + MP2629_ADC_CHAN(SYSTEM_VOLT, IIO_VOLTAGE), + MP2629_ADC_CHAN(INPUT_VOLT, IIO_VOLTAGE), + MP2629_ADC_CHAN(BATT_CURRENT, IIO_CURRENT), + MP2629_ADC_CHAN(INPUT_CURRENT, IIO_CURRENT) +}; + +static struct iio_map mp2629_adc_maps[] = { + MP2629_MAP(BATT_VOLT, "batt-volt"), + MP2629_MAP(SYSTEM_VOLT, "system-volt"), + MP2629_MAP(INPUT_VOLT, "input-volt"), + MP2629_MAP(BATT_CURRENT, "batt-current"), + MP2629_MAP(INPUT_CURRENT, "input-current"), + { } +}; + +static int mp2629_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mp2629_adc *info = iio_priv(indio_dev); + unsigned int rval; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(info->regmap, chan->address, &rval); + if (ret) + return ret; + + if (chan->channel == MP2629_INPUT_VOLT) + rval &= GENMASK(6, 0); + *val = rval; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->channel) { + case MP2629_BATT_VOLT: + case MP2629_SYSTEM_VOLT: + *val = 20; + return IIO_VAL_INT; + + case MP2629_INPUT_VOLT: + *val = 60; + return IIO_VAL_INT; + + case MP2629_BATT_CURRENT: + *val = 175; + *val2 = 10; + return IIO_VAL_FRACTIONAL; + + case MP2629_INPUT_CURRENT: + *val = 133; + *val2 = 10; + return IIO_VAL_FRACTIONAL; + + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +static const struct iio_info mp2629_adc_info = { + .read_raw = &mp2629_read_raw, +}; + +static int mp2629_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mp2629_data *ddata = dev_get_drvdata(dev->parent); + struct mp2629_adc *info; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->regmap = ddata->regmap; + info->dev = dev; + platform_set_drvdata(pdev, indio_dev); + + ret = regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL, + MP2629_ADC_START | MP2629_ADC_CONTINUOUS, + MP2629_ADC_START | MP2629_ADC_CONTINUOUS); + if (ret) { + dev_err(dev, "adc enable fail: %d\n", ret); + return ret; + } + + ret = iio_map_array_register(indio_dev, mp2629_adc_maps); + if (ret) { + dev_err(dev, "IIO maps register fail: %d\n", ret); + goto fail_disable; + } + + indio_dev->name = "mp2629-adc"; + indio_dev->dev.parent = dev; + indio_dev->channels = mp2629_channels; + indio_dev->num_channels = ARRAY_SIZE(mp2629_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mp2629_adc_info; + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "IIO device register fail: %d\n", ret); + goto fail_map_unregister; + } + + return 0; + +fail_map_unregister: + iio_map_array_unregister(indio_dev); + +fail_disable: + regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL, + MP2629_ADC_CONTINUOUS, 0); + regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL, + MP2629_ADC_START, 0); + + return ret; +} + +static int mp2629_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct mp2629_adc *info = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + iio_map_array_unregister(indio_dev); + + regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL, + MP2629_ADC_CONTINUOUS, 0); + regmap_update_bits(info->regmap, MP2629_REG_ADC_CTRL, + MP2629_ADC_START, 0); + + return 0; +} + +static const struct of_device_id mp2629_adc_of_match[] = { + { .compatible = "mps,mp2629_adc"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2629_adc_of_match); + +static struct platform_driver mp2629_adc_driver = { + .driver = { + .name = "mp2629_adc", + .of_match_table = mp2629_adc_of_match, + }, + .probe = mp2629_adc_probe, + .remove = mp2629_adc_remove, +}; +module_platform_driver(mp2629_adc_driver); + +MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>"); +MODULE_DESCRIPTION("MP2629 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c new file mode 100644 index 000000000..d4fccd52e --- /dev/null +++ b/drivers/iio/adc/mt6577_auxadc.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Zhiyong Tao <zhiyong.tao@mediatek.com> + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/iopoll.h> +#include <linux/io.h> +#include <linux/iio/iio.h> + +/* Register definitions */ +#define MT6577_AUXADC_CON0 0x00 +#define MT6577_AUXADC_CON1 0x04 +#define MT6577_AUXADC_CON2 0x10 +#define MT6577_AUXADC_STA BIT(0) + +#define MT6577_AUXADC_DAT0 0x14 +#define MT6577_AUXADC_RDY0 BIT(12) + +#define MT6577_AUXADC_MISC 0x94 +#define MT6577_AUXADC_PDN_EN BIT(14) + +#define MT6577_AUXADC_DAT_MASK 0xfff +#define MT6577_AUXADC_SLEEP_US 1000 +#define MT6577_AUXADC_TIMEOUT_US 10000 +#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) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ +} + +static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = { + MT6577_AUXADC_CHANNEL(0), + MT6577_AUXADC_CHANNEL(1), + MT6577_AUXADC_CHANNEL(2), + MT6577_AUXADC_CHANNEL(3), + MT6577_AUXADC_CHANNEL(4), + MT6577_AUXADC_CHANNEL(5), + MT6577_AUXADC_CHANNEL(6), + MT6577_AUXADC_CHANNEL(7), + MT6577_AUXADC_CHANNEL(8), + MT6577_AUXADC_CHANNEL(9), + MT6577_AUXADC_CHANNEL(10), + MT6577_AUXADC_CHANNEL(11), + MT6577_AUXADC_CHANNEL(12), + MT6577_AUXADC_CHANNEL(13), + MT6577_AUXADC_CHANNEL(14), + MT6577_AUXADC_CHANNEL(15), +}; + +/* For Voltage calculation */ +#define VOLTAGE_FULL_RANGE 1500 /* VA voltage */ +#define AUXADC_PRECISE 4096 /* 12 bits */ + +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) +{ + u32 val; + + val = readl(reg); + val |= or_mask; + val &= ~and_mask; + writel(val, reg); +} + +static int mt6577_auxadc_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + u32 val; + void __iomem *reg_channel; + int ret; + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + + reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 + + chan->channel * 0x04; + + mutex_lock(&adc_dev->lock); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1, + 0, 1 << chan->channel); + + /* read channel and make sure old ready bit == 0 */ + ret = readl_poll_timeout(reg_channel, val, + ((val & MT6577_AUXADC_RDY0) == 0), + MT6577_AUXADC_SLEEP_US, + MT6577_AUXADC_TIMEOUT_US); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "wait for channel[%d] ready bit clear time out\n", + chan->channel); + goto err_timeout; + } + + /* set bit to trigger sample */ + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1, + 1 << chan->channel, 0); + + /* we must delay here for hardware sample channel data */ + udelay(MT6577_AUXADC_SAMPLE_READY_US); + + 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 */ + ret = readl_poll_timeout(reg_channel, val, + ((val & MT6577_AUXADC_RDY0) != 0), + MT6577_AUXADC_SLEEP_US, + MT6577_AUXADC_TIMEOUT_US); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "wait for channel[%d] data ready time out\n", + chan->channel); + goto err_timeout; + } + + /* read data */ + val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK; + + mutex_unlock(&adc_dev->lock); + + return val; + +err_timeout: + + mutex_unlock(&adc_dev->lock); + + return -ETIMEDOUT; +} + +static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + 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); + if (*val < 0) { + dev_err(indio_dev->dev.parent, + "failed to sample data on channel[%d]\n", + chan->channel); + return *val; + } + if (adc_dev->dev_comp->sample_data_cali) + *val = mt_auxadc_get_cali_data(*val, true); + + /* Convert adc raw data to voltage: 0 - 1500 mV */ + *val = *val * VOLTAGE_FULL_RANGE / AUXADC_PRECISE; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static const struct iio_info mt6577_auxadc_info = { + .read_raw = &mt6577_auxadc_read_raw, +}; + +static int __maybe_unused mt6577_auxadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + int ret; + + ret = clk_prepare_enable(adc_dev->adc_clk); + if (ret) { + pr_err("failed to enable auxadc clock\n"); + return ret; + } + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + MT6577_AUXADC_PDN_EN, 0); + mdelay(MT6577_AUXADC_POWER_READY_MS); + + return 0; +} + +static int __maybe_unused mt6577_auxadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); + clk_disable_unprepare(adc_dev->adc_clk); + + return 0; +} + +static int mt6577_auxadc_probe(struct platform_device *pdev) +{ + struct mt6577_auxadc_device *adc_dev; + unsigned long adc_clk_rate; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev)); + if (!indio_dev) + return -ENOMEM; + + adc_dev = iio_priv(indio_dev); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &mt6577_auxadc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mt6577_auxadc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels); + + adc_dev->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc_dev->reg_base)) { + dev_err(&pdev->dev, "failed to get auxadc base address\n"); + return PTR_ERR(adc_dev->reg_base); + } + + adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main"); + if (IS_ERR(adc_dev->adc_clk)) { + dev_err(&pdev->dev, "failed to get auxadc clock\n"); + return PTR_ERR(adc_dev->adc_clk); + } + + ret = clk_prepare_enable(adc_dev->adc_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable auxadc clock\n"); + return ret; + } + + adc_clk_rate = clk_get_rate(adc_dev->adc_clk); + if (!adc_clk_rate) { + ret = -EINVAL; + dev_err(&pdev->dev, "null clock rate\n"); + goto err_disable_clk; + } + + adc_dev->dev_comp = device_get_match_data(&pdev->dev); + + mutex_init(&adc_dev->lock); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + MT6577_AUXADC_PDN_EN, 0); + mdelay(MT6577_AUXADC_POWER_READY_MS); + + platform_set_drvdata(pdev, indio_dev); + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register iio device\n"); + goto err_power_off; + } + + return 0; + +err_power_off: + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); +err_disable_clk: + clk_disable_unprepare(adc_dev->adc_clk); + return ret; +} + +static int mt6577_auxadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); + + clk_disable_unprepare(adc_dev->adc_clk); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mt6577_auxadc_pm_ops, + mt6577_auxadc_suspend, + mt6577_auxadc_resume); + +static const struct of_device_id mt6577_auxadc_of_match[] = { + { .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); + +static struct platform_driver mt6577_auxadc_driver = { + .driver = { + .name = "mt6577-auxadc", + .of_match_table = mt6577_auxadc_of_match, + .pm = &mt6577_auxadc_pm_ops, + }, + .probe = mt6577_auxadc_probe, + .remove = mt6577_auxadc_remove, +}; +module_platform_driver(mt6577_auxadc_driver); + +MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao@mediatek.com>"); +MODULE_DESCRIPTION("MTK AUXADC Device Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c new file mode 100644 index 000000000..c37e39b96 --- /dev/null +++ b/drivers/iio/adc/mxs-lradc-adc.c @@ -0,0 +1,835 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Freescale MXS LRADC ADC driver + * + * Copyright (c) 2012 DENX Software Engineering, GmbH. + * Copyright (c) 2017 Ksenija Stanojevic <ksenija.stanojevic@gmail.com> + * + * Authors: + * Marek Vasut <marex@denx.de> + * Ksenija Stanojevic <ksenija.stanojevic@gmail.com> + */ + +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/mfd/core.h> +#include <linux/mfd/mxs-lradc.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/sysfs.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/sysfs.h> + +/* + * Make this runtime configurable if necessary. Currently, if the buffered mode + * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before + * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000) + * seconds. The result is that the samples arrive every 500mS. + */ +#define LRADC_DELAY_TIMER_PER 200 +#define LRADC_DELAY_TIMER_LOOP 5 + +#define VREF_MV_BASE 1850 + +static const char *mx23_lradc_adc_irq_names[] = { + "mxs-lradc-channel0", + "mxs-lradc-channel1", + "mxs-lradc-channel2", + "mxs-lradc-channel3", + "mxs-lradc-channel4", + "mxs-lradc-channel5", +}; + +static const char *mx28_lradc_adc_irq_names[] = { + "mxs-lradc-thresh0", + "mxs-lradc-thresh1", + "mxs-lradc-channel0", + "mxs-lradc-channel1", + "mxs-lradc-channel2", + "mxs-lradc-channel3", + "mxs-lradc-channel4", + "mxs-lradc-channel5", + "mxs-lradc-button0", + "mxs-lradc-button1", +}; + +static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = { + [IMX23_LRADC] = { + VREF_MV_BASE, /* CH0 */ + VREF_MV_BASE, /* CH1 */ + VREF_MV_BASE, /* CH2 */ + VREF_MV_BASE, /* CH3 */ + VREF_MV_BASE, /* CH4 */ + VREF_MV_BASE, /* CH5 */ + VREF_MV_BASE * 2, /* CH6 VDDIO */ + VREF_MV_BASE * 4, /* CH7 VBATT */ + VREF_MV_BASE, /* CH8 Temp sense 0 */ + VREF_MV_BASE, /* CH9 Temp sense 1 */ + VREF_MV_BASE, /* CH10 */ + VREF_MV_BASE, /* CH11 */ + VREF_MV_BASE, /* CH12 USB_DP */ + VREF_MV_BASE, /* CH13 USB_DN */ + VREF_MV_BASE, /* CH14 VBG */ + VREF_MV_BASE * 4, /* CH15 VDD5V */ + }, + [IMX28_LRADC] = { + VREF_MV_BASE, /* CH0 */ + VREF_MV_BASE, /* CH1 */ + VREF_MV_BASE, /* CH2 */ + VREF_MV_BASE, /* CH3 */ + VREF_MV_BASE, /* CH4 */ + VREF_MV_BASE, /* CH5 */ + VREF_MV_BASE, /* CH6 */ + VREF_MV_BASE * 4, /* CH7 VBATT */ + VREF_MV_BASE, /* CH8 Temp sense 0 */ + VREF_MV_BASE, /* CH9 Temp sense 1 */ + VREF_MV_BASE * 2, /* CH10 VDDIO */ + VREF_MV_BASE, /* CH11 VTH */ + VREF_MV_BASE * 2, /* CH12 VDDA */ + VREF_MV_BASE, /* CH13 VDDD */ + VREF_MV_BASE, /* CH14 VBG */ + VREF_MV_BASE * 4, /* CH15 VDD5V */ + }, +}; + +enum mxs_lradc_divbytwo { + MXS_LRADC_DIV_DISABLED = 0, + MXS_LRADC_DIV_ENABLED, +}; + +struct mxs_lradc_scale { + unsigned int integer; + unsigned int nano; +}; + +struct mxs_lradc_adc { + struct mxs_lradc *lradc; + struct device *dev; + + void __iomem *base; + /* Maximum of 8 channels + 8 byte ts */ + u32 buffer[10] __aligned(8); + struct iio_trigger *trig; + struct completion completion; + spinlock_t lock; + + const u32 *vref_mv; + struct mxs_lradc_scale scale_avail[LRADC_MAX_TOTAL_CHANS][2]; + unsigned long is_divided; +}; + + +/* Raw I/O operations */ +static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan, + int *val) +{ + struct mxs_lradc_adc *adc = iio_priv(iio_dev); + struct mxs_lradc *lradc = adc->lradc; + int ret; + + /* + * See if there is no buffered operation in progress. If there is simply + * bail out. This can be improved to support both buffered and raw IO at + * the same time, yet the code becomes horribly complicated. Therefore I + * applied KISS principle here. + */ + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + reinit_completion(&adc->completion); + + /* + * No buffered operation in progress, map the channel and trigger it. + * Virtual channel 0 is always used here as the others are always not + * used if doing raw sampling. + */ + if (lradc->soc == IMX28_LRADC) + writel(LRADC_CTRL1_LRADC_IRQ_EN(0), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + writel(0x1, adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + + /* Enable / disable the divider per requirement */ + if (test_bit(chan, &adc->is_divided)) + writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, + adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_SET); + else + writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET, + adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_CLR); + + /* Clean the slot's previous content, then set new one. */ + writel(LRADC_CTRL4_LRADCSELECT_MASK(0), + adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR); + writel(chan, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET); + + writel(0, adc->base + LRADC_CH(0)); + + /* Enable the IRQ and start sampling the channel. */ + writel(LRADC_CTRL1_LRADC_IRQ_EN(0), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET); + writel(BIT(0), adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET); + + /* Wait for completion on the channel, 1 second max. */ + ret = wait_for_completion_killable_timeout(&adc->completion, HZ); + if (!ret) + ret = -ETIMEDOUT; + if (ret < 0) + goto err; + + /* Read the data. */ + *val = readl(adc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK; + ret = IIO_VAL_INT; + +err: + writel(LRADC_CTRL1_LRADC_IRQ_EN(0), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + iio_device_release_direct_mode(iio_dev); + + return ret; +} + +static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val) +{ + int ret, min, max; + + ret = mxs_lradc_adc_read_single(iio_dev, 8, &min); + if (ret != IIO_VAL_INT) + return ret; + + ret = mxs_lradc_adc_read_single(iio_dev, 9, &max); + if (ret != IIO_VAL_INT) + return ret; + + *val = max - min; + + return IIO_VAL_INT; +} + +static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long m) +{ + struct mxs_lradc_adc *adc = iio_priv(iio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + if (chan->type == IIO_TEMP) + return mxs_lradc_adc_read_temp(iio_dev, val); + + return mxs_lradc_adc_read_single(iio_dev, chan->channel, val); + + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + /* + * From the datasheet, we have to multiply by 1.012 and + * divide by 4 + */ + *val = 0; + *val2 = 253000; + return IIO_VAL_INT_PLUS_MICRO; + } + + *val = adc->vref_mv[chan->channel]; + *val2 = chan->scan_type.realbits - + test_bit(chan->channel, &adc->is_divided); + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_OFFSET: + if (chan->type == IIO_TEMP) { + /* + * The calculated value from the ADC is in Kelvin, we + * want Celsius for hwmon so the offset is -273.15 + * The offset is applied before scaling so it is + * actually -213.15 * 4 / 1.012 = -1079.644268 + */ + *val = -1079; + *val2 = 644268; + + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; + + default: + break; + } + + return -EINVAL; +} + +static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int val, int val2, long m) +{ + struct mxs_lradc_adc *adc = iio_priv(iio_dev); + struct mxs_lradc_scale *scale_avail = + adc->scale_avail[chan->channel]; + int ret; + + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + ret = -EINVAL; + if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer && + val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) { + /* divider by two disabled */ + clear_bit(chan->channel, &adc->is_divided); + ret = 0; + } else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer && + val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) { + /* divider by two enabled */ + set_bit(chan->channel, &adc->is_divided); + ret = 0; + } + + break; + default: + ret = -EINVAL; + break; + } + + iio_device_release_direct_mode(iio_dev); + + return ret; +} + +static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + long m) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *iio = dev_to_iio_dev(dev); + struct mxs_lradc_adc *adc = iio_priv(iio); + struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr); + int i, ch, len = 0; + + ch = iio_attr->address; + for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++) + len += sprintf(buf + len, "%u.%09u ", + adc->scale_avail[ch][i].integer, + adc->scale_avail[ch][i].nano); + + len += sprintf(buf + len, "\n"); + + return len; +} + +#define SHOW_SCALE_AVAILABLE_ATTR(ch)\ + IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, 0444,\ + mxs_lradc_adc_show_scale_avail, NULL, ch) + +static SHOW_SCALE_AVAILABLE_ATTR(0); +static SHOW_SCALE_AVAILABLE_ATTR(1); +static SHOW_SCALE_AVAILABLE_ATTR(2); +static SHOW_SCALE_AVAILABLE_ATTR(3); +static SHOW_SCALE_AVAILABLE_ATTR(4); +static SHOW_SCALE_AVAILABLE_ATTR(5); +static SHOW_SCALE_AVAILABLE_ATTR(6); +static SHOW_SCALE_AVAILABLE_ATTR(7); +static SHOW_SCALE_AVAILABLE_ATTR(10); +static SHOW_SCALE_AVAILABLE_ATTR(11); +static SHOW_SCALE_AVAILABLE_ATTR(12); +static SHOW_SCALE_AVAILABLE_ATTR(13); +static SHOW_SCALE_AVAILABLE_ATTR(14); +static SHOW_SCALE_AVAILABLE_ATTR(15); + +static struct attribute *mxs_lradc_adc_attributes[] = { + &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage2_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage3_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage4_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage5_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage6_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage7_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage10_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage11_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage12_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage13_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage14_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage15_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group mxs_lradc_adc_attribute_group = { + .attrs = mxs_lradc_adc_attributes, +}; + +static const struct iio_info mxs_lradc_adc_iio_info = { + .read_raw = mxs_lradc_adc_read_raw, + .write_raw = mxs_lradc_adc_write_raw, + .write_raw_get_fmt = mxs_lradc_adc_write_raw_get_fmt, + .attrs = &mxs_lradc_adc_attribute_group, +}; + +/* IRQ Handling */ +static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data) +{ + struct iio_dev *iio = data; + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + unsigned long reg = readl(adc->base + LRADC_CTRL1); + unsigned long flags; + + if (!(reg & mxs_lradc_irq_mask(lradc))) + return IRQ_NONE; + + if (iio_buffer_enabled(iio)) { + if (reg & lradc->buffer_vchans) { + spin_lock_irqsave(&adc->lock, flags); + iio_trigger_poll(iio->trig); + spin_unlock_irqrestore(&adc->lock, flags); + } + } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) { + complete(&adc->completion); + } + + writel(reg & mxs_lradc_irq_mask(lradc), + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + return IRQ_HANDLED; +} + + +/* Trigger handling */ +static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *iio = pf->indio_dev; + struct mxs_lradc_adc *adc = iio_priv(iio); + const u32 chan_value = LRADC_CH_ACCUMULATE | + ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); + unsigned int i, j = 0; + + for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { + adc->buffer[j] = readl(adc->base + LRADC_CH(j)); + writel(chan_value, adc->base + LRADC_CH(j)); + adc->buffer[j] &= LRADC_CH_VALUE_MASK; + adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP; + j++; + } + + iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp); + + iio_trigger_notify_done(iio->trig); + + return IRQ_HANDLED; +} + +static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state) +{ + struct iio_dev *iio = iio_trigger_get_drvdata(trig); + struct mxs_lradc_adc *adc = iio_priv(iio); + const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR; + + writel(LRADC_DELAY_KICK, adc->base + (LRADC_DELAY(0) + st)); + + return 0; +} + +static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = { + .set_trigger_state = &mxs_lradc_adc_configure_trigger, +}; + +static int mxs_lradc_adc_trigger_init(struct iio_dev *iio) +{ + int ret; + struct iio_trigger *trig; + struct mxs_lradc_adc *adc = iio_priv(iio); + + 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); + trig->ops = &mxs_lradc_adc_trigger_ops; + + ret = iio_trigger_register(trig); + if (ret) + return ret; + + adc->trig = trig; + + return 0; +} + +static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + + iio_trigger_unregister(adc->trig); +} + +static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + int chan, ofs = 0; + unsigned long enable = 0; + u32 ctrl4_set = 0; + u32 ctrl4_clr = 0; + u32 ctrl1_irq = 0; + const u32 chan_value = LRADC_CH_ACCUMULATE | + ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET); + + if (lradc->soc == IMX28_LRADC) + writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + writel(lradc->buffer_vchans, + adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + + for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) { + ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs); + ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs); + ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs); + writel(chan_value, adc->base + LRADC_CH(ofs)); + bitmap_set(&enable, ofs, 1); + ofs++; + } + + writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK, + adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR); + writel(ctrl4_clr, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR); + writel(ctrl4_set, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET); + writel(ctrl1_irq, adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET); + writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET, + adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET); + + return 0; +} + +static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + + writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK, + adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR); + + writel(lradc->buffer_vchans, + adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR); + if (lradc->soc == IMX28_LRADC) + writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR); + + return 0; +} + +static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio, + const unsigned long *mask) +{ + struct mxs_lradc_adc *adc = iio_priv(iio); + struct mxs_lradc *lradc = adc->lradc; + const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS); + int rsvd_chans = 0; + unsigned long rsvd_mask = 0; + + if (lradc->use_touchbutton) + rsvd_mask |= CHAN_MASK_TOUCHBUTTON; + if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_4WIRE) + rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE; + if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE) + rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE; + + if (lradc->use_touchbutton) + rsvd_chans++; + if (lradc->touchscreen_wire) + rsvd_chans += 2; + + /* Test for attempts to map channels with special mode of operation. */ + if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS)) + return false; + + /* Test for attempts to map more channels then available slots. */ + if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS) + return false; + + return true; +} + +static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = { + .preenable = &mxs_lradc_adc_buffer_preenable, + .postdisable = &mxs_lradc_adc_buffer_postdisable, + .validate_scan_mask = &mxs_lradc_adc_validate_scan_mask, +}; + +/* Driver initialization */ +#define MXS_ADC_CHAN(idx, chan_type, name) { \ + .type = (chan_type), \ + .indexed = 1, \ + .scan_index = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .channel = (idx), \ + .address = (idx), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = LRADC_RESOLUTION, \ + .storagebits = 32, \ + }, \ + .datasheet_name = (name), \ +} + +static const struct iio_chan_spec mx23_lradc_chan_spec[] = { + MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"), + MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"), + MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"), + MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"), + MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"), + MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"), + MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"), + MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"), + /* Combined Temperature sensors */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = 8, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .channel = 8, + .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,}, + .datasheet_name = "TEMP_DIE", + }, + /* Hidden channel to keep indexes */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = -1, + .channel = 9, + }, + MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL), + MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL), + MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"), + MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"), + MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"), + MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"), +}; + +static const struct iio_chan_spec mx28_lradc_chan_spec[] = { + MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"), + MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"), + MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"), + MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"), + MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"), + MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"), + MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"), + MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"), + /* Combined Temperature sensors */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = 8, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .channel = 8, + .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,}, + .datasheet_name = "TEMP_DIE", + }, + /* Hidden channel to keep indexes */ + { + .type = IIO_TEMP, + .indexed = 1, + .scan_index = -1, + .channel = 9, + }, + MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"), + MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"), + MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"), + MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"), + MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"), + MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"), +}; + +static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc) +{ + /* The ADC always uses DELAY CHANNEL 0. */ + const u32 adc_cfg = + (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) | + (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET); + + /* Configure DELAY CHANNEL 0 for generic ADC sampling. */ + writel(adc_cfg, adc->base + LRADC_DELAY(0)); + + /* + * Start internal temperature sensing by clearing bit + * HW_LRADC_CTRL2_TEMPSENSE_PWD. This bit can be left cleared + * after power up. + */ + writel(0, adc->base + LRADC_CTRL2); +} + +static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc) +{ + writel(0, adc->base + LRADC_DELAY(0)); +} + +static int mxs_lradc_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mxs_lradc *lradc = dev_get_drvdata(dev->parent); + struct mxs_lradc_adc *adc; + struct iio_dev *iio; + struct resource *iores; + int ret, irq, virq, i, s, n; + u64 scale_uv; + const char **irq_name; + + /* Allocate the IIO device. */ + iio = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!iio) { + dev_err(dev, "Failed to allocate IIO device\n"); + return -ENOMEM; + } + + adc = iio_priv(iio); + adc->lradc = lradc; + adc->dev = dev; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iores) + return -EINVAL; + + adc->base = devm_ioremap(dev, iores->start, resource_size(iores)); + if (!adc->base) + return -ENOMEM; + + init_completion(&adc->completion); + spin_lock_init(&adc->lock); + + platform_set_drvdata(pdev, iio); + + iio->name = pdev->name; + iio->dev.of_node = dev->parent->of_node; + iio->info = &mxs_lradc_adc_iio_info; + iio->modes = INDIO_DIRECT_MODE; + iio->masklength = LRADC_MAX_TOTAL_CHANS; + + if (lradc->soc == IMX23_LRADC) { + iio->channels = mx23_lradc_chan_spec; + iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec); + irq_name = mx23_lradc_adc_irq_names; + n = ARRAY_SIZE(mx23_lradc_adc_irq_names); + } else { + iio->channels = mx28_lradc_chan_spec; + iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec); + irq_name = mx28_lradc_adc_irq_names; + n = ARRAY_SIZE(mx28_lradc_adc_irq_names); + } + + ret = stmp_reset_block(adc->base); + if (ret) + return ret; + + for (i = 0; i < n; i++) { + irq = platform_get_irq_byname(pdev, irq_name[i]); + if (irq < 0) + return irq; + + virq = irq_of_parse_and_map(dev->parent->of_node, irq); + + ret = devm_request_irq(dev, virq, mxs_lradc_adc_handle_irq, + 0, irq_name[i], iio); + if (ret) + return ret; + } + + ret = mxs_lradc_adc_trigger_init(iio); + if (ret) + return ret; + + ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time, + &mxs_lradc_adc_trigger_handler, + &mxs_lradc_adc_buffer_ops); + if (ret) + goto err_trig; + + adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc]; + + /* Populate available ADC input ranges */ + for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) { + for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) { + /* + * [s=0] = optional divider by two disabled (default) + * [s=1] = optional divider by two enabled + * + * The scale is calculated by doing: + * Vref >> (realbits - s) + * which multiplies by two on the second component + * of the array. + */ + scale_uv = ((u64)adc->vref_mv[i] * 100000000) >> + (LRADC_RESOLUTION - s); + adc->scale_avail[i][s].nano = + do_div(scale_uv, 100000000) * 10; + adc->scale_avail[i][s].integer = scale_uv; + } + } + + /* Configure the hardware. */ + mxs_lradc_adc_hw_init(adc); + + /* Register IIO device. */ + ret = iio_device_register(iio); + if (ret) { + dev_err(dev, "Failed to register IIO device\n"); + goto err_dev; + } + + return 0; + +err_dev: + mxs_lradc_adc_hw_stop(adc); + iio_triggered_buffer_cleanup(iio); +err_trig: + mxs_lradc_adc_trigger_remove(iio); + return ret; +} + +static int mxs_lradc_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *iio = platform_get_drvdata(pdev); + struct mxs_lradc_adc *adc = iio_priv(iio); + + iio_device_unregister(iio); + mxs_lradc_adc_hw_stop(adc); + iio_triggered_buffer_cleanup(iio); + mxs_lradc_adc_trigger_remove(iio); + + return 0; +} + +static struct platform_driver mxs_lradc_adc_driver = { + .driver = { + .name = "mxs-lradc-adc", + }, + .probe = mxs_lradc_adc_probe, + .remove = mxs_lradc_adc_remove, +}; +module_platform_driver(mxs_lradc_adc_driver); + +MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); +MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mxs-lradc-adc"); diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c new file mode 100644 index 000000000..07c85434b --- /dev/null +++ b/drivers/iio/adc/nau7802.c @@ -0,0 +1,598 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for the Nuvoton NAU7802 ADC + * + * Copyright 2013 Free Electrons + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/wait.h> +#include <linux/log2.h> +#include <linux/of.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define NAU7802_REG_PUCTRL 0x00 +#define NAU7802_PUCTRL_RR(x) (x << 0) +#define NAU7802_PUCTRL_RR_BIT NAU7802_PUCTRL_RR(1) +#define NAU7802_PUCTRL_PUD(x) (x << 1) +#define NAU7802_PUCTRL_PUD_BIT NAU7802_PUCTRL_PUD(1) +#define NAU7802_PUCTRL_PUA(x) (x << 2) +#define NAU7802_PUCTRL_PUA_BIT NAU7802_PUCTRL_PUA(1) +#define NAU7802_PUCTRL_PUR(x) (x << 3) +#define NAU7802_PUCTRL_PUR_BIT NAU7802_PUCTRL_PUR(1) +#define NAU7802_PUCTRL_CS(x) (x << 4) +#define NAU7802_PUCTRL_CS_BIT NAU7802_PUCTRL_CS(1) +#define NAU7802_PUCTRL_CR(x) (x << 5) +#define NAU7802_PUCTRL_CR_BIT NAU7802_PUCTRL_CR(1) +#define NAU7802_PUCTRL_AVDDS(x) (x << 7) +#define NAU7802_PUCTRL_AVDDS_BIT NAU7802_PUCTRL_AVDDS(1) +#define NAU7802_REG_CTRL1 0x01 +#define NAU7802_CTRL1_VLDO(x) (x << 3) +#define NAU7802_CTRL1_GAINS(x) (x) +#define NAU7802_CTRL1_GAINS_BITS 0x07 +#define NAU7802_REG_CTRL2 0x02 +#define NAU7802_CTRL2_CHS(x) (x << 7) +#define NAU7802_CTRL2_CRS(x) (x << 4) +#define NAU7802_SAMP_FREQ_320 0x07 +#define NAU7802_CTRL2_CHS_BIT NAU7802_CTRL2_CHS(1) +#define NAU7802_REG_ADC_B2 0x12 +#define NAU7802_REG_ADC_B1 0x13 +#define NAU7802_REG_ADC_B0 0x14 +#define NAU7802_REG_ADC_CTRL 0x15 + +#define NAU7802_MIN_CONVERSIONS 6 + +struct nau7802_state { + struct i2c_client *client; + s32 last_value; + struct mutex lock; + struct mutex data_lock; + u32 vref_mv; + u32 conversion_count; + u32 min_conversions; + u8 sample_rate; + u32 scale_avail[8]; + struct completion value_ok; +}; + +#define NAU7802_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan), \ + .scan_index = (chan), \ + .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 nau7802_chan_array[] = { + NAU7802_CHANNEL(0), + NAU7802_CHANNEL(1), +}; + +static const u16 nau7802_sample_freq_avail[] = {10, 20, 40, 80, + 10, 10, 10, 320}; + +static ssize_t nau7802_show_scales(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nau7802_state *st = iio_priv(dev_to_iio_dev(dev)); + int i, len = 0; + + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09d ", + st->scale_avail[i]); + + buf[len-1] = '\n'; + + return len; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 40 80 320"); + +static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, nau7802_show_scales, + NULL, 0); + +static struct attribute *nau7802_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group nau7802_attribute_group = { + .attrs = nau7802_attributes, +}; + +static int nau7802_set_gain(struct nau7802_state *st, int gain) +{ + int ret; + + mutex_lock(&st->lock); + st->conversion_count = 0; + + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1); + if (ret < 0) + goto nau7802_sysfs_set_gain_out; + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1, + (ret & (~NAU7802_CTRL1_GAINS_BITS)) | + gain); + +nau7802_sysfs_set_gain_out: + mutex_unlock(&st->lock); + + return ret; +} + +static int nau7802_read_conversion(struct nau7802_state *st) +{ + int data; + + mutex_lock(&st->data_lock); + data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B2); + if (data < 0) + goto nau7802_read_conversion_out; + st->last_value = data << 16; + + data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B1); + if (data < 0) + goto nau7802_read_conversion_out; + st->last_value |= data << 8; + + data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B0); + if (data < 0) + goto nau7802_read_conversion_out; + st->last_value |= data; + + st->last_value = sign_extend32(st->last_value, 23); + +nau7802_read_conversion_out: + mutex_unlock(&st->data_lock); + + return data; +} + +/* + * Conversions are synchronised on the rising edge of NAU7802_PUCTRL_CS_BIT + */ +static int nau7802_sync(struct nau7802_state *st) +{ + int ret; + + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, + ret | NAU7802_PUCTRL_CS_BIT); + + return ret; +} + +static irqreturn_t nau7802_eoc_trigger(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct nau7802_state *st = iio_priv(indio_dev); + int status; + + status = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); + if (status < 0) + return IRQ_HANDLED; + + if (!(status & NAU7802_PUCTRL_CR_BIT)) + return IRQ_NONE; + + if (nau7802_read_conversion(st) < 0) + return IRQ_HANDLED; + + /* + * Because there is actually only one ADC for both channels, we have to + * wait for enough conversions to happen before getting a significant + * value when changing channels and the values are far apart. + */ + if (st->conversion_count < NAU7802_MIN_CONVERSIONS) + st->conversion_count++; + if (st->conversion_count >= NAU7802_MIN_CONVERSIONS) + complete(&st->value_ok); + + return IRQ_HANDLED; +} + +static int nau7802_read_irq(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct nau7802_state *st = iio_priv(indio_dev); + int ret; + + reinit_completion(&st->value_ok); + enable_irq(st->client->irq); + + nau7802_sync(st); + + /* read registers to ensure we flush everything */ + ret = nau7802_read_conversion(st); + if (ret < 0) + goto read_chan_info_failure; + + /* Wait for a conversion to finish */ + ret = wait_for_completion_interruptible_timeout(&st->value_ok, + msecs_to_jiffies(1000)); + if (ret == 0) + ret = -ETIMEDOUT; + + if (ret < 0) + goto read_chan_info_failure; + + disable_irq(st->client->irq); + + *val = st->last_value; + + return IIO_VAL_INT; + +read_chan_info_failure: + disable_irq(st->client->irq); + + return ret; +} + +static int nau7802_read_poll(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct nau7802_state *st = iio_priv(indio_dev); + int ret; + + nau7802_sync(st); + + /* read registers to ensure we flush everything */ + ret = nau7802_read_conversion(st); + if (ret < 0) + return ret; + + /* + * Because there is actually only one ADC for both channels, we have to + * wait for enough conversions to happen before getting a significant + * value when changing channels and the values are far appart. + */ + do { + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); + if (ret < 0) + return ret; + + while (!(ret & NAU7802_PUCTRL_CR_BIT)) { + if (st->sample_rate != NAU7802_SAMP_FREQ_320) + msleep(20); + else + mdelay(4); + ret = i2c_smbus_read_byte_data(st->client, + NAU7802_REG_PUCTRL); + if (ret < 0) + return ret; + } + + ret = nau7802_read_conversion(st); + if (ret < 0) + return ret; + if (st->conversion_count < NAU7802_MIN_CONVERSIONS) + st->conversion_count++; + } while (st->conversion_count < NAU7802_MIN_CONVERSIONS); + + *val = st->last_value; + + return IIO_VAL_INT; +} + +static int nau7802_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct nau7802_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&st->lock); + /* + * Select the channel to use + * - Channel 1 is value 0 in the CHS register + * - Channel 2 is value 1 in the CHS register + */ + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL2); + if (ret < 0) { + mutex_unlock(&st->lock); + return ret; + } + + if (((ret & NAU7802_CTRL2_CHS_BIT) && !chan->channel) || + (!(ret & NAU7802_CTRL2_CHS_BIT) && + chan->channel)) { + st->conversion_count = 0; + ret = i2c_smbus_write_byte_data(st->client, + NAU7802_REG_CTRL2, + NAU7802_CTRL2_CHS(chan->channel) | + NAU7802_CTRL2_CRS(st->sample_rate)); + + if (ret < 0) { + mutex_unlock(&st->lock); + return ret; + } + } + + if (st->client->irq) + ret = nau7802_read_irq(indio_dev, chan, val); + else + ret = nau7802_read_poll(indio_dev, chan, val); + + mutex_unlock(&st->lock); + return ret; + + case IIO_CHAN_INFO_SCALE: + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1); + if (ret < 0) + return ret; + + /* + * We have 24 bits of signed data, that means 23 bits of data + * plus the sign bit + */ + *val = st->vref_mv; + *val2 = 23 + (ret & NAU7802_CTRL1_GAINS_BITS); + + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = nau7802_sample_freq_avail[st->sample_rate]; + *val2 = 0; + return IIO_VAL_INT; + + default: + break; + } + + return -EINVAL; +} + +static int nau7802_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct nau7802_state *st = iio_priv(indio_dev); + int i, ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + if (val2 == st->scale_avail[i]) + return nau7802_set_gain(st, i); + + break; + + case IIO_CHAN_INFO_SAMP_FREQ: + for (i = 0; i < ARRAY_SIZE(nau7802_sample_freq_avail); i++) + if (val == nau7802_sample_freq_avail[i]) { + mutex_lock(&st->lock); + st->sample_rate = i; + st->conversion_count = 0; + ret = i2c_smbus_write_byte_data(st->client, + NAU7802_REG_CTRL2, + NAU7802_CTRL2_CRS(st->sample_rate)); + mutex_unlock(&st->lock); + return ret; + } + + break; + + default: + break; + } + + return -EINVAL; +} + +static int nau7802_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT_PLUS_NANO; +} + +static const struct iio_info nau7802_info = { + .read_raw = &nau7802_read_raw, + .write_raw = &nau7802_write_raw, + .write_raw_get_fmt = nau7802_write_raw_get_fmt, + .attrs = &nau7802_attribute_group, +}; + +static int nau7802_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct nau7802_state *st; + struct device_node *np = client->dev.of_node; + int i, ret; + u8 data; + u32 tmp = 0; + + if (!client->dev.of_node) { + dev_err(&client->dev, "No device tree node available.\n"); + return -EINVAL; + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + i2c_set_clientdata(client, indio_dev); + + indio_dev->name = dev_name(&client->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &nau7802_info; + + st->client = client; + + /* Reset the device */ + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, + NAU7802_PUCTRL_RR_BIT); + if (ret < 0) + return ret; + + /* Enter normal operation mode */ + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, + NAU7802_PUCTRL_PUD_BIT); + if (ret < 0) + return ret; + + /* + * After about 200 usecs, the device should be ready and then + * the Power Up bit will be set to 1. If not, wait for it. + */ + udelay(210); + ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL); + if (ret < 0) + return ret; + if (!(ret & NAU7802_PUCTRL_PUR_BIT)) + return ret; + + of_property_read_u32(np, "nuvoton,vldo", &tmp); + st->vref_mv = tmp; + + data = NAU7802_PUCTRL_PUD_BIT | NAU7802_PUCTRL_PUA_BIT | + NAU7802_PUCTRL_CS_BIT; + if (tmp >= 2400) + data |= NAU7802_PUCTRL_AVDDS_BIT; + + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, data); + if (ret < 0) + return ret; + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_ADC_CTRL, 0x30); + if (ret < 0) + return ret; + + if (tmp >= 2400) { + data = NAU7802_CTRL1_VLDO((4500 - tmp) / 300); + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1, + data); + if (ret < 0) + return ret; + } + + /* Populate available ADC input ranges */ + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + st->scale_avail[i] = (((u64)st->vref_mv) * 1000000000ULL) + >> (23 + i); + + init_completion(&st->value_ok); + + /* + * The ADC fires continuously and we can't do anything about + * it. So we need to have the IRQ disabled by default, and we + * will enable them back when we will need them.. + */ + if (client->irq) { + ret = request_threaded_irq(client->irq, + NULL, + nau7802_eoc_trigger, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + client->dev.driver->name, + indio_dev); + if (ret) { + /* + * What may happen here is that our IRQ controller is + * not able to get level interrupt but this is required + * by this ADC as when going over 40 sample per second, + * the interrupt line may stay high between conversions. + * So, we continue no matter what but we switch to + * polling mode. + */ + dev_info(&client->dev, + "Failed to allocate IRQ, using polling mode\n"); + client->irq = 0; + } else + disable_irq(client->irq); + } + + if (!client->irq) { + /* + * We are polling, use the fastest sample rate by + * default + */ + st->sample_rate = NAU7802_SAMP_FREQ_320; + ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL2, + NAU7802_CTRL2_CRS(st->sample_rate)); + if (ret) + goto error_free_irq; + } + + /* Setup the ADC channels available on the board */ + indio_dev->num_channels = ARRAY_SIZE(nau7802_chan_array); + indio_dev->channels = nau7802_chan_array; + + mutex_init(&st->lock); + mutex_init(&st->data_lock); + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "Couldn't register the device.\n"); + goto error_device_register; + } + + return 0; + +error_device_register: + mutex_destroy(&st->lock); + mutex_destroy(&st->data_lock); +error_free_irq: + if (client->irq) + free_irq(client->irq, indio_dev); + + return ret; +} + +static int nau7802_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct nau7802_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + mutex_destroy(&st->lock); + mutex_destroy(&st->data_lock); + if (client->irq) + free_irq(client->irq, indio_dev); + + return 0; +} + +static const struct i2c_device_id nau7802_i2c_id[] = { + { "nau7802", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id); + +static const struct of_device_id nau7802_dt_ids[] = { + { .compatible = "nuvoton,nau7802" }, + {}, +}; +MODULE_DEVICE_TABLE(of, nau7802_dt_ids); + +static struct i2c_driver nau7802_driver = { + .probe = nau7802_probe, + .remove = nau7802_remove, + .id_table = nau7802_i2c_id, + .driver = { + .name = "nau7802", + .of_match_table = nau7802_dt_ids, + }, +}; + +module_i2c_driver(nau7802_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Nuvoton NAU7802 ADC Driver"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); +MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c new file mode 100644 index 000000000..d9d105920 --- /dev/null +++ b/drivers/iio/adc/npcm_adc.c @@ -0,0 +1,319 @@ +// 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> +#include <linux/reset.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 reset_control *reset; +}; + +/* 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) { + /* if conversion failed - reset ADC module */ + reset_control_assert(info->reset); + msleep(100); + reset_control_deassert(info->reset); + 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 npcm_adc *info; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + info = iio_priv(indio_dev); + + info->dev = &pdev->dev; + + info->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + info->reset = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(info->reset)) + return PTR_ERR(info->reset); + + 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); + + 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->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 new file mode 100644 index 000000000..6ed0d151a --- /dev/null +++ b/drivers/iio/adc/palmas_gpadc.c @@ -0,0 +1,842 @@ +// 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> + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/pm.h> +#include <linux/mfd/palmas.h> +#include <linux/completion.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> + +#define MOD_NAME "palmas-gpadc" +#define PALMAS_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(5000)) +#define PALMAS_TO_BE_CALCULATED 0 +#define PALMAS_GPADC_TRIMINVALID -1 + +struct palmas_gpadc_info { +/* calibration codes and regs */ + int x1; /* lower ideal code */ + int x2; /* higher ideal code */ + int v1; /* expected lower volt reading */ + int v2; /* expected higher volt reading */ + u8 trim1_reg; /* register number for lower trim */ + u8 trim2_reg; /* register number for upper trim */ + int gain; /* calculated from above (after reading trim regs) */ + int offset; /* calculated from above (after reading trim regs) */ + int gain_error; /* calculated from above (after reading trim regs) */ + bool is_uncalibrated; /* if channel has calibration data */ +}; + +#define PALMAS_ADC_INFO(_chan, _x1, _x2, _v1, _v2, _t1, _t2, _is_uncalibrated) \ + [PALMAS_ADC_CH_##_chan] = { \ + .x1 = _x1, \ + .x2 = _x2, \ + .v1 = _v1, \ + .v2 = _v2, \ + .gain = PALMAS_TO_BE_CALCULATED, \ + .offset = PALMAS_TO_BE_CALCULATED, \ + .gain_error = PALMAS_TO_BE_CALCULATED, \ + .trim1_reg = PALMAS_GPADC_TRIM##_t1, \ + .trim2_reg = PALMAS_GPADC_TRIM##_t2, \ + .is_uncalibrated = _is_uncalibrated \ + } + +static struct palmas_gpadc_info palmas_gpadc_info[] = { + PALMAS_ADC_INFO(IN0, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN1, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN2, 2064, 3112, 1260, 1900, 3, 4, false), + PALMAS_ADC_INFO(IN3, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN4, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN5, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN6, 2064, 3112, 2520, 3800, 5, 6, false), + PALMAS_ADC_INFO(IN7, 2064, 3112, 2520, 3800, 7, 8, false), + PALMAS_ADC_INFO(IN8, 2064, 3112, 3150, 4750, 9, 10, false), + PALMAS_ADC_INFO(IN9, 2064, 3112, 5670, 8550, 11, 12, false), + PALMAS_ADC_INFO(IN10, 2064, 3112, 3465, 5225, 13, 14, false), + PALMAS_ADC_INFO(IN11, 0, 0, 0, 0, INVALID, INVALID, true), + PALMAS_ADC_INFO(IN12, 0, 0, 0, 0, INVALID, INVALID, true), + PALMAS_ADC_INFO(IN13, 0, 0, 0, 0, INVALID, INVALID, true), + PALMAS_ADC_INFO(IN14, 2064, 3112, 3645, 5225, 15, 16, false), + PALMAS_ADC_INFO(IN15, 0, 0, 0, 0, INVALID, INVALID, true), +}; + +/* + * struct palmas_gpadc - the palmas_gpadc structure + * @ch0_current: channel 0 current source setting + * 0: 0 uA + * 1: 5 uA + * 2: 15 uA + * 3: 20 uA + * @ch3_current: channel 0 current source setting + * 0: 0 uA + * 1: 10 uA + * 2: 400 uA + * 3: 800 uA + * @extended_delay: enable the gpadc extended delay mode + * @auto_conversion_period: define the auto_conversion_period + * + * This is the palmas_gpadc structure to store run-time information + * and pointers for this driver instance. + */ +struct palmas_gpadc { + struct device *dev; + struct palmas *palmas; + u8 ch0_current; + u8 ch3_current; + bool extended_delay; + int irq; + int irq_auto_0; + int irq_auto_1; + struct palmas_gpadc_info *adc_info; + struct completion conv_completion; + struct palmas_adc_wakeup_property wakeup1_data; + struct palmas_adc_wakeup_property wakeup2_data; + bool wakeup1_enable; + bool wakeup2_enable; + int auto_conversion_period; +}; + +/* + * GPADC lock issue in AUTO mode. + * Impact: In AUTO mode, GPADC conversion can be locked after disabling AUTO + * mode feature. + * Details: + * When the AUTO mode is the only conversion mode enabled, if the AUTO + * mode feature is disabled with bit GPADC_AUTO_CTRL. AUTO_CONV1_EN = 0 + * or bit GPADC_AUTO_CTRL. AUTO_CONV0_EN = 0 during a conversion, the + * conversion mechanism can be seen as locked meaning that all following + * conversion will give 0 as a result. Bit GPADC_STATUS.GPADC_AVAILABLE + * will stay at 0 meaning that GPADC is busy. An RT conversion can unlock + * the GPADC. + * + * Workaround(s): + * To avoid the lock mechanism, the workaround to follow before any stop + * conversion request is: + * Force the GPADC state machine to be ON by using the GPADC_CTRL1. + * GPADC_FORCE bit = 1 + * Shutdown the GPADC AUTO conversion using + * GPADC_AUTO_CTRL.SHUTDOWN_CONV[01] = 0. + * After 100us, force the GPADC state machine to be OFF by using the + * GPADC_CTRL1. GPADC_FORCE bit = 0 + */ + +static int palmas_disable_auto_conversion(struct palmas_gpadc *adc) +{ + int ret; + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, + PALMAS_GPADC_CTRL1_GPADC_FORCE, + PALMAS_GPADC_CTRL1_GPADC_FORCE); + if (ret < 0) { + dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret); + return ret; + } + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_CTRL, + PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV1 | + PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV0, + 0); + if (ret < 0) { + dev_err(adc->dev, "AUTO_CTRL update failed: %d\n", ret); + return ret; + } + + udelay(100); + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, + PALMAS_GPADC_CTRL1_GPADC_FORCE, 0); + if (ret < 0) + dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret); + + return ret; +} + +static irqreturn_t palmas_gpadc_irq(int irq, void *data) +{ + struct palmas_gpadc *adc = data; + + complete(&adc->conv_completion); + + return IRQ_HANDLED; +} + +static irqreturn_t palmas_gpadc_irq_auto(int irq, void *data) +{ + struct palmas_gpadc *adc = data; + + dev_dbg(adc->dev, "Threshold interrupt %d occurs\n", irq); + palmas_disable_auto_conversion(adc); + + return IRQ_HANDLED; +} + +static int palmas_gpadc_start_mask_interrupt(struct palmas_gpadc *adc, + bool mask) +{ + int ret; + + if (!mask) + ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE, + PALMAS_INT3_MASK, + PALMAS_INT3_MASK_GPADC_EOC_SW, 0); + else + ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE, + PALMAS_INT3_MASK, + PALMAS_INT3_MASK_GPADC_EOC_SW, + PALMAS_INT3_MASK_GPADC_EOC_SW); + if (ret < 0) + dev_err(adc->dev, "GPADC INT MASK update failed: %d\n", ret); + + return ret; +} + +static int palmas_gpadc_enable(struct palmas_gpadc *adc, int adc_chan, + int enable) +{ + unsigned int mask, val; + int ret; + + if (enable) { + val = (adc->extended_delay + << PALMAS_GPADC_RT_CTRL_EXTEND_DELAY_SHIFT); + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_RT_CTRL, + PALMAS_GPADC_RT_CTRL_EXTEND_DELAY, val); + if (ret < 0) { + dev_err(adc->dev, "RT_CTRL update failed: %d\n", ret); + return ret; + } + + mask = (PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_MASK | + PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_MASK | + PALMAS_GPADC_CTRL1_GPADC_FORCE); + val = (adc->ch0_current + << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_SHIFT); + val |= (adc->ch3_current + << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_SHIFT); + val |= PALMAS_GPADC_CTRL1_GPADC_FORCE; + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, mask, val); + if (ret < 0) { + dev_err(adc->dev, + "Failed to update current setting: %d\n", ret); + return ret; + } + + mask = (PALMAS_GPADC_SW_SELECT_SW_CONV0_SEL_MASK | + PALMAS_GPADC_SW_SELECT_SW_CONV_EN); + val = (adc_chan | PALMAS_GPADC_SW_SELECT_SW_CONV_EN); + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_SELECT, mask, val); + if (ret < 0) { + dev_err(adc->dev, "SW_SELECT update failed: %d\n", ret); + return ret; + } + } else { + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_SELECT, 0); + if (ret < 0) + dev_err(adc->dev, "SW_SELECT write failed: %d\n", ret); + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, + PALMAS_GPADC_CTRL1_GPADC_FORCE, 0); + if (ret < 0) { + dev_err(adc->dev, "CTRL1 update failed: %d\n", ret); + return ret; + } + } + + return ret; +} + +static int palmas_gpadc_read_prepare(struct palmas_gpadc *adc, int adc_chan) +{ + int ret; + + ret = palmas_gpadc_enable(adc, adc_chan, true); + if (ret < 0) + return ret; + + return palmas_gpadc_start_mask_interrupt(adc, 0); +} + +static void palmas_gpadc_read_done(struct palmas_gpadc *adc, int adc_chan) +{ + palmas_gpadc_start_mask_interrupt(adc, 1); + palmas_gpadc_enable(adc, adc_chan, false); +} + +static int palmas_gpadc_calibrate(struct palmas_gpadc *adc, int adc_chan) +{ + int k; + int d1; + int d2; + int ret; + int gain; + int x1 = adc->adc_info[adc_chan].x1; + int x2 = adc->adc_info[adc_chan].x2; + int v1 = adc->adc_info[adc_chan].v1; + int v2 = adc->adc_info[adc_chan].v2; + + ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE, + adc->adc_info[adc_chan].trim1_reg, &d1); + if (ret < 0) { + dev_err(adc->dev, "TRIM read failed: %d\n", ret); + goto scrub; + } + + ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE, + adc->adc_info[adc_chan].trim2_reg, &d2); + if (ret < 0) { + dev_err(adc->dev, "TRIM read failed: %d\n", ret); + goto scrub; + } + + /* gain error calculation */ + k = (1000 + (1000 * (d2 - d1)) / (x2 - x1)); + + /* gain calculation */ + gain = ((v2 - v1) * 1000) / (x2 - x1); + + adc->adc_info[adc_chan].gain_error = k; + adc->adc_info[adc_chan].gain = gain; + /* offset Calculation */ + adc->adc_info[adc_chan].offset = (d1 * 1000) - ((k - 1000) * x1); + +scrub: + return ret; +} + +static int palmas_gpadc_start_conversion(struct palmas_gpadc *adc, int adc_chan) +{ + unsigned int val; + int ret; + + init_completion(&adc->conv_completion); + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_SELECT, + PALMAS_GPADC_SW_SELECT_SW_START_CONV0, + PALMAS_GPADC_SW_SELECT_SW_START_CONV0); + if (ret < 0) { + dev_err(adc->dev, "SELECT_SW_START write failed: %d\n", ret); + return ret; + } + + ret = wait_for_completion_timeout(&adc->conv_completion, + PALMAS_ADC_CONVERSION_TIMEOUT); + if (ret == 0) { + dev_err(adc->dev, "conversion not completed\n"); + return -ETIMEDOUT; + } + + ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_CONV0_LSB, &val, 2); + if (ret < 0) { + dev_err(adc->dev, "SW_CONV0_LSB read failed: %d\n", ret); + return ret; + } + + ret = val & 0xFFF; + + return ret; +} + +static int palmas_gpadc_get_calibrated_code(struct palmas_gpadc *adc, + int adc_chan, int val) +{ + if (!adc->adc_info[adc_chan].is_uncalibrated) + val = (val*1000 - adc->adc_info[adc_chan].offset) / + adc->adc_info[adc_chan].gain_error; + + if (val < 0) { + dev_err(adc->dev, "Mismatch with calibration\n"); + return 0; + } + + val = (val * adc->adc_info[adc_chan].gain) / 1000; + + return val; +} + +static int palmas_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long mask) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret = 0; + + if (adc_chan > PALMAS_ADC_CH_MAX) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + ret = palmas_gpadc_read_prepare(adc, adc_chan); + if (ret < 0) + goto out; + + ret = palmas_gpadc_start_conversion(adc, adc_chan); + if (ret < 0) { + dev_err(adc->dev, + "ADC start conversion failed\n"); + goto out; + } + + if (mask == IIO_CHAN_INFO_PROCESSED) + ret = palmas_gpadc_get_calibrated_code( + adc, adc_chan, ret); + + *val = ret; + + ret = IIO_VAL_INT; + goto out; + } + + mutex_unlock(&indio_dev->mlock); + return ret; + +out: + palmas_gpadc_read_done(adc, adc_chan); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static const struct iio_info palmas_gpadc_iio_info = { + .read_raw = palmas_gpadc_read_raw, +}; + +#define PALMAS_ADC_CHAN_IIO(chan, _type, chan_info) \ +{ \ + .datasheet_name = PALMAS_DATASHEET_NAME(chan), \ + .type = _type, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(chan_info), \ + .indexed = 1, \ + .channel = PALMAS_ADC_CH_##chan, \ +} + +static const struct iio_chan_spec palmas_gpadc_iio_channel[] = { + PALMAS_ADC_CHAN_IIO(IN0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN1, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN3, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN4, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN11, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN12, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN13, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN15, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), +}; + +static int palmas_gpadc_get_adc_dt_data(struct platform_device *pdev, + struct palmas_gpadc_platform_data **gpadc_pdata) +{ + struct device_node *np = pdev->dev.of_node; + struct palmas_gpadc_platform_data *gp_data; + int ret; + u32 pval; + + gp_data = devm_kzalloc(&pdev->dev, sizeof(*gp_data), GFP_KERNEL); + if (!gp_data) + return -ENOMEM; + + ret = of_property_read_u32(np, "ti,channel0-current-microamp", &pval); + if (!ret) + gp_data->ch0_current = pval; + + ret = of_property_read_u32(np, "ti,channel3-current-microamp", &pval); + if (!ret) + gp_data->ch3_current = pval; + + gp_data->extended_delay = of_property_read_bool(np, + "ti,enable-extended-delay"); + + *gpadc_pdata = gp_data; + + return 0; +} + +static int palmas_gpadc_probe(struct platform_device *pdev) +{ + struct palmas_gpadc *adc; + struct palmas_platform_data *pdata; + struct palmas_gpadc_platform_data *gpadc_pdata = NULL; + struct iio_dev *indio_dev; + int ret, i; + + pdata = dev_get_platdata(pdev->dev.parent); + + if (pdata && pdata->gpadc_pdata) + gpadc_pdata = pdata->gpadc_pdata; + + if (!gpadc_pdata && pdev->dev.of_node) { + ret = palmas_gpadc_get_adc_dt_data(pdev, &gpadc_pdata); + if (ret < 0) + return ret; + } + if (!gpadc_pdata) + return -EINVAL; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!indio_dev) { + dev_err(&pdev->dev, "iio_device_alloc failed\n"); + return -ENOMEM; + } + + adc = iio_priv(indio_dev); + adc->dev = &pdev->dev; + adc->palmas = dev_get_drvdata(pdev->dev.parent); + adc->adc_info = palmas_gpadc_info; + init_completion(&adc->conv_completion); + dev_set_drvdata(&pdev->dev, indio_dev); + + adc->auto_conversion_period = gpadc_pdata->auto_conversion_period_ms; + adc->irq = palmas_irq_get_virq(adc->palmas, PALMAS_GPADC_EOC_SW_IRQ); + if (adc->irq < 0) { + dev_err(adc->dev, + "get virq failed: %d\n", adc->irq); + ret = adc->irq; + goto out; + } + ret = request_threaded_irq(adc->irq, NULL, + palmas_gpadc_irq, + IRQF_ONESHOT, dev_name(adc->dev), + adc); + if (ret < 0) { + dev_err(adc->dev, + "request irq %d failed: %d\n", adc->irq, ret); + goto out; + } + + if (gpadc_pdata->adc_wakeup1_data) { + memcpy(&adc->wakeup1_data, gpadc_pdata->adc_wakeup1_data, + sizeof(adc->wakeup1_data)); + adc->wakeup1_enable = true; + adc->irq_auto_0 = platform_get_irq(pdev, 1); + ret = request_threaded_irq(adc->irq_auto_0, NULL, + palmas_gpadc_irq_auto, + IRQF_ONESHOT, + "palmas-adc-auto-0", adc); + if (ret < 0) { + dev_err(adc->dev, "request auto0 irq %d failed: %d\n", + adc->irq_auto_0, ret); + goto out_irq_free; + } + } + + if (gpadc_pdata->adc_wakeup2_data) { + memcpy(&adc->wakeup2_data, gpadc_pdata->adc_wakeup2_data, + sizeof(adc->wakeup2_data)); + adc->wakeup2_enable = true; + adc->irq_auto_1 = platform_get_irq(pdev, 2); + ret = request_threaded_irq(adc->irq_auto_1, NULL, + palmas_gpadc_irq_auto, + IRQF_ONESHOT, + "palmas-adc-auto-1", adc); + if (ret < 0) { + dev_err(adc->dev, "request auto1 irq %d failed: %d\n", + adc->irq_auto_1, ret); + goto out_irq_auto0_free; + } + } + + /* set the current source 0 (value 0/5/15/20 uA => 0..3) */ + if (gpadc_pdata->ch0_current <= 1) + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_0; + else if (gpadc_pdata->ch0_current <= 5) + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_5; + else if (gpadc_pdata->ch0_current <= 15) + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_15; + else + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_20; + + /* set the current source 3 (value 0/10/400/800 uA => 0..3) */ + if (gpadc_pdata->ch3_current <= 1) + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_0; + else if (gpadc_pdata->ch3_current <= 10) + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_10; + else if (gpadc_pdata->ch3_current <= 400) + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_400; + else + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_800; + + adc->extended_delay = gpadc_pdata->extended_delay; + + indio_dev->name = MOD_NAME; + indio_dev->info = &palmas_gpadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = palmas_gpadc_iio_channel; + indio_dev->num_channels = ARRAY_SIZE(palmas_gpadc_iio_channel); + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(adc->dev, "iio_device_register() failed: %d\n", ret); + goto out_irq_auto1_free; + } + + device_set_wakeup_capable(&pdev->dev, 1); + for (i = 0; i < PALMAS_ADC_CH_MAX; i++) { + if (!(adc->adc_info[i].is_uncalibrated)) + palmas_gpadc_calibrate(adc, i); + } + + if (adc->wakeup1_enable || adc->wakeup2_enable) + device_wakeup_enable(&pdev->dev); + + return 0; + +out_irq_auto1_free: + if (gpadc_pdata->adc_wakeup2_data) + free_irq(adc->irq_auto_1, adc); +out_irq_auto0_free: + if (gpadc_pdata->adc_wakeup1_data) + free_irq(adc->irq_auto_0, adc); +out_irq_free: + free_irq(adc->irq, adc); +out: + return ret; +} + +static int palmas_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(&pdev->dev); + struct palmas_gpadc *adc = iio_priv(indio_dev); + + if (adc->wakeup1_enable || adc->wakeup2_enable) + device_wakeup_disable(&pdev->dev); + iio_device_unregister(indio_dev); + free_irq(adc->irq, adc); + if (adc->wakeup1_enable) + free_irq(adc->irq_auto_0, adc); + if (adc->wakeup2_enable) + free_irq(adc->irq_auto_1, adc); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) +{ + int adc_period, conv; + int i; + int ch0 = 0, ch1 = 0; + int thres; + int ret; + + adc_period = adc->auto_conversion_period; + for (i = 0; i < 16; ++i) { + if (((1000 * (1 << i)) / 32) >= adc_period) + break; + } + if (i > 0) + i--; + adc_period = i; + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_CTRL, + PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK, + adc_period); + if (ret < 0) { + dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret); + return ret; + } + + conv = 0; + if (adc->wakeup1_enable) { + int polarity; + + ch0 = adc->wakeup1_data.adc_channel_number; + conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN; + if (adc->wakeup1_data.adc_high_threshold > 0) { + thres = adc->wakeup1_data.adc_high_threshold; + polarity = 0; + } else { + thres = adc->wakeup1_data.adc_low_threshold; + polarity = PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_POL; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV0_LSB, thres & 0xFF); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV0_LSB write failed: %d\n", ret); + return ret; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV0_MSB, + ((thres >> 8) & 0xF) | polarity); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV0_MSB write failed: %d\n", ret); + return ret; + } + } + + if (adc->wakeup2_enable) { + int polarity; + + ch1 = adc->wakeup2_data.adc_channel_number; + conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN; + if (adc->wakeup2_data.adc_high_threshold > 0) { + thres = adc->wakeup2_data.adc_high_threshold; + polarity = 0; + } else { + thres = adc->wakeup2_data.adc_low_threshold; + polarity = PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_POL; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV1_LSB, thres & 0xFF); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV1_LSB write failed: %d\n", ret); + return ret; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV1_MSB, + ((thres >> 8) & 0xF) | polarity); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV1_MSB write failed: %d\n", ret); + return ret; + } + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_SELECT, (ch1 << 4) | ch0); + if (ret < 0) { + dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret); + return ret; + } + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_CTRL, + PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN | + PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN, conv); + if (ret < 0) + dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret); + + return ret; +} + +static int palmas_adc_wakeup_reset(struct palmas_gpadc *adc) +{ + int ret; + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_SELECT, 0); + if (ret < 0) { + dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret); + return ret; + } + + ret = palmas_disable_auto_conversion(adc); + if (ret < 0) + dev_err(adc->dev, "Disable auto conversion failed: %d\n", ret); + + return ret; +} + +static int palmas_gpadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct palmas_gpadc *adc = iio_priv(indio_dev); + int wakeup = adc->wakeup1_enable || adc->wakeup2_enable; + int ret; + + if (!device_may_wakeup(dev) || !wakeup) + return 0; + + ret = palmas_adc_wakeup_configure(adc); + if (ret < 0) + return ret; + + if (adc->wakeup1_enable) + enable_irq_wake(adc->irq_auto_0); + + if (adc->wakeup2_enable) + enable_irq_wake(adc->irq_auto_1); + + return 0; +} + +static int palmas_gpadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct palmas_gpadc *adc = iio_priv(indio_dev); + int wakeup = adc->wakeup1_enable || adc->wakeup2_enable; + int ret; + + if (!device_may_wakeup(dev) || !wakeup) + return 0; + + ret = palmas_adc_wakeup_reset(adc); + if (ret < 0) + return ret; + + if (adc->wakeup1_enable) + disable_irq_wake(adc->irq_auto_0); + + if (adc->wakeup2_enable) + disable_irq_wake(adc->irq_auto_1); + + return 0; +}; +#endif + +static const struct dev_pm_ops palmas_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(palmas_gpadc_suspend, + palmas_gpadc_resume) +}; + +static const struct of_device_id of_palmas_gpadc_match_tbl[] = { + { .compatible = "ti,palmas-gpadc", }, + { /* end */ } +}; +MODULE_DEVICE_TABLE(of, of_palmas_gpadc_match_tbl); + +static struct platform_driver palmas_gpadc_driver = { + .probe = palmas_gpadc_probe, + .remove = palmas_gpadc_remove, + .driver = { + .name = MOD_NAME, + .pm = &palmas_pm_ops, + .of_match_table = of_palmas_gpadc_match_tbl, + }, +}; +module_platform_driver(palmas_gpadc_driver); + +MODULE_DESCRIPTION("palmas GPADC driver"); +MODULE_AUTHOR("Pradeep Goudagunta<pgoudagunta@nvidia.com>"); +MODULE_ALIAS("platform:palmas-gpadc"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c new file mode 100644 index 000000000..7e108da7d --- /dev/null +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -0,0 +1,1031 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Qualcomm PM8xxx PMIC XOADC driver + * + * These ADCs are known as HK/XO (house keeping / chrystal oscillator) + * "XO" in "XOADC" means Chrystal Oscillator. It's a bunch of + * specific-purpose and general purpose ADC converters and channels. + * + * Copyright (C) 2017 Linaro Ltd. + * Author: Linus Walleij <linus.walleij@linaro.org> + */ + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/regulator/consumer.h> + +#include "qcom-vadc-common.h" + +/* + * Definitions for the "user processor" registers lifted from the v3.4 + * Qualcomm tree. Their kernel has two out-of-tree drivers for the ADC: + * drivers/misc/pmic8058-xoadc.c + * drivers/hwmon/pm8xxx-adc.c + * None of them contain any complete register specification, so this is + * a best effort of combining the information. + */ + +/* These appear to be "battery monitor" registers */ +#define ADC_ARB_BTM_CNTRL1 0x17e +#define ADC_ARB_BTM_CNTRL1_EN_BTM BIT(0) +#define ADC_ARB_BTM_CNTRL1_SEL_OP_MODE BIT(1) +#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL1 BIT(2) +#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL2 BIT(3) +#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL3 BIT(4) +#define ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL4 BIT(5) +#define ADC_ARB_BTM_CNTRL1_EOC BIT(6) +#define ADC_ARB_BTM_CNTRL1_REQ BIT(7) + +#define ADC_ARB_BTM_AMUX_CNTRL 0x17f +#define ADC_ARB_BTM_ANA_PARAM 0x180 +#define ADC_ARB_BTM_DIG_PARAM 0x181 +#define ADC_ARB_BTM_RSV 0x182 +#define ADC_ARB_BTM_DATA1 0x183 +#define ADC_ARB_BTM_DATA0 0x184 +#define ADC_ARB_BTM_BAT_COOL_THR1 0x185 +#define ADC_ARB_BTM_BAT_COOL_THR0 0x186 +#define ADC_ARB_BTM_BAT_WARM_THR1 0x187 +#define ADC_ARB_BTM_BAT_WARM_THR0 0x188 +#define ADC_ARB_BTM_CNTRL2 0x18c + +/* Proper ADC registers */ + +#define ADC_ARB_USRP_CNTRL 0x197 +#define ADC_ARB_USRP_CNTRL_EN_ARB BIT(0) +#define ADC_ARB_USRP_CNTRL_RSV1 BIT(1) +#define ADC_ARB_USRP_CNTRL_RSV2 BIT(2) +#define ADC_ARB_USRP_CNTRL_RSV3 BIT(3) +#define ADC_ARB_USRP_CNTRL_RSV4 BIT(4) +#define ADC_ARB_USRP_CNTRL_RSV5 BIT(5) +#define ADC_ARB_USRP_CNTRL_EOC BIT(6) +#define ADC_ARB_USRP_CNTRL_REQ BIT(7) + +#define ADC_ARB_USRP_AMUX_CNTRL 0x198 +/* + * The channel mask includes the bits selecting channel mux and prescaler + * on PM8058, or channel mux and premux on PM8921. + */ +#define ADC_ARB_USRP_AMUX_CNTRL_CHAN_MASK 0xfc +#define ADC_ARB_USRP_AMUX_CNTRL_RSV0 BIT(0) +#define ADC_ARB_USRP_AMUX_CNTRL_RSV1 BIT(1) +/* On PM8058 this is prescaling, on PM8921 this is premux */ +#define ADC_ARB_USRP_AMUX_CNTRL_PRESCALEMUX0 BIT(2) +#define ADC_ARB_USRP_AMUX_CNTRL_PRESCALEMUX1 BIT(3) +#define ADC_ARB_USRP_AMUX_CNTRL_SEL0 BIT(4) +#define ADC_ARB_USRP_AMUX_CNTRL_SEL1 BIT(5) +#define ADC_ARB_USRP_AMUX_CNTRL_SEL2 BIT(6) +#define ADC_ARB_USRP_AMUX_CNTRL_SEL3 BIT(7) +#define ADC_AMUX_PREMUX_SHIFT 2 +#define ADC_AMUX_SEL_SHIFT 4 + +/* We know very little about the bits in this register */ +#define ADC_ARB_USRP_ANA_PARAM 0x199 +#define ADC_ARB_USRP_ANA_PARAM_DIS 0xFE +#define ADC_ARB_USRP_ANA_PARAM_EN 0xFF + +#define ADC_ARB_USRP_DIG_PARAM 0x19A +#define ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0 BIT(0) +#define ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1 BIT(1) +#define ADC_ARB_USRP_DIG_PARAM_CLK_RATE0 BIT(2) +#define ADC_ARB_USRP_DIG_PARAM_CLK_RATE1 BIT(3) +#define ADC_ARB_USRP_DIG_PARAM_EOC BIT(4) +/* + * On a later ADC the decimation factors are defined as + * 00 = 512, 01 = 1024, 10 = 2048, 11 = 4096 so assume this + * holds also for this older XOADC. + */ +#define ADC_ARB_USRP_DIG_PARAM_DEC_RATE0 BIT(5) +#define ADC_ARB_USRP_DIG_PARAM_DEC_RATE1 BIT(6) +#define ADC_ARB_USRP_DIG_PARAM_EN BIT(7) +#define ADC_DIG_PARAM_DEC_SHIFT 5 + +#define ADC_ARB_USRP_RSV 0x19B +#define ADC_ARB_USRP_RSV_RST BIT(0) +#define ADC_ARB_USRP_RSV_DTEST0 BIT(1) +#define ADC_ARB_USRP_RSV_DTEST1 BIT(2) +#define ADC_ARB_USRP_RSV_OP BIT(3) +#define ADC_ARB_USRP_RSV_IP_SEL0 BIT(4) +#define ADC_ARB_USRP_RSV_IP_SEL1 BIT(5) +#define ADC_ARB_USRP_RSV_IP_SEL2 BIT(6) +#define ADC_ARB_USRP_RSV_TRM BIT(7) +#define ADC_RSV_IP_SEL_SHIFT 4 + +#define ADC_ARB_USRP_DATA0 0x19D +#define ADC_ARB_USRP_DATA1 0x19C + +/* + * Physical channels which MUST exist on all PM variants in order to provide + * proper reference points for calibration. + * + * @PM8XXX_CHANNEL_INTERNAL: 625mV reference channel + * @PM8XXX_CHANNEL_125V: 1250mV reference channel + * @PM8XXX_CHANNEL_INTERNAL_2: 325mV reference channel + * @PM8XXX_CHANNEL_MUXOFF: channel to reduce input load on mux, apparently also + * measures XO temperature + */ +#define PM8XXX_CHANNEL_INTERNAL 0x0c +#define PM8XXX_CHANNEL_125V 0x0d +#define PM8XXX_CHANNEL_INTERNAL_2 0x0e +#define PM8XXX_CHANNEL_MUXOFF 0x0f + +/* + * PM8058 AMUX premux scaling, two bits. This is done of the channel before + * reaching the AMUX. + */ +#define PM8058_AMUX_PRESCALE_0 0x0 /* No scaling on the signal */ +#define PM8058_AMUX_PRESCALE_1 0x1 /* Unity scaling selected by the user */ +#define PM8058_AMUX_PRESCALE_1_DIV3 0x2 /* 1/3 prescaler on the input */ + +/* Defines reference voltage for the XOADC */ +#define AMUX_RSV0 0x0 /* XO_IN/XOADC_GND, special selection to read XO temp */ +#define AMUX_RSV1 0x1 /* PMIC_IN/XOADC_GND */ +#define AMUX_RSV2 0x2 /* PMIC_IN/BMS_CSP */ +#define AMUX_RSV3 0x3 /* not used */ +#define AMUX_RSV4 0x4 /* XOADC_GND/XOADC_GND */ +#define AMUX_RSV5 0x5 /* XOADC_VREF/XOADC_GND */ +#define XOADC_RSV_MAX 5 /* 3 bits 0..7, 3 and 6,7 are invalid */ + +/** + * struct xoadc_channel - encodes channel properties and defaults + * @datasheet_name: the hardwarename of this channel + * @pre_scale_mux: prescale (PM8058) or premux (PM8921) for selecting + * this channel. Both this and the amux channel is needed to uniquely + * identify a channel. Values 0..3. + * @amux_channel: value of the ADC_ARB_USRP_AMUX_CNTRL register for this + * channel, bits 4..7, selects the amux, values 0..f + * @prescale: the channels have hard-coded prescale ratios defined + * by the hardware, this tells us what it is + * @type: corresponding IIO channel type, usually IIO_VOLTAGE or + * IIO_TEMP + * @scale_fn_type: the liner interpolation etc to convert the + * ADC code to the value that IIO expects, in uV or millicelsius + * etc. This scale function can be pretty elaborate if different + * thermistors are connected or other hardware characteristics are + * deployed. + * @amux_ip_rsv: ratiometric scale value used by the analog muxer: this + * selects the reference voltage for ratiometric scaling + */ +struct xoadc_channel { + const char *datasheet_name; + u8 pre_scale_mux:2; + u8 amux_channel:4; + const struct vadc_prescale_ratio prescale; + enum iio_chan_type type; + enum vadc_scale_fn_type scale_fn_type; + u8 amux_ip_rsv:3; +}; + +/** + * struct xoadc_variant - encodes the XOADC variant characteristics + * @name: name of this PMIC variant + * @channels: the hardware channels and respective settings and defaults + * @broken_ratiometric: if the PMIC has broken ratiometric scaling (this + * is a known problem on PM8058) + * @prescaling: this variant uses AMUX bits 2 & 3 for prescaling (PM8058) + * @second_level_mux: this variant uses AMUX bits 2 & 3 for a second level + * mux + */ +struct xoadc_variant { + const char name[16]; + const struct xoadc_channel *channels; + bool broken_ratiometric; + bool prescaling; + bool second_level_mux; +}; + +/* + * XOADC_CHAN macro parameters: + * _dname: the name of the channel + * _presmux: prescaler (PM8058) or premux (PM8921) setting for this channel + * _amux: the value in bits 2..7 of the ADC_ARB_USRP_AMUX_CNTRL register + * for this channel. On some PMICs some of the bits select a prescaler, and + * on some PMICs some of the bits select various complex multiplex settings. + * _type: IIO channel type + * _prenum: prescaler numerator (dividend) + * _preden: prescaler denominator (divisor) + * _scale: scaling function type, this selects how the raw valued is mangled + * to output the actual processed measurement + * _amip: analog mux input parent when using ratiometric measurements + */ +#define XOADC_CHAN(_dname, _presmux, _amux, _type, _prenum, _preden, _scale, _amip) \ + { \ + .datasheet_name = __stringify(_dname), \ + .pre_scale_mux = _presmux, \ + .amux_channel = _amux, \ + .prescale = { .num = _prenum, .den = _preden }, \ + .type = _type, \ + .scale_fn_type = _scale, \ + .amux_ip_rsv = _amip, \ + } + +/* + * Taken from arch/arm/mach-msm/board-9615.c in the vendor tree: + * TODO: incomplete, needs testing. + */ +static const struct xoadc_channel pm8018_xoadc_channels[] = { + XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VPH_PWR, 0x00, 0x02, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1), + /* Used for battery ID or battery temperature */ + XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV2), + XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0), + { }, /* Sentinel */ +}; + +/* + * Taken from arch/arm/mach-msm/board-8930-pmic.c in the vendor tree: + * TODO: needs testing. + */ +static const struct xoadc_channel pm8038_xoadc_channels[] = { + XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ICHG, 0x00, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX5, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX6, 0x00, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX7, 0x00, 0x07, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* AMUX8 used for battery temperature in most cases */ + XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_TEMP, 1, 1, SCALE_THERM_100K_PULLUP, AMUX_RSV2), + XOADC_CHAN(AMUX9, 0x00, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 4, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1), + XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(INTERNAL_2, 0x00, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0), + { }, /* Sentinel */ +}; + +/* + * This was created by cross-referencing the vendor tree + * arch/arm/mach-msm/board-msm8x60.c msm_adc_channels_data[] + * with the "channel types" (first field) to find the right + * configuration for these channels on an MSM8x60 i.e. PM8058 + * setup. + */ +static const struct xoadc_channel pm8058_xoadc_channels[] = { + XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 10, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ICHG, 0x00, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + /* + * AMUX channels 5 thru 9 are referred to as MPP5 thru MPP9 in + * some code and documentation. But they are really just 5 + * channels just like any other. They are connected to a switching + * matrix where they can be routed to any of the MPPs, not just + * 1-to-1 onto MPP5 thru 9, so naming them MPP5 thru MPP9 is + * very confusing. + */ + XOADC_CHAN(AMUX5, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX6, 0x00, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX7, 0x00, 0x07, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX8, 0x00, 0x08, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX9, 0x00, 0x09, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1), + XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(INTERNAL_2, 0x00, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0), + /* There are also "unity" and divided by 3 channels (prescaler) but noone is using them */ + { }, /* Sentinel */ +}; + +/* + * The PM8921 has some pre-muxing on its channels, this comes from the vendor tree + * include/linux/mfd/pm8xxx/pm8xxx-adc.h + * board-flo-pmic.c (Nexus 7) and board-8064-pmic.c + */ +static const struct xoadc_channel pm8921_xoadc_channels[] = { + XOADC_CHAN(VCOIN, 0x00, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(VBAT, 0x00, 0x01, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DCIN, 0x00, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1), + /* channel "ICHG" is reserved and not used on PM8921 */ + XOADC_CHAN(VPH_PWR, 0x00, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(IBAT, 0x00, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* CHAN 6 & 7 (MPP1 & MPP2) are reserved for MPP channels on PM8921 */ + XOADC_CHAN(BATT_THERM, 0x00, 0x08, IIO_TEMP, 1, 1, SCALE_THERM_100K_PULLUP, AMUX_RSV1), + XOADC_CHAN(BATT_ID, 0x00, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(USB_VBUS, 0x00, 0x0a, IIO_VOLTAGE, 1, 4, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DIE_TEMP, 0x00, 0x0b, IIO_TEMP, 1, 1, SCALE_PMIC_THERM, AMUX_RSV1), + XOADC_CHAN(INTERNAL, 0x00, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(125V, 0x00, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* FIXME: look into the scaling of this temperature */ + XOADC_CHAN(CHG_TEMP, 0x00, 0x0e, IIO_TEMP, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(MUXOFF, 0x00, 0x0f, IIO_TEMP, 1, 1, SCALE_XOTHERM, AMUX_RSV0), + /* The following channels have premux bit 0 set to 1 (all end in 4) */ + XOADC_CHAN(ATEST_8, 0x01, 0x00, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* Set scaling to 1/2 based on the name for these two */ + XOADC_CHAN(USB_SNS_DIV20, 0x01, 0x01, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DCIN_SNS_DIV20, 0x01, 0x02, IIO_VOLTAGE, 1, 2, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX3, 0x01, 0x03, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX4, 0x01, 0x04, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX5, 0x01, 0x05, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX6, 0x01, 0x06, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX7, 0x01, 0x07, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX8, 0x01, 0x08, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* Internal test signals, I think */ + XOADC_CHAN(ATEST_1, 0x01, 0x09, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_2, 0x01, 0x0a, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_3, 0x01, 0x0b, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_4, 0x01, 0x0c, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_5, 0x01, 0x0d, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_6, 0x01, 0x0e, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_7, 0x01, 0x0f, IIO_VOLTAGE, 1, 1, SCALE_DEFAULT, AMUX_RSV1), + /* The following channels have premux bit 1 set to 1 (all end in 8) */ + /* I guess even ATEST8 will be divided by 3 here */ + XOADC_CHAN(ATEST_8, 0x02, 0x00, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + /* I guess div 2 div 3 becomes div 6 */ + XOADC_CHAN(USB_SNS_DIV20_DIV3, 0x02, 0x01, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(DCIN_SNS_DIV20_DIV3, 0x02, 0x02, IIO_VOLTAGE, 1, 6, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX3_DIV3, 0x02, 0x03, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX4_DIV3, 0x02, 0x04, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX5_DIV3, 0x02, 0x05, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX6_DIV3, 0x02, 0x06, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX7_DIV3, 0x02, 0x07, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(AMUX8_DIV3, 0x02, 0x08, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_1_DIV3, 0x02, 0x09, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_2_DIV3, 0x02, 0x0a, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_3_DIV3, 0x02, 0x0b, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_4_DIV3, 0x02, 0x0c, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_5_DIV3, 0x02, 0x0d, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_6_DIV3, 0x02, 0x0e, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + XOADC_CHAN(ATEST_7_DIV3, 0x02, 0x0f, IIO_VOLTAGE, 1, 3, SCALE_DEFAULT, AMUX_RSV1), + { }, /* Sentinel */ +}; + +/** + * struct pm8xxx_chan_info - ADC channel information + * @name: name of this channel + * @hwchan: pointer to hardware channel information (muxing & scaling settings) + * @calibration: whether to use absolute or ratiometric calibration + * @scale_fn_type: scaling function type + * @decimation: 0,1,2,3 + * @amux_ip_rsv: ratiometric scale value if using ratiometric + * calibration: 0, 1, 2, 4, 5. + */ +struct pm8xxx_chan_info { + const char *name; + const struct xoadc_channel *hwchan; + enum vadc_calibration calibration; + u8 decimation:2; + u8 amux_ip_rsv:3; +}; + +/** + * struct pm8xxx_xoadc - state container for the XOADC + * @dev: pointer to device + * @map: regmap to access registers + * @variant: XOADC variant characteristics + * @vref: reference voltage regulator + * characteristics of the channels, and sensible default settings + * @nchans: number of channels, configured by the device tree + * @chans: the channel information per-channel, configured by the device tree + * @iio_chans: IIO channel specifiers + * @graph: linear calibration parameters for absolute and + * ratiometric measurements + * @complete: completion to indicate end of conversion + * @lock: lock to restrict access to the hardware to one client at the time + */ +struct pm8xxx_xoadc { + struct device *dev; + struct regmap *map; + const struct xoadc_variant *variant; + struct regulator *vref; + unsigned int nchans; + struct pm8xxx_chan_info *chans; + struct iio_chan_spec *iio_chans; + struct vadc_linear_graph graph[2]; + struct completion complete; + struct mutex lock; +}; + +static irqreturn_t pm8xxx_eoc_irq(int irq, void *d) +{ + struct iio_dev *indio_dev = d; + struct pm8xxx_xoadc *adc = iio_priv(indio_dev); + + complete(&adc->complete); + + return IRQ_HANDLED; +} + +static struct pm8xxx_chan_info * +pm8xxx_get_channel(struct pm8xxx_xoadc *adc, u8 chan) +{ + int i; + + for (i = 0; i < adc->nchans; i++) { + struct pm8xxx_chan_info *ch = &adc->chans[i]; + if (ch->hwchan->amux_channel == chan) + return ch; + } + return NULL; +} + +static int pm8xxx_read_channel_rsv(struct pm8xxx_xoadc *adc, + const struct pm8xxx_chan_info *ch, + u8 rsv, u16 *adc_code, + bool force_ratiometric) +{ + int ret; + unsigned int val; + u8 rsvmask, rsvval; + u8 lsb, msb; + + dev_dbg(adc->dev, "read channel \"%s\", amux %d, prescale/mux: %d, rsv %d\n", + ch->name, ch->hwchan->amux_channel, ch->hwchan->pre_scale_mux, rsv); + + mutex_lock(&adc->lock); + + /* Mux in this channel */ + val = ch->hwchan->amux_channel << ADC_AMUX_SEL_SHIFT; + val |= ch->hwchan->pre_scale_mux << ADC_AMUX_PREMUX_SHIFT; + ret = regmap_write(adc->map, ADC_ARB_USRP_AMUX_CNTRL, val); + if (ret) + goto unlock; + + /* Set up ratiometric scale value, mask off all bits except these */ + rsvmask = (ADC_ARB_USRP_RSV_RST | ADC_ARB_USRP_RSV_DTEST0 | + ADC_ARB_USRP_RSV_DTEST1 | ADC_ARB_USRP_RSV_OP); + if (adc->variant->broken_ratiometric && !force_ratiometric) { + /* + * Apparently the PM8058 has some kind of bug which is + * reflected in the vendor tree drivers/misc/pmix8058-xoadc.c + * which just hardcodes the RSV selector to SEL1 (0x20) for + * most cases and SEL0 (0x10) for the MUXOFF channel only. + * If we force ratiometric (currently only done when attempting + * to do ratiometric calibration) this doesn't seem to work + * very well and I suspect ratiometric conversion is simply + * broken or not supported on the PM8058. + * + * Maybe IO_SEL2 doesn't exist on PM8058 and bits 4 & 5 select + * the mode alone. + * + * Some PM8058 register documentation would be nice to get + * this right. + */ + if (ch->hwchan->amux_channel == PM8XXX_CHANNEL_MUXOFF) + rsvval = ADC_ARB_USRP_RSV_IP_SEL0; + else + rsvval = ADC_ARB_USRP_RSV_IP_SEL1; + } else { + if (rsv == 0xff) + rsvval = (ch->amux_ip_rsv << ADC_RSV_IP_SEL_SHIFT) | + ADC_ARB_USRP_RSV_TRM; + else + rsvval = (rsv << ADC_RSV_IP_SEL_SHIFT) | + ADC_ARB_USRP_RSV_TRM; + } + + ret = regmap_update_bits(adc->map, + ADC_ARB_USRP_RSV, + ~rsvmask, + rsvval); + if (ret) + goto unlock; + + ret = regmap_write(adc->map, ADC_ARB_USRP_ANA_PARAM, + ADC_ARB_USRP_ANA_PARAM_DIS); + if (ret) + goto unlock; + + /* Decimation factor */ + ret = regmap_write(adc->map, ADC_ARB_USRP_DIG_PARAM, + ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0 | + ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1 | + ch->decimation << ADC_DIG_PARAM_DEC_SHIFT); + if (ret) + goto unlock; + + ret = regmap_write(adc->map, ADC_ARB_USRP_ANA_PARAM, + ADC_ARB_USRP_ANA_PARAM_EN); + if (ret) + goto unlock; + + /* Enable the arbiter, the Qualcomm code does it twice like this */ + ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, + ADC_ARB_USRP_CNTRL_EN_ARB); + if (ret) + goto unlock; + ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, + ADC_ARB_USRP_CNTRL_EN_ARB); + if (ret) + goto unlock; + + + /* Fire a request! */ + reinit_completion(&adc->complete); + ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, + ADC_ARB_USRP_CNTRL_EN_ARB | + ADC_ARB_USRP_CNTRL_REQ); + if (ret) + goto unlock; + + /* Next the interrupt occurs */ + ret = wait_for_completion_timeout(&adc->complete, + VADC_CONV_TIME_MAX_US); + if (!ret) { + dev_err(adc->dev, "conversion timed out\n"); + ret = -ETIMEDOUT; + goto unlock; + } + + ret = regmap_read(adc->map, ADC_ARB_USRP_DATA0, &val); + if (ret) + goto unlock; + lsb = val; + ret = regmap_read(adc->map, ADC_ARB_USRP_DATA1, &val); + if (ret) + goto unlock; + msb = val; + *adc_code = (msb << 8) | lsb; + + /* Turn off the ADC by setting the arbiter to 0 twice */ + ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, 0); + if (ret) + goto unlock; + ret = regmap_write(adc->map, ADC_ARB_USRP_CNTRL, 0); + if (ret) + goto unlock; + +unlock: + mutex_unlock(&adc->lock); + return ret; +} + +static int pm8xxx_read_channel(struct pm8xxx_xoadc *adc, + const struct pm8xxx_chan_info *ch, + u16 *adc_code) +{ + /* + * Normally we just use the ratiometric scale value (RSV) predefined + * for the channel, but during calibration we need to modify this + * so this wrapper is a helper hiding the more complex version. + */ + return pm8xxx_read_channel_rsv(adc, ch, 0xff, adc_code, false); +} + +static int pm8xxx_calibrate_device(struct pm8xxx_xoadc *adc) +{ + const struct pm8xxx_chan_info *ch; + u16 read_1250v; + u16 read_0625v; + u16 read_nomux_rsv5; + u16 read_nomux_rsv4; + int ret; + + adc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV; + adc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE; + + /* Common reference channel calibration */ + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_125V); + if (!ch) + return -ENODEV; + ret = pm8xxx_read_channel(adc, ch, &read_1250v); + if (ret) { + dev_err(adc->dev, "could not read 1.25V reference channel\n"); + return -ENODEV; + } + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_INTERNAL); + if (!ch) + return -ENODEV; + ret = pm8xxx_read_channel(adc, ch, &read_0625v); + if (ret) { + dev_err(adc->dev, "could not read 0.625V reference channel\n"); + return -ENODEV; + } + if (read_1250v == read_0625v) { + dev_err(adc->dev, "read same ADC code for 1.25V and 0.625V\n"); + return -ENODEV; + } + + adc->graph[VADC_CALIB_ABSOLUTE].dy = read_1250v - read_0625v; + adc->graph[VADC_CALIB_ABSOLUTE].gnd = read_0625v; + + dev_info(adc->dev, "absolute calibration dx = %d uV, dy = %d units\n", + VADC_ABSOLUTE_RANGE_UV, adc->graph[VADC_CALIB_ABSOLUTE].dy); + + /* Ratiometric calibration */ + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_MUXOFF); + if (!ch) + return -ENODEV; + ret = pm8xxx_read_channel_rsv(adc, ch, AMUX_RSV5, + &read_nomux_rsv5, true); + if (ret) { + dev_err(adc->dev, "could not read MUXOFF reference channel\n"); + return -ENODEV; + } + ret = pm8xxx_read_channel_rsv(adc, ch, AMUX_RSV4, + &read_nomux_rsv4, true); + if (ret) { + dev_err(adc->dev, "could not read MUXOFF reference channel\n"); + return -ENODEV; + } + adc->graph[VADC_CALIB_RATIOMETRIC].dy = + read_nomux_rsv5 - read_nomux_rsv4; + adc->graph[VADC_CALIB_RATIOMETRIC].gnd = read_nomux_rsv4; + + dev_info(adc->dev, "ratiometric calibration dx = %d, dy = %d units\n", + VADC_RATIOMETRIC_RANGE, + adc->graph[VADC_CALIB_RATIOMETRIC].dy); + + return 0; +} + +static int pm8xxx_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct pm8xxx_xoadc *adc = iio_priv(indio_dev); + const struct pm8xxx_chan_info *ch; + u16 adc_code; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ch = pm8xxx_get_channel(adc, chan->address); + if (!ch) { + dev_err(adc->dev, "no such channel %lu\n", + chan->address); + return -EINVAL; + } + ret = pm8xxx_read_channel(adc, ch, &adc_code); + if (ret) + return ret; + + ret = qcom_vadc_scale(ch->hwchan->scale_fn_type, + &adc->graph[ch->calibration], + &ch->hwchan->prescale, + (ch->calibration == VADC_CALIB_ABSOLUTE), + adc_code, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + ch = pm8xxx_get_channel(adc, chan->address); + if (!ch) { + dev_err(adc->dev, "no such channel %lu\n", + chan->address); + return -EINVAL; + } + ret = pm8xxx_read_channel(adc, ch, &adc_code); + if (ret) + return ret; + + *val = (int)adc_code; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int pm8xxx_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct pm8xxx_xoadc *adc = iio_priv(indio_dev); + u8 pre_scale_mux; + u8 amux_channel; + unsigned int i; + + /* + * First cell is prescaler or premux, second cell is analog + * mux. + */ + if (iiospec->args_count != 2) { + dev_err(&indio_dev->dev, "wrong number of arguments for %pOFn need 2 got %d\n", + iiospec->np, + iiospec->args_count); + return -EINVAL; + } + pre_scale_mux = (u8)iiospec->args[0]; + amux_channel = (u8)iiospec->args[1]; + dev_dbg(&indio_dev->dev, "pre scale/mux: %02x, amux: %02x\n", + pre_scale_mux, amux_channel); + + /* We need to match exactly on the prescale/premux and channel */ + for (i = 0; i < adc->nchans; i++) + if (adc->chans[i].hwchan->pre_scale_mux == pre_scale_mux && + adc->chans[i].hwchan->amux_channel == amux_channel) + return i; + + return -EINVAL; +} + +static const struct iio_info pm8xxx_xoadc_info = { + .of_xlate = pm8xxx_of_xlate, + .read_raw = pm8xxx_read_raw, +}; + +static int pm8xxx_xoadc_parse_channel(struct device *dev, + struct device_node *np, + const struct xoadc_channel *hw_channels, + struct iio_chan_spec *iio_chan, + struct pm8xxx_chan_info *ch) +{ + const char *name = np->name; + const struct xoadc_channel *hwchan; + u32 pre_scale_mux, amux_channel; + u32 rsv, dec; + int ret; + int chid; + + ret = of_property_read_u32_index(np, "reg", 0, &pre_scale_mux); + if (ret) { + dev_err(dev, "invalid pre scale/mux number %s\n", name); + return ret; + } + ret = of_property_read_u32_index(np, "reg", 1, &amux_channel); + if (ret) { + dev_err(dev, "invalid amux channel number %s\n", name); + return ret; + } + + /* Find the right channel setting */ + chid = 0; + hwchan = &hw_channels[0]; + while (hwchan && hwchan->datasheet_name) { + if (hwchan->pre_scale_mux == pre_scale_mux && + hwchan->amux_channel == amux_channel) + break; + hwchan++; + chid++; + } + /* The sentinel does not have a name assigned */ + if (!hwchan->datasheet_name) { + dev_err(dev, "could not locate channel %02x/%02x\n", + pre_scale_mux, amux_channel); + return -EINVAL; + } + ch->name = name; + ch->hwchan = hwchan; + /* Everyone seems to use absolute calibration except in special cases */ + ch->calibration = VADC_CALIB_ABSOLUTE; + /* Everyone seems to use default ("type 2") decimation */ + ch->decimation = VADC_DEF_DECIMATION; + + if (!of_property_read_u32(np, "qcom,ratiometric", &rsv)) { + ch->calibration = VADC_CALIB_RATIOMETRIC; + if (rsv > XOADC_RSV_MAX) { + dev_err(dev, "%s too large RSV value %d\n", name, rsv); + return -EINVAL; + } + if (rsv == AMUX_RSV3) { + dev_err(dev, "%s invalid RSV value %d\n", name, rsv); + return -EINVAL; + } + } + + /* Optional decimation, if omitted we use the default */ + ret = of_property_read_u32(np, "qcom,decimation", &dec); + if (!ret) { + ret = qcom_vadc_decimation_from_dt(dec); + if (ret < 0) { + dev_err(dev, "%s invalid decimation %d\n", + name, dec); + return ret; + } + ch->decimation = ret; + } + + iio_chan->channel = chid; + iio_chan->address = hwchan->amux_channel; + iio_chan->datasheet_name = hwchan->datasheet_name; + iio_chan->type = hwchan->type; + /* All channels are raw or processed */ + iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_PROCESSED); + iio_chan->indexed = 1; + + dev_dbg(dev, "channel [PRESCALE/MUX: %02x AMUX: %02x] \"%s\" " + "ref voltage: %d, decimation %d " + "prescale %d/%d, scale function %d\n", + hwchan->pre_scale_mux, hwchan->amux_channel, ch->name, + ch->amux_ip_rsv, ch->decimation, hwchan->prescale.num, + hwchan->prescale.den, hwchan->scale_fn_type); + + return 0; +} + +static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc, + struct device_node *np) +{ + struct device_node *child; + struct pm8xxx_chan_info *ch; + int ret; + int i; + + adc->nchans = of_get_available_child_count(np); + if (!adc->nchans) { + dev_err(adc->dev, "no channel children\n"); + return -ENODEV; + } + dev_dbg(adc->dev, "found %d ADC channels\n", adc->nchans); + + adc->iio_chans = devm_kcalloc(adc->dev, adc->nchans, + sizeof(*adc->iio_chans), GFP_KERNEL); + if (!adc->iio_chans) + return -ENOMEM; + + adc->chans = devm_kcalloc(adc->dev, adc->nchans, + sizeof(*adc->chans), GFP_KERNEL); + if (!adc->chans) + return -ENOMEM; + + i = 0; + for_each_available_child_of_node(np, child) { + ch = &adc->chans[i]; + ret = pm8xxx_xoadc_parse_channel(adc->dev, child, + adc->variant->channels, + &adc->iio_chans[i], + ch); + if (ret) { + of_node_put(child); + return ret; + } + i++; + } + + /* Check for required channels */ + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_125V); + if (!ch) { + dev_err(adc->dev, "missing 1.25V reference channel\n"); + return -ENODEV; + } + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_INTERNAL); + if (!ch) { + dev_err(adc->dev, "missing 0.625V reference channel\n"); + return -ENODEV; + } + ch = pm8xxx_get_channel(adc, PM8XXX_CHANNEL_MUXOFF); + if (!ch) { + dev_err(adc->dev, "missing MUXOFF reference channel\n"); + return -ENODEV; + } + + return 0; +} + +static int pm8xxx_xoadc_probe(struct platform_device *pdev) +{ + const struct xoadc_variant *variant; + struct pm8xxx_xoadc *adc; + struct iio_dev *indio_dev; + struct device_node *np = pdev->dev.of_node; + struct regmap *map; + struct device *dev = &pdev->dev; + int ret; + + variant = of_device_get_match_data(dev); + if (!variant) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + platform_set_drvdata(pdev, indio_dev); + + adc = iio_priv(indio_dev); + adc->dev = dev; + adc->variant = variant; + init_completion(&adc->complete); + mutex_init(&adc->lock); + + ret = pm8xxx_xoadc_parse_channels(adc, np); + if (ret) + return ret; + + map = dev_get_regmap(dev->parent, NULL); + if (!map) { + dev_err(dev, "parent regmap unavailable.\n"); + return -ENXIO; + } + adc->map = map; + + /* Bring up regulator */ + adc->vref = devm_regulator_get(dev, "xoadc-ref"); + if (IS_ERR(adc->vref)) { + dev_err(dev, "failed to get XOADC VREF regulator\n"); + return PTR_ERR(adc->vref); + } + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(dev, "failed to enable XOADC VREF regulator\n"); + return ret; + } + + ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), + pm8xxx_eoc_irq, NULL, 0, variant->name, indio_dev); + if (ret) { + dev_err(dev, "unable to request IRQ\n"); + goto out_disable_vref; + } + + indio_dev->name = variant->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &pm8xxx_xoadc_info; + indio_dev->channels = adc->iio_chans; + indio_dev->num_channels = adc->nchans; + + ret = iio_device_register(indio_dev); + if (ret) + goto out_disable_vref; + + ret = pm8xxx_calibrate_device(adc); + if (ret) + goto out_unreg_device; + + dev_info(dev, "%s XOADC driver enabled\n", variant->name); + + return 0; + +out_unreg_device: + iio_device_unregister(indio_dev); +out_disable_vref: + regulator_disable(adc->vref); + + return ret; +} + +static int pm8xxx_xoadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct pm8xxx_xoadc *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + regulator_disable(adc->vref); + + return 0; +} + +static const struct xoadc_variant pm8018_variant = { + .name = "PM8018-XOADC", + .channels = pm8018_xoadc_channels, +}; + +static const struct xoadc_variant pm8038_variant = { + .name = "PM8038-XOADC", + .channels = pm8038_xoadc_channels, +}; + +static const struct xoadc_variant pm8058_variant = { + .name = "PM8058-XOADC", + .channels = pm8058_xoadc_channels, + .broken_ratiometric = true, + .prescaling = true, +}; + +static const struct xoadc_variant pm8921_variant = { + .name = "PM8921-XOADC", + .channels = pm8921_xoadc_channels, + .second_level_mux = true, +}; + +static const struct of_device_id pm8xxx_xoadc_id_table[] = { + { + .compatible = "qcom,pm8018-adc", + .data = &pm8018_variant, + }, + { + .compatible = "qcom,pm8038-adc", + .data = &pm8038_variant, + }, + { + .compatible = "qcom,pm8058-adc", + .data = &pm8058_variant, + }, + { + .compatible = "qcom,pm8921-adc", + .data = &pm8921_variant, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, pm8xxx_xoadc_id_table); + +static struct platform_driver pm8xxx_xoadc_driver = { + .driver = { + .name = "pm8xxx-adc", + .of_match_table = pm8xxx_xoadc_id_table, + }, + .probe = pm8xxx_xoadc_probe, + .remove = pm8xxx_xoadc_remove, +}; +module_platform_driver(pm8xxx_xoadc_driver); + +MODULE_DESCRIPTION("PM8xxx XOADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:pm8xxx-xoadc"); diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c new file mode 100644 index 000000000..c10aa28be --- /dev/null +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -0,0 +1,994 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, 2020, 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_CONV_FAULT BIT(7) +#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 + +#define ADC_CHANNEL_OFFSET 0x8 +#define ADC_CHANNEL_MASK GENMASK(7, 0) + +/* + * 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 + +/* For PMIC7 */ +#define ADC_APP_SID 0x40 +#define ADC_APP_SID_MASK GENMASK(3, 0) +#define ADC7_CONV_TIMEOUT msecs_to_jiffies(10) + +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. + * @sid: slave id of PMIC owning the channel, for PMIC7. + * @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 sid; + 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_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val) +{ + return regmap_update_bits(adc->regmap, adc->base + offset, mask, val); +} + +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) { + dev_err(adc->dev, "Invalid data:0x%x\n", *data); + return -EINVAL; + } + + dev_dbg(adc->dev, "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) + 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 adc7_configure(struct adc5_chip *adc, + struct adc5_channel_prop *prop) +{ + int ret; + u8 conv_req = 0, buf[4]; + + ret = adc5_masked_write(adc, ADC_APP_SID, ADC_APP_SID_MASK, prop->sid); + if (ret) + return ret; + + ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf)); + if (ret) + return ret; + + /* Digital param selection */ + adc5_update_dig_param(adc, prop, &buf[0]); + + /* Update fast average sample value */ + buf[1] &= ~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] &= ~ADC5_USR_HW_SETTLE_DELAY_MASK; + buf[3] |= prop->hw_settle_time; + + /* Select CONV request */ + conv_req = ADC5_USR_CONV_REQ_REQ; + + if (!adc->poll_eoc) + reinit_completion(&adc->complete); + + ret = adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf)); + if (ret) + return ret; + + return adc5_write(adc, ADC5_USR_CONV_REQ, &conv_req, 1); +} + +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) { + dev_err(adc->dev, "ADC configure failed with %d\n", ret); + goto unlock; + } + + if (adc->poll_eoc) { + ret = adc5_poll_wait_eoc(adc); + if (ret) { + dev_err(adc->dev, "EOC bit not set\n"); + goto unlock; + } + } else { + ret = wait_for_completion_timeout(&adc->complete, + ADC5_CONV_TIMEOUT); + if (!ret) { + dev_dbg(adc->dev, "Did not get completion timeout.\n"); + ret = adc5_poll_wait_eoc(adc); + if (ret) { + dev_err(adc->dev, "EOC bit not set\n"); + goto unlock; + } + } + } + + ret = adc5_read_voltage_data(adc, data_volt); +unlock: + mutex_unlock(&adc->lock); + + return ret; +} + +static int adc7_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; + u8 status; + + mutex_lock(&adc->lock); + + ret = adc7_configure(adc, prop); + if (ret) { + dev_err(adc->dev, "ADC configure failed with %d\n", ret); + goto unlock; + } + + /* No support for polling mode at present */ + wait_for_completion_timeout(&adc->complete, ADC7_CONV_TIMEOUT); + + ret = adc5_read(adc, ADC5_USR_STATUS1, &status, 1); + if (ret) + goto unlock; + + if (status & ADC5_USR_STATUS1_CONV_FAULT) { + dev_err(adc->dev, "Unexpected conversion fault\n"); + ret = -EIO; + goto unlock; + } + + ret = adc5_read_voltage_data(adc, data_volt); + +unlock: + mutex_unlock(&adc->lock); + + return ret; +} + +typedef int (*adc_do_conversion)(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct iio_chan_spec const *chan, + u16 *data_volt, u16 *data_cur); + +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 adc7_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + int i, v_channel; + + for (i = 0; i < adc->nchannels; i++) { + v_channel = (adc->chan_props[i].sid << ADC_CHANNEL_OFFSET) | + adc->chan_props[i].channel; + if (v_channel == iiospec->args[0]) + return i; + } + + return -EINVAL; +} + +static int adc_read_raw_common(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask, adc_do_conversion do_conv) +{ + 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 = do_conv(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; + } +} + +static int adc5_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + return adc_read_raw_common(indio_dev, chan, val, val2, + mask, adc5_do_conversion); +} + +static int adc7_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + return adc_read_raw_common(indio_dev, chan, val, val2, + mask, adc7_do_conversion); +} + +static const struct iio_info adc5_info = { + .read_raw = adc5_read_raw, + .of_xlate = adc5_of_xlate, +}; + +static const struct iio_info adc7_info = { + .read_raw = adc7_read_raw, + .of_xlate = adc7_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 adc7_chans_pmic[ADC5_MAX_CHANNEL] = { + [ADC7_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0, + SCALE_HW_CALIB_DEFAULT) + [ADC7_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0, + SCALE_HW_CALIB_DEFAULT) + [ADC7_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC7_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC7_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0, + SCALE_HW_CALIB_PMIC_THERM_PM7) + [ADC7_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_AMUX_THM6_100K_PU] = ADC5_CHAN_TEMP("amux_thm6_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_GPIO3_100K_PU] = ADC5_CHAN_TEMP("gpio3_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) + [ADC7_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_pu2", 0, + SCALE_HW_CALIB_THERM_100K_PU_PM7) +}; + +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]; + u32 sid = 0; + 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; + } + + /* Value read from "reg" is virtual channel number */ + + /* virtual channel number = sid << 8 | channel number */ + + if (adc->data->info == &adc7_info) { + sid = chan >> ADC_CHANNEL_OFFSET; + chan = chan & ADC_CHANNEL_MASK; + } + + 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; + prop->sid = sid; + + channel_name = of_get_property(node, + "label", NULL) ? : node->name; + if (!channel_name) { + dev_err(dev, "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) { + dev_err(dev, "Invalid dig version read %d\n", ret); + return ret; + } + + dev_dbg(dev, "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) || + adc->data->info == &adc7_info) + 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, + .info = &adc5_info, + .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 adc7_data_pmic = { + .full_scale_code_volt = 0x70e4, + .adc_chans = adc7_chans_pmic, + .info = &adc7_info, + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX]) + {85, 340, 1360}, + .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {15, 100, 200, 300, 400, 500, 600, 700, + 1000, 2000, 4000, 8000, 16000, 32000, + 64000, 128000}, +}; + +static const struct adc5_data adc5_data_pmic_rev2 = { + .full_scale_code_volt = 0x4000, + .full_scale_code_cur = 0x1800, + .adc_chans = adc5_chans_rev2, + .info = &adc5_info, + .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-adc7", + .data = &adc7_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) { + dev_err(dev, "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->name = pdev->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = adc->data->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", + .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 new file mode 100644 index 000000000..acbda6636 --- /dev/null +++ b/drivers/iio/adc/qcom-spmi-iadc.c @@ -0,0 +1,585 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2014, 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/mutex.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +/* IADC register and bit definition */ +#define IADC_REVISION2 0x1 +#define IADC_REVISION2_SUPPORTED_IADC 1 + +#define IADC_PERPH_TYPE 0x4 +#define IADC_PERPH_TYPE_ADC 8 + +#define IADC_PERPH_SUBTYPE 0x5 +#define IADC_PERPH_SUBTYPE_IADC 3 + +#define IADC_STATUS1 0x8 +#define IADC_STATUS1_OP_MODE 4 +#define IADC_STATUS1_REQ_STS BIT(1) +#define IADC_STATUS1_EOC BIT(0) +#define IADC_STATUS1_REQ_STS_EOC_MASK 0x3 + +#define IADC_MODE_CTL 0x40 +#define IADC_OP_MODE_SHIFT 3 +#define IADC_OP_MODE_NORMAL 0 +#define IADC_TRIM_EN BIT(0) + +#define IADC_EN_CTL1 0x46 +#define IADC_EN_CTL1_SET BIT(7) + +#define IADC_CH_SEL_CTL 0x48 + +#define IADC_DIG_PARAM 0x50 +#define IADC_DIG_DEC_RATIO_SEL_SHIFT 2 + +#define IADC_HW_SETTLE_DELAY 0x51 + +#define IADC_CONV_REQ 0x52 +#define IADC_CONV_REQ_SET BIT(7) + +#define IADC_FAST_AVG_CTL 0x5a +#define IADC_FAST_AVG_EN 0x5b +#define IADC_FAST_AVG_EN_SET BIT(7) + +#define IADC_PERH_RESET_CTL3 0xda +#define IADC_FOLLOW_WARM_RB BIT(2) + +#define IADC_DATA 0x60 /* 16 bits */ + +#define IADC_SEC_ACCESS 0xd0 +#define IADC_SEC_ACCESS_DATA 0xa5 + +#define IADC_NOMINAL_RSENSE 0xf4 +#define IADC_NOMINAL_RSENSE_SIGN_MASK BIT(7) + +#define IADC_REF_GAIN_MICRO_VOLTS 17857 + +#define IADC_INT_RSENSE_DEVIATION 15625 /* nano Ohms per bit */ + +#define IADC_INT_RSENSE_IDEAL_VALUE 10000 /* micro Ohms */ +#define IADC_INT_RSENSE_DEFAULT_VALUE 7800 /* micro Ohms */ +#define IADC_INT_RSENSE_DEFAULT_GF 9000 /* micro Ohms */ +#define IADC_INT_RSENSE_DEFAULT_SMIC 9700 /* micro Ohms */ + +#define IADC_CONV_TIME_MIN_US 2000 +#define IADC_CONV_TIME_MAX_US 2100 + +#define IADC_DEF_PRESCALING 0 /* 1:1 */ +#define IADC_DEF_DECIMATION 0 /* 512 */ +#define IADC_DEF_HW_SETTLE_TIME 0 /* 0 us */ +#define IADC_DEF_AVG_SAMPLES 0 /* 1 sample */ + +/* IADC channel list */ +#define IADC_INT_RSENSE 0 +#define IADC_EXT_RSENSE 1 +#define IADC_GAIN_17P857MV 3 +#define IADC_EXT_OFFSET_CSP_CSN 5 +#define IADC_INT_OFFSET_CSP2_CSN2 6 + +/** + * struct iadc_chip - IADC Current ADC device structure. + * @regmap: regmap for register read/write. + * @dev: This device pointer. + * @base: base offset for the ADC peripheral. + * @rsense: Values of the internal and external sense resister in micro Ohms. + * @poll_eoc: Poll for end of conversion instead of waiting for IRQ. + * @offset: Raw offset values for the internal and external channels. + * @gain: Raw gain of the channels. + * @lock: ADC lock for access to the peripheral. + * @complete: ADC notification after end of conversion interrupt is received. + */ +struct iadc_chip { + struct regmap *regmap; + struct device *dev; + u16 base; + bool poll_eoc; + u32 rsense[2]; + u16 offset[2]; + u16 gain; + struct mutex lock; + struct completion complete; +}; + +static int iadc_read(struct iadc_chip *iadc, u16 offset, u8 *data) +{ + unsigned int val; + int ret; + + ret = regmap_read(iadc->regmap, iadc->base + offset, &val); + if (ret < 0) + return ret; + + *data = val; + return 0; +} + +static int iadc_write(struct iadc_chip *iadc, u16 offset, u8 data) +{ + return regmap_write(iadc->regmap, iadc->base + offset, data); +} + +static int iadc_reset(struct iadc_chip *iadc) +{ + u8 data; + int ret; + + ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA); + if (ret < 0) + return ret; + + ret = iadc_read(iadc, IADC_PERH_RESET_CTL3, &data); + if (ret < 0) + return ret; + + ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA); + if (ret < 0) + return ret; + + data |= IADC_FOLLOW_WARM_RB; + + return iadc_write(iadc, IADC_PERH_RESET_CTL3, data); +} + +static int iadc_set_state(struct iadc_chip *iadc, bool state) +{ + return iadc_write(iadc, IADC_EN_CTL1, state ? IADC_EN_CTL1_SET : 0); +} + +static void iadc_status_show(struct iadc_chip *iadc) +{ + u8 mode, sta1, chan, dig, en, req; + int ret; + + ret = iadc_read(iadc, IADC_MODE_CTL, &mode); + if (ret < 0) + return; + + ret = iadc_read(iadc, IADC_DIG_PARAM, &dig); + if (ret < 0) + return; + + ret = iadc_read(iadc, IADC_CH_SEL_CTL, &chan); + if (ret < 0) + return; + + ret = iadc_read(iadc, IADC_CONV_REQ, &req); + if (ret < 0) + return; + + ret = iadc_read(iadc, IADC_STATUS1, &sta1); + if (ret < 0) + return; + + ret = iadc_read(iadc, IADC_EN_CTL1, &en); + if (ret < 0) + return; + + dev_err(iadc->dev, + "mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n", + mode, en, chan, dig, req, sta1); +} + +static int iadc_configure(struct iadc_chip *iadc, int channel) +{ + u8 decim, mode; + int ret; + + /* Mode selection */ + mode = (IADC_OP_MODE_NORMAL << IADC_OP_MODE_SHIFT) | IADC_TRIM_EN; + ret = iadc_write(iadc, IADC_MODE_CTL, mode); + if (ret < 0) + return ret; + + /* Channel selection */ + ret = iadc_write(iadc, IADC_CH_SEL_CTL, channel); + if (ret < 0) + return ret; + + /* Digital parameter setup */ + decim = IADC_DEF_DECIMATION << IADC_DIG_DEC_RATIO_SEL_SHIFT; + ret = iadc_write(iadc, IADC_DIG_PARAM, decim); + if (ret < 0) + return ret; + + /* HW settle time delay */ + ret = iadc_write(iadc, IADC_HW_SETTLE_DELAY, IADC_DEF_HW_SETTLE_TIME); + if (ret < 0) + return ret; + + ret = iadc_write(iadc, IADC_FAST_AVG_CTL, IADC_DEF_AVG_SAMPLES); + if (ret < 0) + return ret; + + if (IADC_DEF_AVG_SAMPLES) + ret = iadc_write(iadc, IADC_FAST_AVG_EN, IADC_FAST_AVG_EN_SET); + else + ret = iadc_write(iadc, IADC_FAST_AVG_EN, 0); + + if (ret < 0) + return ret; + + if (!iadc->poll_eoc) + reinit_completion(&iadc->complete); + + ret = iadc_set_state(iadc, true); + if (ret < 0) + return ret; + + /* Request conversion */ + return iadc_write(iadc, IADC_CONV_REQ, IADC_CONV_REQ_SET); +} + +static int iadc_poll_wait_eoc(struct iadc_chip *iadc, unsigned int interval_us) +{ + unsigned int count, retry; + int ret; + u8 sta1; + + retry = interval_us / IADC_CONV_TIME_MIN_US; + + for (count = 0; count < retry; count++) { + ret = iadc_read(iadc, IADC_STATUS1, &sta1); + if (ret < 0) + return ret; + + sta1 &= IADC_STATUS1_REQ_STS_EOC_MASK; + if (sta1 == IADC_STATUS1_EOC) + return 0; + + usleep_range(IADC_CONV_TIME_MIN_US, IADC_CONV_TIME_MAX_US); + } + + iadc_status_show(iadc); + + return -ETIMEDOUT; +} + +static int iadc_read_result(struct iadc_chip *iadc, u16 *data) +{ + return regmap_bulk_read(iadc->regmap, iadc->base + IADC_DATA, data, 2); +} + +static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data) +{ + unsigned int wait; + int ret; + + ret = iadc_configure(iadc, chan); + if (ret < 0) + goto exit; + + wait = BIT(IADC_DEF_AVG_SAMPLES) * IADC_CONV_TIME_MIN_US * 2; + + if (iadc->poll_eoc) { + ret = iadc_poll_wait_eoc(iadc, wait); + } else { + ret = wait_for_completion_timeout(&iadc->complete, + usecs_to_jiffies(wait)); + if (!ret) + ret = -ETIMEDOUT; + else + /* double check conversion status */ + ret = iadc_poll_wait_eoc(iadc, IADC_CONV_TIME_MIN_US); + } + + if (!ret) + ret = iadc_read_result(iadc, data); +exit: + iadc_set_state(iadc, false); + if (ret < 0) + dev_err(iadc->dev, "conversion failed\n"); + + return ret; +} + +static int iadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct iadc_chip *iadc = iio_priv(indio_dev); + s32 isense_ua, vsense_uv; + u16 adc_raw, vsense_raw; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&iadc->lock); + ret = iadc_do_conversion(iadc, chan->channel, &adc_raw); + mutex_unlock(&iadc->lock); + if (ret < 0) + return ret; + + vsense_raw = adc_raw - iadc->offset[chan->channel]; + + vsense_uv = vsense_raw * IADC_REF_GAIN_MICRO_VOLTS; + vsense_uv /= (s32)iadc->gain - iadc->offset[chan->channel]; + + isense_ua = vsense_uv / iadc->rsense[chan->channel]; + + dev_dbg(iadc->dev, "off %d gain %d adc %d %duV I %duA\n", + iadc->offset[chan->channel], iadc->gain, + adc_raw, vsense_uv, isense_ua); + + *val = isense_ua; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; +} + +static const struct iio_info iadc_info = { + .read_raw = iadc_read_raw, +}; + +static irqreturn_t iadc_isr(int irq, void *dev_id) +{ + struct iadc_chip *iadc = dev_id; + + complete(&iadc->complete); + + return IRQ_HANDLED; +} + +static int iadc_update_offset(struct iadc_chip *iadc) +{ + int ret; + + ret = iadc_do_conversion(iadc, IADC_GAIN_17P857MV, &iadc->gain); + if (ret < 0) + return ret; + + ret = iadc_do_conversion(iadc, IADC_INT_OFFSET_CSP2_CSN2, + &iadc->offset[IADC_INT_RSENSE]); + if (ret < 0) + return ret; + + if (iadc->gain == iadc->offset[IADC_INT_RSENSE]) { + dev_err(iadc->dev, "error: internal offset == gain %d\n", + iadc->gain); + return -EINVAL; + } + + ret = iadc_do_conversion(iadc, IADC_EXT_OFFSET_CSP_CSN, + &iadc->offset[IADC_EXT_RSENSE]); + if (ret < 0) + return ret; + + if (iadc->gain == iadc->offset[IADC_EXT_RSENSE]) { + dev_err(iadc->dev, "error: external offset == gain %d\n", + iadc->gain); + return -EINVAL; + } + + return 0; +} + +static int iadc_version_check(struct iadc_chip *iadc) +{ + u8 val; + int ret; + + ret = iadc_read(iadc, IADC_PERPH_TYPE, &val); + if (ret < 0) + return ret; + + if (val < IADC_PERPH_TYPE_ADC) { + dev_err(iadc->dev, "%d is not ADC\n", val); + return -EINVAL; + } + + ret = iadc_read(iadc, IADC_PERPH_SUBTYPE, &val); + if (ret < 0) + return ret; + + if (val < IADC_PERPH_SUBTYPE_IADC) { + dev_err(iadc->dev, "%d is not IADC\n", val); + return -EINVAL; + } + + ret = iadc_read(iadc, IADC_REVISION2, &val); + if (ret < 0) + return ret; + + if (val < IADC_REVISION2_SUPPORTED_IADC) { + dev_err(iadc->dev, "revision %d not supported\n", val); + return -EINVAL; + } + + return 0; +} + +static int iadc_rsense_read(struct iadc_chip *iadc, struct device_node *node) +{ + int ret, sign, int_sense; + u8 deviation; + + ret = of_property_read_u32(node, "qcom,external-resistor-micro-ohms", + &iadc->rsense[IADC_EXT_RSENSE]); + if (ret < 0) + iadc->rsense[IADC_EXT_RSENSE] = IADC_INT_RSENSE_IDEAL_VALUE; + + if (!iadc->rsense[IADC_EXT_RSENSE]) { + dev_err(iadc->dev, "external resistor can't be zero Ohms"); + return -EINVAL; + } + + ret = iadc_read(iadc, IADC_NOMINAL_RSENSE, &deviation); + if (ret < 0) + return ret; + + /* + * Deviation value stored is an offset from 10 mili Ohms, bit 7 is + * the sign, the remaining bits have an LSB of 15625 nano Ohms. + */ + sign = (deviation & IADC_NOMINAL_RSENSE_SIGN_MASK) ? -1 : 1; + + deviation &= ~IADC_NOMINAL_RSENSE_SIGN_MASK; + + /* Scale it to nono Ohms */ + int_sense = IADC_INT_RSENSE_IDEAL_VALUE * 1000; + int_sense += sign * deviation * IADC_INT_RSENSE_DEVIATION; + int_sense /= 1000; /* micro Ohms */ + + iadc->rsense[IADC_INT_RSENSE] = int_sense; + return 0; +} + +static const struct iio_chan_spec iadc_channels[] = { + { + .type = IIO_CURRENT, + .datasheet_name = "INTERNAL_RSENSE", + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + }, + { + .type = IIO_CURRENT, + .datasheet_name = "EXTERNAL_RSENSE", + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + }, +}; + +static int iadc_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct iadc_chip *iadc; + int ret, irq_eoc; + u32 res; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*iadc)); + if (!indio_dev) + return -ENOMEM; + + iadc = iio_priv(indio_dev); + iadc->dev = dev; + + iadc->regmap = dev_get_regmap(dev->parent, NULL); + if (!iadc->regmap) + return -ENODEV; + + init_completion(&iadc->complete); + mutex_init(&iadc->lock); + + ret = of_property_read_u32(node, "reg", &res); + if (ret < 0) + return -ENODEV; + + iadc->base = res; + + ret = iadc_version_check(iadc); + if (ret < 0) + return -ENODEV; + + ret = iadc_rsense_read(iadc, node); + if (ret < 0) + return -ENODEV; + + dev_dbg(iadc->dev, "sense resistors %d and %d micro Ohm\n", + iadc->rsense[IADC_INT_RSENSE], + iadc->rsense[IADC_EXT_RSENSE]); + + irq_eoc = platform_get_irq(pdev, 0); + if (irq_eoc == -EPROBE_DEFER) + return irq_eoc; + + if (irq_eoc < 0) + iadc->poll_eoc = true; + + ret = iadc_reset(iadc); + if (ret < 0) { + dev_err(dev, "reset failed\n"); + return ret; + } + + if (!iadc->poll_eoc) { + ret = devm_request_irq(dev, irq_eoc, iadc_isr, 0, + "spmi-iadc", iadc); + if (!ret) + enable_irq_wake(irq_eoc); + else + return ret; + } else { + device_init_wakeup(iadc->dev, 1); + } + + ret = iadc_update_offset(iadc); + if (ret < 0) { + dev_err(dev, "failed offset calibration\n"); + return ret; + } + + indio_dev->name = pdev->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &iadc_info; + indio_dev->channels = iadc_channels; + indio_dev->num_channels = ARRAY_SIZE(iadc_channels); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id iadc_match_table[] = { + { .compatible = "qcom,spmi-iadc" }, + { } +}; + +MODULE_DEVICE_TABLE(of, iadc_match_table); + +static struct platform_driver iadc_driver = { + .driver = { + .name = "qcom-spmi-iadc", + .of_match_table = iadc_match_table, + }, + .probe = iadc_probe, +}; + +module_platform_driver(iadc_driver); + +MODULE_ALIAS("platform:qcom-spmi-iadc"); +MODULE_DESCRIPTION("Qualcomm SPMI PMIC current ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c new file mode 100644 index 000000000..7e7d40845 --- /dev/null +++ b/drivers/iio/adc/qcom-spmi-vadc.c @@ -0,0 +1,938 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2016, 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/math64.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/log2.h> + +#include <dt-bindings/iio/qcom,spmi-vadc.h> + +#include "qcom-vadc-common.h" + +/* VADC register and bit definitions */ +#define VADC_REVISION2 0x1 +#define VADC_REVISION2_SUPPORTED_VADC 1 + +#define VADC_PERPH_TYPE 0x4 +#define VADC_PERPH_TYPE_ADC 8 + +#define VADC_PERPH_SUBTYPE 0x5 +#define VADC_PERPH_SUBTYPE_VADC 1 + +#define VADC_STATUS1 0x8 +#define VADC_STATUS1_OP_MODE 4 +#define VADC_STATUS1_REQ_STS BIT(1) +#define VADC_STATUS1_EOC BIT(0) +#define VADC_STATUS1_REQ_STS_EOC_MASK 0x3 + +#define VADC_MODE_CTL 0x40 +#define VADC_OP_MODE_SHIFT 3 +#define VADC_OP_MODE_NORMAL 0 +#define VADC_AMUX_TRIM_EN BIT(1) +#define VADC_ADC_TRIM_EN BIT(0) + +#define VADC_EN_CTL1 0x46 +#define VADC_EN_CTL1_SET BIT(7) + +#define VADC_ADC_CH_SEL_CTL 0x48 + +#define VADC_ADC_DIG_PARAM 0x50 +#define VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT 2 + +#define VADC_HW_SETTLE_DELAY 0x51 + +#define VADC_CONV_REQ 0x52 +#define VADC_CONV_REQ_SET BIT(7) + +#define VADC_FAST_AVG_CTL 0x5a +#define VADC_FAST_AVG_EN 0x5b +#define VADC_FAST_AVG_EN_SET BIT(7) + +#define VADC_ACCESS 0xd0 +#define VADC_ACCESS_DATA 0xa5 + +#define VADC_PERH_RESET_CTL3 0xda +#define VADC_FOLLOW_WARM_RB BIT(2) + +#define VADC_DATA 0x60 /* 16 bits */ + +#define VADC_CHAN_MIN VADC_USBIN +#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM + +/** + * struct vadc_channel_prop - VADC channel property. + * @channel: channel number, refer to the channel list. + * @calibration: calibration type. + * @decimation: sampling rate supported for the channel. + * @prescale: channel scaling performed on the input signal. + * @hw_settle_time: the time between AMUX being configured and the + * start of conversion. + * @avg_samples: ability to provide single result from the ADC + * that is an average of multiple measurements. + * @scale_fn_type: Represents the scaling function to convert voltage + * physical units desired by the client for the channel. + */ +struct vadc_channel_prop { + unsigned int channel; + enum vadc_calibration calibration; + unsigned int decimation; + unsigned int prescale; + unsigned int hw_settle_time; + unsigned int avg_samples; + enum vadc_scale_fn_type scale_fn_type; +}; + +/** + * struct vadc_priv - VADC private structure. + * @regmap: pointer to struct regmap. + * @dev: pointer to struct device. + * @base: base address for the ADC peripheral. + * @nchannels: number of VADC channels. + * @chan_props: array of VADC channel properties. + * @iio_chans: array of IIO channels specification. + * @are_ref_measured: are reference points measured. + * @poll_eoc: use polling instead of interrupt. + * @complete: VADC result notification after interrupt is received. + * @graph: store parameters for calibration. + * @lock: ADC lock for access to the peripheral. + */ +struct vadc_priv { + struct regmap *regmap; + struct device *dev; + u16 base; + unsigned int nchannels; + struct vadc_channel_prop *chan_props; + struct iio_chan_spec *iio_chans; + bool are_ref_measured; + bool poll_eoc; + struct completion complete; + struct vadc_linear_graph graph[2]; + struct mutex lock; +}; + +static const struct vadc_prescale_ratio vadc_prescale_ratios[] = { + {.num = 1, .den = 1}, + {.num = 1, .den = 3}, + {.num = 1, .den = 4}, + {.num = 1, .den = 6}, + {.num = 1, .den = 20}, + {.num = 1, .den = 8}, + {.num = 10, .den = 81}, + {.num = 1, .den = 10} +}; + +static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data) +{ + return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1); +} + +static int vadc_write(struct vadc_priv *vadc, u16 offset, u8 data) +{ + return regmap_write(vadc->regmap, vadc->base + offset, data); +} + +static int vadc_reset(struct vadc_priv *vadc) +{ + u8 data; + int ret; + + ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA); + if (ret) + return ret; + + ret = vadc_read(vadc, VADC_PERH_RESET_CTL3, &data); + if (ret) + return ret; + + ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA); + if (ret) + return ret; + + data |= VADC_FOLLOW_WARM_RB; + + return vadc_write(vadc, VADC_PERH_RESET_CTL3, data); +} + +static int vadc_set_state(struct vadc_priv *vadc, bool state) +{ + return vadc_write(vadc, VADC_EN_CTL1, state ? VADC_EN_CTL1_SET : 0); +} + +static void vadc_show_status(struct vadc_priv *vadc) +{ + u8 mode, sta1, chan, dig, en, req; + int ret; + + ret = vadc_read(vadc, VADC_MODE_CTL, &mode); + if (ret) + return; + + ret = vadc_read(vadc, VADC_ADC_DIG_PARAM, &dig); + if (ret) + return; + + ret = vadc_read(vadc, VADC_ADC_CH_SEL_CTL, &chan); + if (ret) + return; + + ret = vadc_read(vadc, VADC_CONV_REQ, &req); + if (ret) + return; + + ret = vadc_read(vadc, VADC_STATUS1, &sta1); + if (ret) + return; + + ret = vadc_read(vadc, VADC_EN_CTL1, &en); + if (ret) + return; + + dev_err(vadc->dev, + "mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n", + mode, en, chan, dig, req, sta1); +} + +static int vadc_configure(struct vadc_priv *vadc, + struct vadc_channel_prop *prop) +{ + u8 decimation, mode_ctrl; + int ret; + + /* Mode selection */ + mode_ctrl = (VADC_OP_MODE_NORMAL << VADC_OP_MODE_SHIFT) | + VADC_ADC_TRIM_EN | VADC_AMUX_TRIM_EN; + ret = vadc_write(vadc, VADC_MODE_CTL, mode_ctrl); + if (ret) + return ret; + + /* Channel selection */ + ret = vadc_write(vadc, VADC_ADC_CH_SEL_CTL, prop->channel); + if (ret) + return ret; + + /* Digital parameter setup */ + decimation = prop->decimation << VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT; + ret = vadc_write(vadc, VADC_ADC_DIG_PARAM, decimation); + if (ret) + return ret; + + /* HW settle time delay */ + ret = vadc_write(vadc, VADC_HW_SETTLE_DELAY, prop->hw_settle_time); + if (ret) + return ret; + + ret = vadc_write(vadc, VADC_FAST_AVG_CTL, prop->avg_samples); + if (ret) + return ret; + + if (prop->avg_samples) + ret = vadc_write(vadc, VADC_FAST_AVG_EN, VADC_FAST_AVG_EN_SET); + else + ret = vadc_write(vadc, VADC_FAST_AVG_EN, 0); + + return ret; +} + +static int vadc_poll_wait_eoc(struct vadc_priv *vadc, unsigned int interval_us) +{ + unsigned int count, retry; + u8 sta1; + int ret; + + retry = interval_us / VADC_CONV_TIME_MIN_US; + + for (count = 0; count < retry; count++) { + ret = vadc_read(vadc, VADC_STATUS1, &sta1); + if (ret) + return ret; + + sta1 &= VADC_STATUS1_REQ_STS_EOC_MASK; + if (sta1 == VADC_STATUS1_EOC) + return 0; + + usleep_range(VADC_CONV_TIME_MIN_US, VADC_CONV_TIME_MAX_US); + } + + vadc_show_status(vadc); + + return -ETIMEDOUT; +} + +static int vadc_read_result(struct vadc_priv *vadc, u16 *data) +{ + int ret; + + ret = regmap_bulk_read(vadc->regmap, vadc->base + VADC_DATA, data, 2); + if (ret) + return ret; + + *data = clamp_t(u16, *data, VADC_MIN_ADC_CODE, VADC_MAX_ADC_CODE); + + return 0; +} + +static struct vadc_channel_prop *vadc_get_channel(struct vadc_priv *vadc, + unsigned int num) +{ + unsigned int i; + + for (i = 0; i < vadc->nchannels; i++) + if (vadc->chan_props[i].channel == num) + return &vadc->chan_props[i]; + + dev_dbg(vadc->dev, "no such channel %02x\n", num); + + return NULL; +} + +static int vadc_do_conversion(struct vadc_priv *vadc, + struct vadc_channel_prop *prop, u16 *data) +{ + unsigned int timeout; + int ret; + + mutex_lock(&vadc->lock); + + ret = vadc_configure(vadc, prop); + if (ret) + goto unlock; + + if (!vadc->poll_eoc) + reinit_completion(&vadc->complete); + + ret = vadc_set_state(vadc, true); + if (ret) + goto unlock; + + ret = vadc_write(vadc, VADC_CONV_REQ, VADC_CONV_REQ_SET); + if (ret) + goto err_disable; + + timeout = BIT(prop->avg_samples) * VADC_CONV_TIME_MIN_US * 2; + + if (vadc->poll_eoc) { + ret = vadc_poll_wait_eoc(vadc, timeout); + } else { + ret = wait_for_completion_timeout(&vadc->complete, timeout); + if (!ret) { + ret = -ETIMEDOUT; + goto err_disable; + } + + /* Double check conversion status */ + ret = vadc_poll_wait_eoc(vadc, VADC_CONV_TIME_MIN_US); + if (ret) + goto err_disable; + } + + ret = vadc_read_result(vadc, data); + +err_disable: + vadc_set_state(vadc, false); + if (ret) + dev_err(vadc->dev, "conversion failed\n"); +unlock: + mutex_unlock(&vadc->lock); + return ret; +} + +static int vadc_measure_ref_points(struct vadc_priv *vadc) +{ + struct vadc_channel_prop *prop; + u16 read_1, read_2; + int ret; + + vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE; + vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV; + + prop = vadc_get_channel(vadc, VADC_REF_1250MV); + ret = vadc_do_conversion(vadc, prop, &read_1); + if (ret) + goto err; + + /* Try with buffered 625mV channel first */ + prop = vadc_get_channel(vadc, VADC_SPARE1); + if (!prop) + prop = vadc_get_channel(vadc, VADC_REF_625MV); + + ret = vadc_do_conversion(vadc, prop, &read_2); + if (ret) + goto err; + + if (read_1 == read_2) { + ret = -EINVAL; + goto err; + } + + vadc->graph[VADC_CALIB_ABSOLUTE].dy = read_1 - read_2; + vadc->graph[VADC_CALIB_ABSOLUTE].gnd = read_2; + + /* Ratiometric calibration */ + prop = vadc_get_channel(vadc, VADC_VDD_VADC); + ret = vadc_do_conversion(vadc, prop, &read_1); + if (ret) + goto err; + + prop = vadc_get_channel(vadc, VADC_GND_REF); + ret = vadc_do_conversion(vadc, prop, &read_2); + if (ret) + goto err; + + if (read_1 == read_2) { + ret = -EINVAL; + goto err; + } + + vadc->graph[VADC_CALIB_RATIOMETRIC].dy = read_1 - read_2; + vadc->graph[VADC_CALIB_RATIOMETRIC].gnd = read_2; +err: + if (ret) + dev_err(vadc->dev, "measure reference points failed\n"); + + return ret; +} + +static int vadc_prescaling_from_dt(u32 num, u32 den) +{ + unsigned int pre; + + for (pre = 0; pre < ARRAY_SIZE(vadc_prescale_ratios); pre++) + if (vadc_prescale_ratios[pre].num == num && + vadc_prescale_ratios[pre].den == den) + break; + + if (pre == ARRAY_SIZE(vadc_prescale_ratios)) + return -EINVAL; + + return pre; +} + +static int vadc_hw_settle_time_from_dt(u32 value) +{ + if ((value <= 1000 && value % 100) || (value > 1000 && value % 2000)) + return -EINVAL; + + if (value <= 1000) + value /= 100; + else + value = value / 2000 + 10; + + return value; +} + +static int vadc_avg_samples_from_dt(u32 value) +{ + if (!is_power_of_2(value) || value > VADC_AVG_SAMPLES_MAX) + return -EINVAL; + + return __ffs64(value); +} + +static int vadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + struct vadc_priv *vadc = iio_priv(indio_dev); + struct vadc_channel_prop *prop; + u16 adc_code; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + prop = &vadc->chan_props[chan->address]; + ret = vadc_do_conversion(vadc, prop, &adc_code); + if (ret) + break; + + ret = qcom_vadc_scale(prop->scale_fn_type, + &vadc->graph[prop->calibration], + &vadc_prescale_ratios[prop->prescale], + (prop->calibration == VADC_CALIB_ABSOLUTE), + adc_code, val); + if (ret) + break; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + prop = &vadc->chan_props[chan->address]; + ret = vadc_do_conversion(vadc, prop, &adc_code); + if (ret) + break; + + *val = (int)adc_code; + return IIO_VAL_INT; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int vadc_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct vadc_priv *vadc = iio_priv(indio_dev); + unsigned int i; + + for (i = 0; i < vadc->nchannels; i++) + if (vadc->iio_chans[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info vadc_info = { + .read_raw = vadc_read_raw, + .of_xlate = vadc_of_xlate, +}; + +struct vadc_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; +}; + +#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \ + [VADC_##_dname] = { \ + .datasheet_name = __stringify(_dname), \ + .prescale_index = _pre, \ + .type = _type, \ + .info_mask = _mask, \ + .scale_fn_type = _scale \ + }, \ + +#define VADC_NO_CHAN(_dname, _type, _mask, _pre) \ + [VADC_##_dname] = { \ + .datasheet_name = __stringify(_dname), \ + .prescale_index = _pre, \ + .type = _type, \ + .info_mask = _mask \ + }, + +#define VADC_CHAN_TEMP(_dname, _pre, _scale) \ + VADC_CHAN(_dname, IIO_TEMP, \ + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \ + _pre, _scale) \ + +#define VADC_CHAN_VOLT(_dname, _pre, _scale) \ + VADC_CHAN(_dname, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ + _pre, _scale) \ + +#define VADC_CHAN_NO_SCALE(_dname, _pre) \ + VADC_NO_CHAN(_dname, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_RAW), \ + _pre) \ + +/* + * The array represents all possible ADC channels found in the supported PMICs. + * Every index in the array is equal to the channel number per datasheet. The + * gaps in the array should be treated as reserved channels. + */ +static const struct vadc_channels vadc_chans[] = { + VADC_CHAN_VOLT(USBIN, 4, SCALE_DEFAULT) + VADC_CHAN_VOLT(DCIN, 4, SCALE_DEFAULT) + VADC_CHAN_NO_SCALE(VCHG_SNS, 3) + VADC_CHAN_NO_SCALE(SPARE1_03, 1) + VADC_CHAN_NO_SCALE(USB_ID_MV, 1) + VADC_CHAN_VOLT(VCOIN, 1, SCALE_DEFAULT) + VADC_CHAN_NO_SCALE(VBAT_SNS, 1) + VADC_CHAN_VOLT(VSYS, 1, SCALE_DEFAULT) + VADC_CHAN_TEMP(DIE_TEMP, 0, SCALE_PMIC_THERM) + VADC_CHAN_VOLT(REF_625MV, 0, SCALE_DEFAULT) + VADC_CHAN_VOLT(REF_1250MV, 0, SCALE_DEFAULT) + VADC_CHAN_NO_SCALE(CHG_TEMP, 0) + VADC_CHAN_NO_SCALE(SPARE1, 0) + VADC_CHAN_TEMP(SPARE2, 0, SCALE_PMI_CHG_TEMP) + VADC_CHAN_VOLT(GND_REF, 0, SCALE_DEFAULT) + VADC_CHAN_VOLT(VDD_VADC, 0, SCALE_DEFAULT) + + VADC_CHAN_NO_SCALE(P_MUX1_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX2_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX3_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX4_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX5_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX6_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX7_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX8_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX9_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX10_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX11_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX12_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX13_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX14_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX15_1_1, 0) + VADC_CHAN_NO_SCALE(P_MUX16_1_1, 0) + + VADC_CHAN_NO_SCALE(P_MUX1_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX2_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX3_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX4_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX5_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX6_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX7_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX8_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX9_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX10_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX11_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX12_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX13_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX14_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX15_1_3, 1) + VADC_CHAN_NO_SCALE(P_MUX16_1_3, 1) + + VADC_CHAN_NO_SCALE(LR_MUX1_BAT_THERM, 0) + VADC_CHAN_VOLT(LR_MUX2_BAT_ID, 0, SCALE_DEFAULT) + VADC_CHAN_NO_SCALE(LR_MUX3_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX4_AMUX_THM1, 0) + VADC_CHAN_NO_SCALE(LR_MUX5_AMUX_THM2, 0) + VADC_CHAN_NO_SCALE(LR_MUX6_AMUX_THM3, 0) + VADC_CHAN_NO_SCALE(LR_MUX7_HW_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX8_AMUX_THM4, 0) + VADC_CHAN_NO_SCALE(LR_MUX9_AMUX_THM5, 0) + VADC_CHAN_NO_SCALE(LR_MUX10_USB_ID, 0) + VADC_CHAN_NO_SCALE(AMUX_PU1, 0) + VADC_CHAN_NO_SCALE(AMUX_PU2, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_BUF_XO_THERM, 0) + + VADC_CHAN_NO_SCALE(LR_MUX1_PU1_BAT_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX2_PU1_BAT_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_PU1_XO_THERM, 0) + VADC_CHAN_TEMP(LR_MUX4_PU1_AMUX_THM1, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_TEMP(LR_MUX5_PU1_AMUX_THM2, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_TEMP(LR_MUX6_PU1_AMUX_THM3, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_NO_SCALE(LR_MUX7_PU1_AMUX_HW_ID, 0) + VADC_CHAN_TEMP(LR_MUX8_PU1_AMUX_THM4, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_TEMP(LR_MUX9_PU1_AMUX_THM5, 0, SCALE_THERM_100K_PULLUP) + VADC_CHAN_NO_SCALE(LR_MUX10_PU1_AMUX_USB_ID, 0) + VADC_CHAN_TEMP(LR_MUX3_BUF_PU1_XO_THERM, 0, SCALE_XOTHERM) + + VADC_CHAN_NO_SCALE(LR_MUX1_PU2_BAT_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX2_PU2_BAT_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_PU2_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX4_PU2_AMUX_THM1, 0) + VADC_CHAN_NO_SCALE(LR_MUX5_PU2_AMUX_THM2, 0) + VADC_CHAN_NO_SCALE(LR_MUX6_PU2_AMUX_THM3, 0) + VADC_CHAN_NO_SCALE(LR_MUX7_PU2_AMUX_HW_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX8_PU2_AMUX_THM4, 0) + VADC_CHAN_NO_SCALE(LR_MUX9_PU2_AMUX_THM5, 0) + VADC_CHAN_NO_SCALE(LR_MUX10_PU2_AMUX_USB_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU2_XO_THERM, 0) + + VADC_CHAN_NO_SCALE(LR_MUX1_PU1_PU2_BAT_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX2_PU1_PU2_BAT_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_PU1_PU2_XO_THERM, 0) + VADC_CHAN_NO_SCALE(LR_MUX4_PU1_PU2_AMUX_THM1, 0) + VADC_CHAN_NO_SCALE(LR_MUX5_PU1_PU2_AMUX_THM2, 0) + VADC_CHAN_NO_SCALE(LR_MUX6_PU1_PU2_AMUX_THM3, 0) + VADC_CHAN_NO_SCALE(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX8_PU1_PU2_AMUX_THM4, 0) + VADC_CHAN_NO_SCALE(LR_MUX9_PU1_PU2_AMUX_THM5, 0) + VADC_CHAN_NO_SCALE(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0) + VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0) +}; + +static int vadc_get_dt_channel_data(struct device *dev, + struct vadc_channel_prop *prop, + struct device_node *node) +{ + const char *name = node->name; + u32 chan, value, varr[2]; + int ret; + + ret = of_property_read_u32(node, "reg", &chan); + if (ret) { + dev_err(dev, "invalid channel number %s\n", name); + return ret; + } + + if (chan > VADC_CHAN_MAX || chan < VADC_CHAN_MIN) { + dev_err(dev, "%s invalid channel number %d\n", name, chan); + return -EINVAL; + } + + /* the channel has DT description */ + prop->channel = chan; + + ret = of_property_read_u32(node, "qcom,decimation", &value); + if (!ret) { + ret = qcom_vadc_decimation_from_dt(value); + if (ret < 0) { + dev_err(dev, "%02x invalid decimation %d\n", + chan, value); + return ret; + } + prop->decimation = ret; + } else { + prop->decimation = VADC_DEF_DECIMATION; + } + + ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2); + if (!ret) { + ret = vadc_prescaling_from_dt(varr[0], varr[1]); + if (ret < 0) { + dev_err(dev, "%02x invalid pre-scaling <%d %d>\n", + chan, varr[0], varr[1]); + return ret; + } + prop->prescale = ret; + } else { + prop->prescale = vadc_chans[prop->channel].prescale_index; + } + + ret = of_property_read_u32(node, "qcom,hw-settle-time", &value); + if (!ret) { + ret = vadc_hw_settle_time_from_dt(value); + if (ret < 0) { + dev_err(dev, "%02x invalid hw-settle-time %d us\n", + chan, value); + return ret; + } + prop->hw_settle_time = ret; + } else { + prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME; + } + + ret = of_property_read_u32(node, "qcom,avg-samples", &value); + if (!ret) { + ret = vadc_avg_samples_from_dt(value); + if (ret < 0) { + dev_err(dev, "%02x invalid avg-samples %d\n", + chan, value); + return ret; + } + prop->avg_samples = ret; + } else { + prop->avg_samples = VADC_DEF_AVG_SAMPLES; + } + + if (of_property_read_bool(node, "qcom,ratiometric")) + prop->calibration = VADC_CALIB_RATIOMETRIC; + else + prop->calibration = VADC_CALIB_ABSOLUTE; + + dev_dbg(dev, "%02x name %s\n", chan, name); + + return 0; +} + +static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node) +{ + const struct vadc_channels *vadc_chan; + struct iio_chan_spec *iio_chan; + struct vadc_channel_prop prop; + struct device_node *child; + unsigned int index = 0; + int ret; + + vadc->nchannels = of_get_available_child_count(node); + if (!vadc->nchannels) + return -EINVAL; + + vadc->iio_chans = devm_kcalloc(vadc->dev, vadc->nchannels, + sizeof(*vadc->iio_chans), GFP_KERNEL); + if (!vadc->iio_chans) + return -ENOMEM; + + vadc->chan_props = devm_kcalloc(vadc->dev, vadc->nchannels, + sizeof(*vadc->chan_props), GFP_KERNEL); + if (!vadc->chan_props) + return -ENOMEM; + + iio_chan = vadc->iio_chans; + + for_each_available_child_of_node(node, child) { + ret = vadc_get_dt_channel_data(vadc->dev, &prop, child); + if (ret) { + of_node_put(child); + return ret; + } + + prop.scale_fn_type = vadc_chans[prop.channel].scale_fn_type; + vadc->chan_props[index] = prop; + + vadc_chan = &vadc_chans[prop.channel]; + + iio_chan->channel = prop.channel; + iio_chan->datasheet_name = vadc_chan->datasheet_name; + iio_chan->info_mask_separate = vadc_chan->info_mask; + iio_chan->type = vadc_chan->type; + iio_chan->indexed = 1; + iio_chan->address = index++; + + iio_chan++; + } + + /* These channels are mandatory, they are used as reference points */ + if (!vadc_get_channel(vadc, VADC_REF_1250MV)) { + dev_err(vadc->dev, "Please define 1.25V channel\n"); + return -ENODEV; + } + + if (!vadc_get_channel(vadc, VADC_REF_625MV)) { + dev_err(vadc->dev, "Please define 0.625V channel\n"); + return -ENODEV; + } + + if (!vadc_get_channel(vadc, VADC_VDD_VADC)) { + dev_err(vadc->dev, "Please define VDD channel\n"); + return -ENODEV; + } + + if (!vadc_get_channel(vadc, VADC_GND_REF)) { + dev_err(vadc->dev, "Please define GND channel\n"); + return -ENODEV; + } + + return 0; +} + +static irqreturn_t vadc_isr(int irq, void *dev_id) +{ + struct vadc_priv *vadc = dev_id; + + complete(&vadc->complete); + + return IRQ_HANDLED; +} + +static int vadc_check_revision(struct vadc_priv *vadc) +{ + u8 val; + int ret; + + ret = vadc_read(vadc, VADC_PERPH_TYPE, &val); + if (ret) + return ret; + + if (val < VADC_PERPH_TYPE_ADC) { + dev_err(vadc->dev, "%d is not ADC\n", val); + return -ENODEV; + } + + ret = vadc_read(vadc, VADC_PERPH_SUBTYPE, &val); + if (ret) + return ret; + + if (val < VADC_PERPH_SUBTYPE_VADC) { + dev_err(vadc->dev, "%d is not VADC\n", val); + return -ENODEV; + } + + ret = vadc_read(vadc, VADC_REVISION2, &val); + if (ret) + return ret; + + if (val < VADC_REVISION2_SUPPORTED_VADC) { + dev_err(vadc->dev, "revision %d not supported\n", val); + return -ENODEV; + } + + return 0; +} + +static int vadc_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct vadc_priv *vadc; + struct regmap *regmap; + int ret, irq_eoc; + u32 reg; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + ret = of_property_read_u32(node, "reg", ®); + if (ret < 0) + return ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*vadc)); + if (!indio_dev) + return -ENOMEM; + + vadc = iio_priv(indio_dev); + vadc->regmap = regmap; + vadc->dev = dev; + vadc->base = reg; + vadc->are_ref_measured = false; + init_completion(&vadc->complete); + mutex_init(&vadc->lock); + + ret = vadc_check_revision(vadc); + if (ret) + return ret; + + ret = vadc_get_dt_data(vadc, node); + if (ret) + return ret; + + irq_eoc = platform_get_irq(pdev, 0); + if (irq_eoc < 0) { + if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL) + return irq_eoc; + vadc->poll_eoc = true; + } else { + ret = devm_request_irq(dev, irq_eoc, vadc_isr, 0, + "spmi-vadc", vadc); + if (ret) + return ret; + } + + ret = vadc_reset(vadc); + if (ret) { + dev_err(dev, "reset failed\n"); + return ret; + } + + ret = vadc_measure_ref_points(vadc); + if (ret) + return ret; + + indio_dev->name = pdev->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &vadc_info; + indio_dev->channels = vadc->iio_chans; + indio_dev->num_channels = vadc->nchannels; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id vadc_match_table[] = { + { .compatible = "qcom,spmi-vadc" }, + { } +}; +MODULE_DEVICE_TABLE(of, vadc_match_table); + +static struct platform_driver vadc_driver = { + .driver = { + .name = "qcom-spmi-vadc", + .of_match_table = vadc_match_table, + }, + .probe = vadc_probe, +}; +module_platform_driver(vadc_driver); + +MODULE_ALIAS("platform:qcom-spmi-vadc"); +MODULE_DESCRIPTION("Qualcomm SPMI PMIC voltage ADC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>"); +MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c new file mode 100644 index 000000000..5113aaa6b --- /dev/null +++ b/drivers/iio/adc/qcom-vadc-common.c @@ -0,0 +1,676 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/bug.h> +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <linux/math64.h> +#include <linux/log2.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/units.h> + +#include "qcom-vadc-common.h" + +/* Voltage to temperature */ +static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = { + {1758, -40}, + {1742, -35}, + {1719, -30}, + {1691, -25}, + {1654, -20}, + {1608, -15}, + {1551, -10}, + {1483, -5}, + {1404, 0}, + {1315, 5}, + {1218, 10}, + {1114, 15}, + {1007, 20}, + {900, 25}, + {795, 30}, + {696, 35}, + {605, 40}, + {522, 45}, + {448, 50}, + {383, 55}, + {327, 60}, + {278, 65}, + {237, 70}, + {202, 75}, + {172, 80}, + {146, 85}, + {125, 90}, + {107, 95}, + {92, 100}, + {79, 105}, + {68, 110}, + {59, 115}, + {51, 120}, + {44, 125} +}; + +/* + * 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 const struct vadc_map_pt adcmap7_die_temp[] = { + { 433700, 1967}, + { 473100, 1964}, + { 512400, 1957}, + { 551500, 1949}, + { 590500, 1940}, + { 629300, 1930}, + { 667900, 1921}, + { 706400, 1910}, + { 744600, 1896}, + { 782500, 1878}, + { 820100, 1859}, + { 857300, 0}, +}; + +/* + * Resistance to temperature table for 100k pull up for NTCG104EF104. + */ +static const struct vadc_map_pt adcmap7_100k[] = { + { 4250657, -40960 }, + { 3962085, -39936 }, + { 3694875, -38912 }, + { 3447322, -37888 }, + { 3217867, -36864 }, + { 3005082, -35840 }, + { 2807660, -34816 }, + { 2624405, -33792 }, + { 2454218, -32768 }, + { 2296094, -31744 }, + { 2149108, -30720 }, + { 2012414, -29696 }, + { 1885232, -28672 }, + { 1766846, -27648 }, + { 1656598, -26624 }, + { 1553884, -25600 }, + { 1458147, -24576 }, + { 1368873, -23552 }, + { 1285590, -22528 }, + { 1207863, -21504 }, + { 1135290, -20480 }, + { 1067501, -19456 }, + { 1004155, -18432 }, + { 944935, -17408 }, + { 889550, -16384 }, + { 837731, -15360 }, + { 789229, -14336 }, + { 743813, -13312 }, + { 701271, -12288 }, + { 661405, -11264 }, + { 624032, -10240 }, + { 588982, -9216 }, + { 556100, -8192 }, + { 525239, -7168 }, + { 496264, -6144 }, + { 469050, -5120 }, + { 443480, -4096 }, + { 419448, -3072 }, + { 396851, -2048 }, + { 375597, -1024 }, + { 355598, 0 }, + { 336775, 1024 }, + { 319052, 2048 }, + { 302359, 3072 }, + { 286630, 4096 }, + { 271806, 5120 }, + { 257829, 6144 }, + { 244646, 7168 }, + { 232209, 8192 }, + { 220471, 9216 }, + { 209390, 10240 }, + { 198926, 11264 }, + { 189040, 12288 }, + { 179698, 13312 }, + { 170868, 14336 }, + { 162519, 15360 }, + { 154622, 16384 }, + { 147150, 17408 }, + { 140079, 18432 }, + { 133385, 19456 }, + { 127046, 20480 }, + { 121042, 21504 }, + { 115352, 22528 }, + { 109960, 23552 }, + { 104848, 24576 }, + { 100000, 25600 }, + { 95402, 26624 }, + { 91038, 27648 }, + { 86897, 28672 }, + { 82965, 29696 }, + { 79232, 30720 }, + { 75686, 31744 }, + { 72316, 32768 }, + { 69114, 33792 }, + { 66070, 34816 }, + { 63176, 35840 }, + { 60423, 36864 }, + { 57804, 37888 }, + { 55312, 38912 }, + { 52940, 39936 }, + { 50681, 40960 }, + { 48531, 41984 }, + { 46482, 43008 }, + { 44530, 44032 }, + { 42670, 45056 }, + { 40897, 46080 }, + { 39207, 47104 }, + { 37595, 48128 }, + { 36057, 49152 }, + { 34590, 50176 }, + { 33190, 51200 }, + { 31853, 52224 }, + { 30577, 53248 }, + { 29358, 54272 }, + { 28194, 55296 }, + { 27082, 56320 }, + { 26020, 57344 }, + { 25004, 58368 }, + { 24033, 59392 }, + { 23104, 60416 }, + { 22216, 61440 }, + { 21367, 62464 }, + { 20554, 63488 }, + { 19776, 64512 }, + { 19031, 65536 }, + { 18318, 66560 }, + { 17636, 67584 }, + { 16982, 68608 }, + { 16355, 69632 }, + { 15755, 70656 }, + { 15180, 71680 }, + { 14628, 72704 }, + { 14099, 73728 }, + { 13592, 74752 }, + { 13106, 75776 }, + { 12640, 76800 }, + { 12192, 77824 }, + { 11762, 78848 }, + { 11350, 79872 }, + { 10954, 80896 }, + { 10574, 81920 }, + { 10209, 82944 }, + { 9858, 83968 }, + { 9521, 84992 }, + { 9197, 86016 }, + { 8886, 87040 }, + { 8587, 88064 }, + { 8299, 89088 }, + { 8023, 90112 }, + { 7757, 91136 }, + { 7501, 92160 }, + { 7254, 93184 }, + { 7017, 94208 }, + { 6789, 95232 }, + { 6570, 96256 }, + { 6358, 97280 }, + { 6155, 98304 }, + { 5959, 99328 }, + { 5770, 100352 }, + { 5588, 101376 }, + { 5412, 102400 }, + { 5243, 103424 }, + { 5080, 104448 }, + { 4923, 105472 }, + { 4771, 106496 }, + { 4625, 107520 }, + { 4484, 108544 }, + { 4348, 109568 }, + { 4217, 110592 }, + { 4090, 111616 }, + { 3968, 112640 }, + { 3850, 113664 }, + { 3736, 114688 }, + { 3626, 115712 }, + { 3519, 116736 }, + { 3417, 117760 }, + { 3317, 118784 }, + { 3221, 119808 }, + { 3129, 120832 }, + { 3039, 121856 }, + { 2952, 122880 }, + { 2868, 123904 }, + { 2787, 124928 }, + { 2709, 125952 }, + { 2633, 126976 }, + { 2560, 128000 }, + { 2489, 129024 }, + { 2420, 130048 } +}; + +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_vadc7_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 int qcom_vadc7_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_THERM_100K_PU_PM7] = { + qcom_vadc7_scale_hw_calib_therm}, + [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp}, + [SCALE_HW_CALIB_PMIC_THERM_PM7] = { + qcom_vadc7_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, int *output) +{ + bool descending = 1; + u32 i = 0; + + if (!pts) + return -EINVAL; + + /* Check if table is descending or ascending */ + if (tablesize > 1) { + if (pts[0].x < pts[1].x) + descending = 0; + } + + while (i < tablesize) { + if ((descending) && (pts[i].x < input)) { + /* table entry is less than measured*/ + /* value and table is descending, stop */ + break; + } else if ((!descending) && + (pts[i].x > input)) { + /* table entry is greater than measured*/ + /*value and table is ascending, stop */ + break; + } + i++; + } + + if (i == 0) { + *output = pts[0].y; + } else if (i == tablesize) { + *output = pts[tablesize - 1].y; + } else { + /* result is between search_index and search_index-1 */ + /* interpolate linearly */ + *output = (((s32)((pts[i].y - pts[i - 1].y) * + (input - pts[i - 1].x)) / + (pts[i].x - pts[i - 1].x)) + + pts[i - 1].y); + } + + return 0; +} + +static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph, + u16 adc_code, + bool absolute, + s64 *scale_voltage) +{ + *scale_voltage = (adc_code - calib_graph->gnd); + *scale_voltage *= calib_graph->dx; + *scale_voltage = div64_s64(*scale_voltage, calib_graph->dy); + if (absolute) + *scale_voltage += calib_graph->dx; + + if (*scale_voltage < 0) + *scale_voltage = 0; +} + +static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph, + const struct vadc_prescale_ratio *prescale, + bool absolute, u16 adc_code, + int *result_uv) +{ + s64 voltage = 0, result = 0; + + qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); + + voltage = voltage * prescale->den; + result = div64_s64(voltage, prescale->num); + *result_uv = result; + + return 0; +} + +static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, + const struct vadc_prescale_ratio *prescale, + bool absolute, u16 adc_code, + int *result_mdec) +{ + s64 voltage = 0; + int ret; + + qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); + + if (absolute) + voltage = div64_s64(voltage, 1000); + + ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb, + ARRAY_SIZE(adcmap_100k_104ef_104fb), + voltage, result_mdec); + if (ret) + return ret; + + *result_mdec *= 1000; + + return 0; +} + +static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph, + const struct vadc_prescale_ratio *prescale, + bool absolute, + u16 adc_code, int *result_mdec) +{ + s64 voltage = 0; + u64 temp; /* Temporary variable for do_div */ + + qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); + + if (voltage > 0) { + temp = voltage * prescale->den; + do_div(temp, prescale->num * 2); + voltage = temp; + } else { + voltage = 0; + } + + *result_mdec = milli_kelvin_to_millicelsius(voltage); + + return 0; +} + +static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph, + const struct vadc_prescale_ratio *prescale, + bool absolute, + u16 adc_code, int *result_mdec) +{ + s64 voltage = 0, result = 0; + + qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); + + voltage = voltage * prescale->den; + voltage = div64_s64(voltage, prescale->num); + voltage = ((PMI_CHG_SCALE_1) * (voltage * 2)); + voltage = (voltage + PMI_CHG_SCALE_2); + result = div64_s64(voltage, 1000000); + *result_mdec = result; + + return 0; +} + +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_vadc7_scale_hw_calib_therm( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + s64 resistance = adc_code; + int ret, result; + + if (adc_code >= RATIO_MAX_ADC7) + return -EINVAL; + + /* (ADC code * R_PULLUP (100Kohm)) / (full_scale_code - ADC code)*/ + resistance *= R_PU_100K; + resistance = div64_s64(resistance, RATIO_MAX_ADC7 - adc_code); + + ret = qcom_vadc_map_voltage_temp(adcmap7_100k, + ARRAY_SIZE(adcmap7_100k), + resistance, &result); + if (ret) + return ret; + + *result_mdec = result; + + return 0; +} + +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 = milli_kelvin_to_millicelsius(*result_mdec); + + return 0; +} + +static int qcom_vadc7_scale_hw_calib_die_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + + int voltage, vtemp0, temp, i; + + voltage = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 1); + + if (adcmap7_die_temp[0].x > voltage) { + *result_mdec = DIE_TEMP_ADC7_SCALE_1; + return 0; + } + + if (adcmap7_die_temp[ARRAY_SIZE(adcmap7_die_temp) - 1].x <= voltage) { + *result_mdec = DIE_TEMP_ADC7_MAX; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(adcmap7_die_temp); i++) + if (adcmap7_die_temp[i].x > voltage) + break; + + vtemp0 = adcmap7_die_temp[i - 1].x; + voltage = voltage - vtemp0; + temp = div64_s64(voltage * DIE_TEMP_ADC7_SCALE_FACTOR, + adcmap7_die_temp[i - 1].y); + temp += DIE_TEMP_ADC7_SCALE_1 + (DIE_TEMP_ADC7_SCALE_2 * (i - 1)); + *result_mdec = temp; + + 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, + bool absolute, + u16 adc_code, int *result) +{ + switch (scaletype) { + case SCALE_DEFAULT: + return qcom_vadc_scale_volt(calib_graph, prescale, + absolute, adc_code, + result); + case SCALE_THERM_100K_PULLUP: + case SCALE_XOTHERM: + return qcom_vadc_scale_therm(calib_graph, prescale, + absolute, adc_code, + result); + case SCALE_PMIC_THERM: + return qcom_vadc_scale_die_temp(calib_graph, prescale, + absolute, adc_code, + result); + case SCALE_PMI_CHG_TEMP: + return qcom_vadc_scale_chg_temp(calib_graph, prescale, + absolute, adc_code, + result); + default: + return -EINVAL; + } +} +EXPORT_SYMBOL(qcom_vadc_scale); + +int qcom_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 || + value > VADC_DECIMATION_MAX) + return -EINVAL; + + return __ffs64(value / VADC_DECIMATION_MIN); +} +EXPORT_SYMBOL(qcom_vadc_decimation_from_dt); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm ADC common functionality"); diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h new file mode 100644 index 000000000..17b2fc4d8 --- /dev/null +++ b/drivers/iio/adc/qcom-vadc-common.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Code shared between the different Qualcomm PMIC voltage ADCs + */ + +#ifndef QCOM_VADC_COMMON_H +#define QCOM_VADC_COMMON_H + +#define VADC_CONV_TIME_MIN_US 2000 +#define VADC_CONV_TIME_MAX_US 2100 + +/* Min ADC code represents 0V */ +#define VADC_MIN_ADC_CODE 0x6000 +/* Max ADC code represents full-scale range of 1.8V */ +#define VADC_MAX_ADC_CODE 0xa800 + +#define VADC_ABSOLUTE_RANGE_UV 625000 +#define VADC_RATIOMETRIC_RANGE 1800 + +#define VADC_DEF_PRESCALING 0 /* 1:1 */ +#define VADC_DEF_DECIMATION 0 /* 512 */ +#define VADC_DEF_HW_SETTLE_TIME 0 /* 0 us */ +#define VADC_DEF_AVG_SAMPLES 0 /* 1 sample */ +#define VADC_DEF_CALIB_TYPE VADC_CALIB_ABSOLUTE + +#define VADC_DECIMATION_MIN 512 +#define VADC_DECIMATION_MAX 4096 +#define 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 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 + +#define R_PU_100K 100000 +#define RATIO_MAX_ADC7 BIT(14) + +#define DIE_TEMP_ADC7_SCALE_1 -60000 +#define DIE_TEMP_ADC7_SCALE_2 20000 +#define DIE_TEMP_ADC7_SCALE_FACTOR 1000 +#define DIE_TEMP_ADC7_MAX 160000 + +/** + * struct vadc_map_pt - Map the graph representation for ADC channel + * @x: Represent the ADC digitized code. + * @y: Represent the physical data which can be temperature, voltage, + * resistance. + */ +struct vadc_map_pt { + s32 x; + s32 y; +}; + +/* + * VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels. + * VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for + * calibration. + */ +enum vadc_calibration { + VADC_CALIB_ABSOLUTE = 0, + VADC_CALIB_RATIOMETRIC +}; + +/** + * struct vadc_linear_graph - Represent ADC characteristics. + * @dy: numerator slope to calculate the gain. + * @dx: denominator slope to calculate the gain. + * @gnd: A/D word of the ground reference used for the channel. + * + * Each ADC device has different offset and gain parameters which are + * computed to calibrate the device. + */ +struct vadc_linear_graph { + s32 dy; + s32 dx; + s32 gnd; +}; + +/** + * struct vadc_prescale_ratio - Represent scaling ratio for ADC input. + * @num: the inverse numerator of the gain applied to the input channel. + * @den: the inverse denominator of the gain applied to the input channel. + */ +struct vadc_prescale_ratio { + u32 num; + u32 den; +}; + +/** + * enum vadc_scale_fn_type - Scaling function to convert ADC code to + * physical scaled units for the channel. + * SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV). + * SCALE_THERM_100K_PULLUP: Returns temperature in millidegC. + * Uses a mapping table with 100K pullup. + * SCALE_PMIC_THERM: Returns result in milli degree's Centigrade. + * SCALE_XOTHERM: Returns XO thermistor voltage in millidegC. + * SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp + * 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_THERM_100K_PU_PM7: Returns temperature in millidegC using + * lookup table for PMIC7. 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_PMIC_THERM: Returns result in milli degree's Centigrade. + * The hardware applies offset/slope to adc code. This is for PMIC7. + * 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, + SCALE_THERM_100K_PULLUP, + 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_THERM_100K_PU_PM7, + SCALE_HW_CALIB_PMIC_THERM, + SCALE_HW_CALIB_PMIC_THERM_PM7, + 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; + const struct iio_info *info; + unsigned int *decimation; + unsigned int *hw_settle_1; + unsigned int *hw_settle_2; +}; + +int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, + const struct vadc_linear_graph *calib_graph, + const struct vadc_prescale_ratio *prescale, + bool absolute, + u16 adc_code, int *result_mdec); + +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 new file mode 100644 index 000000000..9f38cf3c7 --- /dev/null +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Renesas R-Car GyroADC driver + * + * Copyright 2016 Marek Vasut <marek.vasut@gmail.com> + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/regulator/consumer.h> +#include <linux/of_platform.h> +#include <linux/err.h> +#include <linux/pm_runtime.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> + +#define DRIVER_NAME "rcar-gyroadc" + +/* GyroADC registers. */ +#define RCAR_GYROADC_MODE_SELECT 0x00 +#define RCAR_GYROADC_MODE_SELECT_1_MB88101A 0x0 +#define RCAR_GYROADC_MODE_SELECT_2_ADCS7476 0x1 +#define RCAR_GYROADC_MODE_SELECT_3_MAX1162 0x3 + +#define RCAR_GYROADC_START_STOP 0x04 +#define RCAR_GYROADC_START_STOP_START BIT(0) + +#define RCAR_GYROADC_CLOCK_LENGTH 0x08 +#define RCAR_GYROADC_1_25MS_LENGTH 0x0c + +#define RCAR_GYROADC_REALTIME_DATA(ch) (0x10 + ((ch) * 4)) +#define RCAR_GYROADC_100MS_ADDED_DATA(ch) (0x30 + ((ch) * 4)) +#define RCAR_GYROADC_10MS_AVG_DATA(ch) (0x50 + ((ch) * 4)) + +#define RCAR_GYROADC_FIFO_STATUS 0x70 +#define RCAR_GYROADC_FIFO_STATUS_EMPTY(ch) BIT(0 + (4 * (ch))) +#define RCAR_GYROADC_FIFO_STATUS_FULL(ch) BIT(1 + (4 * (ch))) +#define RCAR_GYROADC_FIFO_STATUS_ERROR(ch) BIT(2 + (4 * (ch))) + +#define RCAR_GYROADC_INTR 0x74 +#define RCAR_GYROADC_INTR_INT BIT(0) + +#define RCAR_GYROADC_INTENR 0x78 +#define RCAR_GYROADC_INTENR_INTEN BIT(0) + +#define RCAR_GYROADC_SAMPLE_RATE 800 /* Hz */ + +#define RCAR_GYROADC_RUNTIME_PM_DELAY_MS 2000 + +enum rcar_gyroadc_model { + RCAR_GYROADC_MODEL_DEFAULT, + RCAR_GYROADC_MODEL_R8A7792, +}; + +struct rcar_gyroadc { + struct device *dev; + void __iomem *regs; + struct clk *clk; + struct regulator *vref[8]; + unsigned int num_channels; + enum rcar_gyroadc_model model; + unsigned int mode; + unsigned int sample_width; +}; + +static void rcar_gyroadc_hw_init(struct rcar_gyroadc *priv) +{ + const unsigned long clk_mhz = clk_get_rate(priv->clk) / 1000000; + const unsigned long clk_mul = + (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) ? 10 : 5; + unsigned long clk_len = clk_mhz * clk_mul; + + /* + * According to the R-Car Gen2 datasheet Rev. 1.01, Sept 08 2014, + * page 77-7, clock length must be even number. If it's odd number, + * add one. + */ + if (clk_len & 1) + clk_len++; + + /* Stop the GyroADC. */ + writel(0, priv->regs + RCAR_GYROADC_START_STOP); + + /* Disable IRQ on V2H. */ + if (priv->model == RCAR_GYROADC_MODEL_R8A7792) + writel(0, priv->regs + RCAR_GYROADC_INTENR); + + /* Set mode and timing. */ + writel(priv->mode, priv->regs + RCAR_GYROADC_MODE_SELECT); + writel(clk_len, priv->regs + RCAR_GYROADC_CLOCK_LENGTH); + writel(clk_mhz * 1250, priv->regs + RCAR_GYROADC_1_25MS_LENGTH); +} + +static void rcar_gyroadc_hw_start(struct rcar_gyroadc *priv) +{ + /* Start sampling. */ + writel(RCAR_GYROADC_START_STOP_START, + priv->regs + RCAR_GYROADC_START_STOP); + + /* + * Wait for the first conversion to complete. This is longer than + * the 1.25 mS in the datasheet because 1.25 mS is not enough for + * the hardware to deliver the first sample and the hardware does + * then return zeroes instead of valid data. + */ + mdelay(3); +} + +static void rcar_gyroadc_hw_stop(struct rcar_gyroadc *priv) +{ + /* Stop the GyroADC. */ + writel(0, priv->regs + RCAR_GYROADC_START_STOP); +} + +#define RCAR_GYROADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec rcar_gyroadc_iio_channels_1[] = { + RCAR_GYROADC_CHAN(0), + RCAR_GYROADC_CHAN(1), + RCAR_GYROADC_CHAN(2), + RCAR_GYROADC_CHAN(3), +}; + +static const struct iio_chan_spec rcar_gyroadc_iio_channels_2[] = { + RCAR_GYROADC_CHAN(0), + RCAR_GYROADC_CHAN(1), + RCAR_GYROADC_CHAN(2), + RCAR_GYROADC_CHAN(3), + RCAR_GYROADC_CHAN(4), + RCAR_GYROADC_CHAN(5), + RCAR_GYROADC_CHAN(6), + RCAR_GYROADC_CHAN(7), +}; + +static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = { + RCAR_GYROADC_CHAN(0), + RCAR_GYROADC_CHAN(1), + RCAR_GYROADC_CHAN(2), + RCAR_GYROADC_CHAN(3), + RCAR_GYROADC_CHAN(4), + RCAR_GYROADC_CHAN(5), + RCAR_GYROADC_CHAN(6), + RCAR_GYROADC_CHAN(7), +}; + +static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on) +{ + struct device *dev = priv->dev; + int ret; + + if (on) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + pm_runtime_put_noidle(dev); + } else { + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + } + + return ret; +} + +static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct regulator *consumer; + unsigned int datareg = RCAR_GYROADC_REALTIME_DATA(chan->channel); + unsigned int vref; + int ret; + + /* + * MB88101 is special in that it has only single regulator for + * all four channels. + */ + if (priv->mode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) + consumer = priv->vref[0]; + else + consumer = priv->vref[chan->channel]; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_VOLTAGE) + return -EINVAL; + + /* Channel not connected. */ + if (!consumer) + return -EINVAL; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = rcar_gyroadc_set_power(priv, true); + if (ret < 0) { + iio_device_release_direct_mode(indio_dev); + return ret; + } + + *val = readl(priv->regs + datareg); + *val &= BIT(priv->sample_width) - 1; + + ret = rcar_gyroadc_set_power(priv, false); + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* Channel not connected. */ + if (!consumer) + return -EINVAL; + + vref = regulator_get_voltage(consumer); + *val = vref / 1000; + *val2 = 1 << priv->sample_width; + + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = RCAR_GYROADC_SAMPLE_RATE; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int rcar_gyroadc_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + unsigned int maxreg = RCAR_GYROADC_FIFO_STATUS; + + if (readval == NULL) + return -EINVAL; + + if (reg % 4) + return -EINVAL; + + /* Handle the V2H case with extra interrupt block. */ + if (priv->model == RCAR_GYROADC_MODEL_R8A7792) + maxreg = RCAR_GYROADC_INTENR; + + if (reg > maxreg) + return -EINVAL; + + *readval = readl(priv->regs + reg); + + return 0; +} + +static const struct iio_info rcar_gyroadc_iio_info = { + .read_raw = rcar_gyroadc_read_raw, + .debugfs_reg_access = rcar_gyroadc_reg_access, +}; + +static const struct of_device_id rcar_gyroadc_match[] = { + { + /* R-Car compatible GyroADC */ + .compatible = "renesas,rcar-gyroadc", + .data = (void *)RCAR_GYROADC_MODEL_DEFAULT, + }, { + /* R-Car V2H specialty with interrupt registers. */ + .compatible = "renesas,r8a7792-gyroadc", + .data = (void *)RCAR_GYROADC_MODEL_R8A7792, + }, { + /* sentinel */ + } +}; + +MODULE_DEVICE_TABLE(of, rcar_gyroadc_match); + +static const struct of_device_id rcar_gyroadc_child_match[] = { + /* Mode 1 ADCs */ + { + .compatible = "fujitsu,mb88101a", + .data = (void *)RCAR_GYROADC_MODE_SELECT_1_MB88101A, + }, + /* Mode 2 ADCs */ + { + .compatible = "ti,adcs7476", + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476, + }, { + .compatible = "ti,adc121", + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476, + }, { + .compatible = "adi,ad7476", + .data = (void *)RCAR_GYROADC_MODE_SELECT_2_ADCS7476, + }, + /* Mode 3 ADCs */ + { + .compatible = "maxim,max1162", + .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162, + }, { + .compatible = "maxim,max11100", + .data = (void *)RCAR_GYROADC_MODE_SELECT_3_MAX1162, + }, + { /* sentinel */ } +}; + +static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) +{ + const struct of_device_id *of_id; + const struct iio_chan_spec *channels; + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + struct device_node *np = dev->of_node; + struct device_node *child; + struct regulator *vref; + unsigned int reg; + unsigned int adcmode = -1, childmode; + unsigned int sample_width; + unsigned int num_channels; + int ret, first = 1; + + 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 \"%pOFn\".", + child); + continue; + } + + childmode = (uintptr_t)of_id->data; + switch (childmode) { + case RCAR_GYROADC_MODE_SELECT_1_MB88101A: + sample_width = 12; + channels = rcar_gyroadc_iio_channels_1; + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_1); + break; + case RCAR_GYROADC_MODE_SELECT_2_ADCS7476: + sample_width = 15; + channels = rcar_gyroadc_iio_channels_2; + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_2); + break; + case RCAR_GYROADC_MODE_SELECT_3_MAX1162: + sample_width = 16; + channels = rcar_gyroadc_iio_channels_3; + num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3); + break; + default: + goto err_e_inval; + } + + /* + * MB88101 is special in that it's only a single chip taking + * up all the CHS lines. Thus, the DT binding is also special + * and has no reg property. If we run into such ADC, handle + * it here. + */ + if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) { + reg = 0; + } else { + ret = of_property_read_u32(child, "reg", ®); + if (ret) { + dev_err(dev, + "Failed to get child reg property of ADC \"%pOFn\".\n", + child); + goto err_of_node_put; + } + + /* Channel number is too high. */ + if (reg >= num_channels) { + dev_err(dev, + "Only %i channels supported with %pOFn, but reg = <%i>.\n", + num_channels, child, reg); + goto err_e_inval; + } + } + + /* Child node selected different mode than the rest. */ + if (!first && (adcmode != childmode)) { + dev_err(dev, + "Channel %i uses different ADC mode than the rest.\n", + reg); + goto err_e_inval; + } + + /* Channel is valid, grab the regulator. */ + dev->of_node = child; + vref = devm_regulator_get(dev, "vref"); + dev->of_node = np; + if (IS_ERR(vref)) { + dev_dbg(dev, "Channel %i 'vref' supply not connected.\n", + reg); + ret = PTR_ERR(vref); + goto err_of_node_put; + } + + priv->vref[reg] = vref; + + if (!first) + continue; + + /* First child node which passed sanity tests. */ + adcmode = childmode; + first = 0; + + priv->num_channels = num_channels; + priv->mode = childmode; + priv->sample_width = sample_width; + + indio_dev->channels = channels; + indio_dev->num_channels = num_channels; + + /* + * MB88101 is special and we only have one such device + * attached to the GyroADC at a time, so if we found it, + * we can stop parsing here. + */ + if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) { + of_node_put(child); + break; + } + } + + if (first) { + dev_err(dev, "No valid ADC channels found, aborting.\n"); + return -EINVAL; + } + + return 0; + +err_e_inval: + ret = -EINVAL; +err_of_node_put: + of_node_put(child); + return ret; +} + +static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + unsigned int i; + + for (i = 0; i < priv->num_channels; i++) { + if (!priv->vref[i]) + continue; + + regulator_disable(priv->vref[i]); + } +} + +static int rcar_gyroadc_init_supplies(struct iio_dev *indio_dev) +{ + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + unsigned int i; + int ret; + + for (i = 0; i < priv->num_channels; i++) { + if (!priv->vref[i]) + continue; + + ret = regulator_enable(priv->vref[i]); + if (ret) { + dev_err(dev, "Failed to enable regulator %i (ret=%i)\n", + i, ret); + goto err; + } + } + + return 0; + +err: + rcar_gyroadc_deinit_supplies(indio_dev); + return ret; +} + +static int rcar_gyroadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rcar_gyroadc *priv; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->dev = dev; + + priv->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + priv->clk = devm_clk_get(dev, "fck"); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), + "Failed to get IF clock\n"); + + ret = rcar_gyroadc_parse_subdevs(indio_dev); + if (ret) + return ret; + + ret = rcar_gyroadc_init_supplies(indio_dev); + if (ret) + return ret; + + priv->model = (enum rcar_gyroadc_model) + of_device_get_match_data(&pdev->dev); + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = DRIVER_NAME; + indio_dev->info = &rcar_gyroadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(dev, "Could not prepare or enable the IF clock.\n"); + goto err_clk_if_enable; + } + + pm_runtime_set_autosuspend_delay(dev, RCAR_GYROADC_RUNTIME_PM_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + pm_runtime_get_sync(dev); + rcar_gyroadc_hw_init(priv); + rcar_gyroadc_hw_start(priv); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "Couldn't register IIO device.\n"); + goto err_iio_device_register; + } + + pm_runtime_put_sync(dev); + + return 0; + +err_iio_device_register: + rcar_gyroadc_hw_stop(priv); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + clk_disable_unprepare(priv->clk); +err_clk_if_enable: + rcar_gyroadc_deinit_supplies(indio_dev); + + return ret; +} + +static int rcar_gyroadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct rcar_gyroadc *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + + iio_device_unregister(indio_dev); + pm_runtime_get_sync(dev); + rcar_gyroadc_hw_stop(priv); + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + clk_disable_unprepare(priv->clk); + rcar_gyroadc_deinit_supplies(indio_dev); + + return 0; +} + +#if defined(CONFIG_PM) +static int rcar_gyroadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rcar_gyroadc *priv = iio_priv(indio_dev); + + rcar_gyroadc_hw_stop(priv); + + return 0; +} + +static int rcar_gyroadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rcar_gyroadc *priv = iio_priv(indio_dev); + + rcar_gyroadc_hw_start(priv); + + return 0; +} +#endif + +static const struct dev_pm_ops rcar_gyroadc_pm_ops = { + SET_RUNTIME_PM_OPS(rcar_gyroadc_suspend, rcar_gyroadc_resume, NULL) +}; + +static struct platform_driver rcar_gyroadc_driver = { + .probe = rcar_gyroadc_probe, + .remove = rcar_gyroadc_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = rcar_gyroadc_match, + .pm = &rcar_gyroadc_pm_ops, + }, +}; + +module_platform_driver(rcar_gyroadc_driver); + +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("Renesas R-Car GyroADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/rn5t618-adc.c b/drivers/iio/adc/rn5t618-adc.c new file mode 100644 index 000000000..7010c4276 --- /dev/null +++ b/drivers/iio/adc/rn5t618-adc.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADC driver for the RICOH RN5T618 power management chip family + * + * Copyright (C) 2019 Andreas Kemnade + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mfd/rn5t618.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/regmap.h> +#include <linux/iio/iio.h> +#include <linux/slab.h> + +#define RN5T618_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(500)) +#define RN5T618_REFERENCE_VOLT 2500 + +/* mask for selecting channels for single conversion */ +#define RN5T618_ADCCNT3_CHANNEL_MASK 0x7 +/* average 4-time conversion mode */ +#define RN5T618_ADCCNT3_AVG BIT(3) +/* set for starting a single conversion, gets cleared by hw when done */ +#define RN5T618_ADCCNT3_GODONE BIT(4) +/* automatic conversion, period is in ADCCNT2, selected channels are + * in ADCCNT1 + */ +#define RN5T618_ADCCNT3_AUTO BIT(5) +#define RN5T618_ADCEND_IRQ BIT(0) + +struct rn5t618_adc_data { + struct device *dev; + struct rn5t618 *rn5t618; + struct completion conv_completion; + int irq; +}; + +struct rn5t618_channel_ratios { + u16 numerator; + u16 denominator; +}; + +enum rn5t618_channels { + LIMMON = 0, + VBAT, + VADP, + VUSB, + VSYS, + VTHM, + AIN1, + AIN0 +}; + +static const struct rn5t618_channel_ratios rn5t618_ratios[8] = { + [LIMMON] = {50, 32}, /* measured across 20mOhm, amplified by 32 */ + [VBAT] = {2, 1}, + [VADP] = {3, 1}, + [VUSB] = {3, 1}, + [VSYS] = {3, 1}, + [VTHM] = {1, 1}, + [AIN1] = {1, 1}, + [AIN0] = {1, 1}, +}; + +static int rn5t618_read_adc_reg(struct rn5t618 *rn5t618, int reg, u16 *val) +{ + u8 data[2]; + int ret; + + ret = regmap_bulk_read(rn5t618->regmap, reg, data, sizeof(data)); + if (ret < 0) + return ret; + + *val = (data[0] << 4) | (data[1] & 0xF); + + return 0; +} + +static irqreturn_t rn5t618_adc_irq(int irq, void *data) +{ + struct rn5t618_adc_data *adc = data; + unsigned int r = 0; + int ret; + + /* clear low & high threshold irqs */ + regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC1, 0); + regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC2, 0); + + ret = regmap_read(adc->rn5t618->regmap, RN5T618_IR_ADC3, &r); + if (ret < 0) + dev_err(adc->dev, "failed to read IRQ status: %d\n", ret); + + regmap_write(adc->rn5t618->regmap, RN5T618_IR_ADC3, 0); + + if (r & RN5T618_ADCEND_IRQ) + complete(&adc->conv_completion); + + return IRQ_HANDLED; +} + +static int rn5t618_adc_read(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct rn5t618_adc_data *adc = iio_priv(iio_dev); + u16 raw; + int ret; + + if (mask == IIO_CHAN_INFO_SCALE) { + *val = RN5T618_REFERENCE_VOLT * + rn5t618_ratios[chan->channel].numerator; + *val2 = rn5t618_ratios[chan->channel].denominator * 4095; + + return IIO_VAL_FRACTIONAL; + } + + /* select channel */ + ret = regmap_update_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3, + RN5T618_ADCCNT3_CHANNEL_MASK, + chan->channel); + if (ret < 0) + return ret; + + ret = regmap_write(adc->rn5t618->regmap, RN5T618_EN_ADCIR3, + RN5T618_ADCEND_IRQ); + if (ret < 0) + return ret; + + ret = regmap_update_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3, + RN5T618_ADCCNT3_AVG, + mask == IIO_CHAN_INFO_AVERAGE_RAW ? + RN5T618_ADCCNT3_AVG : 0); + if (ret < 0) + return ret; + + init_completion(&adc->conv_completion); + /* single conversion */ + ret = regmap_update_bits(adc->rn5t618->regmap, RN5T618_ADCCNT3, + RN5T618_ADCCNT3_GODONE, + RN5T618_ADCCNT3_GODONE); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout(&adc->conv_completion, + RN5T618_ADC_CONVERSION_TIMEOUT); + if (ret == 0) { + dev_warn(adc->dev, "timeout waiting for adc result\n"); + return -ETIMEDOUT; + } + + ret = rn5t618_read_adc_reg(adc->rn5t618, + RN5T618_ILIMDATAH + 2 * chan->channel, + &raw); + if (ret < 0) + return ret; + + *val = raw; + + return IIO_VAL_INT; +} + +static const struct iio_info rn5t618_adc_iio_info = { + .read_raw = &rn5t618_adc_read, +}; + +#define RN5T618_ADC_CHANNEL(_channel, _type, _name) { \ + .type = _type, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _name, \ + .indexed = 1. \ +} + +static const struct iio_chan_spec rn5t618_adc_iio_channels[] = { + RN5T618_ADC_CHANNEL(LIMMON, IIO_CURRENT, "LIMMON"), + RN5T618_ADC_CHANNEL(VBAT, IIO_VOLTAGE, "VBAT"), + RN5T618_ADC_CHANNEL(VADP, IIO_VOLTAGE, "VADP"), + RN5T618_ADC_CHANNEL(VUSB, IIO_VOLTAGE, "VUSB"), + RN5T618_ADC_CHANNEL(VSYS, IIO_VOLTAGE, "VSYS"), + RN5T618_ADC_CHANNEL(VTHM, IIO_VOLTAGE, "VTHM"), + RN5T618_ADC_CHANNEL(AIN1, IIO_VOLTAGE, "AIN1"), + RN5T618_ADC_CHANNEL(AIN0, IIO_VOLTAGE, "AIN0") +}; + +static int rn5t618_adc_probe(struct platform_device *pdev) +{ + int ret; + struct iio_dev *iio_dev; + struct rn5t618_adc_data *adc; + struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent); + + iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!iio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + adc = iio_priv(iio_dev); + adc->dev = &pdev->dev; + adc->rn5t618 = rn5t618; + + if (rn5t618->irq_data) + adc->irq = regmap_irq_get_virq(rn5t618->irq_data, + RN5T618_IRQ_ADC); + + if (adc->irq <= 0) { + dev_err(&pdev->dev, "get virq failed\n"); + return -EINVAL; + } + + init_completion(&adc->conv_completion); + + iio_dev->name = dev_name(&pdev->dev); + iio_dev->info = &rn5t618_adc_iio_info; + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->channels = rn5t618_adc_iio_channels; + iio_dev->num_channels = ARRAY_SIZE(rn5t618_adc_iio_channels); + + /* stop any auto-conversion */ + ret = regmap_write(rn5t618->regmap, RN5T618_ADCCNT3, 0); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, iio_dev); + + ret = devm_request_threaded_irq(adc->dev, adc->irq, NULL, + rn5t618_adc_irq, + IRQF_ONESHOT, dev_name(adc->dev), + adc); + if (ret < 0) { + dev_err(adc->dev, "request irq %d failed: %d\n", adc->irq, ret); + return ret; + } + + return devm_iio_device_register(adc->dev, iio_dev); +} + +static struct platform_driver rn5t618_adc_driver = { + .driver = { + .name = "rn5t618-adc", + }, + .probe = rn5t618_adc_probe, +}; + +module_platform_driver(rn5t618_adc_driver); +MODULE_ALIAS("platform:rn5t618-adc"); +MODULE_DESCRIPTION("RICOH RN5T618 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c new file mode 100644 index 000000000..12584f163 --- /dev/null +++ b/drivers/iio/adc/rockchip_saradc.c @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Rockchip Successive Approximation Register (SAR) A/D Converter + * Copyright (C) 2014 ROCKCHIP, Inc. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/reset.h> +#include <linux/regulator/consumer.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define SARADC_DATA 0x00 + +#define SARADC_STAS 0x04 +#define SARADC_STAS_BUSY BIT(0) + +#define SARADC_CTRL 0x08 +#define SARADC_CTRL_IRQ_STATUS BIT(6) +#define SARADC_CTRL_IRQ_ENABLE BIT(5) +#define SARADC_CTRL_POWER_CTRL BIT(3) +#define SARADC_CTRL_CHN_MASK 0x7 + +#define SARADC_DLY_PU_SOC 0x0c +#define SARADC_DLY_PU_SOC_MASK 0x3f + +#define SARADC_TIMEOUT msecs_to_jiffies(100) +#define SARADC_MAX_CHANNELS 6 + +struct rockchip_saradc_data { + const struct iio_chan_spec *channels; + int num_channels; + unsigned long clk_rate; +}; + +struct rockchip_saradc { + void __iomem *regs; + struct clk *pclk; + struct clk *clk; + struct completion completion; + struct regulator *vref; + struct reset_control *reset; + const struct rockchip_saradc_data *data; + u16 last_val; + const struct iio_chan_spec *last_chan; +}; + +static void rockchip_saradc_power_down(struct rockchip_saradc *info) +{ + /* Clear irq & power down adc */ + writel_relaxed(0, info->regs + SARADC_CTRL); +} + +static int rockchip_saradc_conversion(struct rockchip_saradc *info, + struct iio_chan_spec const *chan) +{ + reinit_completion(&info->completion); + + /* 8 clock periods as delay between power up and start cmd */ + writel_relaxed(8, info->regs + SARADC_DLY_PU_SOC); + + info->last_chan = chan; + + /* Select the channel to be used and trigger conversion */ + writel(SARADC_CTRL_POWER_CTRL + | (chan->channel & SARADC_CTRL_CHN_MASK) + | SARADC_CTRL_IRQ_ENABLE, + info->regs + SARADC_CTRL); + + if (!wait_for_completion_timeout(&info->completion, SARADC_TIMEOUT)) + return -ETIMEDOUT; + + return 0; +} + +static int rockchip_saradc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct rockchip_saradc *info = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + + ret = rockchip_saradc_conversion(info, chan); + if (ret) { + rockchip_saradc_power_down(info); + mutex_unlock(&indio_dev->mlock); + return ret; + } + + *val = info->last_val; + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(info->vref); + if (ret < 0) { + dev_err(&indio_dev->dev, "failed to get voltage\n"); + return ret; + } + + *val = ret / 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id) +{ + struct rockchip_saradc *info = dev_id; + + /* Read value */ + info->last_val = readl_relaxed(info->regs + SARADC_DATA); + info->last_val &= GENMASK(info->last_chan->scan_type.realbits - 1, 0); + + rockchip_saradc_power_down(info); + + complete(&info->completion); + + return IRQ_HANDLED; +} + +static const struct iio_info rockchip_saradc_iio_info = { + .read_raw = rockchip_saradc_read_raw, +}; + +#define SARADC_CHANNEL(_index, _id, _res) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _id, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = _res, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +static const struct iio_chan_spec rockchip_saradc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), +}; + +static const struct rockchip_saradc_data saradc_data = { + .channels = rockchip_saradc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels), + .clk_rate = 1000000, +}; + +static const struct iio_chan_spec rockchip_rk3066_tsadc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 12), + SARADC_CHANNEL(1, "adc1", 12), +}; + +static const struct rockchip_saradc_data rk3066_tsadc_data = { + .channels = rockchip_rk3066_tsadc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_rk3066_tsadc_iio_channels), + .clk_rate = 50000, +}; + +static const struct iio_chan_spec rockchip_rk3399_saradc_iio_channels[] = { + SARADC_CHANNEL(0, "adc0", 10), + SARADC_CHANNEL(1, "adc1", 10), + SARADC_CHANNEL(2, "adc2", 10), + SARADC_CHANNEL(3, "adc3", 10), + SARADC_CHANNEL(4, "adc4", 10), + SARADC_CHANNEL(5, "adc5", 10), +}; + +static const struct rockchip_saradc_data rk3399_saradc_data = { + .channels = rockchip_rk3399_saradc_iio_channels, + .num_channels = ARRAY_SIZE(rockchip_rk3399_saradc_iio_channels), + .clk_rate = 1000000, +}; + +static const struct of_device_id rockchip_saradc_match[] = { + { + .compatible = "rockchip,saradc", + .data = &saradc_data, + }, { + .compatible = "rockchip,rk3066-tsadc", + .data = &rk3066_tsadc_data, + }, { + .compatible = "rockchip,rk3399-saradc", + .data = &rk3399_saradc_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, rockchip_saradc_match); + +/* + * Reset SARADC Controller. + */ +static void rockchip_saradc_reset_controller(struct reset_control *reset) +{ + reset_control_assert(reset); + usleep_range(10, 20); + reset_control_deassert(reset); +} + +static void rockchip_saradc_clk_disable(void *data) +{ + struct rockchip_saradc *info = data; + + clk_disable_unprepare(info->clk); +} + +static void rockchip_saradc_pclk_disable(void *data) +{ + struct rockchip_saradc *info = data; + + clk_disable_unprepare(info->pclk); +} + +static void rockchip_saradc_regulator_disable(void *data) +{ + struct rockchip_saradc *info = data; + + regulator_disable(info->vref); +} + +static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *i_dev = pf->indio_dev; + struct rockchip_saradc *info = iio_priv(i_dev); + /* + * @values: each channel takes an u16 value + * @timestamp: will be 8-byte aligned automatically + */ + struct { + u16 values[SARADC_MAX_CHANNELS]; + int64_t timestamp; + } data; + int ret; + int i, j = 0; + + mutex_lock(&i_dev->mlock); + + for_each_set_bit(i, i_dev->active_scan_mask, i_dev->masklength) { + const struct iio_chan_spec *chan = &i_dev->channels[i]; + + ret = rockchip_saradc_conversion(info, chan); + if (ret) { + rockchip_saradc_power_down(info); + goto out; + } + + data.values[j] = info->last_val; + j++; + } + + iio_push_to_buffers_with_timestamp(i_dev, &data, iio_get_time_ns(i_dev)); +out: + mutex_unlock(&i_dev->mlock); + + iio_trigger_notify_done(i_dev->trig); + + return IRQ_HANDLED; +} + +static int rockchip_saradc_probe(struct platform_device *pdev) +{ + struct rockchip_saradc *info = NULL; + struct device_node *np = pdev->dev.of_node; + struct iio_dev *indio_dev = NULL; + struct resource *mem; + const struct of_device_id *match; + int ret; + int irq; + + if (!np) + return -ENODEV; + + 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); + + match = of_match_device(rockchip_saradc_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "failed to match device\n"); + return -ENODEV; + } + + info->data = match->data; + + /* Sanity check for possible later IP variants with more channels */ + if (info->data->num_channels > SARADC_MAX_CHANNELS) { + dev_err(&pdev->dev, "max channels exceeded"); + return -EINVAL; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + info->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + /* + * The reset should be an optional property, as it should work + * with old devicetrees as well + */ + info->reset = devm_reset_control_get_exclusive(&pdev->dev, + "saradc-apb"); + if (IS_ERR(info->reset)) { + ret = PTR_ERR(info->reset); + if (ret != -ENOENT) + return ret; + + dev_dbg(&pdev->dev, "no reset control found\n"); + info->reset = NULL; + } + + init_completion(&info->completion); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr, + 0, dev_name(&pdev->dev), info); + if (ret < 0) { + dev_err(&pdev->dev, "failed requesting irq %d\n", irq); + return ret; + } + + info->pclk = devm_clk_get(&pdev->dev, "apb_pclk"); + if (IS_ERR(info->pclk)) { + dev_err(&pdev->dev, "failed to get pclk\n"); + return PTR_ERR(info->pclk); + } + + info->clk = devm_clk_get(&pdev->dev, "saradc"); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "failed to get adc clock\n"); + return PTR_ERR(info->clk); + } + + info->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(info->vref)) { + dev_err(&pdev->dev, "failed to get regulator, %ld\n", + PTR_ERR(info->vref)); + return PTR_ERR(info->vref); + } + + if (info->reset) + rockchip_saradc_reset_controller(info->reset); + + /* + * Use a default value for the converter clock. + * This may become user-configurable in the future. + */ + ret = clk_set_rate(info->clk, info->data->clk_rate); + if (ret < 0) { + dev_err(&pdev->dev, "failed to set adc clk rate, %d\n", ret); + return ret; + } + + ret = regulator_enable(info->vref); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable vref regulator\n"); + return ret; + } + ret = devm_add_action_or_reset(&pdev->dev, + rockchip_saradc_regulator_disable, info); + if (ret) { + dev_err(&pdev->dev, "failed to register devm action, %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(info->pclk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable pclk\n"); + return ret; + } + ret = devm_add_action_or_reset(&pdev->dev, + rockchip_saradc_pclk_disable, info); + if (ret) { + dev_err(&pdev->dev, "failed to register devm action, %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(info->clk); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable converter clock\n"); + return ret; + } + ret = devm_add_action_or_reset(&pdev->dev, + rockchip_saradc_clk_disable, info); + if (ret) { + dev_err(&pdev->dev, "failed to register devm action, %d\n", + ret); + return ret; + } + + platform_set_drvdata(pdev, indio_dev); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &rockchip_saradc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->channels = info->data->channels; + indio_dev->num_channels = info->data->num_channels; + ret = devm_iio_triggered_buffer_setup(&indio_dev->dev, indio_dev, NULL, + rockchip_saradc_trigger_handler, + NULL); + if (ret) + return ret; + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +#ifdef CONFIG_PM_SLEEP +static int rockchip_saradc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rockchip_saradc *info = iio_priv(indio_dev); + + clk_disable_unprepare(info->clk); + clk_disable_unprepare(info->pclk); + regulator_disable(info->vref); + + return 0; +} + +static int rockchip_saradc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct rockchip_saradc *info = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(info->vref); + if (ret) + return ret; + + ret = clk_prepare_enable(info->pclk); + if (ret) + return ret; + + ret = clk_prepare_enable(info->clk); + if (ret) + clk_disable_unprepare(info->pclk); + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops, + rockchip_saradc_suspend, rockchip_saradc_resume); + +static struct platform_driver rockchip_saradc_driver = { + .probe = rockchip_saradc_probe, + .driver = { + .name = "rockchip-saradc", + .of_match_table = rockchip_saradc_match, + .pm = &rockchip_saradc_pm_ops, + }, +}; + +module_platform_driver(rockchip_saradc_driver); + +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_DESCRIPTION("Rockchip SARADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c new file mode 100644 index 000000000..2b463e1cf --- /dev/null +++ b/drivers/iio/adc/sc27xx_adc.c @@ -0,0 +1,565 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Spreadtrum Communications Inc. + +#include <linux/hwspinlock.h> +#include <linux/iio/iio.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 +#define SC27XX_MODULE_ADC_EN BIT(5) +#define SC27XX_ARM_CLK_EN 0xc10 +#define SC27XX_CLK_ADC_EN BIT(5) +#define SC27XX_CLK_ADC_CLK_EN BIT(6) + +/* ADC controller registers definition */ +#define SC27XX_ADC_CTL 0x0 +#define SC27XX_ADC_CH_CFG 0x4 +#define SC27XX_ADC_DATA 0x4c +#define SC27XX_ADC_INT_EN 0x50 +#define SC27XX_ADC_INT_CLR 0x54 +#define SC27XX_ADC_INT_STS 0x58 +#define SC27XX_ADC_INT_RAW 0x5c + +/* Bits and mask definition for SC27XX_ADC_CTL register */ +#define SC27XX_ADC_EN BIT(0) +#define SC27XX_ADC_CHN_RUN BIT(1) +#define SC27XX_ADC_12BIT_MODE BIT(2) +#define SC27XX_ADC_RUN_NUM_MASK GENMASK(7, 4) +#define SC27XX_ADC_RUN_NUM_SHIFT 4 + +/* Bits and mask definition for SC27XX_ADC_CH_CFG register */ +#define SC27XX_ADC_CHN_ID_MASK GENMASK(4, 0) +#define SC27XX_ADC_SCALE_MASK GENMASK(10, 9) +#define SC27XX_ADC_SCALE_SHIFT 9 + +/* Bits definitions for SC27XX_ADC_INT_EN registers */ +#define SC27XX_ADC_IRQ_EN BIT(0) + +/* 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 + +/* ADC voltage ratio definition */ +#define SC27XX_VOLT_RATIO(n, d) \ + (((n) << SC27XX_RATIO_NUMERATOR_OFFSET) | (d)) +#define SC27XX_RATIO_NUMERATOR_OFFSET 16 +#define SC27XX_RATIO_DENOMINATOR_MASK GENMASK(15, 0) + +struct sc27xx_adc_data { + struct device *dev; + struct regmap *regmap; + /* + * One hardware spinlock to synchronize between the multiple + * subsystems which will access the unique ADC controller. + */ + struct hwspinlock *hwlock; + int channel_scale[SC27XX_ADC_CHANNEL_MAX]; + u32 base; + int irq; +}; + +struct sc27xx_adc_linear_graph { + int volt0; + int adc0; + int volt1; + int adc1; +}; + +/* + * According to the datasheet, we can convert one ADC value to one voltage value + * through 2 points in the linear graph. If the voltage is less than 1.2v, we + * should use the small-scale graph, and if more than 1.2v, we should use the + * big-scale graph. + */ +static struct sc27xx_adc_linear_graph big_scale_graph = { + 4200, 3310, + 3600, 2832, +}; + +static struct sc27xx_adc_linear_graph small_scale_graph = { + 1000, 3413, + 100, 341, +}; + +static const struct sc27xx_adc_linear_graph sc2731_big_scale_graph_calib = { + 4200, 850, + 3600, 728, +}; + +static const struct sc27xx_adc_linear_graph sc2731_small_scale_graph_calib = { + 1000, 838, + 100, 84, +}; + +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 = &sc2731_big_scale_graph_calib; + graph = &big_scale_graph; + cell_name = "big_scale_calib"; + } else { + calib_graph = &sc2731_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) { + case 1: + case 2: + case 3: + case 4: + return scale ? SC27XX_VOLT_RATIO(400, 1025) : + SC27XX_VOLT_RATIO(1, 1); + case 5: + return SC27XX_VOLT_RATIO(7, 29); + case 6: + return SC27XX_VOLT_RATIO(375, 9000); + case 7: + case 8: + return scale ? SC27XX_VOLT_RATIO(100, 125) : + SC27XX_VOLT_RATIO(1, 1); + case 19: + return SC27XX_VOLT_RATIO(1, 3); + default: + return SC27XX_VOLT_RATIO(1, 1); + } + return SC27XX_VOLT_RATIO(1, 1); +} + +static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel, + int scale, int *val) +{ + int ret; + u32 tmp, value, status; + + ret = hwspin_lock_timeout_raw(data->hwlock, SC27XX_ADC_HWLOCK_TIMEOUT); + if (ret) { + dev_err(data->dev, "timeout to get the hwspinlock\n"); + return ret; + } + + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, + SC27XX_ADC_EN, SC27XX_ADC_EN); + 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; + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CH_CFG, + SC27XX_ADC_CHN_ID_MASK | SC27XX_ADC_SCALE_MASK, + tmp); + if (ret) + goto disable_adc; + + /* Select 12bit conversion mode, and only sample 1 time */ + tmp = SC27XX_ADC_12BIT_MODE; + tmp |= (0 << SC27XX_ADC_RUN_NUM_SHIFT) & SC27XX_ADC_RUN_NUM_MASK; + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, + SC27XX_ADC_RUN_NUM_MASK | SC27XX_ADC_12BIT_MODE, + tmp); + if (ret) + goto disable_adc; + + ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, + SC27XX_ADC_CHN_RUN, SC27XX_ADC_CHN_RUN); + if (ret) + goto disable_adc; + + 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, + SC27XX_ADC_EN, 0); +unlock_adc: + hwspin_unlock_raw(data->hwlock); + + if (!ret) + *val = value; + + return ret; +} + +static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data, + int channel, int scale, + u32 *div_numerator, u32 *div_denominator) +{ + u32 ratio = sc27xx_adc_get_ratio(channel, scale); + + *div_numerator = ratio >> SC27XX_RATIO_NUMERATOR_OFFSET; + *div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK; +} + +static int sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph, + int raw_adc) +{ + int tmp; + + tmp = (graph->volt0 - graph->volt1) * (raw_adc - graph->adc1); + tmp /= (graph->adc0 - graph->adc1); + tmp += graph->volt1; + + return tmp < 0 ? 0 : tmp; +} + +static int sc27xx_adc_convert_volt(struct sc27xx_adc_data *data, int channel, + int scale, int raw_adc) +{ + u32 numerator, denominator; + u32 volt; + + /* + * Convert ADC values to voltage values according to the linear graph, + * and channel 5 and channel 1 has been calibrated, so we can just + * return the voltage values calculated by the linear graph. But other + * channels need be calculated to the real voltage values with the + * voltage ratio. + */ + switch (channel) { + case 5: + return sc27xx_adc_to_volt(&big_scale_graph, raw_adc); + + case 1: + return sc27xx_adc_to_volt(&small_scale_graph, raw_adc); + + default: + volt = sc27xx_adc_to_volt(&small_scale_graph, raw_adc); + break; + } + + sc27xx_adc_volt_ratio(data, channel, scale, &numerator, &denominator); + + return (volt * denominator + numerator / 2) / numerator; +} + +static int sc27xx_adc_read_processed(struct sc27xx_adc_data *data, + int channel, int scale, int *val) +{ + int ret, raw_adc; + + ret = sc27xx_adc_read(data, channel, scale, &raw_adc); + if (ret) + return ret; + + *val = sc27xx_adc_convert_volt(data, channel, scale, raw_adc); + return 0; +} + +static int sc27xx_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct sc27xx_adc_data *data = iio_priv(indio_dev); + int scale = data->channel_scale[chan->channel]; + 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, + &tmp); + mutex_unlock(&indio_dev->mlock); + + if (ret) + return ret; + + *val = tmp; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = scale; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int sc27xx_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct sc27xx_adc_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + data->channel_scale[chan->channel] = val; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static const struct iio_info sc27xx_info = { + .read_raw = &sc27xx_adc_read_raw, + .write_raw = &sc27xx_adc_write_raw, +}; + +#define SC27XX_ADC_CHANNEL(index, mask) { \ + .type = IIO_VOLTAGE, \ + .channel = index, \ + .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, 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) +{ + int ret; + + ret = regmap_update_bits(data->regmap, SC27XX_MODULE_EN, + SC27XX_MODULE_ADC_EN, SC27XX_MODULE_ADC_EN); + if (ret) + return ret; + + /* Enable ADC work clock and controller clock */ + ret = regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN, + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN); + if (ret) + goto disable_adc; + + /* 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; + + return 0; + +disable_clk: + regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN, + SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0); +disable_adc: + regmap_update_bits(data->regmap, SC27XX_MODULE_EN, + SC27XX_MODULE_ADC_EN, 0); + + return ret; +} + +static void sc27xx_adc_disable(void *_data) +{ + struct sc27xx_adc_data *data = _data; + + /* 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); + + regmap_update_bits(data->regmap, SC27XX_MODULE_EN, + SC27XX_MODULE_ADC_EN, 0); +} + +static int sc27xx_adc_probe(struct platform_device *pdev) +{ + 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(dev, sizeof(*sc27xx_data)); + if (!indio_dev) + return -ENOMEM; + + sc27xx_data = iio_priv(indio_dev); + + sc27xx_data->regmap = dev_get_regmap(dev->parent, NULL); + if (!sc27xx_data->regmap) { + 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(dev, "failed to get ADC base address\n"); + return ret; + } + + sc27xx_data->irq = platform_get_irq(pdev, 0); + if (sc27xx_data->irq < 0) + return sc27xx_data->irq; + + ret = of_hwspin_lock_get_id(np, 0); + if (ret < 0) { + dev_err(dev, "failed to get hwspinlock id\n"); + return ret; + } + + sc27xx_data->hwlock = devm_hwspin_lock_request_specific(dev, ret); + if (!sc27xx_data->hwlock) { + dev_err(dev, "failed to request hwspinlock\n"); + return -ENXIO; + } + + sc27xx_data->dev = dev; + + ret = sc27xx_adc_enable(sc27xx_data); + if (ret) { + dev_err(dev, "failed to enable ADC module\n"); + return ret; + } + + ret = devm_add_action_or_reset(dev, sc27xx_adc_disable, sc27xx_data); + if (ret) { + dev_err(dev, "failed to add ADC disable action\n"); + return ret; + } + + 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(dev, indio_dev); + if (ret) + dev_err(dev, "could not register iio (ADC)"); + + return ret; +} + +static const struct of_device_id sc27xx_adc_of_match[] = { + { .compatible = "sprd,sc2731-adc", }, + { } +}; + +static struct platform_driver sc27xx_adc_driver = { + .probe = sc27xx_adc_probe, + .driver = { + .name = "sc27xx-adc", + .of_match_table = sc27xx_adc_of_match, + }, +}; + +module_platform_driver(sc27xx_adc_driver); + +MODULE_AUTHOR("Freeman Liu <freeman.liu@spreadtrum.com>"); +MODULE_DESCRIPTION("Spreadtrum SC27XX ADC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/sd_adc_modulator.c b/drivers/iio/adc/sd_adc_modulator.c new file mode 100644 index 000000000..327cc2097 --- /dev/null +++ b/drivers/iio/adc/sd_adc_modulator.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic sigma delta modulator driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>. + */ + +#include <linux/iio/iio.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> + +static const struct iio_info iio_sd_mod_iio_info; + +static const struct iio_chan_spec iio_sd_mod_ch = { + .type = IIO_VOLTAGE, + .indexed = 1, + .scan_type = { + .sign = 'u', + .realbits = 1, + .shift = 0, + }, +}; + +static int iio_sd_mod_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *iio; + + iio = devm_iio_device_alloc(dev, 0); + if (!iio) + return -ENOMEM; + + iio->name = dev_name(dev); + iio->info = &iio_sd_mod_iio_info; + iio->modes = INDIO_BUFFER_HARDWARE; + + iio->num_channels = 1; + iio->channels = &iio_sd_mod_ch; + + platform_set_drvdata(pdev, iio); + + return devm_iio_device_register(&pdev->dev, iio); +} + +static const struct of_device_id sd_adc_of_match[] = { + { .compatible = "sd-modulator" }, + { .compatible = "ads1201" }, + { } +}; +MODULE_DEVICE_TABLE(of, sd_adc_of_match); + +static struct platform_driver iio_sd_mod_adc = { + .driver = { + .name = "iio_sd_adc_mod", + .of_match_table = sd_adc_of_match, + }, + .probe = iio_sd_mod_probe, +}; + +module_platform_driver(iio_sd_mod_adc); + +MODULE_DESCRIPTION("Basic sigma delta modulator"); +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c new file mode 100644 index 000000000..1bc986a70 --- /dev/null +++ b/drivers/iio/adc/spear_adc.c @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ST SPEAr ADC driver + * + * Copyright 2012 Stefan Roese <sr@denx.de> + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/completion.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* SPEAR registers definitions */ +#define SPEAR600_ADC_SCAN_RATE_LO(x) ((x) & 0xFFFF) +#define SPEAR600_ADC_SCAN_RATE_HI(x) (((x) >> 0x10) & 0xFFFF) +#define SPEAR_ADC_CLK_LOW(x) (((x) & 0xf) << 0) +#define SPEAR_ADC_CLK_HIGH(x) (((x) & 0xf) << 4) + +/* Bit definitions for SPEAR_ADC_STATUS */ +#define SPEAR_ADC_STATUS_START_CONVERSION BIT(0) +#define SPEAR_ADC_STATUS_CHANNEL_NUM(x) ((x) << 1) +#define SPEAR_ADC_STATUS_ADC_ENABLE BIT(4) +#define SPEAR_ADC_STATUS_AVG_SAMPLE(x) ((x) << 5) +#define SPEAR_ADC_STATUS_VREF_INTERNAL BIT(9) + +#define SPEAR_ADC_DATA_MASK 0x03ff +#define SPEAR_ADC_DATA_BITS 10 + +#define SPEAR_ADC_MOD_NAME "spear-adc" + +#define SPEAR_ADC_CHANNEL_NUM 8 + +#define SPEAR_ADC_CLK_MIN 2500000 +#define SPEAR_ADC_CLK_MAX 20000000 + +struct adc_regs_spear3xx { + u32 status; + u32 average; + u32 scan_rate; + u32 clk; /* Not avail for 1340 & 1310 */ + u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM]; + u32 ch_data[SPEAR_ADC_CHANNEL_NUM]; +}; + +struct chan_data { + u32 lsb; + u32 msb; +}; + +struct adc_regs_spear6xx { + u32 status; + u32 pad[2]; + u32 clk; + u32 ch_ctrl[SPEAR_ADC_CHANNEL_NUM]; + struct chan_data ch_data[SPEAR_ADC_CHANNEL_NUM]; + u32 scan_rate_lo; + u32 scan_rate_hi; + struct chan_data average; +}; + +struct spear_adc_state { + struct device_node *np; + struct adc_regs_spear3xx __iomem *adc_base_spear3xx; + struct adc_regs_spear6xx __iomem *adc_base_spear6xx; + struct clk *clk; + struct completion completion; + u32 current_clk; + u32 sampling_freq; + u32 avg_samples; + u32 vref_external; + u32 value; +}; + +/* + * Functions to access some SPEAr ADC register. Abstracted into + * static inline functions, because of different register offsets + * on different SoC variants (SPEAr300 vs SPEAr600 etc). + */ +static void spear_adc_set_status(struct spear_adc_state *st, u32 val) +{ + __raw_writel(val, &st->adc_base_spear6xx->status); +} + +static void spear_adc_set_clk(struct spear_adc_state *st, u32 val) +{ + u32 clk_high, clk_low, count; + u32 apb_clk = clk_get_rate(st->clk); + + count = DIV_ROUND_UP(apb_clk, val); + clk_low = count / 2; + clk_high = count - clk_low; + st->current_clk = apb_clk / count; + + __raw_writel(SPEAR_ADC_CLK_LOW(clk_low) | SPEAR_ADC_CLK_HIGH(clk_high), + &st->adc_base_spear6xx->clk); +} + +static void spear_adc_set_ctrl(struct spear_adc_state *st, int n, + u32 val) +{ + __raw_writel(val, &st->adc_base_spear6xx->ch_ctrl[n]); +} + +static u32 spear_adc_get_average(struct spear_adc_state *st) +{ + if (of_device_is_compatible(st->np, "st,spear600-adc")) { + return __raw_readl(&st->adc_base_spear6xx->average.msb) & + SPEAR_ADC_DATA_MASK; + } else { + return __raw_readl(&st->adc_base_spear3xx->average) & + SPEAR_ADC_DATA_MASK; + } +} + +static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate) +{ + if (of_device_is_compatible(st->np, "st,spear600-adc")) { + __raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate), + &st->adc_base_spear6xx->scan_rate_lo); + __raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate), + &st->adc_base_spear6xx->scan_rate_hi); + } else { + __raw_writel(rate, &st->adc_base_spear3xx->scan_rate); + } +} + +static int spear_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct spear_adc_state *st = iio_priv(indio_dev); + u32 status; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + + status = SPEAR_ADC_STATUS_CHANNEL_NUM(chan->channel) | + SPEAR_ADC_STATUS_AVG_SAMPLE(st->avg_samples) | + SPEAR_ADC_STATUS_START_CONVERSION | + SPEAR_ADC_STATUS_ADC_ENABLE; + if (st->vref_external == 0) + status |= SPEAR_ADC_STATUS_VREF_INTERNAL; + + spear_adc_set_status(st, status); + wait_for_completion(&st->completion); /* set by ISR */ + *val = st->value; + + mutex_unlock(&indio_dev->mlock); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = st->vref_external; + *val2 = SPEAR_ADC_DATA_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->current_clk; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int spear_adc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct spear_adc_state *st = iio_priv(indio_dev); + int ret = 0; + + if (mask != IIO_CHAN_INFO_SAMP_FREQ) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + + if ((val < SPEAR_ADC_CLK_MIN) || + (val > SPEAR_ADC_CLK_MAX) || + (val2 != 0)) { + ret = -EINVAL; + goto out; + } + + spear_adc_set_clk(st, val); + +out: + mutex_unlock(&indio_dev->mlock); + return ret; +} + +#define SPEAR_ADC_CHAN(idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .channel = idx, \ +} + +static const struct iio_chan_spec spear_adc_iio_channels[] = { + SPEAR_ADC_CHAN(0), + SPEAR_ADC_CHAN(1), + SPEAR_ADC_CHAN(2), + SPEAR_ADC_CHAN(3), + SPEAR_ADC_CHAN(4), + SPEAR_ADC_CHAN(5), + SPEAR_ADC_CHAN(6), + SPEAR_ADC_CHAN(7), +}; + +static irqreturn_t spear_adc_isr(int irq, void *dev_id) +{ + struct spear_adc_state *st = dev_id; + + /* Read value to clear IRQ */ + st->value = spear_adc_get_average(st); + complete(&st->completion); + + return IRQ_HANDLED; +} + +static int spear_adc_configure(struct spear_adc_state *st) +{ + int i; + + /* Reset ADC core */ + spear_adc_set_status(st, 0); + __raw_writel(0, &st->adc_base_spear6xx->clk); + for (i = 0; i < 8; i++) + spear_adc_set_ctrl(st, i, 0); + spear_adc_set_scanrate(st, 0); + + spear_adc_set_clk(st, st->sampling_freq); + + return 0; +} + +static const struct iio_info spear_adc_info = { + .read_raw = &spear_adc_read_raw, + .write_raw = &spear_adc_write_raw, +}; + +static int spear_adc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct spear_adc_state *st; + struct iio_dev *indio_dev = NULL; + int ret = -ENODEV; + int irq; + + indio_dev = devm_iio_device_alloc(dev, sizeof(struct spear_adc_state)); + if (!indio_dev) { + dev_err(dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + st->np = np; + + /* + * SPEAr600 has a different register layout than other SPEAr SoC's + * (e.g. SPEAr3xx). Let's provide two register base addresses + * to support multi-arch kernels. + */ + st->adc_base_spear6xx = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(st->adc_base_spear6xx)) + return PTR_ERR(st->adc_base_spear6xx); + + st->adc_base_spear3xx = + (struct adc_regs_spear3xx __iomem *)st->adc_base_spear6xx; + + st->clk = devm_clk_get(dev, NULL); + if (IS_ERR(st->clk)) { + dev_err(dev, "failed getting clock\n"); + return PTR_ERR(st->clk); + } + + ret = clk_prepare_enable(st->clk); + if (ret) { + dev_err(dev, "failed enabling clock\n"); + return ret; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + ret = -EINVAL; + goto errout2; + } + + ret = devm_request_irq(dev, irq, spear_adc_isr, 0, SPEAR_ADC_MOD_NAME, + st); + if (ret < 0) { + dev_err(dev, "failed requesting interrupt\n"); + goto errout2; + } + + if (of_property_read_u32(np, "sampling-frequency", + &st->sampling_freq)) { + dev_err(dev, "sampling-frequency missing in DT\n"); + ret = -EINVAL; + goto errout2; + } + + /* + * Optional avg_samples defaults to 0, resulting in single data + * conversion + */ + of_property_read_u32(np, "average-samples", &st->avg_samples); + + /* + * Optional vref_external defaults to 0, resulting in internal vref + * selection + */ + of_property_read_u32(np, "vref-external", &st->vref_external); + + spear_adc_configure(st); + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&st->completion); + + indio_dev->name = SPEAR_ADC_MOD_NAME; + indio_dev->info = &spear_adc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = spear_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(spear_adc_iio_channels); + + ret = iio_device_register(indio_dev); + if (ret) + goto errout2; + + dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq); + + return 0; + +errout2: + clk_disable_unprepare(st->clk); + return ret; +} + +static int spear_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct spear_adc_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + clk_disable_unprepare(st->clk); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id spear_adc_dt_ids[] = { + { .compatible = "st,spear600-adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, spear_adc_dt_ids); +#endif + +static struct platform_driver spear_adc_driver = { + .probe = spear_adc_probe, + .remove = spear_adc_remove, + .driver = { + .name = SPEAR_ADC_MOD_NAME, + .of_match_table = of_match_ptr(spear_adc_dt_ids), + }, +}; + +module_platform_driver(spear_adc_driver); + +MODULE_AUTHOR("Stefan Roese <sr@denx.de>"); +MODULE_DESCRIPTION("SPEAr ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c new file mode 100644 index 000000000..20fc867e3 --- /dev/null +++ b/drivers/iio/adc/stm32-adc-core.c @@ -0,0 +1,839 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part of STM32 ADC driver + * + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>. + * + * Inspired from: fsl-imx25-tsadc + * + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#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" + +#define STM32_ADC_CORE_SLEEP_DELAY_MS 2000 + +/* SYSCFG registers */ +#define STM32MP1_SYSCFG_PMCSETR 0x04 +#define STM32MP1_SYSCFG_PMCCLRR 0x44 + +/* SYSCFG bit fields */ +#define STM32MP1_SYSCFG_ANASWVDD_MASK BIT(9) + +/* SYSCFG capability flags */ +#define HAS_VBOOSTER BIT(0) +#define HAS_ANASWVDD BIT(1) + +/** + * struct stm32_adc_common_regs - stm32 common registers + * @csr: common status register offset + * @ccr: common control register offset + * @eoc_msk: array of eoc (end of conversion flag) masks in csr for adc1..n + * @ovr_msk: array of ovr (overrun flag) masks in csr for adc1..n + * @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 eoc_msk[STM32_ADC_MAX_ADCS]; + u32 ovr_msk[STM32_ADC_MAX_ADCS]; + u32 ier; + u32 eocie_msk; +}; + +struct stm32_adc_priv; + +/** + * struct stm32_adc_priv_cfg - stm32 core compatible configuration data + * @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 + * @num_irqs: number of interrupt lines + * @num_adcs: maximum number of ADC instances in the common registers + */ +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; + unsigned int num_irqs; + unsigned int num_adcs; +}; + +/** + * struct stm32_adc_priv - stm32 ADC core private data + * @irq: irq(s) for ADC block + * @domain: irq domain reference + * @aclk: clock reference for the analog circuitry + * @bclk: bus clock common for all ADCs, depends on part used + * @max_clk_rate: desired maximum clock rate + * @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; + u32 max_clk_rate; + 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) +{ + return container_of(com, struct stm32_adc_priv, common); +} + +/* STM32F4 ADC internal common clock prescaler division ratios */ +static int stm32f4_pclk_div[] = {2, 4, 6, 8}; + +/** + * stm32f4_adc_clk_sel() - Select stm32f4 ADC common clock prescaler + * @pdev: platform device + * @priv: stm32 ADC core private data + * Select clock prescaler used for analog conversions, before using ADC. + */ +static int stm32f4_adc_clk_sel(struct platform_device *pdev, + struct stm32_adc_priv *priv) +{ + unsigned long rate; + u32 val; + int i; + + /* stm32f4 has one clk input for analog (mandatory), enforce it here */ + if (!priv->aclk) { + dev_err(&pdev->dev, "No 'adc' clock found\n"); + return -ENOENT; + } + + rate = clk_get_rate(priv->aclk); + if (!rate) { + dev_err(&pdev->dev, "Invalid clock rate: 0\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) { + if ((rate / stm32f4_pclk_div[i]) <= priv->max_clk_rate) + break; + } + if (i >= ARRAY_SIZE(stm32f4_pclk_div)) { + dev_err(&pdev->dev, "adc clk selection failed\n"); + return -EINVAL; + } + + priv->common.rate = rate / stm32f4_pclk_div[i]; + val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR); + val &= ~STM32F4_ADC_ADCPRE_MASK; + val |= i << STM32F4_ADC_ADCPRE_SHIFT; + writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR); + + dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n", + priv->common.rate / 1000); + + return 0; +} + +/** + * struct stm32h7_adc_ck_spec - specification for stm32h7 adc clock + * @ckmode: ADC clock mode, Async or sync with prescaler. + * @presc: prescaler bitfield for async clock mode + * @div: prescaler division ratio + */ +struct stm32h7_adc_ck_spec { + u32 ckmode; + u32 presc; + int div; +}; + +static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = { + /* 00: CK_ADC[1..3]: Asynchronous clock modes */ + { 0, 0, 1 }, + { 0, 1, 2 }, + { 0, 2, 4 }, + { 0, 3, 6 }, + { 0, 4, 8 }, + { 0, 5, 10 }, + { 0, 6, 12 }, + { 0, 7, 16 }, + { 0, 8, 32 }, + { 0, 9, 64 }, + { 0, 10, 128 }, + { 0, 11, 256 }, + /* HCLK used: Synchronous clock modes (1, 2 or 4 prescaler) */ + { 1, 0, 1 }, + { 2, 0, 2 }, + { 3, 0, 4 }, +}; + +static int stm32h7_adc_clk_sel(struct platform_device *pdev, + struct stm32_adc_priv *priv) +{ + u32 ckmode, presc, val; + unsigned long rate; + int i, div; + + /* stm32h7 bus clock is common for all ADC instances (mandatory) */ + if (!priv->bclk) { + dev_err(&pdev->dev, "No 'bus' clock found\n"); + return -ENOENT; + } + + /* + * stm32h7 can use either 'bus' or 'adc' clock for analog circuitry. + * So, choice is to have bus clock mandatory and adc clock optional. + * If optional 'adc' clock has been found, then try to use it first. + */ + if (priv->aclk) { + /* + * Asynchronous clock modes (e.g. ckmode == 0) + * From spec: PLL output musn't exceed max rate + */ + rate = clk_get_rate(priv->aclk); + if (!rate) { + dev_err(&pdev->dev, "Invalid adc clock rate: 0\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) { + ckmode = stm32h7_adc_ckmodes_spec[i].ckmode; + presc = stm32h7_adc_ckmodes_spec[i].presc; + div = stm32h7_adc_ckmodes_spec[i].div; + + if (ckmode) + continue; + + if ((rate / div) <= priv->max_clk_rate) + goto out; + } + } + + /* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */ + rate = clk_get_rate(priv->bclk); + if (!rate) { + dev_err(&pdev->dev, "Invalid bus clock rate: 0\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) { + ckmode = stm32h7_adc_ckmodes_spec[i].ckmode; + presc = stm32h7_adc_ckmodes_spec[i].presc; + div = stm32h7_adc_ckmodes_spec[i].div; + + if (!ckmode) + continue; + + if ((rate / div) <= priv->max_clk_rate) + goto out; + } + + dev_err(&pdev->dev, "adc clk selection failed\n"); + return -EINVAL; + +out: + /* rate used later by each ADC instance to control BOOST mode */ + priv->common.rate = rate / div; + + /* Set common clock mode and prescaler */ + val = readl_relaxed(priv->common.base + STM32H7_ADC_CCR); + val &= ~(STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK); + val |= ckmode << STM32H7_CKMODE_SHIFT; + val |= presc << STM32H7_PRESC_SHIFT; + writel_relaxed(val, priv->common.base + STM32H7_ADC_CCR); + + dev_dbg(&pdev->dev, "Using %s clock/%d source at %ld kHz\n", + ckmode ? "bus" : "adc", div, priv->common.rate / 1000); + + return 0; +} + +/* STM32F4 common registers definitions */ +static const struct stm32_adc_common_regs stm32f4_adc_common_regs = { + .csr = STM32F4_ADC_CSR, + .ccr = STM32F4_ADC_CCR, + .eoc_msk = { STM32F4_EOC1, STM32F4_EOC2, STM32F4_EOC3}, + .ovr_msk = { STM32F4_OVR1, STM32F4_OVR2, STM32F4_OVR3}, + .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, + .eoc_msk = { STM32H7_EOC_MST, STM32H7_EOC_SLV}, + .ovr_msk = { STM32H7_OVR_MST, STM32H7_OVR_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) +{ + struct stm32_adc_priv *priv = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + int i; + u32 status; + + chained_irq_enter(chip, desc); + status = readl_relaxed(priv->common.base + priv->cfg->regs->csr); + + /* + * 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). + */ + for (i = 0; i < priv->cfg->num_adcs; i++) { + if ((status & priv->cfg->regs->eoc_msk[i] && + stm32_adc_eoc_enabled(priv, i)) || + (status & priv->cfg->regs->ovr_msk[i])) + generic_handle_irq(irq_find_mapping(priv->domain, i)); + } + + chained_irq_exit(chip, desc); +}; + +static int stm32_adc_domain_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_chip_data(irq, d->host_data); + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_level_irq); + + return 0; +} + +static void stm32_adc_domain_unmap(struct irq_domain *d, unsigned int irq) +{ + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); +} + +static const struct irq_domain_ops stm32_adc_domain_ops = { + .map = stm32_adc_domain_map, + .unmap = stm32_adc_domain_unmap, + .xlate = irq_domain_xlate_onecell, +}; + +static int stm32_adc_irq_probe(struct platform_device *pdev, + struct stm32_adc_priv *priv) +{ + struct device_node *np = pdev->dev.of_node; + unsigned int i; + + /* + * Interrupt(s) must be provided, depending on the compatible: + * - stm32f4/h7 shares a common interrupt line. + * - stm32mp1, has one line per ADC + */ + for (i = 0; i < priv->cfg->num_irqs; i++) { + priv->irq[i] = platform_get_irq(pdev, i); + if (priv->irq[i] < 0) + return priv->irq[i]; + } + + priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0, + &stm32_adc_domain_ops, + priv); + if (!priv->domain) { + dev_err(&pdev->dev, "Failed to add irq domain\n"); + return -ENOMEM; + } + + for (i = 0; i < priv->cfg->num_irqs; i++) { + irq_set_chained_handler(priv->irq[i], stm32_adc_irq_handler); + irq_set_handler_data(priv->irq[i], priv); + } + + return 0; +} + +static void stm32_adc_irq_remove(struct platform_device *pdev, + struct stm32_adc_priv *priv) +{ + int hwirq; + unsigned int i; + + for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++) + irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq)); + irq_domain_remove(priv->domain); + + for (i = 0; i < priv->cfg->num_irqs; i++) + irq_set_chained_handler(priv->irq[i], NULL); +} + +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) + return dev_err_probe(dev, ret, "Can't probe syscfg\n"); + + 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) + return dev_err_probe(dev, ret, "can't get booster\n"); + + 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) + return dev_err_probe(dev, ret, "can't get vdd\n"); + + 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; + struct device *dev = &pdev->dev; + struct device_node *np = pdev->dev.of_node; + struct resource *res; + u32 max_rate; + int ret; + + if (!pdev->dev.of_node) + return -ENODEV; + + 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; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->common.base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->common.base)) + 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)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->vdda), + "vdda get failed\n"); + + priv->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(priv->vref)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->vref), + "vref get failed\n"); + + priv->aclk = devm_clk_get_optional(&pdev->dev, "adc"); + if (IS_ERR(priv->aclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->aclk), + "Can't get 'adc' clock\n"); + + priv->bclk = devm_clk_get_optional(&pdev->dev, "bus"); + if (IS_ERR(priv->bclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->bclk), + "Can't get 'bus' clock\n"); + + 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 = of_property_read_u32(pdev->dev.of_node, "st,max-clk-rate-hz", + &max_rate); + if (!ret) + priv->max_clk_rate = min(max_rate, priv->cfg->max_clk_rate_hz); + else + priv->max_clk_rate = priv->cfg->max_clk_rate_hz; + + ret = priv->cfg->clk_sel(pdev, priv); + if (ret < 0) + goto err_hw_stop; + + ret = stm32_adc_irq_probe(pdev, priv); + if (ret < 0) + goto err_hw_stop; + + ret = of_platform_populate(np, NULL, NULL, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to populate DT children\n"); + 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_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; +} + +static int stm32_adc_remove(struct platform_device *pdev) +{ + 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); + 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); +} + +static int stm32_adc_core_runtime_idle(struct device *dev) +{ + pm_runtime_mark_last_busy(dev); + + return 0; +} +#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, + stm32_adc_core_runtime_idle) +}; + +static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = { + .regs = &stm32f4_adc_common_regs, + .clk_sel = stm32f4_adc_clk_sel, + .max_clk_rate_hz = 36000000, + .num_irqs = 1, + .num_adcs = 3, +}; + +static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = { + .regs = &stm32h7_adc_common_regs, + .clk_sel = stm32h7_adc_clk_sel, + .max_clk_rate_hz = 36000000, + .has_syscfg = HAS_VBOOSTER, + .num_irqs = 1, + .num_adcs = 2, +}; + +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 = 36000000, + .has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD, + .num_irqs = 2, + .num_adcs = 2, +}; + +static const struct of_device_id stm32_adc_of_match[] = { + { + .compatible = "st,stm32f4-adc-core", + .data = (void *)&stm32f4_adc_priv_cfg + }, { + .compatible = "st,stm32h7-adc-core", + .data = (void *)&stm32h7_adc_priv_cfg + }, { + .compatible = "st,stm32mp1-adc-core", + .data = (void *)&stm32mp1_adc_priv_cfg + }, { + }, +}; +MODULE_DEVICE_TABLE(of, stm32_adc_of_match); + +static struct platform_driver stm32_adc_driver = { + .probe = stm32_adc_probe, + .remove = stm32_adc_remove, + .driver = { + .name = "stm32-adc-core", + .of_match_table = stm32_adc_of_match, + .pm = &stm32_adc_core_pm_ops, + }, +}; +module_platform_driver(stm32_adc_driver); + +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC core driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:stm32-adc-core"); diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h new file mode 100644 index 000000000..2322809bf --- /dev/null +++ b/drivers/iio/adc/stm32-adc-core.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is part of STM32 ADC driver + * + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>. + * + */ + +#ifndef __STM32_ADC_H +#define __STM32_ADC_H + +/* + * STM32 - ADC global register map + * ________________________________________________________ + * | Offset | Register | + * -------------------------------------------------------- + * | 0x000 | Master ADC1 | + * -------------------------------------------------------- + * | 0x100 | Slave ADC2 | + * -------------------------------------------------------- + * | 0x200 | Slave ADC3 | + * -------------------------------------------------------- + * | 0x300 | Master & Slave common regs | + * -------------------------------------------------------- + */ +#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_OVR BIT(5) +#define STM32F4_STRT BIT(4) +#define STM32F4_EOC BIT(1) + +/* STM32F4_ADC_CR1 - bit fields */ +#define STM32F4_OVRIE BIT(26) +#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_OVR3 BIT(21) +#define STM32F4_EOC3 BIT(17) +#define STM32F4_OVR2 BIT(13) +#define STM32F4_EOC2 BIT(9) +#define STM32F4_OVR1 BIT(5) +#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_OVR BIT(4) +#define STM32H7_EOC BIT(2) +#define STM32H7_ADRDY BIT(0) + +/* STM32H7_ADC_IER - bit fields */ +#define STM32H7_OVRIE STM32H7_OVR +#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_OVR_SLV BIT(20) +#define STM32H7_EOC_SLV BIT(18) +#define STM32H7_OVR_MST BIT(4) +#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 + * @phys_base: control registers base physical addr + * @rate: clock rate used for analog circuitry + * @vref_mv: vref voltage (mv) + */ +struct stm32_adc_common { + void __iomem *base; + phys_addr_t phys_base; + unsigned long rate; + int vref_mv; +}; + +#endif diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c new file mode 100644 index 000000000..e60ad4819 --- /dev/null +++ b/drivers/iio/adc/stm32-adc.c @@ -0,0 +1,2133 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part of STM32 ADC driver + * + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/iio/iio.h> +#include <linux/iio/buffer.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/io.h> +#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" + +/* Number of linear calibration shadow registers / LINCALRDYW control bits */ +#define STM32H7_LINCALFACT_NUM 6 + +/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */ +#define STM32H7_BOOST_CLKRATE 20000000UL + +#define STM32_ADC_CH_MAX 20 /* max number of channels */ +#define STM32_ADC_CH_SZ 10 /* max channel name size */ +#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */ +#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 + +/* External trigger enable */ +enum stm32_adc_exten { + STM32_EXTEN_SWTRIG, + STM32_EXTEN_HWTRIG_RISING_EDGE, + STM32_EXTEN_HWTRIG_FALLING_EDGE, + STM32_EXTEN_HWTRIG_BOTH_EDGES, +}; + +/* extsel - trigger mux selection value */ +enum stm32_adc_extsel { + STM32_EXT0, + STM32_EXT1, + STM32_EXT2, + STM32_EXT3, + STM32_EXT4, + STM32_EXT5, + STM32_EXT6, + STM32_EXT7, + STM32_EXT8, + STM32_EXT9, + STM32_EXT10, + STM32_EXT11, + STM32_EXT12, + STM32_EXT13, + STM32_EXT14, + STM32_EXT15, + STM32_EXT16, + STM32_EXT17, + STM32_EXT18, + STM32_EXT19, + STM32_EXT20, +}; + +/** + * struct stm32_adc_trig_info - ADC trigger info + * @name: name of the trigger, corresponding to its source + * @extsel: trigger selection + */ +struct stm32_adc_trig_info { + const char *name; + enum stm32_adc_extsel extsel; +}; + +/** + * struct stm32_adc_calib - optional adc calibration data + * @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; +}; + +/** + * struct stm32_adc_regs - stm32 ADC misc registers & bitfield desc + * @reg: register offset + * @mask: bitfield mask + * @shift: left shift + */ +struct stm32_adc_regs { + int reg; + int mask; + int shift; +}; + +/** + * struct stm32_adc_regspec - stm32 registers definition + * @dr: data register offset + * @ier_eoc: interrupt enable register & eocie bitfield + * @ier_ovr: interrupt enable register & overrun bitfield + * @isr_eoc: interrupt status register & eoc bitfield + * @isr_ovr: interrupt status register & overrun bitfield + * @sqr: reference to sequence registers array + * @exten: trigger control register & bitfield + * @extsel: trigger selection register & bitfield + * @res: resolution selection register & bitfield + * @smpr: smpr1 & smpr2 registers offset array + * @smp_bits: smpr1 & smpr2 index and bitfields + */ +struct stm32_adc_regspec { + const u32 dr; + const struct stm32_adc_regs ier_eoc; + const struct stm32_adc_regs ier_ovr; + const struct stm32_adc_regs isr_eoc; + const struct stm32_adc_regs isr_ovr; + const struct stm32_adc_regs *sqr; + const struct stm32_adc_regs exten; + const struct stm32_adc_regs extsel; + const struct stm32_adc_regs res; + const u32 smpr[2]; + const struct stm32_adc_regs *smp_bits; +}; + +struct stm32_adc; + +/** + * struct stm32_adc_cfg - stm32 compatible configuration data + * @regs: registers descriptions + * @adc_info: per instance input channels definitions + * @trigs: external trigger sources + * @clk_required: clock is required + * @has_vregready: vregready status flag presence + * @prepare: optional prepare routine (power-up, enable) + * @start_conv: routine to start conversions + * @stop_conv: routine to stop conversions + * @unprepare: optional unprepare routine (disable, power-down) + * @irq_clear: routine to clear irqs + * @smp_cycles: programmable sampling time (ADC clock cycles) + */ +struct stm32_adc_cfg { + const struct stm32_adc_regspec *regs; + const struct stm32_adc_info *adc_info; + struct stm32_adc_trig_info *trigs; + bool clk_required; + bool has_vregready; + int (*prepare)(struct iio_dev *); + void (*start_conv)(struct iio_dev *, bool dma); + void (*stop_conv)(struct iio_dev *); + void (*unprepare)(struct iio_dev *); + void (*irq_clear)(struct iio_dev *indio_dev, u32 msk); + const unsigned int *smp_cycles; +}; + +/** + * struct stm32_adc - private data of each ADC IIO instance + * @common: reference to ADC block common data + * @offset: ADC instance register offset in ADC block + * @cfg: compatible configuration data + * @completion: end of single conversion completion + * @buffer: data buffer + * @clk: clock for this adc instance + * @irq: interrupt for this adc instance + * @lock: spinlock + * @bufi: data buffer index + * @num_conv: expected number of scan conversions + * @res: data resolution (e.g. RES bitfield value) + * @trigger_polarity: external trigger polarity (e.g. exten) + * @dma_chan: dma channel + * @rx_buf: dma rx buffer cpu address + * @rx_dma_buf: dma rx buffer bus address + * @rx_buf_sz: dma rx buffer size + * @difsel: bitmask to set single-ended/differential channel + * @pcsel: bitmask to preselect channels on some devices + * @smpr_val: sampling time settings (e.g. smpr1 / smpr2) + * @cal: optional calibration data on some devices + * @chan_name: channel name array + */ +struct stm32_adc { + struct stm32_adc_common *common; + u32 offset; + const struct stm32_adc_cfg *cfg; + struct completion completion; + u16 buffer[STM32_ADC_MAX_SQ]; + struct clk *clk; + int irq; + spinlock_t lock; /* interrupt lock */ + unsigned int bufi; + unsigned int num_conv; + u32 res; + u32 trigger_polarity; + struct dma_chan *dma_chan; + u8 *rx_buf; + dma_addr_t rx_dma_buf; + unsigned int rx_buf_sz; + u32 difsel; + u32 pcsel; + u32 smpr_val[2]; + struct stm32_adc_calib cal; + char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ]; +}; + +struct stm32_adc_diff_channel { + u32 vinp; + u32 vinn; +}; + +/** + * struct stm32_adc_info - stm32 ADC, per instance config data + * @max_channels: Number of channels + * @resolutions: available resolutions + * @num_res: number of available resolutions + */ +struct stm32_adc_info { + int max_channels; + const unsigned int *resolutions; + const unsigned int num_res; +}; + +static const unsigned int stm32f4_adc_resolutions[] = { + /* sorted values so the index matches RES[1:0] in STM32F4_ADC_CR1 */ + 12, 10, 8, 6, +}; + +/* stm32f4 can have up to 16 channels */ +static const struct stm32_adc_info stm32f4_adc_info = { + .max_channels = 16, + .resolutions = stm32f4_adc_resolutions, + .num_res = ARRAY_SIZE(stm32f4_adc_resolutions), +}; + +static const unsigned int stm32h7_adc_resolutions[] = { + /* sorted values so the index matches RES[2:0] in STM32H7_ADC_CFGR */ + 16, 14, 12, 10, 8, +}; + +/* stm32h7 can have up to 20 channels */ +static const struct stm32_adc_info stm32h7_adc_info = { + .max_channels = STM32_ADC_CH_MAX, + .resolutions = stm32h7_adc_resolutions, + .num_res = ARRAY_SIZE(stm32h7_adc_resolutions), +}; + +/* + * stm32f4_sq - describe regular sequence registers + * - L: sequence len (register & bit field) + * - SQ1..SQ16: sequence entries (register & bit field) + */ +static const struct stm32_adc_regs stm32f4_sq[STM32_ADC_MAX_SQ + 1] = { + /* L: len bit field description to be kept as first element */ + { STM32F4_ADC_SQR1, GENMASK(23, 20), 20 }, + /* SQ1..SQ16 registers & bit fields (reg, mask, shift) */ + { STM32F4_ADC_SQR3, GENMASK(4, 0), 0 }, + { STM32F4_ADC_SQR3, GENMASK(9, 5), 5 }, + { STM32F4_ADC_SQR3, GENMASK(14, 10), 10 }, + { STM32F4_ADC_SQR3, GENMASK(19, 15), 15 }, + { STM32F4_ADC_SQR3, GENMASK(24, 20), 20 }, + { STM32F4_ADC_SQR3, GENMASK(29, 25), 25 }, + { STM32F4_ADC_SQR2, GENMASK(4, 0), 0 }, + { STM32F4_ADC_SQR2, GENMASK(9, 5), 5 }, + { STM32F4_ADC_SQR2, GENMASK(14, 10), 10 }, + { STM32F4_ADC_SQR2, GENMASK(19, 15), 15 }, + { STM32F4_ADC_SQR2, GENMASK(24, 20), 20 }, + { STM32F4_ADC_SQR2, GENMASK(29, 25), 25 }, + { STM32F4_ADC_SQR1, GENMASK(4, 0), 0 }, + { STM32F4_ADC_SQR1, GENMASK(9, 5), 5 }, + { STM32F4_ADC_SQR1, GENMASK(14, 10), 10 }, + { STM32F4_ADC_SQR1, GENMASK(19, 15), 15 }, +}; + +/* STM32F4 external trigger sources for all instances */ +static struct stm32_adc_trig_info stm32f4_adc_trigs[] = { + { TIM1_CH1, STM32_EXT0 }, + { TIM1_CH2, STM32_EXT1 }, + { TIM1_CH3, STM32_EXT2 }, + { TIM2_CH2, STM32_EXT3 }, + { TIM2_CH3, STM32_EXT4 }, + { TIM2_CH4, STM32_EXT5 }, + { TIM2_TRGO, STM32_EXT6 }, + { TIM3_CH1, STM32_EXT7 }, + { TIM3_TRGO, STM32_EXT8 }, + { TIM4_CH4, STM32_EXT9 }, + { TIM5_CH1, STM32_EXT10 }, + { TIM5_CH2, STM32_EXT11 }, + { TIM5_CH3, STM32_EXT12 }, + { TIM8_CH1, STM32_EXT13 }, + { TIM8_TRGO, STM32_EXT14 }, + {}, /* sentinel */ +}; + +/* + * stm32f4_smp_bits[] - describe sampling time register index & bit fields + * Sorted so it can be indexed by channel number. + */ +static const struct stm32_adc_regs stm32f4_smp_bits[] = { + /* STM32F4_ADC_SMPR2: smpr[] index, mask, shift for SMP0 to SMP9 */ + { 1, GENMASK(2, 0), 0 }, + { 1, GENMASK(5, 3), 3 }, + { 1, GENMASK(8, 6), 6 }, + { 1, GENMASK(11, 9), 9 }, + { 1, GENMASK(14, 12), 12 }, + { 1, GENMASK(17, 15), 15 }, + { 1, GENMASK(20, 18), 18 }, + { 1, GENMASK(23, 21), 21 }, + { 1, GENMASK(26, 24), 24 }, + { 1, GENMASK(29, 27), 27 }, + /* STM32F4_ADC_SMPR1, smpr[] index, mask, shift for SMP10 to SMP18 */ + { 0, GENMASK(2, 0), 0 }, + { 0, GENMASK(5, 3), 3 }, + { 0, GENMASK(8, 6), 6 }, + { 0, GENMASK(11, 9), 9 }, + { 0, GENMASK(14, 12), 12 }, + { 0, GENMASK(17, 15), 15 }, + { 0, GENMASK(20, 18), 18 }, + { 0, GENMASK(23, 21), 21 }, + { 0, GENMASK(26, 24), 24 }, +}; + +/* STM32F4 programmable sampling time (ADC clock cycles) */ +static const unsigned int stm32f4_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = { + 3, 15, 28, 56, 84, 112, 144, 480, +}; + +static const struct stm32_adc_regspec stm32f4_adc_regspec = { + .dr = STM32F4_ADC_DR, + .ier_eoc = { STM32F4_ADC_CR1, STM32F4_EOCIE }, + .ier_ovr = { STM32F4_ADC_CR1, STM32F4_OVRIE }, + .isr_eoc = { STM32F4_ADC_SR, STM32F4_EOC }, + .isr_ovr = { STM32F4_ADC_SR, STM32F4_OVR }, + .sqr = stm32f4_sq, + .exten = { STM32F4_ADC_CR2, STM32F4_EXTEN_MASK, STM32F4_EXTEN_SHIFT }, + .extsel = { STM32F4_ADC_CR2, STM32F4_EXTSEL_MASK, + STM32F4_EXTSEL_SHIFT }, + .res = { STM32F4_ADC_CR1, STM32F4_RES_MASK, STM32F4_RES_SHIFT }, + .smpr = { STM32F4_ADC_SMPR1, STM32F4_ADC_SMPR2 }, + .smp_bits = stm32f4_smp_bits, +}; + +static const struct stm32_adc_regs stm32h7_sq[STM32_ADC_MAX_SQ + 1] = { + /* L: len bit field description to be kept as first element */ + { STM32H7_ADC_SQR1, GENMASK(3, 0), 0 }, + /* SQ1..SQ16 registers & bit fields (reg, mask, shift) */ + { STM32H7_ADC_SQR1, GENMASK(10, 6), 6 }, + { STM32H7_ADC_SQR1, GENMASK(16, 12), 12 }, + { STM32H7_ADC_SQR1, GENMASK(22, 18), 18 }, + { STM32H7_ADC_SQR1, GENMASK(28, 24), 24 }, + { STM32H7_ADC_SQR2, GENMASK(4, 0), 0 }, + { STM32H7_ADC_SQR2, GENMASK(10, 6), 6 }, + { STM32H7_ADC_SQR2, GENMASK(16, 12), 12 }, + { STM32H7_ADC_SQR2, GENMASK(22, 18), 18 }, + { STM32H7_ADC_SQR2, GENMASK(28, 24), 24 }, + { STM32H7_ADC_SQR3, GENMASK(4, 0), 0 }, + { STM32H7_ADC_SQR3, GENMASK(10, 6), 6 }, + { STM32H7_ADC_SQR3, GENMASK(16, 12), 12 }, + { STM32H7_ADC_SQR3, GENMASK(22, 18), 18 }, + { STM32H7_ADC_SQR3, GENMASK(28, 24), 24 }, + { STM32H7_ADC_SQR4, GENMASK(4, 0), 0 }, + { STM32H7_ADC_SQR4, GENMASK(10, 6), 6 }, +}; + +/* STM32H7 external trigger sources for all instances */ +static struct stm32_adc_trig_info stm32h7_adc_trigs[] = { + { TIM1_CH1, STM32_EXT0 }, + { TIM1_CH2, STM32_EXT1 }, + { TIM1_CH3, STM32_EXT2 }, + { TIM2_CH2, STM32_EXT3 }, + { TIM3_TRGO, STM32_EXT4 }, + { TIM4_CH4, STM32_EXT5 }, + { TIM8_TRGO, STM32_EXT7 }, + { TIM8_TRGO2, STM32_EXT8 }, + { TIM1_TRGO, STM32_EXT9 }, + { TIM1_TRGO2, STM32_EXT10 }, + { TIM2_TRGO, STM32_EXT11 }, + { TIM4_TRGO, STM32_EXT12 }, + { TIM6_TRGO, STM32_EXT13 }, + { TIM15_TRGO, STM32_EXT14 }, + { TIM3_CH4, STM32_EXT15 }, + { LPTIM1_OUT, STM32_EXT18 }, + { LPTIM2_OUT, STM32_EXT19 }, + { LPTIM3_OUT, STM32_EXT20 }, + {}, +}; + +/* + * stm32h7_smp_bits - describe sampling time register index & bit fields + * Sorted so it can be indexed by channel number. + */ +static const struct stm32_adc_regs stm32h7_smp_bits[] = { + /* STM32H7_ADC_SMPR1, smpr[] index, mask, shift for SMP0 to SMP9 */ + { 0, GENMASK(2, 0), 0 }, + { 0, GENMASK(5, 3), 3 }, + { 0, GENMASK(8, 6), 6 }, + { 0, GENMASK(11, 9), 9 }, + { 0, GENMASK(14, 12), 12 }, + { 0, GENMASK(17, 15), 15 }, + { 0, GENMASK(20, 18), 18 }, + { 0, GENMASK(23, 21), 21 }, + { 0, GENMASK(26, 24), 24 }, + { 0, GENMASK(29, 27), 27 }, + /* STM32H7_ADC_SMPR2, smpr[] index, mask, shift for SMP10 to SMP19 */ + { 1, GENMASK(2, 0), 0 }, + { 1, GENMASK(5, 3), 3 }, + { 1, GENMASK(8, 6), 6 }, + { 1, GENMASK(11, 9), 9 }, + { 1, GENMASK(14, 12), 12 }, + { 1, GENMASK(17, 15), 15 }, + { 1, GENMASK(20, 18), 18 }, + { 1, GENMASK(23, 21), 21 }, + { 1, GENMASK(26, 24), 24 }, + { 1, GENMASK(29, 27), 27 }, +}; + +/* STM32H7 programmable sampling time (ADC clock cycles, rounded down) */ +static const unsigned int stm32h7_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = { + 1, 2, 8, 16, 32, 64, 387, 810, +}; + +static const struct stm32_adc_regspec stm32h7_adc_regspec = { + .dr = STM32H7_ADC_DR, + .ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE }, + .ier_ovr = { STM32H7_ADC_IER, STM32H7_OVRIE }, + .isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC }, + .isr_ovr = { STM32H7_ADC_ISR, STM32H7_OVR }, + .sqr = stm32h7_sq, + .exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT }, + .extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK, + STM32H7_EXTSEL_SHIFT }, + .res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT }, + .smpr = { STM32H7_ADC_SMPR1, STM32H7_ADC_SMPR2 }, + .smp_bits = stm32h7_smp_bits, +}; + +/** + * STM32 ADC registers access routines + * @adc: stm32 adc instance + * @reg: reg offset in adc instance + * + * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp. + * for adc1, adc2 and adc3. + */ +static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg) +{ + return readl_relaxed(adc->common->base + adc->offset + reg); +} + +#define stm32_adc_readl_addr(addr) stm32_adc_readl(adc, addr) + +#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \ + readx_poll_timeout(stm32_adc_readl_addr, reg, val, \ + cond, sleep_us, timeout_us) + +static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg) +{ + return readw_relaxed(adc->common->base + adc->offset + reg); +} + +static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val) +{ + writel_relaxed(val, adc->common->base + adc->offset + reg); +} + +static void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits) +{ + unsigned long flags; + + spin_lock_irqsave(&adc->lock, flags); + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits); + spin_unlock_irqrestore(&adc->lock, flags); +} + +static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits) +{ + unsigned long flags; + + spin_lock_irqsave(&adc->lock, flags); + stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits); + spin_unlock_irqrestore(&adc->lock, flags); +} + +/** + * stm32_adc_conv_irq_enable() - Enable end of conversion interrupt + * @adc: stm32 adc instance + */ +static void stm32_adc_conv_irq_enable(struct stm32_adc *adc) +{ + stm32_adc_set_bits(adc, adc->cfg->regs->ier_eoc.reg, + adc->cfg->regs->ier_eoc.mask); +}; + +/** + * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt + * @adc: stm32 adc instance + */ +static void stm32_adc_conv_irq_disable(struct stm32_adc *adc) +{ + stm32_adc_clr_bits(adc, adc->cfg->regs->ier_eoc.reg, + adc->cfg->regs->ier_eoc.mask); +} + +static void stm32_adc_ovr_irq_enable(struct stm32_adc *adc) +{ + stm32_adc_set_bits(adc, adc->cfg->regs->ier_ovr.reg, + adc->cfg->regs->ier_ovr.mask); +} + +static void stm32_adc_ovr_irq_disable(struct stm32_adc *adc) +{ + stm32_adc_clr_bits(adc, adc->cfg->regs->ier_ovr.reg, + adc->cfg->regs->ier_ovr.mask); +} + +static void stm32_adc_set_res(struct stm32_adc *adc) +{ + const struct stm32_adc_regs *res = &adc->cfg->regs->res; + u32 val; + + val = stm32_adc_readl(adc, res->reg); + val = (val & ~res->mask) | (adc->res << res->shift); + stm32_adc_writel(adc, res->reg, val); +} + +static int stm32_adc_hw_stop(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct stm32_adc *adc = iio_priv(indio_dev); + + if (adc->cfg->unprepare) + adc->cfg->unprepare(indio_dev); + + if (adc->clk) + clk_disable_unprepare(adc->clk); + + return 0; +} + +static int stm32_adc_hw_start(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct stm32_adc *adc = iio_priv(indio_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(indio_dev); + 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. + * @indio_dev: IIO device instance + * @dma: use dma to transfer conversion result + * + * Start conversions for regular channels. + * Also take care of normal or DMA mode. Circular DMA may be used for regular + * conversions, in IIO buffer modes. Otherwise, use ADC interrupt with direct + * DR read instead (e.g. read_raw, or triggered buffer mode without DMA). + */ +static void stm32f4_adc_start_conv(struct iio_dev *indio_dev, bool dma) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN); + + if (dma) + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, + STM32F4_DMA | STM32F4_DDS); + + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON); + + /* Wait for Power-up time (tSTAB from datasheet) */ + usleep_range(2, 3); + + /* Software start ? (e.g. trigger detection disabled ?) */ + if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_EXTEN_MASK)) + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART); +} + +static void stm32f4_adc_stop_conv(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK); + stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT); + + stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN); + stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, + STM32F4_ADON | STM32F4_DMA | STM32F4_DDS); +} + +static void stm32f4_adc_irq_clear(struct iio_dev *indio_dev, u32 msk) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + stm32_adc_clr_bits(adc, adc->cfg->regs->isr_eoc.reg, msk); +} + +static void stm32h7_adc_start_conv(struct iio_dev *indio_dev, bool dma) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + enum stm32h7_adc_dmngt dmngt; + unsigned long flags; + u32 val; + + if (dma) + dmngt = STM32H7_DMNGT_DMA_CIRC; + else + dmngt = STM32H7_DMNGT_DR_ONLY; + + spin_lock_irqsave(&adc->lock, flags); + val = stm32_adc_readl(adc, STM32H7_ADC_CFGR); + val = (val & ~STM32H7_DMNGT_MASK) | (dmngt << STM32H7_DMNGT_SHIFT); + stm32_adc_writel(adc, STM32H7_ADC_CFGR, val); + spin_unlock_irqrestore(&adc->lock, flags); + + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART); +} + +static void stm32h7_adc_stop_conv(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + u32 val; + + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTP); + + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & (STM32H7_ADSTART)), + 100, STM32_ADC_TIMEOUT_US); + if (ret) + dev_warn(&indio_dev->dev, "stop failed\n"); + + stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK); +} + +static void stm32h7_adc_irq_clear(struct iio_dev *indio_dev, u32 msk) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + /* On STM32H7 IRQs are cleared by writing 1 into ISR register */ + stm32_adc_set_bits(adc, adc->cfg->regs->isr_eoc.reg, msk); +} + +static int stm32h7_adc_exit_pwr_down(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + u32 val; + + /* Exit deep power down, then enable ADC voltage regulator */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD); + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN); + + if (adc->common->rate > STM32H7_BOOST_CLKRATE) + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST); + + /* Wait for startup time */ + if (!adc->cfg->has_vregready) { + usleep_range(10, 20); + return 0; + } + + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val, + val & STM32MP1_VREGREADY, 100, + STM32_ADC_TIMEOUT_US); + if (ret) { + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD); + dev_err(&indio_dev->dev, "Failed to exit power down\n"); + } + + return ret; +} + +static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc) +{ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST); + + /* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */ + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD); +} + +static int stm32h7_adc_enable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + u32 val; + + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADEN); + + /* Poll for ADRDY to be set (after adc startup time) */ + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val, + val & STM32H7_ADRDY, + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADDIS); + dev_err(&indio_dev->dev, "Failed to enable ADC\n"); + } else { + /* Clear ADRDY by writing one */ + stm32_adc_set_bits(adc, STM32H7_ADC_ISR, STM32H7_ADRDY); + } + + return ret; +} + +static void stm32h7_adc_disable(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + u32 val; + + /* Disable ADC and wait until it's effectively disabled */ + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADDIS); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & STM32H7_ADEN), 100, + STM32_ADC_TIMEOUT_US); + if (ret) + dev_warn(&indio_dev->dev, "Failed to disable\n"); +} + +/** + * stm32h7_adc_read_selfcalib() - read calibration shadow regs, save result + * @indio_dev: IIO device instance + * Note: Must be called once ADC is enabled, so LINCALRDYW[1..6] are writable + */ +static int stm32h7_adc_read_selfcalib(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int i, ret; + u32 lincalrdyw_mask, val; + + /* Read linearity calibration */ + lincalrdyw_mask = STM32H7_LINCALRDYW6; + for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) { + /* Clear STM32H7_LINCALRDYW[6..1]: transfer calib to CALFACT2 */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask); + + /* Poll: wait calib data to be ready in CALFACT2 register */ + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & lincalrdyw_mask), + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "Failed to read calfact\n"); + return ret; + } + + val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2); + adc->cal.lincalfact[i] = (val & STM32H7_LINCALFACT_MASK); + adc->cal.lincalfact[i] >>= STM32H7_LINCALFACT_SHIFT; + + lincalrdyw_mask >>= 1; + } + + /* Read offset calibration */ + val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT); + adc->cal.calfact_s = (val & STM32H7_CALFACT_S_MASK); + 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; + + return 0; +} + +/** + * stm32h7_adc_restore_selfcalib() - Restore saved self-calibration result + * @indio_dev: IIO device instance + * Note: ADC must be enabled, with no on-going conversions. + */ +static int stm32h7_adc_restore_selfcalib(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int i, ret; + u32 lincalrdyw_mask, val; + + val = (adc->cal.calfact_s << STM32H7_CALFACT_S_SHIFT) | + (adc->cal.calfact_d << STM32H7_CALFACT_D_SHIFT); + stm32_adc_writel(adc, STM32H7_ADC_CALFACT, val); + + lincalrdyw_mask = STM32H7_LINCALRDYW6; + for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) { + /* + * Write saved calibration data to shadow registers: + * Write CALFACT2, and set LINCALRDYW[6..1] bit to trigger + * data write. Then poll to wait for complete transfer. + */ + val = adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT; + stm32_adc_writel(adc, STM32H7_ADC_CALFACT2, val); + stm32_adc_set_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + val & lincalrdyw_mask, + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "Failed to write calfact\n"); + return ret; + } + + /* + * Read back calibration data, has two effects: + * - It ensures bits LINCALRDYW[6..1] are kept cleared + * for next time calibration needs to be restored. + * - BTW, bit clear triggers a read, then check data has been + * correctly written. + */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & lincalrdyw_mask), + 100, STM32_ADC_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "Failed to read calfact\n"); + return ret; + } + val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2); + if (val != adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT) { + dev_err(&indio_dev->dev, "calfact not consistent\n"); + return -EIO; + } + + lincalrdyw_mask >>= 1; + } + + return 0; +} + +/** + * Fixed timeout value for ADC calibration. + * worst cases: + * - low clock frequency + * - maximum prescalers + * Calibration requires: + * - 131,072 ADC clock cycle for the linear calibration + * - 20 ADC clock cycle for the offset calibration + * + * Set to 100ms for now + */ +#define STM32H7_ADC_CALIB_TIMEOUT_US 100000 + +/** + * stm32h7_adc_selfcalib() - Procedure to calibrate ADC + * @indio_dev: IIO device instance + * Note: Must be called once ADC is out of power down. + */ +static int stm32h7_adc_selfcalib(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + u32 val; + + if (adc->cal.calibrated) + return true; + + /* + * Select calibration mode: + * - Offset calibration for single ended inputs + * - No linearity calibration (do it later, before reading it) + */ + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF); + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALLIN); + + /* Start calibration, then wait for completion */ + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & STM32H7_ADCAL), 100, + STM32H7_ADC_CALIB_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "calibration failed\n"); + goto out; + } + + /* + * Select calibration mode, then start calibration: + * - Offset calibration for differential input + * - Linearity calibration (needs to be done only once for single/diff) + * will run simultaneously with offset calibration. + */ + stm32_adc_set_bits(adc, STM32H7_ADC_CR, + STM32H7_ADCALDIF | STM32H7_ADCALLIN); + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL); + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val, + !(val & STM32H7_ADCAL), 100, + STM32H7_ADC_CALIB_TIMEOUT_US); + if (ret) { + dev_err(&indio_dev->dev, "calibration failed\n"); + goto out; + } + +out: + stm32_adc_clr_bits(adc, STM32H7_ADC_CR, + STM32H7_ADCALDIF | STM32H7_ADCALLIN); + + return ret; +} + +/** + * stm32h7_adc_prepare() - Leave power down mode to enable ADC. + * @indio_dev: IIO device instance + * Leave power down mode. + * Configure channels as single ended or differential before enabling ADC. + * Enable ADC. + * Restore calibration data. + * Pre-select channels that may be used in PCSEL (required by input MUX / IO): + * - Only one input is selected for single ended (e.g. 'vinp') + * - Two inputs are selected for differential channels (e.g. 'vinp' & 'vinn') + */ +static int stm32h7_adc_prepare(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int calib, ret; + + ret = stm32h7_adc_exit_pwr_down(indio_dev); + if (ret) + return ret; + + ret = stm32h7_adc_selfcalib(indio_dev); + if (ret < 0) + goto pwr_dwn; + calib = ret; + + stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel); + + ret = stm32h7_adc_enable(indio_dev); + if (ret) + goto pwr_dwn; + + /* Either restore or read calibration result for future reference */ + if (calib) + ret = stm32h7_adc_restore_selfcalib(indio_dev); + else + ret = stm32h7_adc_read_selfcalib(indio_dev); + if (ret) + goto disable; + + stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel); + + return 0; + +disable: + stm32h7_adc_disable(indio_dev); +pwr_dwn: + stm32h7_adc_enter_pwr_down(adc); + + return ret; +} + +static void stm32h7_adc_unprepare(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + stm32_adc_writel(adc, STM32H7_ADC_PCSEL, 0); + stm32h7_adc_disable(indio_dev); + stm32h7_adc_enter_pwr_down(adc); +} + +/** + * stm32_adc_conf_scan_seq() - Build regular channels scan sequence + * @indio_dev: IIO device + * @scan_mask: channels to be converted + * + * Conversion sequence : + * Apply sampling time settings for all channels. + * Configure ADC scan sequence based on selected channels in scan_mask. + * Add channels to SQR registers, from scan_mask LSB to MSB, then + * program sequence len. + */ +static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_regs *sqr = adc->cfg->regs->sqr; + const struct iio_chan_spec *chan; + u32 val, bit; + int i = 0; + + /* Apply sampling time settings */ + stm32_adc_writel(adc, adc->cfg->regs->smpr[0], adc->smpr_val[0]); + stm32_adc_writel(adc, adc->cfg->regs->smpr[1], adc->smpr_val[1]); + + for_each_set_bit(bit, scan_mask, indio_dev->masklength) { + chan = indio_dev->channels + bit; + /* + * Assign one channel per SQ entry in regular + * sequence, starting with SQ1. + */ + i++; + if (i > STM32_ADC_MAX_SQ) + return -EINVAL; + + dev_dbg(&indio_dev->dev, "%s chan %d to SQ%d\n", + __func__, chan->channel, i); + + val = stm32_adc_readl(adc, sqr[i].reg); + val &= ~sqr[i].mask; + val |= chan->channel << sqr[i].shift; + stm32_adc_writel(adc, sqr[i].reg, val); + } + + if (!i) + return -EINVAL; + + /* Sequence len */ + val = stm32_adc_readl(adc, sqr[0].reg); + val &= ~sqr[0].mask; + val |= ((i - 1) << sqr[0].shift); + stm32_adc_writel(adc, sqr[0].reg, val); + + return 0; +} + +/** + * stm32_adc_get_trig_extsel() - Get external trigger selection + * @indio_dev: IIO device structure + * @trig: trigger + * + * Returns trigger extsel value, if trig matches, -EINVAL otherwise. + */ +static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int i; + + /* lookup triggers registered by stm32 timer trigger driver */ + for (i = 0; adc->cfg->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(adc->cfg->trigs[i].name, trig->name)) { + return adc->cfg->trigs[i].extsel; + } + } + + return -EINVAL; +} + +/** + * stm32_adc_set_trig() - Set a regular trigger + * @indio_dev: IIO device + * @trig: IIO trigger + * + * Set trigger source/polarity (e.g. SW, or HW with polarity) : + * - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw) + * - if HW trigger enabled, set source & polarity + */ +static int stm32_adc_set_trig(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG; + unsigned long flags; + int ret; + + if (trig) { + ret = stm32_adc_get_trig_extsel(indio_dev, trig); + if (ret < 0) + return ret; + + /* set trigger source and polarity (default to rising edge) */ + extsel = ret; + exten = adc->trigger_polarity + STM32_EXTEN_HWTRIG_RISING_EDGE; + } + + spin_lock_irqsave(&adc->lock, flags); + val = stm32_adc_readl(adc, adc->cfg->regs->exten.reg); + val &= ~(adc->cfg->regs->exten.mask | adc->cfg->regs->extsel.mask); + val |= exten << adc->cfg->regs->exten.shift; + val |= extsel << adc->cfg->regs->extsel.shift; + stm32_adc_writel(adc, adc->cfg->regs->exten.reg, val); + spin_unlock_irqrestore(&adc->lock, flags); + + return 0; +} + +static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int type) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + adc->trigger_polarity = type; + + return 0; +} + +static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + return adc->trigger_polarity; +} + +static const char * const stm32_trig_pol_items[] = { + "rising-edge", "falling-edge", "both-edges", +}; + +static const struct iio_enum stm32_adc_trig_pol = { + .items = stm32_trig_pol_items, + .num_items = ARRAY_SIZE(stm32_trig_pol_items), + .get = stm32_adc_get_trig_pol, + .set = stm32_adc_set_trig_pol, +}; + +/** + * stm32_adc_single_conv() - Performs a single conversion + * @indio_dev: IIO device + * @chan: IIO channel + * @res: conversion result + * + * The function performs a single conversion on a given channel: + * - Apply sampling time settings + * - Program sequencer with one channel (e.g. in SQ1 with len = 1) + * - Use SW trigger + * - Start conversion, then wait for interrupt completion. + */ +static int stm32_adc_single_conv(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + 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; + int ret; + + reinit_completion(&adc->completion); + + adc->bufi = 0; + + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + + /* Apply sampling time settings */ + stm32_adc_writel(adc, regs->smpr[0], adc->smpr_val[0]); + stm32_adc_writel(adc, regs->smpr[1], adc->smpr_val[1]); + + /* Program chan number in regular sequence (SQ1) */ + val = stm32_adc_readl(adc, regs->sqr[1].reg); + val &= ~regs->sqr[1].mask; + val |= chan->channel << regs->sqr[1].shift; + stm32_adc_writel(adc, regs->sqr[1].reg, val); + + /* Set regular sequence len (0 for 1 conversion) */ + stm32_adc_clr_bits(adc, regs->sqr[0].reg, regs->sqr[0].mask); + + /* Trigger detection disabled (conversion can be launched in SW) */ + stm32_adc_clr_bits(adc, regs->exten.reg, regs->exten.mask); + + stm32_adc_conv_irq_enable(adc); + + adc->cfg->start_conv(indio_dev, false); + + timeout = wait_for_completion_interruptible_timeout( + &adc->completion, STM32_ADC_TIMEOUT); + if (timeout == 0) { + ret = -ETIMEDOUT; + } else if (timeout < 0) { + ret = timeout; + } else { + *res = adc->buffer[0]; + ret = IIO_VAL_INT; + } + + adc->cfg->stop_conv(indio_dev); + + stm32_adc_conv_irq_disable(adc); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int stm32_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + if (chan->type == IIO_VOLTAGE) + ret = stm32_adc_single_conv(indio_dev, chan, val); + else + ret = -EINVAL; + iio_device_release_direct_mode(indio_dev); + return ret; + + case IIO_CHAN_INFO_SCALE: + if (chan->differential) { + *val = adc->common->vref_mv * 2; + *val2 = chan->scan_type.realbits; + } else { + *val = adc->common->vref_mv; + *val2 = chan->scan_type.realbits; + } + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_OFFSET: + if (chan->differential) + /* ADC_full_scale / 2 */ + *val = -((1 << chan->scan_type.realbits) / 2); + else + *val = 0; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static void stm32_adc_irq_clear(struct iio_dev *indio_dev, u32 msk) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + + adc->cfg->irq_clear(indio_dev, msk); +} + +static irqreturn_t stm32_adc_threaded_isr(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_regspec *regs = adc->cfg->regs; + u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg); + + /* Check ovr status right now, as ovr mask should be already disabled */ + if (status & regs->isr_ovr.mask) { + /* + * Clear ovr bit to avoid subsequent calls to IRQ handler. + * This requires to stop ADC first. OVR bit state in ISR, + * is propaged to CSR register by hardware. + */ + adc->cfg->stop_conv(indio_dev); + stm32_adc_irq_clear(indio_dev, regs->isr_ovr.mask); + dev_err(&indio_dev->dev, "Overrun, stopping: restart needed\n"); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static irqreturn_t stm32_adc_isr(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_regspec *regs = adc->cfg->regs; + u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg); + + if (status & regs->isr_ovr.mask) { + /* + * Overrun occurred on regular conversions: data for wrong + * channel may be read. Unconditionally disable interrupts + * to stop processing data and print error message. + * Restarting the capture can be done by disabling, then + * re-enabling it (e.g. write 0, then 1 to buffer/enable). + */ + stm32_adc_ovr_irq_disable(adc); + stm32_adc_conv_irq_disable(adc); + return IRQ_WAKE_THREAD; + } + + if (status & regs->isr_eoc.mask) { + /* Reading DR also clears EOC status flag */ + adc->buffer[adc->bufi] = stm32_adc_readw(adc, regs->dr); + if (iio_buffer_enabled(indio_dev)) { + adc->bufi++; + if (adc->bufi >= adc->num_conv) { + stm32_adc_conv_irq_disable(adc); + iio_trigger_poll(indio_dev->trig); + } + } else { + complete(&adc->completion); + } + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/** + * stm32_adc_validate_trigger() - validate trigger for stm32 adc + * @indio_dev: IIO device + * @trig: new trigger + * + * Returns: 0 if trig matches one of the triggers registered by stm32 adc + * driver, -EINVAL otherwise. + */ +static int stm32_adc_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + return stm32_adc_get_trig_extsel(indio_dev, trig) < 0 ? -EINVAL : 0; +} + +static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + unsigned int watermark = STM32_DMA_BUFFER_SIZE / 2; + unsigned int rx_buf_sz = STM32_DMA_BUFFER_SIZE; + + /* + * dma cyclic transfers are used, buffer is split into two periods. + * There should be : + * - always one buffer (period) dma is working on + * - one buffer (period) driver can push with iio_trigger_poll(). + */ + watermark = min(watermark, val * (unsigned)(sizeof(u16))); + adc->rx_buf_sz = min(rx_buf_sz, watermark * 2 * adc->num_conv); + + return 0; +} + +static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev, + 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); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int stm32_adc_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + int i; + + for (i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +/** + * stm32_adc_debugfs_reg_access - read or write register value + * @indio_dev: IIO device structure + * @reg: register offset + * @writeval: value to write + * @readval: value to read + * + * To read a value from an ADC register: + * echo [ADC reg offset] > direct_reg_access + * cat direct_reg_access + * + * To write a value in a ADC register: + * echo [ADC_reg_offset] [value] > direct_reg_access + */ +static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + 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; +} + +static const struct iio_info stm32_adc_iio_info = { + .read_raw = stm32_adc_read_raw, + .validate_trigger = stm32_adc_validate_trigger, + .hwfifo_set_watermark = stm32_adc_set_watermark, + .update_scan_mode = stm32_adc_update_scan_mode, + .debugfs_reg_access = stm32_adc_debugfs_reg_access, + .of_xlate = stm32_adc_of_xlate, +}; + +static unsigned int stm32_adc_dma_residue(struct stm32_adc *adc) +{ + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(adc->dma_chan, + adc->dma_chan->cookie, + &state); + if (status == DMA_IN_PROGRESS) { + /* Residue is size in bytes from end of buffer */ + unsigned int i = adc->rx_buf_sz - state.residue; + unsigned int size; + + /* Return available bytes */ + if (i >= adc->bufi) + size = i - adc->bufi; + else + size = adc->rx_buf_sz + i - adc->bufi; + + return size; + } + + return 0; +} + +static void stm32_adc_dma_buffer_done(void *data) +{ + struct iio_dev *indio_dev = data; + struct stm32_adc *adc = iio_priv(indio_dev); + int residue = stm32_adc_dma_residue(adc); + + /* + * In DMA mode the trigger services of IIO are not used + * (e.g. no call to iio_trigger_poll). + * Calling irq handler associated to the hardware trigger is not + * relevant as the conversions have already been done. Data + * transfers are performed directly in DMA callback instead. + * This implementation avoids to call trigger irq handler that + * may sleep, in an atomic context (DMA irq handler context). + */ + dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi); + + while (residue >= indio_dev->scan_bytes) { + u16 *buffer = (u16 *)&adc->rx_buf[adc->bufi]; + + iio_push_to_buffers(indio_dev, buffer); + + residue -= indio_dev->scan_bytes; + adc->bufi += indio_dev->scan_bytes; + if (adc->bufi >= adc->rx_buf_sz) + adc->bufi = 0; + } +} + +static int stm32_adc_dma_start(struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + int ret; + + if (!adc->dma_chan) + return 0; + + dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, + adc->rx_buf_sz, adc->rx_buf_sz / 2); + + /* Prepare a DMA cyclic transaction */ + desc = dmaengine_prep_dma_cyclic(adc->dma_chan, + adc->rx_dma_buf, + adc->rx_buf_sz, adc->rx_buf_sz / 2, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) + return -EBUSY; + + desc->callback = stm32_adc_dma_buffer_done; + desc->callback_param = indio_dev; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + if (ret) { + dmaengine_terminate_sync(adc->dma_chan); + return ret; + } + + /* Issue pending DMA requests */ + dma_async_issue_pending(adc->dma_chan); + + return 0; +} + +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; + + 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_pm_put; + } + + ret = stm32_adc_dma_start(indio_dev); + if (ret) { + dev_err(&indio_dev->dev, "Can't start dma\n"); + goto err_clr_trig; + } + + /* Reset adc buffer index */ + adc->bufi = 0; + + stm32_adc_ovr_irq_enable(adc); + + if (!adc->dma_chan) + stm32_adc_conv_irq_enable(adc); + + adc->cfg->start_conv(indio_dev, !!adc->dma_chan); + + return 0; + +err_clr_trig: + stm32_adc_set_trig(indio_dev, NULL); +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) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + + adc->cfg->stop_conv(indio_dev); + if (!adc->dma_chan) + stm32_adc_conv_irq_disable(adc); + + stm32_adc_ovr_irq_disable(adc); + + if (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"); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static const struct iio_buffer_setup_ops stm32_adc_buffer_setup_ops = { + .postenable = &stm32_adc_buffer_postenable, + .predisable = &stm32_adc_buffer_predisable, +}; + +static irqreturn_t stm32_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct stm32_adc *adc = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi); + + if (!adc->dma_chan) { + /* reset buffer index */ + adc->bufi = 0; + iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer, + pf->timestamp); + } else { + int residue = stm32_adc_dma_residue(adc); + + while (residue >= indio_dev->scan_bytes) { + u16 *buffer = (u16 *)&adc->rx_buf[adc->bufi]; + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, + pf->timestamp); + residue -= indio_dev->scan_bytes; + adc->bufi += indio_dev->scan_bytes; + if (adc->bufi >= adc->rx_buf_sz) + adc->bufi = 0; + } + } + + iio_trigger_notify_done(indio_dev->trig); + + /* re-enable eoc irq */ + if (!adc->dma_chan) + stm32_adc_conv_irq_enable(adc); + + return IRQ_HANDLED; +} + +static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = { + IIO_ENUM("trigger_polarity", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol), + { + .name = "trigger_polarity_available", + .shared = IIO_SHARED_BY_ALL, + .read = iio_enum_available_read, + .private = (uintptr_t)&stm32_adc_trig_pol, + }, + {}, +}; + +static int stm32_adc_of_get_resolution(struct iio_dev *indio_dev) +{ + struct device_node *node = indio_dev->dev.of_node; + struct stm32_adc *adc = iio_priv(indio_dev); + unsigned int i; + u32 res; + + if (of_property_read_u32(node, "assigned-resolution-bits", &res)) + res = adc->cfg->adc_info->resolutions[0]; + + for (i = 0; i < adc->cfg->adc_info->num_res; i++) + if (res == adc->cfg->adc_info->resolutions[i]) + break; + if (i >= adc->cfg->adc_info->num_res) { + dev_err(&indio_dev->dev, "Bad resolution: %u bits\n", res); + return -EINVAL; + } + + dev_dbg(&indio_dev->dev, "Using %u bits resolution\n", res); + adc->res = i; + + return 0; +} + +static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns) +{ + const struct stm32_adc_regs *smpr = &adc->cfg->regs->smp_bits[channel]; + u32 period_ns, shift = smpr->shift, mask = smpr->mask; + unsigned int smp, r = smpr->reg; + + /* Determine sampling time (ADC clock cycles) */ + period_ns = NSEC_PER_SEC / adc->common->rate; + for (smp = 0; smp <= STM32_ADC_MAX_SMP; smp++) + if ((period_ns * adc->cfg->smp_cycles[smp]) >= smp_ns) + break; + if (smp > STM32_ADC_MAX_SMP) + smp = STM32_ADC_MAX_SMP; + + /* pre-build sampling time registers (e.g. smpr1, smpr2) */ + adc->smpr_val[r] = (adc->smpr_val[r] & ~mask) | (smp << shift); +} + +static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, + struct iio_chan_spec *chan, u32 vinp, + u32 vinn, int scan_index, bool differential) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + char *name = adc->chan_name[vinp]; + + chan->type = IIO_VOLTAGE; + chan->channel = vinp; + if (differential) { + chan->differential = 1; + chan->channel2 = vinn; + snprintf(name, STM32_ADC_CH_SZ, "in%d-in%d", vinp, vinn); + } else { + snprintf(name, STM32_ADC_CH_SZ, "in%d", vinp); + } + chan->datasheet_name = name; + chan->scan_index = scan_index; + chan->indexed = 1; + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET); + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res]; + chan->scan_type.storagebits = 16; + chan->ext_info = stm32_adc_ext_info; + + /* pre-build selected channels mask */ + adc->pcsel |= BIT(chan->channel); + if (differential) { + /* pre-build diff channels mask */ + adc->difsel |= BIT(chan->channel); + /* Also add negative input to pre-selected channels */ + adc->pcsel |= BIT(chan->channel2); + } +} + +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) +{ + struct device_node *node = indio_dev->dev.of_node; + struct stm32_adc *adc = iio_priv(indio_dev); + const struct stm32_adc_info *adc_info = adc->cfg->adc_info; + struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX]; + struct property *prop; + const __be32 *cur; + struct iio_chan_spec *channels; + int scan_index = 0, num_channels = 0, num_diff = 0, ret, i; + u32 val, smp = 0; + + ret = of_property_count_u32_elems(node, "st,adc-channels"); + if (ret > adc_info->max_channels) { + dev_err(&indio_dev->dev, "Bad st,adc-channels?\n"); + return -EINVAL; + } else if (ret > 0) { + num_channels += ret; + } + + ret = of_property_count_elems_of_size(node, "st,adc-diff-channels", + sizeof(*diff)); + if (ret > adc_info->max_channels) { + dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n"); + return -EINVAL; + } else if (ret > 0) { + int size = ret * sizeof(*diff) / sizeof(u32); + + num_diff = ret; + num_channels += ret; + ret = of_property_read_u32_array(node, "st,adc-diff-channels", + (u32 *)diff, size); + if (ret) + return ret; + } + + if (!num_channels) { + dev_err(&indio_dev->dev, "No channels configured\n"); + return -ENODATA; + } + + /* Optional sample time is provided either for each, or all channels */ + ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs"); + if (ret > 1 && ret != num_channels) { + dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n"); + return -EINVAL; + } + + channels = devm_kcalloc(&indio_dev->dev, num_channels, + sizeof(struct iio_chan_spec), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) { + if (val >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel %d\n", val); + return -EINVAL; + } + + /* Channel can't be configured both as single-ended & diff */ + for (i = 0; i < num_diff; i++) { + if (val == diff[i].vinp) { + dev_err(&indio_dev->dev, + "channel %d miss-configured\n", val); + return -EINVAL; + } + } + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val, + 0, scan_index, false); + scan_index++; + } + + for (i = 0; i < num_diff; i++) { + if (diff[i].vinp >= adc_info->max_channels || + diff[i].vinn >= adc_info->max_channels) { + dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n", + diff[i].vinp, diff[i].vinn); + return -EINVAL; + } + stm32_adc_chan_init_one(indio_dev, &channels[scan_index], + diff[i].vinp, diff[i].vinn, scan_index, + true); + scan_index++; + } + + for (i = 0; i < scan_index; i++) { + /* + * Using of_property_read_u32_index(), smp value will only be + * modified if valid u32 value can be decoded. This allows to + * get either no value, 1 shared value for all indexes, or one + * value per channel. + */ + of_property_read_u32_index(node, "st,min-sample-time-nsecs", + i, &smp); + /* Prepare sampling time settings */ + stm32_adc_smpr_init(adc, channels[i].channel, smp); + } + + indio_dev->num_channels = scan_index; + indio_dev->channels = channels; + + return 0; +} + +static int stm32_adc_dma_request(struct device *dev, struct iio_dev *indio_dev) +{ + struct stm32_adc *adc = iio_priv(indio_dev); + struct dma_slave_config config; + int ret; + + adc->dma_chan = dma_request_chan(dev, "rx"); + if (IS_ERR(adc->dma_chan)) { + ret = PTR_ERR(adc->dma_chan); + if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "DMA channel request failed with\n"); + + /* DMA is optional: fall back to IRQ mode */ + adc->dma_chan = NULL; + return 0; + } + + adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev, + STM32_DMA_BUFFER_SIZE, + &adc->rx_dma_buf, GFP_KERNEL); + if (!adc->rx_buf) { + ret = -ENOMEM; + goto err_release; + } + + /* Configure DMA channel to read data register */ + memset(&config, 0, sizeof(config)); + config.src_addr = (dma_addr_t)adc->common->phys_base; + config.src_addr += adc->offset + adc->cfg->regs->dr; + config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + + ret = dmaengine_slave_config(adc->dma_chan, &config); + if (ret) + goto err_free; + + return 0; + +err_free: + dma_free_coherent(adc->dma_chan->device->dev, STM32_DMA_BUFFER_SIZE, + adc->rx_buf, adc->rx_dma_buf); +err_release: + dma_release_channel(adc->dma_chan); + + return ret; +} + +static int stm32_adc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + irqreturn_t (*handler)(int irq, void *p) = NULL; + struct stm32_adc *adc; + int ret; + + if (!pdev->dev.of_node) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->common = dev_get_drvdata(pdev->dev.parent); + spin_lock_init(&adc->lock); + init_completion(&adc->completion); + adc->cfg = (const struct stm32_adc_cfg *) + of_match_device(dev->driver->of_match_table, dev)->data; + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->info = &stm32_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED; + + platform_set_drvdata(pdev, indio_dev); + + ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset); + if (ret != 0) { + dev_err(&pdev->dev, "missing reg property\n"); + return -EINVAL; + } + + adc->irq = platform_get_irq(pdev, 0); + if (adc->irq < 0) + return adc->irq; + + ret = devm_request_threaded_irq(&pdev->dev, adc->irq, stm32_adc_isr, + stm32_adc_threaded_isr, + 0, pdev->name, indio_dev); + if (ret) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + return ret; + } + + adc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(adc->clk)) { + ret = PTR_ERR(adc->clk); + if (ret == -ENOENT && !adc->cfg->clk_required) { + adc->clk = NULL; + } else { + dev_err(&pdev->dev, "Can't get clock\n"); + return ret; + } + } + + ret = stm32_adc_of_get_resolution(indio_dev); + if (ret < 0) + return ret; + + ret = stm32_adc_chan_of_init(indio_dev); + if (ret < 0) + return ret; + + ret = stm32_adc_dma_request(dev, indio_dev); + if (ret < 0) + return ret; + + if (!adc->dma_chan) + handler = &stm32_adc_trigger_handler; + + ret = iio_triggered_buffer_setup(indio_dev, + &iio_pollfunc_store_time, handler, + &stm32_adc_buffer_setup_ops); + if (ret) { + dev_err(&pdev->dev, "buffer setup failed\n"); + 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_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: + if (adc->dma_chan) { + dma_free_coherent(adc->dma_chan->device->dev, + STM32_DMA_BUFFER_SIZE, + adc->rx_buf, adc->rx_dma_buf); + dma_release_channel(adc->dma_chan); + } + + return ret; +} + +static int stm32_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct stm32_adc *adc = iio_priv(indio_dev); + + 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, + STM32_DMA_BUFFER_SIZE, + adc->rx_buf, adc->rx_dma_buf); + dma_release_channel(adc->dma_chan); + } + + return 0; +} + +#if defined(CONFIG_PM_SLEEP) +static int stm32_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + 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 iio_dev *indio_dev = dev_get_drvdata(dev); + 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, + .trigs = stm32f4_adc_trigs, + .clk_required = true, + .start_conv = stm32f4_adc_start_conv, + .stop_conv = stm32f4_adc_stop_conv, + .smp_cycles = stm32f4_adc_smp_cycles, + .irq_clear = stm32f4_adc_irq_clear, +}; + +static const struct stm32_adc_cfg stm32h7_adc_cfg = { + .regs = &stm32h7_adc_regspec, + .adc_info = &stm32h7_adc_info, + .trigs = stm32h7_adc_trigs, + .start_conv = stm32h7_adc_start_conv, + .stop_conv = stm32h7_adc_stop_conv, + .prepare = stm32h7_adc_prepare, + .unprepare = stm32h7_adc_unprepare, + .smp_cycles = stm32h7_adc_smp_cycles, + .irq_clear = stm32h7_adc_irq_clear, +}; + +static const struct stm32_adc_cfg stm32mp1_adc_cfg = { + .regs = &stm32h7_adc_regspec, + .adc_info = &stm32h7_adc_info, + .trigs = stm32h7_adc_trigs, + .has_vregready = true, + .start_conv = stm32h7_adc_start_conv, + .stop_conv = stm32h7_adc_stop_conv, + .prepare = stm32h7_adc_prepare, + .unprepare = stm32h7_adc_unprepare, + .smp_cycles = stm32h7_adc_smp_cycles, + .irq_clear = stm32h7_adc_irq_clear, +}; + +static const struct of_device_id stm32_adc_of_match[] = { + { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg }, + { .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg }, + { .compatible = "st,stm32mp1-adc", .data = (void *)&stm32mp1_adc_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32_adc_of_match); + +static struct platform_driver stm32_adc_driver = { + .probe = stm32_adc_probe, + .remove = stm32_adc_remove, + .driver = { + .name = "stm32-adc", + .of_match_table = stm32_adc_of_match, + .pm = &stm32_adc_pm_ops, + }, +}; +module_platform_driver(stm32_adc_driver); + +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:stm32-adc"); diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c new file mode 100644 index 000000000..171d73efb --- /dev/null +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -0,0 +1,1685 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is the ADC part of the STM32 DFSDM driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author: Arnaud Pouliquen <arnaud.pouliquen@st.com>. + */ + +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/iio/adc/stm32-dfsdm-adc.h> +#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> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#include "stm32-dfsdm.h" + +#define DFSDM_DMA_BUFFER_SIZE (4 * PAGE_SIZE) + +/* Conversion timeout */ +#define DFSDM_TIMEOUT_US 100000 +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000)) + +/* Oversampling attribute default */ +#define DFSDM_DEFAULT_OVERSAMPLING 100 + +/* Oversampling max values */ +#define DFSDM_MAX_INT_OVERSAMPLING 256 +#define DFSDM_MAX_FL_OVERSAMPLING 1024 + +/* 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, + DFSDM_IIO, +}; + +struct stm32_dfsdm_dev_data { + int type; + int (*init)(struct device *dev, struct iio_dev *indio_dev); + unsigned int num_channels; + const struct regmap_config *regmap_cfg; +}; + +struct stm32_dfsdm_adc { + 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; + struct iio_hw_consumer *hwc; + struct completion completion; + u32 *buffer; + + /* Audio specific */ + unsigned int spi_freq; /* SPI bus clock frequency */ + unsigned int sample_freq; /* Sample frequency after filter decimation */ + int (*cb)(const void *data, size_t size, void *cb_priv); + void *cb_priv; + + /* DMA */ + u8 *rx_buf; + unsigned int bufi; /* Buffer current position */ + unsigned int buf_sz; /* Buffer size */ + struct dma_chan *dma_chan; + dma_addr_t dma_buf; +}; + +struct stm32_dfsdm_str2field { + const char *name; + unsigned int val; +}; + +/* DFSDM channel serial interface type */ +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = { + { "SPI_R", 0 }, /* SPI with data on rising edge */ + { "SPI_F", 1 }, /* SPI with data on falling edge */ + { "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */ + { "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */ + {}, +}; + +/* DFSDM channel clock source */ +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = { + /* External SPI clock (CLKIN x) */ + { "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL }, + /* Internal SPI clock (CLKOUT) */ + { "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL }, + /* Internal SPI clock divided by 2 (falling edge) */ + { "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING }, + /* Internal SPI clock divided by 2 (falling edge) */ + { "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING }, + {}, +}; + +static int stm32_dfsdm_str2val(const char *str, + const struct stm32_dfsdm_str2field *list) +{ + const struct stm32_dfsdm_str2field *p = list; + + for (p = list; p && p->name; p++) + if (!strcmp(p->name, str)) + return p->val; + + return -EINVAL; +} + +/** + * 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, 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); + /* + * This function tries to compute filter oversampling and integrator + * oversampling, base on oversampling ratio requested by user. + * + * Decimation d depends on the filter order and the oversampling ratios. + * ford: filter order + * fosr: filter over sampling ratio + * iosr: integrator over sampling ratio + */ + if (fl->ford == DFSDM_FASTSINC_ORDER) { + m = 2; + p = 2; + } + + /* + * Look for filter and integrator oversampling ratios which allows + * to maximize data output resolution. + */ + for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) { + for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) { + if (fast) + d = fosr * iosr; + else if (fl->ford == DFSDM_FASTSINC_ORDER) + d = fosr * (iosr + 3) + 2; + else + d = fosr * (iosr - 1 + p) + p; + + if (d > oversamp) + break; + else if (d != oversamp) + continue; + /* + * Check resolution (limited to signed 32 bits) + * res <= 2^31 + * Sincx filters: + * res = m * fosr^p x iosr (with m=1, p=ford) + * FastSinc filter + * res = m * fosr^p x iosr (with m=2, p=2) + */ + res = fosr; + for (i = p - 1; i > 0; i--) { + res = res * (u64)fosr; + if (res > DFSDM_DATA_MAX) + break; + } + if (res > DFSDM_DATA_MAX) + continue; + + res = res * (u64)m * (u64)iosr; + if (res > DFSDM_DATA_MAX) + continue; + + if (res >= flo->res) { + flo->res = res; + flo->fosr = fosr; + flo->iosr = 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; + flo->bits = bits; + + 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 (!flo->res) + return -EINVAL; + + return 0; +} + +static int stm32_dfsdm_compute_all_osrs(struct iio_dev *indio_dev, + unsigned int oversamp) +{ + 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 int stm32_dfsdm_start_channel(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + 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 iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + 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, + struct stm32_dfsdm_channel *ch) +{ + unsigned int id = ch->id; + struct regmap *regmap = dfsdm->regmap; + int ret; + + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id), + DFSDM_CHCFGR1_SITP_MASK, + DFSDM_CHCFGR1_SITP(ch->type)); + if (ret < 0) + return ret; + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id), + DFSDM_CHCFGR1_SPICKSEL_MASK, + DFSDM_CHCFGR1_SPICKSEL(ch->src)); + if (ret < 0) + return ret; + return regmap_update_bits(regmap, DFSDM_CHCFGR1(id), + DFSDM_CHCFGR1_CHINSEL_MASK, + DFSDM_CHCFGR1_CHINSEL(ch->alt_si)); +} + +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 */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1)); + if (ret < 0) + return ret; + + /* 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)); +} + +static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, + unsigned int fl_id) +{ + /* Disable conversion */ + regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0)); +} + +static int stm32_dfsdm_filter_set_trig(struct iio_dev *indio_dev, + unsigned int fl_id, + struct iio_trigger *trig) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + 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 iio_dev *indio_dev, + unsigned int fl_id, + struct iio_trigger *trig) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + 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; + + dev_dbg(&indio_dev->dev, "Samples actual resolution: %d bits", + min(flo->bits, (u32)DFSDM_DATA_RES - 1)); + + 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 iio_dev *indio_dev, + unsigned int fl_id, + struct iio_trigger *trig) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + 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(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(flo->fosr - 1)); + if (ret) + return ret; + + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FORD_MASK, + DFSDM_FCR_FORD(fl->ford)); + if (ret) + return ret; + + ret = stm32_dfsdm_filter_set_trig(indio_dev, fl_id, trig); + if (ret) + return ret; + + 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, + struct iio_dev *indio_dev, + struct iio_chan_spec *ch) +{ + struct stm32_dfsdm_channel *df_ch; + const char *of_str; + int chan_idx = ch->scan_index; + int ret, val; + + ret = of_property_read_u32_index(indio_dev->dev.of_node, + "st,adc-channels", chan_idx, + &ch->channel); + if (ret < 0) { + dev_err(&indio_dev->dev, + " Error parsing 'st,adc-channels' for idx %d\n", + chan_idx); + return ret; + } + if (ch->channel >= dfsdm->num_chs) { + dev_err(&indio_dev->dev, + " Error bad channel number %d (max = %d)\n", + ch->channel, dfsdm->num_chs); + return -EINVAL; + } + + ret = of_property_read_string_index(indio_dev->dev.of_node, + "st,adc-channel-names", chan_idx, + &ch->datasheet_name); + if (ret < 0) { + dev_err(&indio_dev->dev, + " Error parsing 'st,adc-channel-names' for idx %d\n", + chan_idx); + return ret; + } + + df_ch = &dfsdm->ch_list[ch->channel]; + df_ch->id = ch->channel; + + ret = of_property_read_string_index(indio_dev->dev.of_node, + "st,adc-channel-types", chan_idx, + &of_str); + if (!ret) { + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type); + if (val < 0) + return val; + } else { + val = 0; + } + df_ch->type = val; + + ret = of_property_read_string_index(indio_dev->dev.of_node, + "st,adc-channel-clk-src", chan_idx, + &of_str); + if (!ret) { + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src); + if (val < 0) + return val; + } else { + val = 0; + } + df_ch->src = val; + + ret = of_property_read_u32_index(indio_dev->dev.of_node, + "st,adc-alt-channel", chan_idx, + &df_ch->alt_si); + if (ret < 0) + df_ch->alt_si = 0; + + return 0; +} + +static ssize_t dfsdm_adc_audio_get_spiclk(struct iio_dev *indio_dev, + uintptr_t priv, + const struct iio_chan_spec *chan, + char *buf) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + 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_channel *ch = &adc->dfsdm->ch_list[chan->channel]; + unsigned int sample_freq = adc->sample_freq; + unsigned int spi_freq; + int ret; + + dev_err(&indio_dev->dev, "enter %s\n", __func__); + /* If DFSDM is master on SPI, SPI freq can not be updated */ + if (ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) + return -EPERM; + + ret = kstrtoint(buf, 0, &spi_freq); + if (ret) + return ret; + + if (!spi_freq) + return -EINVAL; + + if (sample_freq) { + ret = dfsdm_adc_set_samp_freq(indio_dev, sample_freq, spi_freq); + if (ret < 0) + return ret; + } + adc->spi_freq = spi_freq; + + return len; +} + +static int stm32_dfsdm_start_conv(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->dfsdm->regmap; + int ret; + + ret = stm32_dfsdm_channels_configure(indio_dev, adc->fl_id, trig); + if (ret < 0) + return ret; + + ret = stm32_dfsdm_start_channel(indio_dev); + if (ret < 0) + return ret; + + ret = stm32_dfsdm_filter_configure(indio_dev, adc->fl_id, trig); + if (ret < 0) + goto stop_channels; + + ret = stm32_dfsdm_start_filter(adc, adc->fl_id, trig); + if (ret < 0) + goto filter_unconfigure; + + return 0; + +filter_unconfigure: + regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), + DFSDM_CR1_CFG_MASK, 0); +stop_channels: + stm32_dfsdm_stop_channel(indio_dev); + + return ret; +} + +static void stm32_dfsdm_stop_conv(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->dfsdm->regmap; + + stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id); + + regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), + DFSDM_CR1_CFG_MASK, 0); + + stm32_dfsdm_stop_channel(indio_dev); +} + +static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev, + unsigned int val) +{ + 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. + * There should be : + * - always one buffer (period) DMA is working on + * - one buffer (period) driver pushed to ASoC side. + */ + watermark = min(watermark, val * (unsigned int)(sizeof(u32))); + adc->buf_sz = min(rx_buf_sz, watermark * 2 * adc->nconv); + + return 0; +} + +static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc) +{ + struct dma_tx_state state; + enum dma_status status; + + status = dmaengine_tx_status(adc->dma_chan, + adc->dma_chan->cookie, + &state); + if (status == DMA_IN_PROGRESS) { + /* Residue is size in bytes from end of buffer */ + unsigned int i = adc->buf_sz - state.residue; + unsigned int size; + + /* Return available bytes */ + if (i >= adc->bufi) + size = i - adc->bufi; + else + size = adc->buf_sz + i - adc->bufi; + + return size; + } + + return 0; +} + +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 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; + + /* + * FIXME: In Kernel interface does not support cyclic DMA buffer,and + * offers only an interface to push data samples per samples. + * For this reason IIO buffer interface is not used and interface is + * bypassed using a private callback registered by ASoC. + * This should be a temporary solution waiting a cyclic DMA engine + * support in IIO. + */ + + dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__, + adc->bufi, available); + old_pos = adc->bufi; + + while (available >= indio_dev->scan_bytes) { + s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi]; + + stm32_dfsdm_process_data(adc, buffer); + + available -= indio_dev->scan_bytes; + adc->bufi += indio_dev->scan_bytes; + if (adc->bufi >= adc->buf_sz) { + if (adc->cb) + adc->cb(&adc->rx_buf[old_pos], + adc->buf_sz - old_pos, adc->cb_priv); + adc->bufi = 0; + old_pos = 0; + } + /* + * In DMA mode the trigger services of IIO are not used + * (e.g. no call to iio_trigger_poll). + * Calling irq handler associated to the hardware trigger is not + * relevant as the conversions have already been done. Data + * transfers are performed directly in DMA callback instead. + * This implementation avoids to call trigger irq handler that + * may sleep, in an atomic context (DMA irq handler context). + */ + 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, + adc->cb_priv); +} + +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; + + if (!adc->dma_chan) + return -EINVAL; + + 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, + adc->buf_sz, adc->buf_sz / 2, + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) + return -EBUSY; + + desc->callback = stm32_dfsdm_dma_buffer_done; + desc->callback_param = indio_dev; + + cookie = dmaengine_submit(desc); + ret = dma_submit_error(cookie); + 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) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + 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) + goto err_stop_hwc; + + ret = stm32_dfsdm_adc_dma_start(indio_dev); + if (ret) { + dev_err(&indio_dev->dev, "Can't start DMA\n"); + goto stop_dfsdm; + } + + ret = stm32_dfsdm_start_conv(indio_dev, indio_dev->trig); + if (ret) { + dev_err(&indio_dev->dev, "Can't start conversion\n"); + goto err_stop_dma; + } + + return 0; + +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) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + stm32_dfsdm_stop_conv(indio_dev); + + stm32_dfsdm_adc_dma_stop(indio_dev); + + stm32_dfsdm_stop_dfsdm(adc->dfsdm); + + if (adc->hwc) + iio_hw_consumer_disable(adc->hwc); + + return 0; +} + +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = { + .postenable = &stm32_dfsdm_postenable, + .predisable = &stm32_dfsdm_predisable, +}; + +/** + * stm32_dfsdm_get_buff_cb() - register a callback that will be called when + * DMA transfer period is achieved. + * + * @iio_dev: Handle to IIO device. + * @cb: Pointer to callback function: + * - data: pointer to data buffer + * - size: size in byte of the data buffer + * - private: pointer to consumer private structure. + * @private: Pointer to consumer private structure. + */ +int stm32_dfsdm_get_buff_cb(struct iio_dev *iio_dev, + int (*cb)(const void *data, size_t size, + void *private), + void *private) +{ + struct stm32_dfsdm_adc *adc; + + if (!iio_dev) + return -EINVAL; + adc = iio_priv(iio_dev); + + adc->cb = cb; + adc->cb_priv = private; + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_get_buff_cb); + +/** + * stm32_dfsdm_release_buff_cb - unregister buffer callback + * + * @iio_dev: Handle to IIO device. + */ +int stm32_dfsdm_release_buff_cb(struct iio_dev *iio_dev) +{ + struct stm32_dfsdm_adc *adc; + + if (!iio_dev) + return -EINVAL; + adc = iio_priv(iio_dev); + + adc->cb = NULL; + adc->cb_priv = NULL; + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_release_buff_cb); + +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *res) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + long timeout; + int ret; + + reinit_completion(&adc->completion); + + adc->buffer = res; + + ret = stm32_dfsdm_start_dfsdm(adc->dfsdm); + if (ret < 0) + return ret; + + ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1)); + if (ret < 0) + goto stop_dfsdm; + + adc->nconv = 1; + adc->smask = BIT(chan->scan_index); + ret = stm32_dfsdm_start_conv(indio_dev, NULL); + if (ret < 0) { + regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0)); + goto stop_dfsdm; + } + + timeout = wait_for_completion_interruptible_timeout(&adc->completion, + DFSDM_TIMEOUT); + + /* Mask IRQ for regular conversion achievement*/ + regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0)); + + if (timeout == 0) + ret = -ETIMEDOUT; + else if (timeout < 0) + ret = timeout; + else + ret = IIO_VAL_INT; + + stm32_dfsdm_stop_conv(indio_dev); + + stm32_dfsdm_process_data(adc, res); + +stop_dfsdm: + stm32_dfsdm_stop_dfsdm(adc->dfsdm); + + return ret; +} + +static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel]; + unsigned int spi_freq; + int ret = -EINVAL; + + switch (ch->src) { + case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL: + spi_freq = adc->dfsdm->spi_master_freq; + break; + case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING: + case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING: + spi_freq = adc->dfsdm->spi_master_freq / 2; + break; + default: + spi_freq = adc->spi_freq; + } + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = stm32_dfsdm_compute_all_osrs(indio_dev, val); + if (!ret) { + dev_dbg(&indio_dev->dev, + "Sampling rate changed from (%u) to (%u)\n", + adc->sample_freq, spi_freq / val); + adc->oversamp = val; + adc->sample_freq = spi_freq / 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; + + ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq); + iio_device_release_direct_mode(indio_dev); + return ret; + } + + return -EINVAL; +} + +static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int ret; + + 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); + iio_hw_consumer_disable(adc->hwc); + if (ret < 0) { + 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: + *val = adc->oversamp; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = adc->sample_freq; + + return IIO_VAL_INT; + } + + 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) +{ + struct iio_dev *indio_dev = arg; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct regmap *regmap = adc->dfsdm->regmap; + unsigned int status, int_en; + + regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status); + regmap_read(regmap, DFSDM_CR2(adc->fl_id), &int_en); + + if (status & DFSDM_ISR_REOCF_MASK) { + /* Read the data register clean the IRQ status */ + regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer); + complete(&adc->completion); + } + + if (status & DFSDM_ISR_ROVRF_MASK) { + if (int_en & DFSDM_CR2_ROVRIE_MASK) + dev_warn(&indio_dev->dev, "Overrun detected\n"); + regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id), + DFSDM_ICR_CLRROVRF_MASK, + DFSDM_ICR_CLRROVRF_MASK); + } + + return IRQ_HANDLED; +} + +/* + * Define external info for SPI Frequency and audio sampling rate that can be + * configured by ASoC driver through consumer.h API + */ +static const struct iio_chan_spec_ext_info dfsdm_adc_audio_ext_info[] = { + /* spi_clk_freq : clock freq on SPI/manchester bus used by channel */ + { + .name = "spi_clk_freq", + .shared = IIO_SHARED_BY_TYPE, + .read = dfsdm_adc_audio_get_spiclk, + .write = dfsdm_adc_audio_set_spiclk, + }, + {}, +}; + +static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + if (adc->dma_chan) { + dma_free_coherent(adc->dma_chan->device->dev, + DFSDM_DMA_BUFFER_SIZE, + adc->rx_buf, adc->dma_buf); + dma_release_channel(adc->dma_chan); + } +} + +static int stm32_dfsdm_dma_request(struct device *dev, + struct iio_dev *indio_dev) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + adc->dma_chan = dma_request_chan(dev, "rx"); + if (IS_ERR(adc->dma_chan)) { + int ret = PTR_ERR(adc->dma_chan); + + adc->dma_chan = NULL; + return ret; + } + + adc->rx_buf = dma_alloc_coherent(adc->dma_chan->device->dev, + DFSDM_DMA_BUFFER_SIZE, + &adc->dma_buf, GFP_KERNEL); + if (!adc->rx_buf) { + dma_release_channel(adc->dma_chan); + return -ENOMEM; + } + + indio_dev->modes |= INDIO_BUFFER_SOFTWARE; + indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops; + + return 0; +} + +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, + struct iio_chan_spec *ch) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int ret; + + ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, ch); + if (ret < 0) + return ret; + + ch->type = IIO_VOLTAGE; + ch->indexed = 1; + + /* + * IIO_CHAN_INFO_RAW: used to compute regular conversion + * 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) | + BIT(IIO_CHAN_INFO_SAMP_FREQ); + + if (adc->dev_data->type == DFSDM_AUDIO) { + ch->ext_info = dfsdm_adc_audio_ext_info; + } else { + ch->scan_type.shift = 8; + } + ch->scan_type.sign = 's'; + ch->scan_type.realbits = 24; + ch->scan_type.storagebits = 32; + + return stm32_dfsdm_chan_configure(adc->dfsdm, + &adc->dfsdm->ch_list[ch->channel]); +} + +static int stm32_dfsdm_audio_init(struct device *dev, struct iio_dev *indio_dev) +{ + struct iio_chan_spec *ch; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct stm32_dfsdm_channel *d_ch; + int ret; + + ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL); + if (!ch) + return -ENOMEM; + + ch->scan_index = 0; + + ret = stm32_dfsdm_adc_chan_init_one(indio_dev, ch); + if (ret < 0) { + dev_err(&indio_dev->dev, "Channels init failed\n"); + return ret; + } + ch->info_mask_separate = BIT(IIO_CHAN_INFO_SAMP_FREQ); + + d_ch = &adc->dfsdm->ch_list[ch->channel]; + if (d_ch->src != DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL) + adc->spi_freq = adc->dfsdm->spi_master_freq; + + indio_dev->num_channels = 1; + indio_dev->channels = ch; + + return stm32_dfsdm_dma_request(dev, indio_dev); +} + +static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev) +{ + struct iio_chan_spec *ch; + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + int num_ch; + int ret, chan_idx; + + adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING; + ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp); + if (ret < 0) + return ret; + + num_ch = of_property_count_u32_elems(indio_dev->dev.of_node, + "st,adc-channels"); + if (num_ch < 0 || num_ch > adc->dfsdm->num_chs) { + dev_err(&indio_dev->dev, "Bad st,adc-channels\n"); + return num_ch < 0 ? num_ch : -EINVAL; + } + + /* Bind to SD modulator IIO device */ + adc->hwc = devm_iio_hw_consumer_alloc(&indio_dev->dev); + if (IS_ERR(adc->hwc)) + return -EPROBE_DEFER; + + ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch), + GFP_KERNEL); + if (!ch) + return -ENOMEM; + + for (chan_idx = 0; chan_idx < num_ch; chan_idx++) { + ch[chan_idx].scan_index = chan_idx; + ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &ch[chan_idx]); + if (ret < 0) { + dev_err(&indio_dev->dev, "Channels init failed\n"); + return ret; + } + } + + indio_dev->num_channels = num_ch; + indio_dev->channels = ch; + + init_completion(&adc->completion); + + /* Optionally request DMA */ + ret = stm32_dfsdm_dma_request(dev, indio_dev); + if (ret) { + if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "DMA channel request failed with\n"); + + dev_dbg(dev, "No DMA support\n"); + return 0; + } + + ret = iio_triggered_buffer_setup(indio_dev, + &iio_pollfunc_store_time, NULL, + &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; +} + +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_adc_data = { + .type = DFSDM_IIO, + .init = stm32_dfsdm_adc_init, +}; + +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_audio_data = { + .type = DFSDM_AUDIO, + .init = stm32_dfsdm_audio_init, +}; + +static const struct of_device_id stm32_dfsdm_adc_match[] = { + { + .compatible = "st,stm32-dfsdm-adc", + .data = &stm32h7_dfsdm_adc_data, + }, + { + .compatible = "st,stm32-dfsdm-dmic", + .data = &stm32h7_dfsdm_audio_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, stm32_dfsdm_adc_match); + +static int stm32_dfsdm_adc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct stm32_dfsdm_adc *adc; + struct device_node *np = dev->of_node; + const struct stm32_dfsdm_dev_data *dev_data; + struct iio_dev *iio; + char *name; + int ret, irq, val; + + dev_data = of_device_get_match_data(dev); + iio = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!iio) { + dev_err(dev, "%s: Failed to allocate IIO\n", __func__); + return -ENOMEM; + } + + adc = iio_priv(iio); + adc->dfsdm = dev_get_drvdata(dev->parent); + + iio->dev.of_node = np; + iio->modes = INDIO_DIRECT_MODE; + + platform_set_drvdata(pdev, iio); + + ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id); + if (ret != 0 || adc->fl_id >= adc->dfsdm->num_fls) { + dev_err(dev, "Missing or bad reg property\n"); + return -EINVAL; + } + + name = devm_kzalloc(dev, sizeof("dfsdm-adc0"), GFP_KERNEL); + if (!name) + return -ENOMEM; + if (dev_data->type == DFSDM_AUDIO) { + iio->info = &stm32_dfsdm_info_audio; + snprintf(name, sizeof("dfsdm-pdm0"), "dfsdm-pdm%d", adc->fl_id); + } else { + iio->info = &stm32_dfsdm_info_adc; + snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id); + } + iio->name = name; + + /* + * In a first step IRQs generated for channels are not treated. + * 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, iio); + if (ret < 0) { + dev_err(dev, "Failed to request IRQ\n"); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "st,filter-order", &val); + if (ret < 0) { + dev_err(dev, "Failed to set filter order\n"); + return ret; + } + + adc->dfsdm->fl_list[adc->fl_id].ford = val; + + ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val); + if (!ret) + adc->dfsdm->fl_list[adc->fl_id].sync_mode = val; + + adc->dev_data = dev_data; + ret = dev_data->init(dev, iio); + if (ret < 0) + return ret; + + ret = iio_device_register(iio); + if (ret < 0) + goto err_cleanup; + + if (dev_data->type == DFSDM_AUDIO) { + ret = of_platform_populate(np, NULL, NULL, dev); + if (ret < 0) { + dev_err(dev, "Failed to find an audio DAI\n"); + goto err_unregister; + } + } + + return 0; + +err_unregister: + iio_device_unregister(iio); +err_cleanup: + stm32_dfsdm_dma_release(iio); + + return ret; +} + +static int stm32_dfsdm_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + if (adc->dev_data->type == DFSDM_AUDIO) + of_platform_depopulate(&pdev->dev); + iio_device_unregister(indio_dev); + stm32_dfsdm_dma_release(indio_dev); + + return 0; +} + +static int __maybe_unused stm32_dfsdm_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + 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 iio_dev *indio_dev = dev_get_drvdata(dev); + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + 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, +}; +module_platform_driver(stm32_dfsdm_adc_driver); + +MODULE_DESCRIPTION("STM32 sigma delta ADC"); +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c new file mode 100644 index 000000000..42a737770 --- /dev/null +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part the core part STM32 DFSDM driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics. + */ + +#include <linux/clk.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#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> + +#include "stm32-dfsdm.h" + +struct stm32_dfsdm_dev_data { + unsigned int num_filters; + unsigned int num_channels; + const struct regmap_config *regmap_cfg; +}; + +#define STM32H7_DFSDM_NUM_FILTERS 4 +#define STM32H7_DFSDM_NUM_CHANNELS 8 +#define STM32MP1_DFSDM_NUM_FILTERS 6 +#define STM32MP1_DFSDM_NUM_CHANNELS 8 + +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg < DFSDM_FILTER_BASE_ADR) + return false; + + /* + * Mask is done on register to avoid to list registers of all + * filter instances. + */ + switch (reg & DFSDM_FILTER_REG_MASK) { + case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK: + case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK: + case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK: + case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK: + return true; + } + + return false; +} + +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = sizeof(u32), + .max_register = 0x2B8, + .volatile_reg = stm32_dfsdm_volatile_reg, + .fast_io = true, +}; + +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = { + .num_filters = STM32H7_DFSDM_NUM_FILTERS, + .num_channels = STM32H7_DFSDM_NUM_CHANNELS, + .regmap_cfg = &stm32h7_dfsdm_regmap_cfg, +}; + +static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = sizeof(u32), + .max_register = 0x7fc, + .volatile_reg = stm32_dfsdm_volatile_reg, + .fast_io = true, +}; + +static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = { + .num_filters = STM32MP1_DFSDM_NUM_FILTERS, + .num_channels = STM32MP1_DFSDM_NUM_CHANNELS, + .regmap_cfg = &stm32mp1_dfsdm_regmap_cfg, +}; + +struct dfsdm_priv { + struct platform_device *pdev; /* platform device */ + + struct stm32_dfsdm dfsdm; /* common data exported for all instances */ + + unsigned int spi_clk_out_div; /* SPI clkout divider value */ + atomic_t n_active_ch; /* number of current active channels */ + + struct clk *clk; /* DFSDM clock */ + 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. + * + * Enable interface if n_active_ch is not null. + * @dfsdm: Handle used to retrieve dfsdm context. + */ +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *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 = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + goto error_ret; + } + + /* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */ + clk_src = priv->aclk ? 1 : 0; + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_CKOUTSRC_MASK, + DFSDM_CHCFGR1_CKOUTSRC(clk_src)); + if (ret < 0) + 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 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 pm_put; + } + + dev_dbg(dev, "%s: n_active_ch %d\n", __func__, + atomic_read(&priv->n_active_ch)); + + return 0; + +pm_put: + pm_runtime_put_sync(dev); +error_ret: + atomic_dec(&priv->n_active_ch); + + return ret; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_start_dfsdm); + +/** + * stm32_dfsdm_stop_dfsdm - stop global DFSDM interface. + * + * Disable interface if n_active_ch is null + * @dfsdm: Handle used to retrieve dfsdm context. + */ +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm) +{ + struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm); + int ret; + + if (atomic_dec_and_test(&priv->n_active_ch)) { + /* Global disable of DFSDM interface */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_DFSDMEN_MASK, + DFSDM_CHCFGR1_DFSDMEN(0)); + if (ret < 0) + return ret; + + /* Stop SPI CLKOUT */ + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0), + DFSDM_CHCFGR1_CKOUTDIV_MASK, + DFSDM_CHCFGR1_CKOUTDIV(0)); + if (ret < 0) + return ret; + + 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)); + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_dfsdm_stop_dfsdm); + +static int stm32_dfsdm_parse_of(struct platform_device *pdev, + struct dfsdm_priv *priv) +{ + struct device_node *node = pdev->dev.of_node; + struct resource *res; + unsigned long clk_freq, divider; + unsigned int spi_freq, rem; + int ret; + + if (!node) + return -EINVAL; + + priv->dfsdm.base = devm_platform_get_and_ioremap_resource(pdev, 0, + &res); + if (IS_ERR(priv->dfsdm.base)) + return PTR_ERR(priv->dfsdm.base); + + priv->dfsdm.phys_base = res->start; + + /* + * "dfsdm" clock is mandatory for DFSDM peripheral clocking. + * "dfsdm" or "audio" clocks can be used as source clock for + * the SPI clock out signal and internal processing, depending + * on use case. + */ + priv->clk = devm_clk_get(&pdev->dev, "dfsdm"); + if (IS_ERR(priv->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk), + "Failed to get clock\n"); + + priv->aclk = devm_clk_get(&pdev->dev, "audio"); + if (IS_ERR(priv->aclk)) + priv->aclk = NULL; + + if (priv->aclk) + clk_freq = clk_get_rate(priv->aclk); + else + clk_freq = clk_get_rate(priv->clk); + + /* SPI clock out frequency */ + ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency", + &spi_freq); + if (ret < 0) { + /* No SPI master mode */ + return 0; + } + + 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; + } + + /* 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"); + dev_warn(&pdev->dev, "%ld = %d * %d + %d\n", + clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem); + } + + return 0; +}; + +static const struct of_device_id stm32_dfsdm_of_match[] = { + { + .compatible = "st,stm32h7-dfsdm", + .data = &stm32h7_dfsdm_data, + }, + { + .compatible = "st,stm32mp1-dfsdm", + .data = &stm32mp1_dfsdm_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match); + +static int stm32_dfsdm_probe(struct platform_device *pdev) +{ + struct dfsdm_priv *priv; + const struct stm32_dfsdm_dev_data *dev_data; + struct stm32_dfsdm *dfsdm; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->pdev = pdev; + + dev_data = of_device_get_match_data(&pdev->dev); + + dfsdm = &priv->dfsdm; + dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters, + sizeof(*dfsdm->fl_list), GFP_KERNEL); + if (!dfsdm->fl_list) + return -ENOMEM; + + dfsdm->num_fls = dev_data->num_filters; + dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels, + sizeof(*dfsdm->ch_list), + GFP_KERNEL); + if (!dfsdm->ch_list) + return -ENOMEM; + dfsdm->num_chs = dev_data->num_channels; + + ret = stm32_dfsdm_parse_of(pdev, priv); + if (ret < 0) + return ret; + + dfsdm->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "dfsdm", + dfsdm->base, + dev_data->regmap_cfg); + if (IS_ERR(dfsdm->regmap)) { + ret = PTR_ERR(dfsdm->regmap); + dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n", + __func__, ret); + return ret; + } + + platform_set_drvdata(pdev, dfsdm); + + 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, + }, +}; + +module_platform_driver(stm32_dfsdm_driver); + +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h new file mode 100644 index 000000000..4afc1f528 --- /dev/null +++ b/drivers/iio/adc/stm32-dfsdm.h @@ -0,0 +1,326 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * This file is part of STM32 DFSDM driver + * + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved + * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com>. + */ + +#ifndef MDF_STM32_DFSDM__H +#define MDF_STM32_DFSDM__H + +#include <linux/bitfield.h> + +/* + * STM32 DFSDM - global register map + * ________________________________________________________ + * | Offset | Registers block | + * -------------------------------------------------------- + * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS | + * -------------------------------------------------------- + * | 0x020 | CHANNEL 1 | + * -------------------------------------------------------- + * | ... | ..... | + * -------------------------------------------------------- + * | 0x0E0 | CHANNEL 7 | + * -------------------------------------------------------- + * | 0x100 | FILTER 0 + COMMON FILTER FIELDs | + * -------------------------------------------------------- + * | 0x200 | FILTER 1 | + * -------------------------------------------------------- + * | 0x300 | FILTER 2 | + * -------------------------------------------------------- + * | 0x400 | FILTER 3 | + * -------------------------------------------------------- + */ + +/* + * Channels register definitions + */ +#define DFSDM_CHCFGR1(y) ((y) * 0x20 + 0x00) +#define DFSDM_CHCFGR2(y) ((y) * 0x20 + 0x04) +#define DFSDM_AWSCDR(y) ((y) * 0x20 + 0x08) +#define DFSDM_CHWDATR(y) ((y) * 0x20 + 0x0C) +#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10) + +/* CHCFGR1: Channel configuration register 1 */ +#define DFSDM_CHCFGR1_SITP_MASK GENMASK(1, 0) +#define DFSDM_CHCFGR1_SITP(v) FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v) +#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2) +#define DFSDM_CHCFGR1_SPICKSEL(v) FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v) +#define DFSDM_CHCFGR1_SCDEN_MASK BIT(5) +#define DFSDM_CHCFGR1_SCDEN(v) FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v) +#define DFSDM_CHCFGR1_CKABEN_MASK BIT(6) +#define DFSDM_CHCFGR1_CKABEN(v) FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v) +#define DFSDM_CHCFGR1_CHEN_MASK BIT(7) +#define DFSDM_CHCFGR1_CHEN(v) FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v) +#define DFSDM_CHCFGR1_CHINSEL_MASK BIT(8) +#define DFSDM_CHCFGR1_CHINSEL(v) FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v) +#define DFSDM_CHCFGR1_DATMPX_MASK GENMASK(13, 12) +#define DFSDM_CHCFGR1_DATMPX(v) FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v) +#define DFSDM_CHCFGR1_DATPACK_MASK GENMASK(15, 14) +#define DFSDM_CHCFGR1_DATPACK(v) FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v) +#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16) +#define DFSDM_CHCFGR1_CKOUTDIV(v) FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v) +#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30) +#define DFSDM_CHCFGR1_CKOUTSRC(v) FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v) +#define DFSDM_CHCFGR1_DFSDMEN_MASK BIT(31) +#define DFSDM_CHCFGR1_DFSDMEN(v) FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v) + +/* CHCFGR2: Channel configuration register 2 */ +#define DFSDM_CHCFGR2_DTRBS_MASK GENMASK(7, 3) +#define DFSDM_CHCFGR2_DTRBS(v) FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v) +#define DFSDM_CHCFGR2_OFFSET_MASK GENMASK(31, 8) +#define DFSDM_CHCFGR2_OFFSET(v) FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v) + +/* AWSCDR: Channel analog watchdog and short circuit detector */ +#define DFSDM_AWSCDR_SCDT_MASK GENMASK(7, 0) +#define DFSDM_AWSCDR_SCDT(v) FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v) +#define DFSDM_AWSCDR_BKSCD_MASK GENMASK(15, 12) +#define DFSDM_AWSCDR_BKSCD(v) FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v) +#define DFSDM_AWSCDR_AWFOSR_MASK GENMASK(20, 16) +#define DFSDM_AWSCDR_AWFOSR(v) FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v) +#define DFSDM_AWSCDR_AWFORD_MASK GENMASK(23, 22) +#define DFSDM_AWSCDR_AWFORD(v) FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v) + +/* + * Filters register definitions + */ +#define DFSDM_FILTER_BASE_ADR 0x100 +#define DFSDM_FILTER_REG_MASK 0x7F +#define DFSDM_FILTER_X_BASE_ADR(x) ((x) * 0x80 + DFSDM_FILTER_BASE_ADR) + +#define DFSDM_CR1(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x00) +#define DFSDM_CR2(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x04) +#define DFSDM_ISR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x08) +#define DFSDM_ICR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x0C) +#define DFSDM_JCHGR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x10) +#define DFSDM_FCR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x14) +#define DFSDM_JDATAR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x18) +#define DFSDM_RDATAR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x1C) +#define DFSDM_AWHTR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x20) +#define DFSDM_AWLTR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x24) +#define DFSDM_AWSR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x28) +#define DFSDM_AWCFR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x2C) +#define DFSDM_EXMAX(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x30) +#define DFSDM_EXMIN(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x34) +#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x38) + +/* CR1 Control register 1 */ +#define DFSDM_CR1_DFEN_MASK BIT(0) +#define DFSDM_CR1_DFEN(v) FIELD_PREP(DFSDM_CR1_DFEN_MASK, v) +#define DFSDM_CR1_JSWSTART_MASK BIT(1) +#define DFSDM_CR1_JSWSTART(v) FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v) +#define DFSDM_CR1_JSYNC_MASK BIT(3) +#define DFSDM_CR1_JSYNC(v) FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v) +#define DFSDM_CR1_JSCAN_MASK BIT(4) +#define DFSDM_CR1_JSCAN(v) FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v) +#define DFSDM_CR1_JDMAEN_MASK BIT(5) +#define DFSDM_CR1_JDMAEN(v) FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v) +#define DFSDM_CR1_JEXTSEL_MASK GENMASK(12, 8) +#define DFSDM_CR1_JEXTSEL(v) FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v) +#define DFSDM_CR1_JEXTEN_MASK GENMASK(14, 13) +#define DFSDM_CR1_JEXTEN(v) FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v) +#define DFSDM_CR1_RSWSTART_MASK BIT(17) +#define DFSDM_CR1_RSWSTART(v) FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v) +#define DFSDM_CR1_RCONT_MASK BIT(18) +#define DFSDM_CR1_RCONT(v) FIELD_PREP(DFSDM_CR1_RCONT_MASK, v) +#define DFSDM_CR1_RSYNC_MASK BIT(19) +#define DFSDM_CR1_RSYNC(v) FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v) +#define DFSDM_CR1_RDMAEN_MASK BIT(21) +#define DFSDM_CR1_RDMAEN(v) FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v) +#define DFSDM_CR1_RCH_MASK GENMASK(26, 24) +#define DFSDM_CR1_RCH(v) FIELD_PREP(DFSDM_CR1_RCH_MASK, v) +#define DFSDM_CR1_FAST_MASK BIT(29) +#define DFSDM_CR1_FAST(v) FIELD_PREP(DFSDM_CR1_FAST_MASK, v) +#define DFSDM_CR1_AWFSEL_MASK BIT(30) +#define DFSDM_CR1_AWFSEL(v) FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v) + +/* CR2: Control register 2 */ +#define DFSDM_CR2_IE_MASK GENMASK(6, 0) +#define DFSDM_CR2_IE(v) FIELD_PREP(DFSDM_CR2_IE_MASK, v) +#define DFSDM_CR2_JEOCIE_MASK BIT(0) +#define DFSDM_CR2_JEOCIE(v) FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v) +#define DFSDM_CR2_REOCIE_MASK BIT(1) +#define DFSDM_CR2_REOCIE(v) FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v) +#define DFSDM_CR2_JOVRIE_MASK BIT(2) +#define DFSDM_CR2_JOVRIE(v) FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v) +#define DFSDM_CR2_ROVRIE_MASK BIT(3) +#define DFSDM_CR2_ROVRIE(v) FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v) +#define DFSDM_CR2_AWDIE_MASK BIT(4) +#define DFSDM_CR2_AWDIE(v) FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v) +#define DFSDM_CR2_SCDIE_MASK BIT(5) +#define DFSDM_CR2_SCDIE(v) FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v) +#define DFSDM_CR2_CKABIE_MASK BIT(6) +#define DFSDM_CR2_CKABIE(v) FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v) +#define DFSDM_CR2_EXCH_MASK GENMASK(15, 8) +#define DFSDM_CR2_EXCH(v) FIELD_PREP(DFSDM_CR2_EXCH_MASK, v) +#define DFSDM_CR2_AWDCH_MASK GENMASK(23, 16) +#define DFSDM_CR2_AWDCH(v) FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v) + +/* ISR: Interrupt status register */ +#define DFSDM_ISR_JEOCF_MASK BIT(0) +#define DFSDM_ISR_JEOCF(v) FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v) +#define DFSDM_ISR_REOCF_MASK BIT(1) +#define DFSDM_ISR_REOCF(v) FIELD_PREP(DFSDM_ISR_REOCF_MASK, v) +#define DFSDM_ISR_JOVRF_MASK BIT(2) +#define DFSDM_ISR_JOVRF(v) FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v) +#define DFSDM_ISR_ROVRF_MASK BIT(3) +#define DFSDM_ISR_ROVRF(v) FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v) +#define DFSDM_ISR_AWDF_MASK BIT(4) +#define DFSDM_ISR_AWDF(v) FIELD_PREP(DFSDM_ISR_AWDF_MASK, v) +#define DFSDM_ISR_JCIP_MASK BIT(13) +#define DFSDM_ISR_JCIP(v) FIELD_PREP(DFSDM_ISR_JCIP_MASK, v) +#define DFSDM_ISR_RCIP_MASK BIT(14) +#define DFSDM_ISR_RCIP(v) FIELD_PREP(DFSDM_ISR_RCIP, v) +#define DFSDM_ISR_CKABF_MASK GENMASK(23, 16) +#define DFSDM_ISR_CKABF(v) FIELD_PREP(DFSDM_ISR_CKABF_MASK, v) +#define DFSDM_ISR_SCDF_MASK GENMASK(31, 24) +#define DFSDM_ISR_SCDF(v) FIELD_PREP(DFSDM_ISR_SCDF_MASK, v) + +/* ICR: Interrupt flag clear register */ +#define DFSDM_ICR_CLRJOVRF_MASK BIT(2) +#define DFSDM_ICR_CLRJOVRF(v) FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v) +#define DFSDM_ICR_CLRROVRF_MASK BIT(3) +#define DFSDM_ICR_CLRROVRF(v) FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v) +#define DFSDM_ICR_CLRCKABF_MASK GENMASK(23, 16) +#define DFSDM_ICR_CLRCKABF(v) FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v) +#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y)) +#define DFSDM_ICR_CLRCKABF_CH(v, y) \ + (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y)) +#define DFSDM_ICR_CLRSCDF_MASK GENMASK(31, 24) +#define DFSDM_ICR_CLRSCDF(v) FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v) +#define DFSDM_ICR_CLRSCDF_CH_MASK(y) BIT(24 + (y)) +#define DFSDM_ICR_CLRSCDF_CH(v, y) \ + (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y)) + +/* FCR: Filter control register */ +#define DFSDM_FCR_IOSR_MASK GENMASK(7, 0) +#define DFSDM_FCR_IOSR(v) FIELD_PREP(DFSDM_FCR_IOSR_MASK, v) +#define DFSDM_FCR_FOSR_MASK GENMASK(25, 16) +#define DFSDM_FCR_FOSR(v) FIELD_PREP(DFSDM_FCR_FOSR_MASK, v) +#define DFSDM_FCR_FORD_MASK GENMASK(31, 29) +#define DFSDM_FCR_FORD(v) FIELD_PREP(DFSDM_FCR_FORD_MASK, v) + +/* RDATAR: Filter data register for regular channel */ +#define DFSDM_DATAR_CH_MASK GENMASK(2, 0) +#define DFSDM_DATAR_DATA_OFFSET 8 +#define DFSDM_DATAR_DATA_MASK GENMASK(31, DFSDM_DATAR_DATA_OFFSET) + +/* AWLTR: Filter analog watchdog low threshold register */ +#define DFSDM_AWLTR_BKAWL_MASK GENMASK(3, 0) +#define DFSDM_AWLTR_BKAWL(v) FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v) +#define DFSDM_AWLTR_AWLT_MASK GENMASK(31, 8) +#define DFSDM_AWLTR_AWLT(v) FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v) + +/* AWHTR: Filter analog watchdog low threshold register */ +#define DFSDM_AWHTR_BKAWH_MASK GENMASK(3, 0) +#define DFSDM_AWHTR_BKAWH(v) FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v) +#define DFSDM_AWHTR_AWHT_MASK GENMASK(31, 8) +#define DFSDM_AWHTR_AWHT(v) FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v) + +/* AWSR: Filter watchdog status register */ +#define DFSDM_AWSR_AWLTF_MASK GENMASK(7, 0) +#define DFSDM_AWSR_AWLTF(v) FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v) +#define DFSDM_AWSR_AWHTF_MASK GENMASK(15, 8) +#define DFSDM_AWSR_AWHTF(v) FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v) + +/* AWCFR: Filter watchdog status register */ +#define DFSDM_AWCFR_AWLTF_MASK GENMASK(7, 0) +#define DFSDM_AWCFR_AWLTF(v) FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v) +#define DFSDM_AWCFR_AWHTF_MASK GENMASK(15, 8) +#define DFSDM_AWCFR_AWHTF(v) FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v) + +/* DFSDM filter order */ +enum stm32_dfsdm_sinc_order { + DFSDM_FASTSINC_ORDER, /* FastSinc filter type */ + DFSDM_SINC1_ORDER, /* Sinc 1 filter type */ + DFSDM_SINC2_ORDER, /* Sinc 2 filter type */ + DFSDM_SINC3_ORDER, /* Sinc 3 filter type */ + DFSDM_SINC4_ORDER, /* Sinc 4 filter type (N.A. for watchdog) */ + DFSDM_SINC5_ORDER, /* Sinc 5 filter type (N.A. for watchdog) */ + DFSDM_NB_SINC_ORDER, +}; + +/** + * struct stm32_dfsdm_filter_osr - DFSDM filter settings linked to oversampling + * @iosr: integrator oversampling + * @fosr: filter oversampling + * @rshift: output sample right shift (hardware shift) + * @lshift: output sample left shift (software shift) + * @res: output sample resolution + * @bits: output sample resolution in bits + * @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; + u32 bits; + 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 { + enum stm32_dfsdm_sinc_order ford; + struct stm32_dfsdm_filter_osr flo[2]; + unsigned int sync_mode; + unsigned int fast; +}; + +/** + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel + * @id: id of the channel + * @type: interface type linked to stm32_dfsdm_chan_type + * @src: interface type linked to stm32_dfsdm_chan_src + * @alt_si: alternative serial input interface + */ +struct stm32_dfsdm_channel { + unsigned int id; + unsigned int type; + unsigned int src; + unsigned int alt_si; +}; + +/** + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances) + * @base: control registers base cpu addr + * @phys_base: DFSDM IP register physical address + * @regmap: regmap for register read/write + * @fl_list: filter resources list + * @num_fls: number of filter resources available + * @ch_list: channel resources list + * @num_chs: number of channel resources available + * @spi_master_freq: SPI clock out frequency + */ +struct stm32_dfsdm { + void __iomem *base; + phys_addr_t phys_base; + struct regmap *regmap; + struct stm32_dfsdm_filter *fl_list; + unsigned int num_fls; + struct stm32_dfsdm_channel *ch_list; + unsigned int num_chs; + unsigned int spi_master_freq; +}; + +/* DFSDM channel serial spi clock source */ +enum stm32_dfsdm_spi_clk_src { + DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL, + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL, + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING, + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING +}; + +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm); +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm); + +#endif diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c new file mode 100644 index 000000000..64305d9fa --- /dev/null +++ b/drivers/iio/adc/stmpe-adc.c @@ -0,0 +1,365 @@ +// 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) +{ + unsigned 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) +{ + unsigned 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; + __be16 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->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 new file mode 100644 index 000000000..b658a75d4 --- /dev/null +++ b/drivers/iio/adc/stx104.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IIO driver for the Apex Embedded Systems STX104 + * Copyright (C) 2016 William Breathitt Gray + */ +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/iio/iio.h> +#include <linux/iio/types.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/isa.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#define STX104_OUT_CHAN(chan) { \ + .type = IIO_VOLTAGE, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .output = 1 \ +} +#define STX104_IN_CHAN(chan, diff) { \ + .type = IIO_VOLTAGE, \ + .channel = chan, \ + .channel2 = chan, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .differential = diff \ +} + +#define STX104_NUM_OUT_CHAN 2 + +#define STX104_EXTENT 16 + +static unsigned int base[max_num_isa_dev(STX104_EXTENT)]; +static unsigned int num_stx104; +module_param_hw_array(base, uint, ioport, &num_stx104, 0); +MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses"); + +/** + * struct stx104_reg - device register structure + * @ssr_ad: Software Strobe Register and ADC Data + * @achan: ADC Channel + * @dio: Digital I/O + * @dac: DAC Channels + * @cir_asr: Clear Interrupts and ADC Status + * @acr: ADC Control + * @pccr_fsh: Pacer Clock Control and FIFO Status MSB + * @acfg: ADC Configuration + */ +struct stx104_reg { + u16 ssr_ad; + u8 achan; + u8 dio; + u16 dac[2]; + u8 cir_asr; + u8 acr; + u8 pccr_fsh; + u8 acfg; +}; + +/** + * struct stx104_iio - IIO device private data structure + * @lock: synchronization lock to prevent I/O race conditions + * @chan_out_states: channels' output states + * @reg: I/O address offset for the device registers + */ +struct stx104_iio { + struct mutex lock; + unsigned int chan_out_states[STX104_NUM_OUT_CHAN]; + struct stx104_reg __iomem *reg; +}; + +/** + * struct stx104_gpio - GPIO device private data structure + * @chip: instance of the gpio_chip + * @lock: synchronization lock to prevent I/O race conditions + * @base: base port address of the GPIO device + * @out_state: output bits state + */ +struct stx104_gpio { + struct gpio_chip chip; + spinlock_t lock; + u8 __iomem *base; + unsigned int out_state; +}; + +static int stx104_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long mask) +{ + struct stx104_iio *const priv = iio_priv(indio_dev); + struct stx104_reg __iomem *const reg = priv->reg; + unsigned int adc_config; + int adbu; + int gain; + + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + /* get gain configuration */ + adc_config = ioread8(®->acfg); + gain = adc_config & 0x3; + + *val = 1 << gain; + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + if (chan->output) { + *val = priv->chan_out_states[chan->channel]; + return IIO_VAL_INT; + } + + mutex_lock(&priv->lock); + + /* select ADC channel */ + iowrite8(chan->channel | (chan->channel << 4), ®->achan); + + /* trigger ADC sample capture by writing to the 8-bit + * Software Strobe Register and wait for completion + */ + iowrite8(0, ®->ssr_ad); + while (ioread8(®->cir_asr) & BIT(7)); + + *val = ioread16(®->ssr_ad); + + mutex_unlock(&priv->lock); + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + /* get ADC bipolar/unipolar configuration */ + adc_config = ioread8(®->acfg); + adbu = !(adc_config & BIT(2)); + + *val = -32768 * adbu; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* get ADC bipolar/unipolar and gain configuration */ + adc_config = ioread8(®->acfg); + adbu = !(adc_config & BIT(2)); + gain = adc_config & 0x3; + + *val = 5; + *val2 = 15 - adbu + gain; + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static int stx104_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct stx104_iio *const priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + /* Only four gain states (x1, x2, x4, x8) */ + switch (val) { + case 1: + iowrite8(0, &priv->reg->acfg); + break; + case 2: + iowrite8(1, &priv->reg->acfg); + break; + case 4: + iowrite8(2, &priv->reg->acfg); + break; + case 8: + iowrite8(3, &priv->reg->acfg); + break; + default: + return -EINVAL; + } + + return 0; + case IIO_CHAN_INFO_RAW: + if (chan->output) { + /* DAC can only accept up to a 16-bit value */ + if ((unsigned int)val > 65535) + return -EINVAL; + + mutex_lock(&priv->lock); + + priv->chan_out_states[chan->channel] = val; + iowrite16(val, &priv->reg->dac[chan->channel]); + + mutex_unlock(&priv->lock); + return 0; + } + return -EINVAL; + } + + return -EINVAL; +} + +static const struct iio_info stx104_info = { + .read_raw = stx104_read_raw, + .write_raw = stx104_write_raw +}; + +/* single-ended input channels configuration */ +static const struct iio_chan_spec stx104_channels_sing[] = { + STX104_OUT_CHAN(0), STX104_OUT_CHAN(1), + STX104_IN_CHAN(0, 0), STX104_IN_CHAN(1, 0), STX104_IN_CHAN(2, 0), + STX104_IN_CHAN(3, 0), STX104_IN_CHAN(4, 0), STX104_IN_CHAN(5, 0), + STX104_IN_CHAN(6, 0), STX104_IN_CHAN(7, 0), STX104_IN_CHAN(8, 0), + STX104_IN_CHAN(9, 0), STX104_IN_CHAN(10, 0), STX104_IN_CHAN(11, 0), + STX104_IN_CHAN(12, 0), STX104_IN_CHAN(13, 0), STX104_IN_CHAN(14, 0), + STX104_IN_CHAN(15, 0) +}; +/* differential input channels configuration */ +static const struct iio_chan_spec stx104_channels_diff[] = { + STX104_OUT_CHAN(0), STX104_OUT_CHAN(1), + STX104_IN_CHAN(0, 1), STX104_IN_CHAN(1, 1), STX104_IN_CHAN(2, 1), + STX104_IN_CHAN(3, 1), STX104_IN_CHAN(4, 1), STX104_IN_CHAN(5, 1), + STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1) +}; + +static int stx104_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + /* GPIO 0-3 are input only, while the rest are output only */ + if (offset < 4) + return 1; + + return 0; +} + +static int stx104_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + if (offset >= 4) + return -EINVAL; + + return 0; +} + +static int stx104_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int value) +{ + if (offset < 4) + return -EINVAL; + + chip->set(chip, offset, value); + return 0; +} + +static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + + if (offset >= 4) + return -EINVAL; + + return !!(ioread8(stx104gpio->base) & BIT(offset)); +} + +static int stx104_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + + *bits = ioread8(stx104gpio->base); + + return 0; +} + +static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + const unsigned int mask = BIT(offset) >> 4; + unsigned long flags; + + if (offset < 4) + return; + + spin_lock_irqsave(&stx104gpio->lock, flags); + + if (value) + stx104gpio->out_state |= mask; + else + stx104gpio->out_state &= ~mask; + + iowrite8(stx104gpio->out_state, stx104gpio->base); + + spin_unlock_irqrestore(&stx104gpio->lock, flags); +} + +#define STX104_NGPIO 8 +static const char *stx104_names[STX104_NGPIO] = { + "DIN0", "DIN1", "DIN2", "DIN3", "DOUT0", "DOUT1", "DOUT2", "DOUT3" +}; + +static void stx104_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + unsigned long flags; + + /* verify masked GPIO are output */ + if (!(*mask & 0xF0)) + return; + + *mask >>= 4; + *bits >>= 4; + + spin_lock_irqsave(&stx104gpio->lock, flags); + + stx104gpio->out_state &= ~*mask; + stx104gpio->out_state |= *mask & *bits; + iowrite8(stx104gpio->out_state, stx104gpio->base); + + spin_unlock_irqrestore(&stx104gpio->lock, flags); +} + +static int stx104_probe(struct device *dev, unsigned int id) +{ + struct iio_dev *indio_dev; + struct stx104_iio *priv; + struct stx104_gpio *stx104gpio; + int err; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + stx104gpio = devm_kzalloc(dev, sizeof(*stx104gpio), GFP_KERNEL); + if (!stx104gpio) + return -ENOMEM; + + if (!devm_request_region(dev, base[id], STX104_EXTENT, + dev_name(dev))) { + dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", + base[id], base[id] + STX104_EXTENT); + return -EBUSY; + } + + priv = iio_priv(indio_dev); + priv->reg = devm_ioport_map(dev, base[id], STX104_EXTENT); + if (!priv->reg) + return -ENOMEM; + + indio_dev->info = &stx104_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + /* determine if differential inputs */ + if (ioread8(&priv->reg->cir_asr) & BIT(5)) { + indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff); + indio_dev->channels = stx104_channels_diff; + } else { + indio_dev->num_channels = ARRAY_SIZE(stx104_channels_sing); + indio_dev->channels = stx104_channels_sing; + } + + indio_dev->name = dev_name(dev); + + mutex_init(&priv->lock); + + /* configure device for software trigger operation */ + iowrite8(0, &priv->reg->acr); + + /* initialize gain setting to x1 */ + iowrite8(0, &priv->reg->acfg); + + /* initialize DAC output to 0V */ + iowrite16(0, &priv->reg->dac[0]); + iowrite16(0, &priv->reg->dac[1]); + + stx104gpio->chip.label = dev_name(dev); + stx104gpio->chip.parent = dev; + stx104gpio->chip.owner = THIS_MODULE; + stx104gpio->chip.base = -1; + stx104gpio->chip.ngpio = STX104_NGPIO; + stx104gpio->chip.names = stx104_names; + stx104gpio->chip.get_direction = stx104_gpio_get_direction; + stx104gpio->chip.direction_input = stx104_gpio_direction_input; + stx104gpio->chip.direction_output = stx104_gpio_direction_output; + stx104gpio->chip.get = stx104_gpio_get; + stx104gpio->chip.get_multiple = stx104_gpio_get_multiple; + stx104gpio->chip.set = stx104_gpio_set; + stx104gpio->chip.set_multiple = stx104_gpio_set_multiple; + stx104gpio->base = &priv->reg->dio; + stx104gpio->out_state = 0x0; + + spin_lock_init(&stx104gpio->lock); + + err = devm_gpiochip_add_data(dev, &stx104gpio->chip, stx104gpio); + if (err) { + dev_err(dev, "GPIO registering failed (%d)\n", err); + return err; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static struct isa_driver stx104_driver = { + .probe = stx104_probe, + .driver = { + .name = "stx104" + }, +}; + +module_isa_driver(stx104_driver, num_stx104); + +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); +MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c new file mode 100644 index 000000000..99b43f28e --- /dev/null +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -0,0 +1,716 @@ +// 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> + * + * The Allwinner SoCs all have an ADC that can also act as a touchscreen + * controller and a thermal sensor. + * The thermal sensor works only when the ADC acts as a touchscreen controller + * and is configured to throw an interrupt every fixed periods of time (let say + * every X seconds). + * One would be tempted to disable the IP on the hardware side rather than + * disabling interrupts to save some power but that resets the internal clock of + * the IP, resulting in having to wait X seconds every time we want to read the + * value of the thermal sensor. + * This is also the reason of using autosuspend in pm_runtime. If there was no + * autosuspend, the thermal sensor would need X seconds after every + * pm_runtime_get_sync to get a value from the ADC. The autosuspend allows the + * thermal sensor to be requested again in a certain time span before it gets + * shutdown for not being used. + */ + +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/thermal.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> +#include <linux/iio/machine.h> +#include <linux/mfd/sun4i-gpadc.h> + +static unsigned int sun4i_gpadc_chan_select(unsigned int chan) +{ + return SUN4I_GPADC_CTRL1_ADC_CHAN_SELECT(chan); +} + +static unsigned int sun6i_gpadc_chan_select(unsigned int chan) +{ + return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan); +} + +struct gpadc_data { + int temp_offset; + int temp_scale; + unsigned int tp_mode_en; + unsigned int tp_adc_select; + unsigned int (*adc_chan_select)(unsigned int chan); + unsigned int adc_chan_mask; +}; + +static const struct gpadc_data sun4i_gpadc_data = { + .temp_offset = -1932, + .temp_scale = 133, + .tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN, + .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT, + .adc_chan_select = &sun4i_gpadc_chan_select, + .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK, +}; + +static const struct gpadc_data sun5i_gpadc_data = { + .temp_offset = -1447, + .temp_scale = 100, + .tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN, + .tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT, + .adc_chan_select = &sun4i_gpadc_chan_select, + .adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK, +}; + +static const struct gpadc_data sun6i_gpadc_data = { + .temp_offset = -1623, + .temp_scale = 167, + .tp_mode_en = SUN6I_GPADC_CTRL1_TP_MODE_EN, + .tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT, + .adc_chan_select = &sun6i_gpadc_chan_select, + .adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK, +}; + +static const struct gpadc_data sun8i_a33_gpadc_data = { + .temp_offset = -1662, + .temp_scale = 162, + .tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN, +}; + +struct sun4i_gpadc_iio { + struct iio_dev *indio_dev; + struct completion completion; + int temp_data; + u32 adc_data; + struct regmap *regmap; + unsigned int fifo_data_irq; + atomic_t ignore_fifo_data_irq; + unsigned int temp_data_irq; + atomic_t ignore_temp_data_irq; + const struct gpadc_data *data; + bool no_irq; + /* prevents concurrent reads of temperature and ADC */ + struct mutex mutex; + struct thermal_zone_device *tzd; + struct device *sensor_device; +}; + +#define SUN4I_GPADC_ADC_CHANNEL(_channel, _name) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = _name, \ +} + +static struct iio_map sun4i_gpadc_hwmon_maps[] = { + { + .adc_channel_label = "temp_adc", + .consumer_dev_name = "iio_hwmon.0", + }, + { /* sentinel */ }, +}; + +static const struct iio_chan_spec sun4i_gpadc_channels[] = { + SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"), + SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"), + SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"), + SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"), + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "temp_adc", + }, +}; + +static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = { + SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"), + SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"), + SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"), + SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"), +}; + +static const struct iio_chan_spec sun8i_a33_gpadc_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "temp_adc", + }, +}; + +static const struct regmap_config sun4i_gpadc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .fast_io = true, +}; + +static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel, + unsigned int irq) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + int ret; + u32 reg; + + pm_runtime_get_sync(indio_dev->dev.parent); + + reinit_completion(&info->completion); + + ret = regmap_write(info->regmap, SUN4I_GPADC_INT_FIFOC, + SUN4I_GPADC_INT_FIFOC_TP_FIFO_TRIG_LEVEL(1) | + SUN4I_GPADC_INT_FIFOC_TP_FIFO_FLUSH); + if (ret) + return ret; + + ret = regmap_read(info->regmap, SUN4I_GPADC_CTRL1, ®); + if (ret) + return ret; + + if (irq == info->fifo_data_irq) { + ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1, + info->data->tp_mode_en | + info->data->tp_adc_select | + info->data->adc_chan_select(channel)); + /* + * When the IP changes channel, it needs a bit of time to get + * correct values. + */ + if ((reg & info->data->adc_chan_mask) != + info->data->adc_chan_select(channel)) + mdelay(10); + + } else { + /* + * The temperature sensor returns valid data only when the ADC + * operates in touchscreen mode. + */ + ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1, + info->data->tp_mode_en); + } + + if (ret) + return ret; + + /* + * When the IP changes mode between ADC or touchscreen, it + * needs a bit of time to get correct values. + */ + if ((reg & info->data->tp_adc_select) != info->data->tp_adc_select) + mdelay(100); + + return 0; +} + +static int sun4i_gpadc_read(struct iio_dev *indio_dev, int channel, int *val, + unsigned int irq) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + int ret; + + mutex_lock(&info->mutex); + + ret = sun4i_prepare_for_irq(indio_dev, channel, irq); + if (ret) + goto err; + + enable_irq(irq); + + /* + * The temperature sensor throws an interruption periodically (currently + * set at periods of ~0.6s in sun4i_gpadc_runtime_resume). A 1s delay + * makes sure an interruption occurs in normal conditions. If it doesn't + * occur, then there is a timeout. + */ + if (!wait_for_completion_timeout(&info->completion, + msecs_to_jiffies(1000))) { + ret = -ETIMEDOUT; + goto err; + } + + if (irq == info->fifo_data_irq) + *val = info->adc_data; + else + *val = info->temp_data; + + ret = 0; + pm_runtime_mark_last_busy(indio_dev->dev.parent); + +err: + pm_runtime_put_autosuspend(indio_dev->dev.parent); + disable_irq(irq); + mutex_unlock(&info->mutex); + + return ret; +} + +static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel, + int *val) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + + return sun4i_gpadc_read(indio_dev, channel, val, info->fifo_data_irq); +} + +static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + + if (info->no_irq) { + pm_runtime_get_sync(indio_dev->dev.parent); + + regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val); + + pm_runtime_mark_last_busy(indio_dev->dev.parent); + pm_runtime_put_autosuspend(indio_dev->dev.parent); + + return 0; + } + + return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq); +} + +static int sun4i_gpadc_temp_offset(struct iio_dev *indio_dev, int *val) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + + *val = info->data->temp_offset; + + return 0; +} + +static int sun4i_gpadc_temp_scale(struct iio_dev *indio_dev, int *val) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + + *val = info->data->temp_scale; + + return 0; +} + +static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret; + + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + ret = sun4i_gpadc_temp_offset(indio_dev, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_RAW: + if (chan->type == IIO_VOLTAGE) + ret = sun4i_gpadc_adc_read(indio_dev, chan->channel, + val); + else + ret = sun4i_gpadc_temp_read(indio_dev, val); + + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_VOLTAGE) { + /* 3000mV / 4096 * raw */ + *val = 0; + *val2 = 732421875; + return IIO_VAL_INT_PLUS_NANO; + } + + ret = sun4i_gpadc_temp_scale(indio_dev, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static const struct iio_info sun4i_gpadc_iio_info = { + .read_raw = sun4i_gpadc_read_raw, +}; + +static irqreturn_t sun4i_gpadc_temp_data_irq_handler(int irq, void *dev_id) +{ + struct sun4i_gpadc_iio *info = dev_id; + + if (atomic_read(&info->ignore_temp_data_irq)) + goto out; + + if (!regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, &info->temp_data)) + complete(&info->completion); + +out: + return IRQ_HANDLED; +} + +static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id) +{ + struct sun4i_gpadc_iio *info = dev_id; + + if (atomic_read(&info->ignore_fifo_data_irq)) + goto out; + + if (!regmap_read(info->regmap, SUN4I_GPADC_DATA, &info->adc_data)) + complete(&info->completion); + +out: + return IRQ_HANDLED; +} + +static int sun4i_gpadc_runtime_suspend(struct device *dev) +{ + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev)); + + /* Disable the ADC on IP */ + regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0); + /* Disable temperature sensor on IP */ + regmap_write(info->regmap, SUN4I_GPADC_TPR, 0); + + return 0; +} + +static int sun4i_gpadc_runtime_resume(struct device *dev) +{ + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev)); + + /* clkin = 6MHz */ + regmap_write(info->regmap, SUN4I_GPADC_CTRL0, + SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) | + SUN4I_GPADC_CTRL0_FS_DIV(7) | + SUN4I_GPADC_CTRL0_T_ACQ(63)); + regmap_write(info->regmap, SUN4I_GPADC_CTRL1, info->data->tp_mode_en); + regmap_write(info->regmap, SUN4I_GPADC_CTRL3, + SUN4I_GPADC_CTRL3_FILTER_EN | + SUN4I_GPADC_CTRL3_FILTER_TYPE(1)); + /* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s */ + regmap_write(info->regmap, SUN4I_GPADC_TPR, + SUN4I_GPADC_TPR_TEMP_ENABLE | + SUN4I_GPADC_TPR_TEMP_PERIOD(800)); + + return 0; +} + +static int sun4i_gpadc_get_temp(void *data, int *temp) +{ + struct sun4i_gpadc_iio *info = data; + int val, scale, offset; + + if (sun4i_gpadc_temp_read(info->indio_dev, &val)) + return -ETIMEDOUT; + + sun4i_gpadc_temp_scale(info->indio_dev, &scale); + sun4i_gpadc_temp_offset(info->indio_dev, &offset); + + *temp = (val + offset) * scale; + + return 0; +} + +static const struct thermal_zone_of_device_ops sun4i_ts_tz_ops = { + .get_temp = &sun4i_gpadc_get_temp, +}; + +static const struct dev_pm_ops sun4i_gpadc_pm_ops = { + .runtime_suspend = &sun4i_gpadc_runtime_suspend, + .runtime_resume = &sun4i_gpadc_runtime_resume, +}; + +static int sun4i_irq_init(struct platform_device *pdev, const char *name, + irq_handler_t handler, const char *devname, + unsigned int *irq, atomic_t *atomic) +{ + int ret; + struct sun4i_gpadc_dev *mfd_dev = dev_get_drvdata(pdev->dev.parent); + struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(&pdev->dev)); + + /* + * Once the interrupt is activated, the IP continuously performs + * conversions thus throws interrupts. The interrupt is activated right + * after being requested but we want to control when these interrupts + * occur thus we disable it right after being requested. However, an + * interrupt might occur between these two instructions and we have to + * make sure that does not happen, by using atomic flags. We set the + * flag before requesting the interrupt and unset it right after + * disabling the interrupt. When an interrupt occurs between these two + * instructions, reading the atomic flag will tell us to ignore the + * interrupt. + */ + atomic_set(atomic, 1); + + ret = platform_get_irq_byname(pdev, name); + if (ret < 0) + return ret; + + ret = regmap_irq_get_virq(mfd_dev->regmap_irqc, ret); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get virq for irq %s\n", name); + return ret; + } + + *irq = ret; + ret = devm_request_any_context_irq(&pdev->dev, *irq, handler, 0, + devname, info); + if (ret < 0) { + dev_err(&pdev->dev, "could not request %s interrupt: %d\n", + name, ret); + return ret; + } + + disable_irq(*irq); + atomic_set(atomic, 0); + + return 0; +} + +static const struct of_device_id sun4i_gpadc_of_id[] = { + { + .compatible = "allwinner,sun8i-a33-ths", + .data = &sun8i_a33_gpadc_data, + }, + { /* sentinel */ } +}; + +static int sun4i_gpadc_probe_dt(struct platform_device *pdev, + struct iio_dev *indio_dev) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + void __iomem *base; + int ret; + + info->data = of_device_get_match_data(&pdev->dev); + if (!info->data) + return -ENODEV; + + info->no_irq = true; + indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels); + indio_dev->channels = sun8i_a33_gpadc_channels; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + info->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &sun4i_gpadc_regmap_config); + if (IS_ERR(info->regmap)) { + ret = PTR_ERR(info->regmap); + dev_err(&pdev->dev, "failed to init regmap: %d\n", ret); + return ret; + } + + if (IS_ENABLED(CONFIG_THERMAL_OF)) + info->sensor_device = &pdev->dev; + + return 0; +} + +static int sun4i_gpadc_probe_mfd(struct platform_device *pdev, + struct iio_dev *indio_dev) +{ + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + struct sun4i_gpadc_dev *sun4i_gpadc_dev = + dev_get_drvdata(pdev->dev.parent); + int ret; + + info->no_irq = false; + info->regmap = sun4i_gpadc_dev->regmap; + + indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels); + indio_dev->channels = sun4i_gpadc_channels; + + info->data = (struct gpadc_data *)platform_get_device_id(pdev)->driver_data; + + /* + * Since the controller needs to be in touchscreen mode for its thermal + * sensor to operate properly, and that switching between the two modes + * needs a delay, always registering in the thermal framework will + * significantly slow down the conversion rate of the ADCs. + * + * Therefore, instead of depending on THERMAL_OF in Kconfig, we only + * register the sensor if that option is enabled, eventually leaving + * that choice to the user. + */ + + if (IS_ENABLED(CONFIG_THERMAL_OF)) { + /* + * This driver is a child of an MFD which has a node in the DT + * but not its children, because of DT backward compatibility + * for A10, A13 and A31 SoCs. Therefore, the resulting devices + * of this driver do not have an of_node variable. + * However, its parent (the MFD driver) has an of_node variable + * and since devm_thermal_zone_of_sensor_register uses its first + * argument to match the phandle defined in the node of the + * thermal driver with the of_node of the device passed as first + * argument and the third argument to call ops from + * thermal_zone_of_device_ops, the solution is to use the parent + * device as first argument to match the phandle with its + * of_node, and the device from this driver as third argument to + * return the temperature. + */ + info->sensor_device = pdev->dev.parent; + } else { + indio_dev->num_channels = + ARRAY_SIZE(sun4i_gpadc_channels_no_temp); + indio_dev->channels = sun4i_gpadc_channels_no_temp; + } + + if (IS_ENABLED(CONFIG_THERMAL_OF)) { + ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING", + sun4i_gpadc_temp_data_irq_handler, + "temp_data", &info->temp_data_irq, + &info->ignore_temp_data_irq); + if (ret < 0) + return ret; + } + + ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING", + sun4i_gpadc_fifo_data_irq_handler, "fifo_data", + &info->fifo_data_irq, &info->ignore_fifo_data_irq); + if (ret < 0) + return ret; + + if (IS_ENABLED(CONFIG_THERMAL_OF)) { + ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to register iio map array\n"); + return ret; + } + } + + return 0; +} + +static int sun4i_gpadc_probe(struct platform_device *pdev) +{ + struct sun4i_gpadc_iio *info; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + platform_set_drvdata(pdev, indio_dev); + + mutex_init(&info->mutex); + info->indio_dev = indio_dev; + init_completion(&info->completion); + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &sun4i_gpadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (pdev->dev.of_node) + ret = sun4i_gpadc_probe_dt(pdev, indio_dev); + else + ret = sun4i_gpadc_probe_mfd(pdev, indio_dev); + + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(&pdev->dev, + SUN4I_GPADC_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + if (IS_ENABLED(CONFIG_THERMAL_OF)) { + info->tzd = thermal_zone_of_sensor_register(info->sensor_device, + 0, info, + &sun4i_ts_tz_ops); + /* + * Do not fail driver probing when failing to register in + * thermal because no thermal DT node is found. + */ + if (IS_ERR(info->tzd) && PTR_ERR(info->tzd) != -ENODEV) { + dev_err(&pdev->dev, + "could not register thermal sensor: %ld\n", + PTR_ERR(info->tzd)); + return PTR_ERR(info->tzd); + } + } + + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "could not register the device\n"); + goto err_map; + } + + return 0; + +err_map: + if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF)) + iio_map_array_unregister(indio_dev); + + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int sun4i_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct sun4i_gpadc_iio *info = iio_priv(indio_dev); + + pm_runtime_put(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + if (!IS_ENABLED(CONFIG_THERMAL_OF)) + return 0; + + thermal_zone_of_sensor_unregister(info->sensor_device, info->tzd); + + if (!info->no_irq) + iio_map_array_unregister(indio_dev); + + return 0; +} + +static const struct platform_device_id sun4i_gpadc_id[] = { + { "sun4i-a10-gpadc-iio", (kernel_ulong_t)&sun4i_gpadc_data }, + { "sun5i-a13-gpadc-iio", (kernel_ulong_t)&sun5i_gpadc_data }, + { "sun6i-a31-gpadc-iio", (kernel_ulong_t)&sun6i_gpadc_data }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, sun4i_gpadc_id); + +static struct platform_driver sun4i_gpadc_driver = { + .driver = { + .name = "sun4i-gpadc-iio", + .of_match_table = sun4i_gpadc_of_id, + .pm = &sun4i_gpadc_pm_ops, + }, + .id_table = sun4i_gpadc_id, + .probe = sun4i_gpadc_probe, + .remove = sun4i_gpadc_remove, +}; +MODULE_DEVICE_TABLE(of, sun4i_gpadc_of_id); + +module_platform_driver(sun4i_gpadc_driver); + +MODULE_DESCRIPTION("ADC driver for sunxi platforms"); +MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c new file mode 100644 index 000000000..c79cd88cd --- /dev/null +++ b/drivers/iio/adc/ti-adc081c.c @@ -0,0 +1,260 @@ +// 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 + * + * Datasheets: + * https://www.ti.com/lit/ds/symlink/adc081c021.pdf + * https://www.ti.com/lit/ds/symlink/adc101c021.pdf + * https://www.ti.com/lit/ds/symlink/adc121c021.pdf + * + * The devices have a very similar interface and differ mostly in the number of + * bits handled. For the 8-bit and 10-bit models the least-significant 4 or 2 + * bits of value registers are reserved. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/property.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/regulator/consumer.h> + +struct adc081c { + struct i2c_client *i2c; + struct regulator *ref; + + /* 8, 10 or 12 */ + int bits; + + /* Ensure natural alignment of buffer elements */ + struct { + u16 channel; + s64 ts __aligned(8); + } scan; +}; + +#define REG_CONV_RES 0x00 + +static int adc081c_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct adc081c *adc = iio_priv(iio); + int err; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES); + if (err < 0) + return err; + + *value = (err & 0xFFF) >> (12 - adc->bits); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + err = regulator_get_voltage(adc->ref); + if (err < 0) + return err; + + *value = err / 1000; + *shift = adc->bits; + + return IIO_VAL_FRACTIONAL_LOG2; + + default: + break; + } + + return -EINVAL; +} + +#define ADCxx1C_CHAN(_bits) { \ + .type = IIO_VOLTAGE, \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_bits), \ + .storagebits = 16, \ + .shift = 12 - (_bits), \ + .endianness = IIO_CPU, \ + }, \ +} + +#define DEFINE_ADCxx1C_CHANNELS(_name, _bits) \ + static const struct iio_chan_spec _name ## _channels[] = { \ + ADCxx1C_CHAN((_bits)), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ + }; \ + +#define ADC081C_NUM_CHANNELS 2 + +struct adcxx1c_model { + const struct iio_chan_spec* channels; + int bits; +}; + +#define ADCxx1C_MODEL(_name, _bits) \ + { \ + .channels = _name ## _channels, \ + .bits = (_bits), \ + } + +DEFINE_ADCxx1C_CHANNELS(adc081c, 8); +DEFINE_ADCxx1C_CHANNELS(adc101c, 10); +DEFINE_ADCxx1C_CHANNELS(adc121c, 12); + +/* Model ids are indexes in _models array */ +enum adcxx1c_model_id { + ADC081C = 0, + ADC101C = 1, + ADC121C = 2, +}; + +static struct adcxx1c_model adcxx1c_models[] = { + ADCxx1C_MODEL(adc081c, 8), + ADCxx1C_MODEL(adc101c, 10), + ADCxx1C_MODEL(adc121c, 12), +}; + +static const struct iio_info adc081c_info = { + .read_raw = adc081c_read_raw, +}; + +static irqreturn_t adc081c_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adc081c *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_read_word_swapped(data->i2c, REG_CONV_RES); + if (ret < 0) + goto out; + data->scan.channel = ret; + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, + iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int adc081c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *iio; + struct adc081c *adc; + const struct adcxx1c_model *model; + int err; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; + + if (dev_fwnode(&client->dev)) + model = device_get_match_data(&client->dev); + else + model = &adcxx1c_models[id->driver_data]; + + iio = devm_iio_device_alloc(&client->dev, sizeof(*adc)); + if (!iio) + return -ENOMEM; + + adc = iio_priv(iio); + adc->i2c = client; + adc->bits = model->bits; + + adc->ref = devm_regulator_get(&client->dev, "vref"); + if (IS_ERR(adc->ref)) + return PTR_ERR(adc->ref); + + err = regulator_enable(adc->ref); + if (err < 0) + return err; + + iio->name = dev_name(&client->dev); + iio->modes = INDIO_DIRECT_MODE; + iio->info = &adc081c_info; + + iio->channels = model->channels; + iio->num_channels = ADC081C_NUM_CHANNELS; + + err = iio_triggered_buffer_setup(iio, NULL, adc081c_trigger_handler, NULL); + if (err < 0) { + dev_err(&client->dev, "iio triggered buffer setup failed\n"); + goto err_regulator_disable; + } + + err = iio_device_register(iio); + if (err < 0) + goto err_buffer_cleanup; + + i2c_set_clientdata(client, iio); + + return 0; + +err_buffer_cleanup: + iio_triggered_buffer_cleanup(iio); +err_regulator_disable: + regulator_disable(adc->ref); + + return err; +} + +static int adc081c_remove(struct i2c_client *client) +{ + struct iio_dev *iio = i2c_get_clientdata(client); + struct adc081c *adc = iio_priv(iio); + + iio_device_unregister(iio); + iio_triggered_buffer_cleanup(iio); + regulator_disable(adc->ref); + + return 0; +} + +static const struct i2c_device_id adc081c_id[] = { + { "adc081c", ADC081C }, + { "adc101c", ADC101C }, + { "adc121c", ADC121C }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adc081c_id); + +static const struct acpi_device_id adc081c_acpi_match[] = { + /* Used on some AAEON boards */ + { "ADC081C", (kernel_ulong_t)&adcxx1c_models[ADC081C] }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adc081c_acpi_match); + +static const struct of_device_id adc081c_of_match[] = { + { .compatible = "ti,adc081c", .data = &adcxx1c_models[ADC081C] }, + { .compatible = "ti,adc101c", .data = &adcxx1c_models[ADC101C] }, + { .compatible = "ti,adc121c", .data = &adcxx1c_models[ADC121C] }, + { } +}; +MODULE_DEVICE_TABLE(of, adc081c_of_match); + +static struct i2c_driver adc081c_driver = { + .driver = { + .name = "adc081c", + .of_match_table = adc081c_of_match, + .acpi_match_table = adc081c_acpi_match, + }, + .probe = adc081c_probe, + .remove = adc081c_remove, + .id_table = adc081c_id, +}; +module_i2c_driver(adc081c_driver); + +MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); +MODULE_DESCRIPTION("Texas Instruments ADC081C/ADC101C/ADC121C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c new file mode 100644 index 000000000..0261b3cfc --- /dev/null +++ b/drivers/iio/adc/ti-adc0832.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADC0831/ADC0832/ADC0834/ADC0838 8-bit ADC driver + * + * Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com> + * + * Datasheet: https://www.ti.com/lit/ds/symlink/adc0832-n.pdf + */ + +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/regulator/consumer.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> + +enum { + adc0831, + adc0832, + adc0834, + adc0838, +}; + +struct adc0832 { + struct spi_device *spi; + struct regulator *reg; + struct mutex lock; + u8 mux_bits; + /* + * Max size needed: 16x 1 byte ADC data + 8 bytes timestamp + * May be shorter if not all channels are enabled subject + * to the timestamp remaining 8 byte aligned. + */ + u8 data[24] __aligned(8); + + u8 tx_buf[2] ____cacheline_aligned; + u8 rx_buf[2]; +}; + +#define ADC0832_VOLTAGE_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), \ + .scan_index = chan, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ + } + +#define ADC0832_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), \ + .scan_index = si, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ + } + +static const struct iio_chan_spec adc0831_channels[] = { + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 0), + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec adc0832_channels[] = { + ADC0832_VOLTAGE_CHANNEL(0), + ADC0832_VOLTAGE_CHANNEL(1), + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 2), + ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 3), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_chan_spec adc0834_channels[] = { + ADC0832_VOLTAGE_CHANNEL(0), + ADC0832_VOLTAGE_CHANNEL(1), + ADC0832_VOLTAGE_CHANNEL(2), + ADC0832_VOLTAGE_CHANNEL(3), + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 4), + ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 5), + ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 6), + ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static const struct iio_chan_spec adc0838_channels[] = { + ADC0832_VOLTAGE_CHANNEL(0), + ADC0832_VOLTAGE_CHANNEL(1), + ADC0832_VOLTAGE_CHANNEL(2), + ADC0832_VOLTAGE_CHANNEL(3), + ADC0832_VOLTAGE_CHANNEL(4), + ADC0832_VOLTAGE_CHANNEL(5), + ADC0832_VOLTAGE_CHANNEL(6), + ADC0832_VOLTAGE_CHANNEL(7), + ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 8), + ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 9), + ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 10), + ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 11), + ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5, 12), + ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4, 13), + ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7, 14), + ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6, 15), + IIO_CHAN_SOFT_TIMESTAMP(16), +}; + +static int adc0831_adc_conversion(struct adc0832 *adc) +{ + struct spi_device *spi = adc->spi; + int ret; + + ret = spi_read(spi, &adc->rx_buf, 2); + if (ret) + return ret; + + /* + * Skip TRI-STATE and a leading zero + */ + return (adc->rx_buf[0] << 2 & 0xff) | (adc->rx_buf[1] >> 6); +} + +static int adc0832_adc_conversion(struct adc0832 *adc, int channel, + bool differential) +{ + struct spi_device *spi = adc->spi; + struct spi_transfer xfer = { + .tx_buf = adc->tx_buf, + .rx_buf = adc->rx_buf, + .len = 2, + }; + int ret; + + if (!adc->mux_bits) + return adc0831_adc_conversion(adc); + + /* start bit */ + adc->tx_buf[0] = 1 << (adc->mux_bits + 1); + /* single-ended or differential */ + adc->tx_buf[0] |= differential ? 0 : (1 << adc->mux_bits); + /* odd / sign */ + adc->tx_buf[0] |= (channel % 2) << (adc->mux_bits - 1); + /* select */ + if (adc->mux_bits > 1) + adc->tx_buf[0] |= channel / 2; + + /* align Data output BIT7 (MSB) to 8-bit boundary */ + adc->tx_buf[0] <<= 1; + + ret = spi_sync_transfer(spi, &xfer, 1); + if (ret) + return ret; + + return adc->rx_buf[1]; +} + +static int adc0832_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct adc0832 *adc = iio_priv(iio); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + *value = adc0832_adc_conversion(adc, channel->channel, + 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 = 8; + + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info adc0832_info = { + .read_raw = adc0832_read_raw, +}; + +static irqreturn_t adc0832_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adc0832 *adc = iio_priv(indio_dev); + int scan_index; + int i = 0; + + mutex_lock(&adc->lock); + + for_each_set_bit(scan_index, indio_dev->active_scan_mask, + indio_dev->masklength) { + const struct iio_chan_spec *scan_chan = + &indio_dev->channels[scan_index]; + int ret = adc0832_adc_conversion(adc, scan_chan->channel, + scan_chan->differential); + if (ret < 0) { + dev_warn(&adc->spi->dev, + "failed to get conversion data\n"); + goto out; + } + + adc->data[i] = ret; + i++; + } + iio_push_to_buffers_with_timestamp(indio_dev, adc->data, + iio_get_time_ns(indio_dev)); +out: + mutex_unlock(&adc->lock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int adc0832_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adc0832 *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 = spi_get_device_id(spi)->name; + indio_dev->info = &adc0832_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + switch (spi_get_device_id(spi)->driver_data) { + case adc0831: + adc->mux_bits = 0; + indio_dev->channels = adc0831_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0831_channels); + break; + case adc0832: + adc->mux_bits = 1; + indio_dev->channels = adc0832_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0832_channels); + break; + case adc0834: + adc->mux_bits = 2; + indio_dev->channels = adc0834_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0834_channels); + break; + case adc0838: + adc->mux_bits = 3; + indio_dev->channels = adc0838_channels; + indio_dev->num_channels = ARRAY_SIZE(adc0838_channels); + break; + default: + return -EINVAL; + } + + 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_triggered_buffer_setup(indio_dev, NULL, + adc0832_trigger_handler, NULL); + if (ret) + goto err_reg_disable; + + ret = iio_device_register(indio_dev); + if (ret) + goto err_buffer_cleanup; + + return 0; +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); +err_reg_disable: + regulator_disable(adc->reg); + + return ret; +} + +static int adc0832_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adc0832 *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(adc->reg); + + return 0; +} + +static const struct of_device_id adc0832_dt_ids[] = { + { .compatible = "ti,adc0831", }, + { .compatible = "ti,adc0832", }, + { .compatible = "ti,adc0834", }, + { .compatible = "ti,adc0838", }, + {} +}; +MODULE_DEVICE_TABLE(of, adc0832_dt_ids); + +static const struct spi_device_id adc0832_id[] = { + { "adc0831", adc0831 }, + { "adc0832", adc0832 }, + { "adc0834", adc0834 }, + { "adc0838", adc0838 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adc0832_id); + +static struct spi_driver adc0832_driver = { + .driver = { + .name = "adc0832", + .of_match_table = adc0832_dt_ids, + }, + .probe = adc0832_probe, + .remove = adc0832_remove, + .id_table = adc0832_id, +}; +module_spi_driver(adc0832_driver); + +MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>"); +MODULE_DESCRIPTION("ADC0831/ADC0832/ADC0834/ADC0838 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c new file mode 100644 index 000000000..dfba34834 --- /dev/null +++ b/drivers/iio/adc/ti-adc084s021.c @@ -0,0 +1,272 @@ +// 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: + * https://www.ti.com/lit/ds/symlink/adc084s021.pdf + */ + +#include <linux/err.h> +#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/interrupt.h> +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/regulator/consumer.h> + +#define ADC084S021_DRIVER_NAME "adc084s021" + +struct adc084s021 { + struct spi_device *spi; + struct spi_message message; + struct spi_transfer spi_trans; + struct regulator *reg; + struct mutex lock; + /* Buffer used to align data */ + struct { + __be16 channels[4]; + s64 ts __aligned(8); + } scan; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache line. + */ + u16 tx_buf[4] ____cacheline_aligned; + __be16 rx_buf[5]; /* First 16-bits are trash */ +}; + +#define ADC084S021_VOLTAGE_CHANNEL(num) \ + { \ + .type = IIO_VOLTAGE, \ + .channel = (num), \ + .indexed = 1, \ + .scan_index = (num), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_BE, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ + } + +static const struct iio_chan_spec adc084s021_channels[] = { + ADC084S021_VOLTAGE_CHANNEL(0), + ADC084S021_VOLTAGE_CHANNEL(1), + ADC084S021_VOLTAGE_CHANNEL(2), + ADC084S021_VOLTAGE_CHANNEL(3), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +/** + * Read an ADC channel and return its value. + * + * @adc: The ADC SPI data. + * @data: Buffer for converted data. + */ +static int adc084s021_adc_conversion(struct adc084s021 *adc, void *data) +{ + int n_words = (adc->spi_trans.len >> 1) - 1; /* Discard first word */ + int ret, i = 0; + u16 *p = data; + + /* Do the transfer */ + ret = spi_sync(adc->spi, &adc->message); + if (ret < 0) + return ret; + + for (; i < n_words; i++) + *(p + i) = adc->rx_buf[i + 1]; + + return ret; +} + +static int adc084s021_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct adc084s021 *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret < 0) + return ret; + + ret = regulator_enable(adc->reg); + if (ret) { + iio_device_release_direct_mode(indio_dev); + return ret; + } + + adc->tx_buf[0] = channel->channel << 3; + ret = adc084s021_adc_conversion(adc, val); + iio_device_release_direct_mode(indio_dev); + regulator_disable(adc->reg); + if (ret < 0) + return ret; + + *val = be16_to_cpu(*val); + *val = (*val >> channel->scan_type.shift) & 0xff; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_enable(adc->reg); + if (ret) + return ret; + + ret = regulator_get_voltage(adc->reg); + regulator_disable(adc->reg); + if (ret < 0) + return ret; + + *val = ret / 1000; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +/** + * Read enabled ADC channels and push data to the buffer. + * + * @irq: The interrupt number (not used). + * @pollfunc: Pointer to the poll func. + */ +static irqreturn_t adc084s021_buffer_trigger_handler(int irq, void *pollfunc) +{ + struct iio_poll_func *pf = pollfunc; + struct iio_dev *indio_dev = pf->indio_dev; + struct adc084s021 *adc = iio_priv(indio_dev); + + mutex_lock(&adc->lock); + + if (adc084s021_adc_conversion(adc, adc->scan.channels) < 0) + dev_err(&adc->spi->dev, "Failed to read data\n"); + + iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan, + iio_get_time_ns(indio_dev)); + mutex_unlock(&adc->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int adc084s021_buffer_preenable(struct iio_dev *indio_dev) +{ + struct adc084s021 *adc = iio_priv(indio_dev); + int scan_index; + int i = 0; + + for_each_set_bit(scan_index, indio_dev->active_scan_mask, + indio_dev->masklength) { + const struct iio_chan_spec *channel = + &indio_dev->channels[scan_index]; + adc->tx_buf[i++] = channel->channel << 3; + } + adc->spi_trans.len = 2 + (i * sizeof(__be16)); /* Trash + channels */ + + return regulator_enable(adc->reg); +} + +static int adc084s021_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct adc084s021 *adc = iio_priv(indio_dev); + + adc->spi_trans.len = 4; /* Trash + single channel */ + + return regulator_disable(adc->reg); +} + +static const struct iio_info adc084s021_info = { + .read_raw = adc084s021_read_raw, +}; + +static const struct iio_buffer_setup_ops adc084s021_buffer_setup_ops = { + .preenable = adc084s021_buffer_preenable, + .postdisable = adc084s021_buffer_postdisable, +}; + +static int adc084s021_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adc084s021 *adc; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) { + dev_err(&spi->dev, "Failed to allocate IIO device\n"); + return -ENOMEM; + } + + adc = iio_priv(indio_dev); + adc->spi = spi; + + /* Connect the SPI device and the iio dev */ + spi_set_drvdata(spi, indio_dev); + + /* Initiate the Industrial I/O device */ + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &adc084s021_info; + indio_dev->channels = adc084s021_channels; + indio_dev->num_channels = ARRAY_SIZE(adc084s021_channels); + + /* Create SPI transfer for channel reads */ + adc->spi_trans.tx_buf = adc->tx_buf; + adc->spi_trans.rx_buf = adc->rx_buf; + adc->spi_trans.len = 4; /* Trash + single channel */ + spi_message_init_with_transfers(&adc->message, &adc->spi_trans, 1); + + adc->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(adc->reg)) + return PTR_ERR(adc->reg); + + mutex_init(&adc->lock); + + /* Setup triggered buffer with pollfunction */ + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + adc084s021_buffer_trigger_handler, + &adc084s021_buffer_setup_ops); + if (ret) { + dev_err(&spi->dev, "Failed to setup triggered buffer\n"); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id adc084s021_of_match[] = { + { .compatible = "ti,adc084s021", }, + {}, +}; +MODULE_DEVICE_TABLE(of, adc084s021_of_match); + +static const struct spi_device_id adc084s021_id[] = { + { ADC084S021_DRIVER_NAME, 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, adc084s021_id); + +static struct spi_driver adc084s021_driver = { + .driver = { + .name = ADC084S021_DRIVER_NAME, + .of_match_table = adc084s021_of_match, + }, + .probe = adc084s021_probe, + .id_table = adc084s021_id, +}; +module_spi_driver(adc084s021_driver); + +MODULE_AUTHOR("MÃ¥rten Lindahl <martenli@axis.com>"); +MODULE_DESCRIPTION("Texas Instruments ADC084S021"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.0"); diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c new file mode 100644 index 000000000..183b2245e --- /dev/null +++ b/drivers/iio/adc/ti-adc108s102.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI ADC108S102 SPI ADC driver + * + * Copyright (c) 2013-2015 Intel Corporation. + * Copyright (c) 2017 Siemens AG + * + * This IIO device driver is designed to work with the following + * analog to digital converters from Texas Instruments: + * ADC108S102 + * ADC128S102 + * The communication with ADC chip is via the SPI bus (mode 3). + */ + +#include <linux/acpi.h> +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/types.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +/* + * In case of ACPI, we use the hard-wired 5000 mV of the Galileo and IOT2000 + * boards as default for the reference pin VA. Device tree users encode that + * via the vref-supply regulator. + */ +#define ADC108S102_VA_MV_ACPI_DEFAULT 5000 + +/* + * Defining the ADC resolution being 12 bits, we can use the same driver for + * both ADC108S102 (10 bits resolution) and ADC128S102 (12 bits resolution) + * chips. The ADC108S102 effectively returns a 12-bit result with the 2 + * least-significant bits unset. + */ +#define ADC108S102_BITS 12 +#define ADC108S102_MAX_CHANNELS 8 + +/* + * 16-bit SPI command format: + * [15:14] Ignored + * [13:11] 3-bit channel address + * [10:0] Ignored + */ +#define ADC108S102_CMD(ch) ((u16)(ch) << 11) + +/* + * 16-bit SPI response format: + * [15:12] Zeros + * [11:0] 12-bit ADC sample (for ADC108S102, [1:0] will always be 0). + */ +#define ADC108S102_RES_DATA(res) ((u16)res & GENMASK(11, 0)) + +struct adc108s102_state { + struct spi_device *spi; + struct regulator *reg; + u32 va_millivolt; + /* SPI transfer used by triggered buffer handler*/ + struct spi_transfer ring_xfer; + /* SPI transfer used by direct scan */ + struct spi_transfer scan_single_xfer; + /* SPI message used by ring_xfer SPI transfer */ + struct spi_message ring_msg; + /* SPI message used by scan_single_xfer SPI transfer */ + struct spi_message scan_single_msg; + + /* + * SPI message buffers: + * tx_buf: |C0|C1|C2|C3|C4|C5|C6|C7|XX| + * rx_buf: |XX|R0|R1|R2|R3|R4|R5|R6|R7|tt|tt|tt|tt| + * + * tx_buf: 8 channel read commands, plus 1 dummy command + * rx_buf: 1 dummy response, 8 channel responses, plus 64-bit timestamp + */ + __be16 rx_buf[13] ____cacheline_aligned; + __be16 tx_buf[9] ____cacheline_aligned; +}; + +#define ADC108S102_V_CHAN(index) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = ADC108S102_BITS, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec adc108s102_channels[] = { + ADC108S102_V_CHAN(0), + ADC108S102_V_CHAN(1), + ADC108S102_V_CHAN(2), + ADC108S102_V_CHAN(3), + ADC108S102_V_CHAN(4), + ADC108S102_V_CHAN(5), + ADC108S102_V_CHAN(6), + ADC108S102_V_CHAN(7), + IIO_CHAN_SOFT_TIMESTAMP(8), +}; + +static int adc108s102_update_scan_mode(struct iio_dev *indio_dev, + unsigned long const *active_scan_mask) +{ + struct adc108s102_state *st = iio_priv(indio_dev); + unsigned int bit, cmds; + + /* + * Fill in the first x shorts of tx_buf with the number of channels + * enabled for sampling by the triggered buffer. + */ + cmds = 0; + for_each_set_bit(bit, active_scan_mask, ADC108S102_MAX_CHANNELS) + st->tx_buf[cmds++] = cpu_to_be16(ADC108S102_CMD(bit)); + + /* One dummy command added, to clock in the last response */ + st->tx_buf[cmds++] = 0x00; + + /* build SPI ring message */ + st->ring_xfer.tx_buf = &st->tx_buf[0]; + st->ring_xfer.rx_buf = &st->rx_buf[0]; + st->ring_xfer.len = cmds * sizeof(st->tx_buf[0]); + + spi_message_init_with_transfers(&st->ring_msg, &st->ring_xfer, 1); + + return 0; +} + +static irqreturn_t adc108s102_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adc108s102_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_sync(st->spi, &st->ring_msg); + if (ret < 0) + goto out_notify; + + /* Skip the dummy response in the first slot */ + iio_push_to_buffers_with_timestamp(indio_dev, + (u8 *)&st->rx_buf[1], + iio_get_time_ns(indio_dev)); + +out_notify: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int adc108s102_scan_direct(struct adc108s102_state *st, unsigned int ch) +{ + int ret; + + st->tx_buf[0] = cpu_to_be16(ADC108S102_CMD(ch)); + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret) + return ret; + + /* Skip the dummy response in the first slot */ + return be16_to_cpu(st->rx_buf[1]); +} + +static int adc108s102_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct adc108s102_state *st = iio_priv(indio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = adc108s102_scan_direct(st, chan->address); + + iio_device_release_direct_mode(indio_dev); + + if (ret < 0) + return ret; + + *val = ADC108S102_RES_DATA(ret); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type != IIO_VOLTAGE) + break; + + *val = st->va_millivolt; + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + break; + } + + return -EINVAL; +} + +static const struct iio_info adc108s102_info = { + .read_raw = &adc108s102_read_raw, + .update_scan_mode = &adc108s102_update_scan_mode, +}; + +static int adc108s102_probe(struct spi_device *spi) +{ + struct adc108s102_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); + + if (ACPI_COMPANION(&spi->dev)) { + st->va_millivolt = ADC108S102_VA_MV_ACPI_DEFAULT; + } else { + st->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret < 0) { + dev_err(&spi->dev, "Cannot enable vref regulator\n"); + return ret; + } + + ret = regulator_get_voltage(st->reg); + if (ret < 0) { + dev_err(&spi->dev, "vref get voltage failed\n"); + return ret; + } + + st->va_millivolt = ret / 1000; + } + + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + + indio_dev->name = spi->modalias; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adc108s102_channels; + indio_dev->num_channels = ARRAY_SIZE(adc108s102_channels); + indio_dev->info = &adc108s102_info; + + /* Setup default message */ + st->scan_single_xfer.tx_buf = st->tx_buf; + st->scan_single_xfer.rx_buf = st->rx_buf; + st->scan_single_xfer.len = 2 * sizeof(st->tx_buf[0]); + + spi_message_init_with_transfers(&st->scan_single_msg, + &st->scan_single_xfer, 1); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &adc108s102_trigger_handler, NULL); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register IIO device\n"); + goto error_cleanup_triggered_buffer; + } + return 0; + +error_cleanup_triggered_buffer: + iio_triggered_buffer_cleanup(indio_dev); + +error_disable_reg: + regulator_disable(st->reg); + + return ret; +} + +static int adc108s102_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adc108s102_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + + regulator_disable(st->reg); + + return 0; +} + +static const struct of_device_id adc108s102_of_match[] = { + { .compatible = "ti,adc108s102" }, + { } +}; +MODULE_DEVICE_TABLE(of, adc108s102_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id adc108s102_acpi_ids[] = { + { "INT3495", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adc108s102_acpi_ids); +#endif + +static const struct spi_device_id adc108s102_id[] = { + { "adc108s102", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adc108s102_id); + +static struct spi_driver adc108s102_driver = { + .driver = { + .name = "adc108s102", + .of_match_table = adc108s102_of_match, + .acpi_match_table = ACPI_PTR(adc108s102_acpi_ids), + }, + .probe = adc108s102_probe, + .remove = adc108s102_remove, + .id_table = adc108s102_id, +}; +module_spi_driver(adc108s102_driver); + +MODULE_AUTHOR("Bogdan Pricop <bogdan.pricop@emutex.com>"); +MODULE_DESCRIPTION("Texas Instruments ADC108S102 and ADC128S102 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c new file mode 100644 index 000000000..fcd5d39dd --- /dev/null +++ b/drivers/iio/adc/ti-adc12138.c @@ -0,0 +1,552 @@ +// 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> + * + * Datasheet: http://www.ti.com/lit/ds/symlink/adc12138.pdf + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/completion.h> +#include <linux/clk.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/regulator/consumer.h> + +#define ADC12138_MODE_AUTO_CAL 0x08 +#define ADC12138_MODE_READ_STATUS 0x0c +#define ADC12138_MODE_ACQUISITION_TIME_6 0x0e +#define ADC12138_MODE_ACQUISITION_TIME_10 0x4e +#define ADC12138_MODE_ACQUISITION_TIME_18 0x8e +#define ADC12138_MODE_ACQUISITION_TIME_34 0xce + +#define ADC12138_STATUS_CAL BIT(6) + +enum { + adc12130, + adc12132, + adc12138, +}; + +struct adc12138 { + struct spi_device *spi; + unsigned int id; + /* conversion clock */ + struct clk *cclk; + /* positive analog voltage reference */ + struct regulator *vref_p; + /* negative analog voltage reference */ + struct regulator *vref_n; + struct mutex lock; + struct completion complete; + /* The number of cclk periods for the S/H's acquisition time */ + unsigned int acquisition_time; + /* + * Maximum size needed: 16x 2 bytes ADC data + 8 bytes timestamp. + * Less may be need if not all channels are enabled, as long as + * the 8 byte alignment of the timestamp is maintained. + */ + __be16 data[20] __aligned(8); + + u8 tx_buf[2] ____cacheline_aligned; + u8 rx_buf[2]; +}; + +#define ADC12138_VOLTAGE_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) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = chan, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 13, \ + .storagebits = 16, \ + .shift = 3, \ + .endianness = IIO_BE, \ + }, \ + } + +#define ADC12138_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) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = si, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 13, \ + .storagebits = 16, \ + .shift = 3, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec adc12132_channels[] = { + ADC12138_VOLTAGE_CHANNEL(0), + ADC12138_VOLTAGE_CHANNEL(1), + ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 2), + ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 3), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_chan_spec adc12138_channels[] = { + ADC12138_VOLTAGE_CHANNEL(0), + ADC12138_VOLTAGE_CHANNEL(1), + ADC12138_VOLTAGE_CHANNEL(2), + ADC12138_VOLTAGE_CHANNEL(3), + ADC12138_VOLTAGE_CHANNEL(4), + ADC12138_VOLTAGE_CHANNEL(5), + ADC12138_VOLTAGE_CHANNEL(6), + ADC12138_VOLTAGE_CHANNEL(7), + ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 8), + ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 9), + ADC12138_VOLTAGE_CHANNEL_DIFF(2, 3, 10), + ADC12138_VOLTAGE_CHANNEL_DIFF(3, 2, 11), + ADC12138_VOLTAGE_CHANNEL_DIFF(4, 5, 12), + ADC12138_VOLTAGE_CHANNEL_DIFF(5, 4, 13), + ADC12138_VOLTAGE_CHANNEL_DIFF(6, 7, 14), + ADC12138_VOLTAGE_CHANNEL_DIFF(7, 6, 15), + IIO_CHAN_SOFT_TIMESTAMP(16), +}; + +static int adc12138_mode_programming(struct adc12138 *adc, u8 mode, + void *rx_buf, int len) +{ + struct spi_transfer xfer = { + .tx_buf = adc->tx_buf, + .rx_buf = adc->rx_buf, + .len = len, + }; + int ret; + + /* Skip unused bits for ADC12130 and ADC12132 */ + if (adc->id != adc12138) + mode = (mode & 0xc0) | ((mode & 0x0f) << 2); + + adc->tx_buf[0] = mode; + + ret = spi_sync_transfer(adc->spi, &xfer, 1); + if (ret) + return ret; + + memcpy(rx_buf, adc->rx_buf, len); + + return 0; +} + +static int adc12138_read_status(struct adc12138 *adc) +{ + u8 rx_buf[2]; + int ret; + + ret = adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS, + rx_buf, 2); + if (ret) + return ret; + + return (rx_buf[0] << 1) | (rx_buf[1] >> 7); +} + +static int __adc12138_start_conv(struct adc12138 *adc, + struct iio_chan_spec const *channel, + void *data, int len) + +{ + static const u8 ch_to_mux[] = { 0, 4, 1, 5, 2, 6, 3, 7 }; + u8 mode = (ch_to_mux[channel->channel] << 4) | + (channel->differential ? 0 : 0x80); + + return adc12138_mode_programming(adc, mode, data, len); +} + +static int adc12138_start_conv(struct adc12138 *adc, + struct iio_chan_spec const *channel) +{ + u8 trash; + + return __adc12138_start_conv(adc, channel, &trash, 1); +} + +static int adc12138_start_and_read_conv(struct adc12138 *adc, + struct iio_chan_spec const *channel, + __be16 *data) +{ + return __adc12138_start_conv(adc, channel, data, 2); +} + +static int adc12138_read_conv_data(struct adc12138 *adc, __be16 *value) +{ + /* Issue a read status instruction and read previous conversion data */ + return adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS, + value, sizeof(*value)); +} + +static int adc12138_wait_eoc(struct adc12138 *adc, unsigned long timeout) +{ + if (!wait_for_completion_timeout(&adc->complete, timeout)) + return -ETIMEDOUT; + + return 0; +} + +static int adc12138_adc_conversion(struct adc12138 *adc, + struct iio_chan_spec const *channel, + __be16 *value) +{ + int ret; + + reinit_completion(&adc->complete); + + ret = adc12138_start_conv(adc, channel); + if (ret) + return ret; + + ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100)); + if (ret) + return ret; + + return adc12138_read_conv_data(adc, value); +} + +static int adc12138_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *channel, int *value, + int *shift, long mask) +{ + struct adc12138 *adc = iio_priv(iio); + int ret; + __be16 data; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&adc->lock); + ret = adc12138_adc_conversion(adc, channel, &data); + mutex_unlock(&adc->lock); + if (ret) + return ret; + + *value = sign_extend32(be16_to_cpu(data) >> 3, 12); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(adc->vref_p); + if (ret < 0) + return ret; + *value = ret; + + if (!IS_ERR(adc->vref_n)) { + ret = regulator_get_voltage(adc->vref_n); + if (ret < 0) + return ret; + *value -= ret; + } + + /* convert regulator output voltage to mV */ + *value /= 1000; + *shift = channel->scan_type.realbits - 1; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + if (!IS_ERR(adc->vref_n)) { + *value = regulator_get_voltage(adc->vref_n); + if (*value < 0) + return *value; + } else { + *value = 0; + } + + /* convert regulator output voltage to mV */ + *value /= 1000; + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static const struct iio_info adc12138_info = { + .read_raw = adc12138_read_raw, +}; + +static int adc12138_init(struct adc12138 *adc) +{ + int ret; + int status; + u8 mode; + u8 trash; + + reinit_completion(&adc->complete); + + ret = adc12138_mode_programming(adc, ADC12138_MODE_AUTO_CAL, &trash, 1); + if (ret) + return ret; + + /* data output at this time has no significance */ + status = adc12138_read_status(adc); + if (status < 0) + return status; + + adc12138_wait_eoc(adc, msecs_to_jiffies(100)); + + status = adc12138_read_status(adc); + if (status & ADC12138_STATUS_CAL) { + dev_warn(&adc->spi->dev, + "Auto Cal sequence is still in progress: %#x\n", + status); + return -EIO; + } + + switch (adc->acquisition_time) { + case 6: + mode = ADC12138_MODE_ACQUISITION_TIME_6; + break; + case 10: + mode = ADC12138_MODE_ACQUISITION_TIME_10; + break; + case 18: + mode = ADC12138_MODE_ACQUISITION_TIME_18; + break; + case 34: + mode = ADC12138_MODE_ACQUISITION_TIME_34; + break; + default: + return -EINVAL; + } + + return adc12138_mode_programming(adc, mode, &trash, 1); +} + +static irqreturn_t adc12138_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adc12138 *adc = iio_priv(indio_dev); + __be16 trash; + int ret; + int scan_index; + int i = 0; + + mutex_lock(&adc->lock); + + for_each_set_bit(scan_index, indio_dev->active_scan_mask, + indio_dev->masklength) { + const struct iio_chan_spec *scan_chan = + &indio_dev->channels[scan_index]; + + reinit_completion(&adc->complete); + + ret = adc12138_start_and_read_conv(adc, scan_chan, + i ? &adc->data[i - 1] : &trash); + if (ret) { + dev_warn(&adc->spi->dev, + "failed to start conversion\n"); + goto out; + } + + ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100)); + if (ret) { + dev_warn(&adc->spi->dev, "wait eoc timeout\n"); + goto out; + } + + i++; + } + + if (i) { + ret = adc12138_read_conv_data(adc, &adc->data[i - 1]); + if (ret) { + dev_warn(&adc->spi->dev, + "failed to get conversion data\n"); + goto out; + } + } + + iio_push_to_buffers_with_timestamp(indio_dev, adc->data, + iio_get_time_ns(indio_dev)); +out: + mutex_unlock(&adc->lock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t adc12138_eoc_handler(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct adc12138 *adc = iio_priv(indio_dev); + + complete(&adc->complete); + + return IRQ_HANDLED; +} + +static int adc12138_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adc12138 *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->id = spi_get_device_id(spi)->driver_data; + mutex_init(&adc->lock); + init_completion(&adc->complete); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &adc12138_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + switch (adc->id) { + case adc12130: + case adc12132: + indio_dev->channels = adc12132_channels; + indio_dev->num_channels = ARRAY_SIZE(adc12132_channels); + break; + case adc12138: + indio_dev->channels = adc12138_channels; + indio_dev->num_channels = ARRAY_SIZE(adc12138_channels); + break; + default: + return -EINVAL; + } + + ret = of_property_read_u32(spi->dev.of_node, "ti,acquisition-time", + &adc->acquisition_time); + if (ret) + adc->acquisition_time = 10; + + adc->cclk = devm_clk_get(&spi->dev, NULL); + if (IS_ERR(adc->cclk)) + return PTR_ERR(adc->cclk); + + adc->vref_p = devm_regulator_get(&spi->dev, "vref-p"); + if (IS_ERR(adc->vref_p)) + return PTR_ERR(adc->vref_p); + + adc->vref_n = devm_regulator_get_optional(&spi->dev, "vref-n"); + if (IS_ERR(adc->vref_n)) { + /* + * Assume vref_n is 0V if an optional regulator is not + * specified, otherwise return the error code. + */ + ret = PTR_ERR(adc->vref_n); + if (ret != -ENODEV) + return ret; + } + + ret = devm_request_irq(&spi->dev, spi->irq, adc12138_eoc_handler, + IRQF_TRIGGER_RISING, indio_dev->name, indio_dev); + if (ret) + return ret; + + ret = clk_prepare_enable(adc->cclk); + if (ret) + return ret; + + ret = regulator_enable(adc->vref_p); + if (ret) + goto err_clk_disable; + + if (!IS_ERR(adc->vref_n)) { + ret = regulator_enable(adc->vref_n); + if (ret) + goto err_vref_p_disable; + } + + ret = adc12138_init(adc); + if (ret) + goto err_vref_n_disable; + + spi_set_drvdata(spi, indio_dev); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + adc12138_trigger_handler, NULL); + if (ret) + goto err_vref_n_disable; + + ret = iio_device_register(indio_dev); + if (ret) + goto err_buffer_cleanup; + + return 0; +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); +err_vref_n_disable: + if (!IS_ERR(adc->vref_n)) + regulator_disable(adc->vref_n); +err_vref_p_disable: + regulator_disable(adc->vref_p); +err_clk_disable: + clk_disable_unprepare(adc->cclk); + + return ret; +} + +static int adc12138_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adc12138 *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + if (!IS_ERR(adc->vref_n)) + regulator_disable(adc->vref_n); + regulator_disable(adc->vref_p); + clk_disable_unprepare(adc->cclk); + + return 0; +} + +#ifdef CONFIG_OF + +static const struct of_device_id adc12138_dt_ids[] = { + { .compatible = "ti,adc12130", }, + { .compatible = "ti,adc12132", }, + { .compatible = "ti,adc12138", }, + {} +}; +MODULE_DEVICE_TABLE(of, adc12138_dt_ids); + +#endif + +static const struct spi_device_id adc12138_id[] = { + { "adc12130", adc12130 }, + { "adc12132", adc12132 }, + { "adc12138", adc12138 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adc12138_id); + +static struct spi_driver adc12138_driver = { + .driver = { + .name = "adc12138", + .of_match_table = of_match_ptr(adc12138_dt_ids), + }, + .probe = adc12138_probe, + .remove = adc12138_remove, + .id_table = adc12138_id, +}; +module_spi_driver(adc12138_driver); + +MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>"); +MODULE_DESCRIPTION("ADC12130/ADC12132/ADC12138 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c new file mode 100644 index 000000000..8618ae7bc --- /dev/null +++ b/drivers/iio/adc/ti-adc128s052.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com> + * + * Driver for Texas Instruments' ADC128S052, ADC122S021 and ADC124S021 ADC chip. + * Datasheets can be found here: + * https://www.ti.com/lit/ds/symlink/adc128s052.pdf + * https://www.ti.com/lit/ds/symlink/adc122s021.pdf + * https://www.ti.com/lit/ds/symlink/adc124s021.pdf + */ + +#include <linux/acpi.h> +#include <linux/err.h> +#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/iio/iio.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> + +struct adc128_configuration { + const struct iio_chan_spec *channels; + u8 num_channels; +}; + +struct adc128 { + struct spi_device *spi; + + struct regulator *reg; + struct mutex lock; + + u8 buffer[2] ____cacheline_aligned; +}; + +static int adc128_adc_conversion(struct adc128 *adc, u8 channel) +{ + int ret; + + mutex_lock(&adc->lock); + + adc->buffer[0] = channel << 3; + adc->buffer[1] = 0; + + ret = spi_write(adc->spi, &adc->buffer, 2); + if (ret < 0) { + mutex_unlock(&adc->lock); + return ret; + } + + ret = spi_read(adc->spi, &adc->buffer, 2); + + mutex_unlock(&adc->lock); + + if (ret < 0) + return ret; + + return ((adc->buffer[0] << 8 | adc->buffer[1]) & 0xFFF); +} + +static int adc128_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct adc128 *adc = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + + ret = adc128_adc_conversion(adc, channel->channel); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + + ret = regulator_get_voltage(adc->reg); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } + +} + +#define ADC128_VOLTAGE_CHANNEL(num) \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (num), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ + } + +static const struct iio_chan_spec adc128s052_channels[] = { + ADC128_VOLTAGE_CHANNEL(0), + ADC128_VOLTAGE_CHANNEL(1), + ADC128_VOLTAGE_CHANNEL(2), + ADC128_VOLTAGE_CHANNEL(3), + ADC128_VOLTAGE_CHANNEL(4), + ADC128_VOLTAGE_CHANNEL(5), + ADC128_VOLTAGE_CHANNEL(6), + ADC128_VOLTAGE_CHANNEL(7), +}; + +static const struct iio_chan_spec adc122s021_channels[] = { + ADC128_VOLTAGE_CHANNEL(0), + ADC128_VOLTAGE_CHANNEL(1), +}; + +static const struct iio_chan_spec adc124s021_channels[] = { + ADC128_VOLTAGE_CHANNEL(0), + ADC128_VOLTAGE_CHANNEL(1), + ADC128_VOLTAGE_CHANNEL(2), + ADC128_VOLTAGE_CHANNEL(3), +}; + +static const struct adc128_configuration adc128_config[] = { + { adc128s052_channels, ARRAY_SIZE(adc128s052_channels) }, + { adc122s021_channels, ARRAY_SIZE(adc122s021_channels) }, + { adc124s021_channels, ARRAY_SIZE(adc124s021_channels) }, +}; + +static const struct iio_info adc128_info = { + .read_raw = adc128_read_raw, +}; + +static int adc128_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + unsigned int config; + struct adc128 *adc; + 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; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &adc128_info; + + indio_dev->channels = adc128_config[config].channels; + indio_dev->num_channels = adc128_config[config].num_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 < 0) + return ret; + + mutex_init(&adc->lock); + + ret = iio_device_register(indio_dev); + if (ret) + goto err_disable_regulator; + + return 0; + +err_disable_regulator: + regulator_disable(adc->reg); + return ret; +} + +static int adc128_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adc128 *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(adc->reg); + + return 0; +} + +static const struct of_device_id adc128_of_match[] = { + { .compatible = "ti,adc128s052", .data = (void*)0L, }, + { .compatible = "ti,adc122s021", .data = (void*)1L, }, + { .compatible = "ti,adc122s051", .data = (void*)1L, }, + { .compatible = "ti,adc122s101", .data = (void*)1L, }, + { .compatible = "ti,adc124s021", .data = (void*)2L, }, + { .compatible = "ti,adc124s051", .data = (void*)2L, }, + { .compatible = "ti,adc124s101", .data = (void*)2L, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, adc128_of_match); + +static const struct spi_device_id adc128_id[] = { + { "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 = adc128_of_match, + .acpi_match_table = ACPI_PTR(adc128_acpi_match), + }, + .probe = adc128_probe, + .remove = adc128_remove, + .id_table = adc128_id, +}; +module_spi_driver(adc128_driver); + +MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>"); +MODULE_DESCRIPTION("Texas Instruments ADC128S052"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c new file mode 100644 index 000000000..607791ffe --- /dev/null +++ b/drivers/iio/adc/ti-adc161s626.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC + * + * ADC Devices Supported: + * adc141s626 - 14-bit ADC + * adc161s626 - 16-bit ADC + * + * Copyright (C) 2016-2018 + * Author: Matt Ranostay <matt.ranostay@konsulko.com> + */ + +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/regulator/consumer.h> + +#define TI_ADC_DRV_NAME "ti-adc161s626" + +enum { + TI_ADC141S626, + TI_ADC161S626, +}; + +static const struct iio_chan_spec ti_adc141s626_channels[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 14, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec ti_adc161s626_channels[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +struct ti_adc_data { + struct iio_dev *indio_dev; + struct spi_device *spi; + struct regulator *ref; + + u8 read_size; + u8 shift; + + u8 buffer[16] ____cacheline_aligned; +}; + +static int ti_adc_read_measurement(struct ti_adc_data *data, + struct iio_chan_spec const *chan, int *val) +{ + int ret; + + switch (data->read_size) { + case 2: { + __be16 buf; + + ret = spi_read(data->spi, (void *) &buf, 2); + if (ret) + return ret; + + *val = be16_to_cpu(buf); + break; + } + case 3: { + __be32 buf; + + ret = spi_read(data->spi, (void *) &buf, 3); + if (ret) + return ret; + + *val = be32_to_cpu(buf) >> 8; + break; + } + default: + return -EINVAL; + } + + *val = sign_extend32(*val >> data->shift, chan->scan_type.realbits - 1); + + return 0; +} + +static irqreturn_t ti_adc_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct ti_adc_data *data = iio_priv(indio_dev); + int ret; + + ret = ti_adc_read_measurement(data, &indio_dev->channels[0], + (int *) &data->buffer); + if (!ret) + iio_push_to_buffers_with_timestamp(indio_dev, + data->buffer, + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ti_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ti_adc_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = ti_adc_read_measurement(data, chan, val); + iio_device_release_direct_mode(indio_dev); + + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(data->ref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + *val = 1 << (chan->scan_type.realbits - 1); + return IIO_VAL_INT; + } + + return 0; +} + +static const struct iio_info ti_adc_info = { + .read_raw = ti_adc_read_raw, +}; + +static int ti_adc_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ti_adc_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->info = &ti_adc_info; + indio_dev->name = TI_ADC_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + spi_set_drvdata(spi, indio_dev); + + data = iio_priv(indio_dev); + data->spi = spi; + + switch (spi_get_device_id(spi)->driver_data) { + case TI_ADC141S626: + indio_dev->channels = ti_adc141s626_channels; + indio_dev->num_channels = ARRAY_SIZE(ti_adc141s626_channels); + data->shift = 0; + data->read_size = 2; + break; + case TI_ADC161S626: + indio_dev->channels = ti_adc161s626_channels; + indio_dev->num_channels = ARRAY_SIZE(ti_adc161s626_channels); + data->shift = 6; + data->read_size = 3; + break; + } + + data->ref = devm_regulator_get(&spi->dev, "vdda"); + if (!IS_ERR(data->ref)) { + ret = regulator_enable(data->ref); + if (ret < 0) + return ret; + } + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + ti_adc_trigger_handler, NULL); + if (ret) + goto error_regulator_disable; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unreg_buffer; + + return 0; + +error_unreg_buffer: + iio_triggered_buffer_cleanup(indio_dev); + +error_regulator_disable: + regulator_disable(data->ref); + + return ret; +} + +static int ti_adc_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ti_adc_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(data->ref); + + return 0; +} + +static const struct of_device_id ti_adc_dt_ids[] = { + { .compatible = "ti,adc141s626", }, + { .compatible = "ti,adc161s626", }, + {} +}; +MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); + +static const struct spi_device_id ti_adc_id[] = { + {"adc141s626", TI_ADC141S626}, + {"adc161s626", TI_ADC161S626}, + {}, +}; +MODULE_DEVICE_TABLE(spi, ti_adc_id); + +static struct spi_driver ti_adc_driver = { + .driver = { + .name = TI_ADC_DRV_NAME, + .of_match_table = ti_adc_dt_ids, + }, + .probe = ti_adc_probe, + .remove = ti_adc_remove, + .id_table = ti_adc_id, +}; +module_spi_driver(ti_adc_driver); + +MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); +MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c new file mode 100644 index 000000000..5b828428b --- /dev/null +++ b/drivers/iio/adc/ti-ads1015.c @@ -0,0 +1,1142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADS1015 - Texas Instruments Analog-to-Digital Converter + * + * Copyright (c) 2016, Intel Corporation. + * + * IIO driver for ADS1015 ADC 7-bit I2C slave address: + * * 0x48 - ADDR connected to Ground + * * 0x49 - ADDR connected to Vdd + * * 0x4A - ADDR connected to SDA + * * 0x4B - ADDR connected to SCL + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/i2c.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/pm_runtime.h> +#include <linux/mutex.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> +#include <linux/iio/types.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/buffer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> + +#define ADS1015_DRV_NAME "ads1015" + +#define ADS1015_CHANNELS 8 + +#define ADS1015_CONV_REG 0x00 +#define ADS1015_CFG_REG 0x01 +#define ADS1015_LO_THRESH_REG 0x02 +#define ADS1015_HI_THRESH_REG 0x03 + +#define ADS1015_CFG_COMP_QUE_SHIFT 0 +#define ADS1015_CFG_COMP_LAT_SHIFT 2 +#define ADS1015_CFG_COMP_POL_SHIFT 3 +#define ADS1015_CFG_COMP_MODE_SHIFT 4 +#define ADS1015_CFG_DR_SHIFT 5 +#define ADS1015_CFG_MOD_SHIFT 8 +#define ADS1015_CFG_PGA_SHIFT 9 +#define ADS1015_CFG_MUX_SHIFT 12 + +#define ADS1015_CFG_COMP_QUE_MASK GENMASK(1, 0) +#define ADS1015_CFG_COMP_LAT_MASK BIT(2) +#define ADS1015_CFG_COMP_POL_MASK BIT(3) +#define ADS1015_CFG_COMP_MODE_MASK BIT(4) +#define ADS1015_CFG_DR_MASK GENMASK(7, 5) +#define ADS1015_CFG_MOD_MASK BIT(8) +#define ADS1015_CFG_PGA_MASK GENMASK(11, 9) +#define ADS1015_CFG_MUX_MASK GENMASK(14, 12) + +/* Comparator queue and disable field */ +#define ADS1015_CFG_COMP_DISABLE 3 + +/* Comparator polarity field */ +#define ADS1015_CFG_COMP_POL_LOW 0 +#define ADS1015_CFG_COMP_POL_HIGH 1 + +/* Comparator mode field */ +#define ADS1015_CFG_COMP_MODE_TRAD 0 +#define ADS1015_CFG_COMP_MODE_WINDOW 1 + +/* device operating modes */ +#define ADS1015_CONTINUOUS 0 +#define ADS1015_SINGLESHOT 1 + +#define ADS1015_SLEEP_DELAY_MS 2000 +#define ADS1015_DEFAULT_PGA 2 +#define ADS1015_DEFAULT_DATA_RATE 4 +#define ADS1015_DEFAULT_CHAN 0 + +enum chip_ids { + ADSXXXX = 0, + ADS1015, + ADS1115, +}; + +enum ads1015_channels { + ADS1015_AIN0_AIN1 = 0, + ADS1015_AIN0_AIN3, + ADS1015_AIN1_AIN3, + ADS1015_AIN2_AIN3, + ADS1015_AIN0, + ADS1015_AIN1, + ADS1015_AIN2, + ADS1015_AIN3, + ADS1015_TIMESTAMP, +}; + +static const unsigned int ads1015_data_rate[] = { + 128, 250, 490, 920, 1600, 2400, 3300, 3300 +}; + +static const unsigned int ads1115_data_rate[] = { + 8, 16, 32, 64, 128, 250, 475, 860 +}; + +/* + * Translation from PGA bits to full-scale positive and negative input voltage + * range in mV + */ +static int ads1015_fullscale_range[] = { + 6144, 4096, 2048, 1024, 512, 256, 256, 256 +}; + +/* + * Translation from COMP_QUE field value to the number of successive readings + * exceed the threshold values before an interrupt is generated + */ +static const int ads1015_comp_queue[] = { 1, 2, 4 }; + +static const struct iio_event_spec ads1015_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_PERIOD), + }, +}; + +#define ADS1015_V_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_CPU, \ + }, \ + .event_spec = ads1015_events, \ + .num_event_specs = ARRAY_SIZE(ads1015_events), \ + .datasheet_name = "AIN"#_chan, \ +} + +#define ADS1015_V_DIFF_CHAN(_chan, _chan2, _addr) { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .channel2 = _chan2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_CPU, \ + }, \ + .event_spec = ads1015_events, \ + .num_event_specs = ARRAY_SIZE(ads1015_events), \ + .datasheet_name = "AIN"#_chan"-AIN"#_chan2, \ +} + +#define ADS1115_V_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + .event_spec = ads1015_events, \ + .num_event_specs = ARRAY_SIZE(ads1015_events), \ + .datasheet_name = "AIN"#_chan, \ +} + +#define ADS1115_V_DIFF_CHAN(_chan, _chan2, _addr) { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .channel2 = _chan2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + .event_spec = ads1015_events, \ + .num_event_specs = ARRAY_SIZE(ads1015_events), \ + .datasheet_name = "AIN"#_chan"-AIN"#_chan2, \ +} + +struct ads1015_channel_data { + bool enabled; + unsigned int pga; + unsigned int data_rate; +}; + +struct ads1015_thresh_data { + unsigned int comp_queue; + int high_thresh; + int low_thresh; +}; + +struct ads1015_data { + struct regmap *regmap; + /* + * Protects ADC ops, e.g: concurrent sysfs/buffered + * data reads, configuration updates + */ + struct mutex lock; + struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; + + unsigned int event_channel; + unsigned int comp_mode; + struct ads1015_thresh_data thresh_data[ADS1015_CHANNELS]; + + unsigned int *data_rate; + /* + * Set to true when the ADC is switched to the continuous-conversion + * mode and exits from a power-down state. This flag is used to avoid + * getting the stale result from the conversion register. + */ + bool conv_invalid; +}; + +static bool ads1015_event_channel_enabled(struct ads1015_data *data) +{ + return (data->event_channel != ADS1015_CHANNELS); +} + +static void ads1015_event_channel_enable(struct ads1015_data *data, int chan, + int comp_mode) +{ + WARN_ON(ads1015_event_channel_enabled(data)); + + data->event_channel = chan; + data->comp_mode = comp_mode; +} + +static void ads1015_event_channel_disable(struct ads1015_data *data, int chan) +{ + data->event_channel = ADS1015_CHANNELS; +} + +static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADS1015_CFG_REG: + case ADS1015_LO_THRESH_REG: + case ADS1015_HI_THRESH_REG: + return true; + default: + return false; + } +} + +static const struct regmap_config ads1015_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = ADS1015_HI_THRESH_REG, + .writeable_reg = ads1015_is_writeable_reg, +}; + +static const struct iio_chan_spec ads1015_channels[] = { + ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1), + ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3), + ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3), + ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3), + ADS1015_V_CHAN(0, ADS1015_AIN0), + ADS1015_V_CHAN(1, ADS1015_AIN1), + ADS1015_V_CHAN(2, ADS1015_AIN2), + ADS1015_V_CHAN(3, ADS1015_AIN3), + IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP), +}; + +static const struct iio_chan_spec ads1115_channels[] = { + ADS1115_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1), + ADS1115_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3), + ADS1115_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3), + ADS1115_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3), + ADS1115_V_CHAN(0, ADS1015_AIN0), + ADS1115_V_CHAN(1, ADS1015_AIN1), + ADS1115_V_CHAN(2, ADS1015_AIN2), + ADS1115_V_CHAN(3, ADS1015_AIN3), + IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP), +}; + +#ifdef CONFIG_PM +static int ads1015_set_power_state(struct ads1015_data *data, bool on) +{ + int ret; + struct device *dev = regmap_get_device(data->regmap); + + if (on) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + pm_runtime_put_noidle(dev); + } else { + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + } + + return ret < 0 ? ret : 0; +} + +#else /* !CONFIG_PM */ + +static int ads1015_set_power_state(struct ads1015_data *data, bool on) +{ + return 0; +} + +#endif /* !CONFIG_PM */ + +static +int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val) +{ + int ret, pga, dr, dr_old, conv_time; + unsigned int old, mask, cfg; + + if (chan < 0 || chan >= ADS1015_CHANNELS) + return -EINVAL; + + ret = regmap_read(data->regmap, ADS1015_CFG_REG, &old); + if (ret) + return ret; + + pga = data->channel_data[chan].pga; + dr = data->channel_data[chan].data_rate; + mask = ADS1015_CFG_MUX_MASK | ADS1015_CFG_PGA_MASK | + ADS1015_CFG_DR_MASK; + cfg = chan << ADS1015_CFG_MUX_SHIFT | pga << ADS1015_CFG_PGA_SHIFT | + dr << ADS1015_CFG_DR_SHIFT; + + if (ads1015_event_channel_enabled(data)) { + mask |= ADS1015_CFG_COMP_QUE_MASK | ADS1015_CFG_COMP_MODE_MASK; + cfg |= data->thresh_data[chan].comp_queue << + ADS1015_CFG_COMP_QUE_SHIFT | + data->comp_mode << + ADS1015_CFG_COMP_MODE_SHIFT; + } + + cfg = (old & ~mask) | (cfg & mask); + if (old != cfg) { + ret = regmap_write(data->regmap, ADS1015_CFG_REG, cfg); + if (ret) + return ret; + data->conv_invalid = true; + } + if (data->conv_invalid) { + dr_old = (old & ADS1015_CFG_DR_MASK) >> ADS1015_CFG_DR_SHIFT; + conv_time = DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr_old]); + conv_time += DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr]); + conv_time += conv_time / 10; /* 10% internal clock inaccuracy */ + usleep_range(conv_time, conv_time + 1); + data->conv_invalid = false; + } + + return regmap_read(data->regmap, ADS1015_CONV_REG, val); +} + +static irqreturn_t ads1015_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads1015_data *data = iio_priv(indio_dev); + /* Ensure natural alignment of timestamp */ + struct { + s16 chan; + s64 timestamp __aligned(8); + } scan; + int chan, ret, res; + + memset(&scan, 0, sizeof(scan)); + + mutex_lock(&data->lock); + chan = find_first_bit(indio_dev->active_scan_mask, + indio_dev->masklength); + ret = ads1015_get_adc_result(data, chan, &res); + if (ret < 0) { + mutex_unlock(&data->lock); + goto err; + } + + scan.chan = res; + mutex_unlock(&data->lock); + + iio_push_to_buffers_with_timestamp(indio_dev, &scan, + iio_get_time_ns(indio_dev)); + +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ads1015_set_scale(struct ads1015_data *data, + struct iio_chan_spec const *chan, + int scale, int uscale) +{ + int i; + int fullscale = div_s64((scale * 1000000LL + uscale) << + (chan->scan_type.realbits - 1), 1000000); + + for (i = 0; i < ARRAY_SIZE(ads1015_fullscale_range); i++) { + if (ads1015_fullscale_range[i] == fullscale) { + data->channel_data[chan->address].pga = i; + return 0; + } + } + + return -EINVAL; +} + +static int ads1015_set_data_rate(struct ads1015_data *data, int chan, int rate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++) { + if (data->data_rate[i] == rate) { + data->channel_data[chan].data_rate = i; + return 0; + } + } + + return -EINVAL; +} + +static int ads1015_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret, idx; + struct ads1015_data *data = iio_priv(indio_dev); + + mutex_lock(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: { + int shift = chan->scan_type.shift; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + break; + + if (ads1015_event_channel_enabled(data) && + data->event_channel != chan->address) { + ret = -EBUSY; + goto release_direct; + } + + ret = ads1015_set_power_state(data, true); + if (ret < 0) + goto release_direct; + + ret = ads1015_get_adc_result(data, chan->address, val); + if (ret < 0) { + ads1015_set_power_state(data, false); + goto release_direct; + } + + *val = sign_extend32(*val >> shift, 15 - shift); + + ret = ads1015_set_power_state(data, false); + if (ret < 0) + goto release_direct; + + ret = IIO_VAL_INT; +release_direct: + iio_device_release_direct_mode(indio_dev); + break; + } + case IIO_CHAN_INFO_SCALE: + idx = data->channel_data[chan->address].pga; + *val = ads1015_fullscale_range[idx]; + *val2 = chan->scan_type.realbits - 1; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + idx = data->channel_data[chan->address].data_rate; + *val = data->data_rate[idx]; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->lock); + + return ret; +} + +static int ads1015_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct ads1015_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + ret = ads1015_set_scale(data, chan, val, val2); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = ads1015_set_data_rate(data, chan->address, val); + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&data->lock); + + return ret; +} + +static int ads1015_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, int *val, + int *val2) +{ + struct ads1015_data *data = iio_priv(indio_dev); + int ret; + unsigned int comp_queue; + int period; + int dr; + + mutex_lock(&data->lock); + + switch (info) { + case IIO_EV_INFO_VALUE: + *val = (dir == IIO_EV_DIR_RISING) ? + data->thresh_data[chan->address].high_thresh : + data->thresh_data[chan->address].low_thresh; + ret = IIO_VAL_INT; + break; + case IIO_EV_INFO_PERIOD: + dr = data->channel_data[chan->address].data_rate; + comp_queue = data->thresh_data[chan->address].comp_queue; + period = ads1015_comp_queue[comp_queue] * + USEC_PER_SEC / data->data_rate[dr]; + + *val = period / USEC_PER_SEC; + *val2 = period % USEC_PER_SEC; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&data->lock); + + return ret; +} + +static int ads1015_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, int val, + int val2) +{ + struct ads1015_data *data = iio_priv(indio_dev); + int realbits = chan->scan_type.realbits; + int ret = 0; + long long period; + int i; + int dr; + + mutex_lock(&data->lock); + + switch (info) { + case IIO_EV_INFO_VALUE: + if (val >= 1 << (realbits - 1) || val < -1 << (realbits - 1)) { + ret = -EINVAL; + break; + } + if (dir == IIO_EV_DIR_RISING) + data->thresh_data[chan->address].high_thresh = val; + else + data->thresh_data[chan->address].low_thresh = val; + break; + case IIO_EV_INFO_PERIOD: + dr = data->channel_data[chan->address].data_rate; + period = val * USEC_PER_SEC + val2; + + for (i = 0; i < ARRAY_SIZE(ads1015_comp_queue) - 1; i++) { + if (period <= ads1015_comp_queue[i] * + USEC_PER_SEC / data->data_rate[dr]) + break; + } + data->thresh_data[chan->address].comp_queue = i; + break; + default: + ret = -EINVAL; + break; + } + + mutex_unlock(&data->lock); + + return ret; +} + +static int ads1015_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ads1015_data *data = iio_priv(indio_dev); + int ret = 0; + + mutex_lock(&data->lock); + if (data->event_channel == chan->address) { + switch (dir) { + case IIO_EV_DIR_RISING: + ret = 1; + break; + case IIO_EV_DIR_EITHER: + ret = (data->comp_mode == ADS1015_CFG_COMP_MODE_WINDOW); + break; + default: + ret = -EINVAL; + break; + } + } + mutex_unlock(&data->lock); + + return ret; +} + +static int ads1015_enable_event_config(struct ads1015_data *data, + const struct iio_chan_spec *chan, int comp_mode) +{ + int low_thresh = data->thresh_data[chan->address].low_thresh; + int high_thresh = data->thresh_data[chan->address].high_thresh; + int ret; + unsigned int val; + + if (ads1015_event_channel_enabled(data)) { + if (data->event_channel != chan->address || + (data->comp_mode == ADS1015_CFG_COMP_MODE_TRAD && + comp_mode == ADS1015_CFG_COMP_MODE_WINDOW)) + return -EBUSY; + + return 0; + } + + if (comp_mode == ADS1015_CFG_COMP_MODE_TRAD) { + low_thresh = max(-1 << (chan->scan_type.realbits - 1), + high_thresh - 1); + } + ret = regmap_write(data->regmap, ADS1015_LO_THRESH_REG, + low_thresh << chan->scan_type.shift); + if (ret) + return ret; + + ret = regmap_write(data->regmap, ADS1015_HI_THRESH_REG, + high_thresh << chan->scan_type.shift); + if (ret) + return ret; + + ret = ads1015_set_power_state(data, true); + if (ret < 0) + return ret; + + ads1015_event_channel_enable(data, chan->address, comp_mode); + + ret = ads1015_get_adc_result(data, chan->address, &val); + if (ret) { + ads1015_event_channel_disable(data, chan->address); + ads1015_set_power_state(data, false); + } + + return ret; +} + +static int ads1015_disable_event_config(struct ads1015_data *data, + const struct iio_chan_spec *chan, int comp_mode) +{ + int ret; + + if (!ads1015_event_channel_enabled(data)) + return 0; + + if (data->event_channel != chan->address) + return 0; + + if (data->comp_mode == ADS1015_CFG_COMP_MODE_TRAD && + comp_mode == ADS1015_CFG_COMP_MODE_WINDOW) + return 0; + + ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG, + ADS1015_CFG_COMP_QUE_MASK, + ADS1015_CFG_COMP_DISABLE << + ADS1015_CFG_COMP_QUE_SHIFT); + if (ret) + return ret; + + ads1015_event_channel_disable(data, chan->address); + + return ads1015_set_power_state(data, false); +} + +static int ads1015_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct ads1015_data *data = iio_priv(indio_dev); + int ret; + int comp_mode = (dir == IIO_EV_DIR_EITHER) ? + ADS1015_CFG_COMP_MODE_WINDOW : ADS1015_CFG_COMP_MODE_TRAD; + + mutex_lock(&data->lock); + + /* Prevent from enabling both buffer and event at a time */ + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) { + mutex_unlock(&data->lock); + return ret; + } + + if (state) + ret = ads1015_enable_event_config(data, chan, comp_mode); + else + ret = ads1015_disable_event_config(data, chan, comp_mode); + + iio_device_release_direct_mode(indio_dev); + mutex_unlock(&data->lock); + + return ret; +} + +static irqreturn_t ads1015_event_handler(int irq, void *priv) +{ + struct iio_dev *indio_dev = priv; + struct ads1015_data *data = iio_priv(indio_dev); + int val; + int ret; + + /* Clear the latched ALERT/RDY pin */ + ret = regmap_read(data->regmap, ADS1015_CONV_REG, &val); + if (ret) + return IRQ_HANDLED; + + if (ads1015_event_channel_enabled(data)) { + enum iio_event_direction dir; + u64 code; + + dir = data->comp_mode == ADS1015_CFG_COMP_MODE_TRAD ? + IIO_EV_DIR_RISING : IIO_EV_DIR_EITHER; + code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, data->event_channel, + IIO_EV_TYPE_THRESH, dir); + iio_push_event(indio_dev, code, iio_get_time_ns(indio_dev)); + } + + return IRQ_HANDLED; +} + +static int ads1015_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ads1015_data *data = iio_priv(indio_dev); + + /* Prevent from enabling both buffer and event at a time */ + if (ads1015_event_channel_enabled(data)) + return -EBUSY; + + return ads1015_set_power_state(iio_priv(indio_dev), true); +} + +static int ads1015_buffer_postdisable(struct iio_dev *indio_dev) +{ + return ads1015_set_power_state(iio_priv(indio_dev), false); +} + +static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = { + .preenable = ads1015_buffer_preenable, + .postdisable = ads1015_buffer_postdisable, + .validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + +static IIO_CONST_ATTR_NAMED(ads1015_scale_available, scale_available, + "3 2 1 0.5 0.25 0.125"); +static IIO_CONST_ATTR_NAMED(ads1115_scale_available, scale_available, + "0.1875 0.125 0.0625 0.03125 0.015625 0.007813"); + +static IIO_CONST_ATTR_NAMED(ads1015_sampling_frequency_available, + sampling_frequency_available, "128 250 490 920 1600 2400 3300"); +static IIO_CONST_ATTR_NAMED(ads1115_sampling_frequency_available, + sampling_frequency_available, "8 16 32 64 128 250 475 860"); + +static struct attribute *ads1015_attributes[] = { + &iio_const_attr_ads1015_scale_available.dev_attr.attr, + &iio_const_attr_ads1015_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ads1015_attribute_group = { + .attrs = ads1015_attributes, +}; + +static struct attribute *ads1115_attributes[] = { + &iio_const_attr_ads1115_scale_available.dev_attr.attr, + &iio_const_attr_ads1115_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ads1115_attribute_group = { + .attrs = ads1115_attributes, +}; + +static const struct iio_info ads1015_info = { + .read_raw = ads1015_read_raw, + .write_raw = ads1015_write_raw, + .read_event_value = ads1015_read_event, + .write_event_value = ads1015_write_event, + .read_event_config = ads1015_read_event_config, + .write_event_config = ads1015_write_event_config, + .attrs = &ads1015_attribute_group, +}; + +static const struct iio_info ads1115_info = { + .read_raw = ads1015_read_raw, + .write_raw = ads1015_write_raw, + .read_event_value = ads1015_read_event, + .write_event_value = ads1015_write_event, + .read_event_config = ads1015_read_event_config, + .write_event_config = ads1015_write_event_config, + .attrs = &ads1115_attribute_group, +}; + +static int ads1015_client_get_channels_config(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ads1015_data *data = iio_priv(indio_dev); + struct device *dev = &client->dev; + struct fwnode_handle *node; + int i = -1; + + device_for_each_child_node(dev, node) { + u32 pval; + unsigned int channel; + unsigned int pga = ADS1015_DEFAULT_PGA; + unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; + + if (fwnode_property_read_u32(node, "reg", &pval)) { + dev_err(dev, "invalid reg on %pfw\n", node); + continue; + } + + channel = pval; + if (channel >= ADS1015_CHANNELS) { + dev_err(dev, "invalid channel index %d on %pfw\n", + channel, node); + continue; + } + + if (!fwnode_property_read_u32(node, "ti,gain", &pval)) { + pga = pval; + if (pga > 6) { + dev_err(dev, "invalid gain on %pfw\n", node); + fwnode_handle_put(node); + return -EINVAL; + } + } + + if (!fwnode_property_read_u32(node, "ti,datarate", &pval)) { + data_rate = pval; + if (data_rate > 7) { + dev_err(dev, "invalid data_rate on %pfw\n", node); + fwnode_handle_put(node); + return -EINVAL; + } + } + + data->channel_data[channel].pga = pga; + data->channel_data[channel].data_rate = data_rate; + + i++; + } + + return i < 0 ? -EINVAL : 0; +} + +static void ads1015_get_channels_config(struct i2c_client *client) +{ + unsigned int k; + + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ads1015_data *data = iio_priv(indio_dev); + + if (!ads1015_client_get_channels_config(client)) + return; + + /* fallback on default configuration */ + for (k = 0; k < ADS1015_CHANNELS; ++k) { + data->channel_data[k].pga = ADS1015_DEFAULT_PGA; + data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE; + } +} + +static int ads1015_set_conv_mode(struct ads1015_data *data, int mode) +{ + return regmap_update_bits(data->regmap, ADS1015_CFG_REG, + ADS1015_CFG_MOD_MASK, + mode << ADS1015_CFG_MOD_SHIFT); +} + +static int ads1015_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ads1015_data *data; + int ret; + enum chip_ids chip; + int i; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + + mutex_init(&data->lock); + + indio_dev->name = ADS1015_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + + chip = (enum chip_ids)device_get_match_data(&client->dev); + if (chip == ADSXXXX) + chip = id->driver_data; + switch (chip) { + case ADS1015: + indio_dev->channels = ads1015_channels; + indio_dev->num_channels = ARRAY_SIZE(ads1015_channels); + indio_dev->info = &ads1015_info; + data->data_rate = (unsigned int *) &ads1015_data_rate; + break; + case ADS1115: + indio_dev->channels = ads1115_channels; + indio_dev->num_channels = ARRAY_SIZE(ads1115_channels); + indio_dev->info = &ads1115_info; + data->data_rate = (unsigned int *) &ads1115_data_rate; + break; + default: + dev_err(&client->dev, "Unknown chip %d\n", chip); + return -EINVAL; + } + + data->event_channel = ADS1015_CHANNELS; + /* + * Set default lower and upper threshold to min and max value + * respectively. + */ + for (i = 0; i < ADS1015_CHANNELS; i++) { + int realbits = indio_dev->channels[i].scan_type.realbits; + + data->thresh_data[i].low_thresh = -1 << (realbits - 1); + data->thresh_data[i].high_thresh = (1 << (realbits - 1)) - 1; + } + + /* we need to keep this ABI the same as used by hwmon ADS1015 driver */ + ads1015_get_channels_config(client); + + data->regmap = devm_regmap_init_i2c(client, &ads1015_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(&client->dev, "Failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + ads1015_trigger_handler, + &ads1015_buffer_setup_ops); + if (ret < 0) { + dev_err(&client->dev, "iio triggered buffer setup failed\n"); + return ret; + } + + if (client->irq) { + unsigned long irq_trig = + irqd_get_trigger_type(irq_get_irq_data(client->irq)); + unsigned int cfg_comp_mask = ADS1015_CFG_COMP_QUE_MASK | + ADS1015_CFG_COMP_LAT_MASK | ADS1015_CFG_COMP_POL_MASK; + unsigned int cfg_comp = + ADS1015_CFG_COMP_DISABLE << ADS1015_CFG_COMP_QUE_SHIFT | + 1 << ADS1015_CFG_COMP_LAT_SHIFT; + + switch (irq_trig) { + case IRQF_TRIGGER_LOW: + cfg_comp |= ADS1015_CFG_COMP_POL_LOW << + ADS1015_CFG_COMP_POL_SHIFT; + break; + case IRQF_TRIGGER_HIGH: + cfg_comp |= ADS1015_CFG_COMP_POL_HIGH << + ADS1015_CFG_COMP_POL_SHIFT; + break; + default: + return -EINVAL; + } + + ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG, + cfg_comp_mask, cfg_comp); + if (ret) + return ret; + + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, ads1015_event_handler, + irq_trig | IRQF_ONESHOT, + client->name, indio_dev); + if (ret) + return ret; + } + + ret = ads1015_set_conv_mode(data, ADS1015_CONTINUOUS); + if (ret) + return ret; + + data->conv_invalid = true; + + ret = pm_runtime_set_active(&client->dev); + if (ret) + return ret; + pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + pm_runtime_enable(&client->dev); + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "Failed to register IIO device\n"); + return ret; + } + + return 0; +} + +static int ads1015_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ads1015_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + /* power down single shot mode */ + return ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); +} + +#ifdef CONFIG_PM +static int ads1015_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ads1015_data *data = iio_priv(indio_dev); + + return ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); +} + +static int ads1015_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct ads1015_data *data = iio_priv(indio_dev); + int ret; + + ret = ads1015_set_conv_mode(data, ADS1015_CONTINUOUS); + if (!ret) + data->conv_invalid = true; + + return ret; +} +#endif + +static const struct dev_pm_ops ads1015_pm_ops = { + SET_RUNTIME_PM_OPS(ads1015_runtime_suspend, + ads1015_runtime_resume, NULL) +}; + +static const struct i2c_device_id ads1015_id[] = { + {"ads1015", ADS1015}, + {"ads1115", ADS1115}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ads1015_id); + +static const struct of_device_id ads1015_of_match[] = { + { + .compatible = "ti,ads1015", + .data = (void *)ADS1015 + }, + { + .compatible = "ti,ads1115", + .data = (void *)ADS1115 + }, + {} +}; +MODULE_DEVICE_TABLE(of, ads1015_of_match); + +static struct i2c_driver ads1015_driver = { + .driver = { + .name = ADS1015_DRV_NAME, + .of_match_table = ads1015_of_match, + .pm = &ads1015_pm_ops, + }, + .probe = ads1015_probe, + .remove = ads1015_remove, + .id_table = ads1015_id, +}; + +module_i2c_driver(ads1015_driver); + +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>"); +MODULE_DESCRIPTION("Texas Instruments ADS1015 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c new file mode 100644 index 000000000..b4a128b19 --- /dev/null +++ b/drivers/iio/adc/ti-ads124s08.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 +/* TI ADS124S0X chip family driver + * Copyright (C) 2018 Texas Instruments Incorporated - https://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> + +#include <asm/unaligned.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; + /* + * Used to correctly align data. + * Ensure timestamp is naturally aligned. + * Note that the full buffer length may not be needed if not + * all channels are enabled, as long as the alignment of the + * timestamp is maintained. + */ + u32 buffer[ADS124S08_MAX_CHANNELS + sizeof(s64)/sizeof(u32)] __aligned(8); + 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; + 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; + + return get_unaligned_be24(&priv->data[2]); +} + +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); + 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"); + + priv->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, priv->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->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 new file mode 100644 index 000000000..d4583b76f --- /dev/null +++ b/drivers/iio/adc/ti-ads7950.c @@ -0,0 +1,727 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments ADS7950 SPI ADC driver + * + * Copyright 2016 David Lechner <david@lechnology.com> + * + * Based on iio/ad7923.c: + * Copyright 2011 Analog Devices Inc + * Copyright 2012 CS Systemes d'Information + * + * And also on hwmon/ads79xx.c + * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ + * Nishanth Menon + */ + +#include <linux/acpi.h> +#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> +#include <linux/regulator/consumer.h> +#include <linux/slab.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_consumer.h> +#include <linux/iio/triggered_buffer.h> + +/* + * In case of ACPI, we use the 5000 mV as default for the reference pin. + * Device tree users encode that via the vref-supply regulator. + */ +#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)) + +/* val = value, dec = left shift, bits = number of bits of the mask */ +#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; + 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; + + /* + * 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. + */ + u16 rx_buf[TI_ADS7950_MAX_CHAN + 2 + TI_ADS7950_TIMESTAMP_SIZE] + ____cacheline_aligned; + u16 tx_buf[TI_ADS7950_MAX_CHAN + 2]; + u16 single_tx; + u16 single_rx; + +}; + +struct ti_ads7950_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +enum ti_ads7950_id { + TI_ADS7950, + TI_ADS7951, + TI_ADS7952, + TI_ADS7953, + TI_ADS7954, + TI_ADS7955, + TI_ADS7956, + TI_ADS7957, + TI_ADS7958, + TI_ADS7959, + TI_ADS7960, + TI_ADS7961, +}; + +#define TI_ADS7950_V_CHAN(index, bits) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = index, \ + .datasheet_name = "CH##index", \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = bits, \ + .storagebits = 16, \ + .shift = 12 - (bits), \ + .endianness = IIO_CPU, \ + }, \ +} + +#define DECLARE_TI_ADS7950_4_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(4), \ +} + +#define DECLARE_TI_ADS7950_8_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + TI_ADS7950_V_CHAN(4, bits), \ + TI_ADS7950_V_CHAN(5, bits), \ + TI_ADS7950_V_CHAN(6, bits), \ + TI_ADS7950_V_CHAN(7, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(8), \ +} + +#define DECLARE_TI_ADS7950_12_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + TI_ADS7950_V_CHAN(4, bits), \ + TI_ADS7950_V_CHAN(5, bits), \ + TI_ADS7950_V_CHAN(6, bits), \ + TI_ADS7950_V_CHAN(7, bits), \ + TI_ADS7950_V_CHAN(8, bits), \ + TI_ADS7950_V_CHAN(9, bits), \ + TI_ADS7950_V_CHAN(10, bits), \ + TI_ADS7950_V_CHAN(11, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(12), \ +} + +#define DECLARE_TI_ADS7950_16_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_ADS7950_V_CHAN(0, bits), \ + TI_ADS7950_V_CHAN(1, bits), \ + TI_ADS7950_V_CHAN(2, bits), \ + TI_ADS7950_V_CHAN(3, bits), \ + TI_ADS7950_V_CHAN(4, bits), \ + TI_ADS7950_V_CHAN(5, bits), \ + TI_ADS7950_V_CHAN(6, bits), \ + TI_ADS7950_V_CHAN(7, bits), \ + TI_ADS7950_V_CHAN(8, bits), \ + TI_ADS7950_V_CHAN(9, bits), \ + TI_ADS7950_V_CHAN(10, bits), \ + TI_ADS7950_V_CHAN(11, bits), \ + TI_ADS7950_V_CHAN(12, bits), \ + TI_ADS7950_V_CHAN(13, bits), \ + TI_ADS7950_V_CHAN(14, bits), \ + TI_ADS7950_V_CHAN(15, bits), \ + IIO_CHAN_SOFT_TIMESTAMP(16), \ +} + +static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7950, 12); +static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7951, 12); +static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7952, 12); +static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7953, 12); +static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7954, 10); +static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7955, 10); +static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7956, 10); +static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7957, 10); +static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7958, 8); +static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7959, 8); +static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7960, 8); +static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7961, 8); + +static const struct ti_ads7950_chip_info ti_ads7950_chip_info[] = { + [TI_ADS7950] = { + .channels = ti_ads7950_channels, + .num_channels = ARRAY_SIZE(ti_ads7950_channels), + }, + [TI_ADS7951] = { + .channels = ti_ads7951_channels, + .num_channels = ARRAY_SIZE(ti_ads7951_channels), + }, + [TI_ADS7952] = { + .channels = ti_ads7952_channels, + .num_channels = ARRAY_SIZE(ti_ads7952_channels), + }, + [TI_ADS7953] = { + .channels = ti_ads7953_channels, + .num_channels = ARRAY_SIZE(ti_ads7953_channels), + }, + [TI_ADS7954] = { + .channels = ti_ads7954_channels, + .num_channels = ARRAY_SIZE(ti_ads7954_channels), + }, + [TI_ADS7955] = { + .channels = ti_ads7955_channels, + .num_channels = ARRAY_SIZE(ti_ads7955_channels), + }, + [TI_ADS7956] = { + .channels = ti_ads7956_channels, + .num_channels = ARRAY_SIZE(ti_ads7956_channels), + }, + [TI_ADS7957] = { + .channels = ti_ads7957_channels, + .num_channels = ARRAY_SIZE(ti_ads7957_channels), + }, + [TI_ADS7958] = { + .channels = ti_ads7958_channels, + .num_channels = ARRAY_SIZE(ti_ads7958_channels), + }, + [TI_ADS7959] = { + .channels = ti_ads7959_channels, + .num_channels = ARRAY_SIZE(ti_ads7959_channels), + }, + [TI_ADS7960] = { + .channels = ti_ads7960_channels, + .num_channels = ARRAY_SIZE(ti_ads7960_channels), + }, + [TI_ADS7961] = { + .channels = ti_ads7961_channels, + .num_channels = ARRAY_SIZE(ti_ads7961_channels), + }, +}; + +/* + * ti_ads7950_update_scan_mode() setup the spi transfer buffer for the new + * scan mask + */ +static int ti_ads7950_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ti_ads7950_state *st = iio_priv(indio_dev); + int i, cmd, len; + + len = 0; + for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) { + 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 */ + st->tx_buf[len++] = 0; + st->tx_buf[len++] = 0; + + st->ring_xfer.len = len * 2; + + return 0; +} + +static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + 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[2], + iio_get_time_ns(indio_dev)); + +out: + mutex_unlock(&st->slock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ti_ads7950_scan_direct(struct iio_dev *indio_dev, unsigned int ch) +{ + struct ti_ads7950_state *st = iio_priv(indio_dev); + int ret, 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 = st->single_rx; + +out: + mutex_unlock(&st->slock); + + return ret; +} + +static int ti_ads7950_get_range(struct ti_ads7950_state *st) +{ + int vref; + + if (st->vref_mv) { + vref = st->vref_mv; + } else { + vref = regulator_get_voltage(st->reg); + if (vref < 0) + return vref; + + vref /= 1000; + } + + if (st->cmd_settings_bitmask & TI_ADS7950_CR_RANGE_5V) + vref *= 2; + + return vref; +} + +static int ti_ads7950_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ti_ads7950_state *st = iio_priv(indio_dev); + int ret; + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ti_ads7950_scan_direct(indio_dev, chan->address); + if (ret < 0) + return ret; + + if (chan->address != TI_ADS7950_EXTRACT(ret, 12, 4)) + return -EIO; + + *val = TI_ADS7950_EXTRACT(ret, chan->scan_type.shift, + chan->scan_type.realbits); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = ti_ads7950_get_range(st); + if (ret < 0) + return ret; + + *val = ret; + *val2 = (1 << chan->scan_type.realbits) - 1; + + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static const struct iio_info ti_ads7950_info = { + .read_raw = &ti_ads7950_read_raw, + .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; + struct iio_dev *indio_dev; + 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; + + st = iio_priv(indio_dev); + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + info = &ti_ads7950_chip_info[spi_get_device_id(spi)->driver_data]; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = info->channels; + 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 */ + + 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 + * more cycle to send the value. The conversion process is driven by + * the SPI clock, which is why we have 3 transfers. The middle one is + * just dummy data sent while the chip is converting the sample that + * was read at the end of the first transfer. + */ + + st->scan_single_xfer[0].tx_buf = &st->single_tx; + st->scan_single_xfer[0].len = 2; + st->scan_single_xfer[0].cs_change = 1; + st->scan_single_xfer[1].tx_buf = &st->single_tx; + st->scan_single_xfer[1].len = 2; + st->scan_single_xfer[1].cs_change = 1; + st->scan_single_xfer[2].rx_buf = &st->single_rx; + st->scan_single_xfer[2].len = 2; + + spi_message_init_with_transfers(&st->scan_single_msg, + st->scan_single_xfer, 3); + + /* Use hard coded value for reference voltage in ACPI case */ + 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 to get regulator \"vref\"\n"); + 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"); + goto error_destroy_mutex; + } + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &ti_ads7950_trigger_handler, NULL); + if (ret) { + dev_err(&spi->dev, "Failed to setup triggered buffer\n"); + 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.can_sleep = true; + 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; +} + +static int ti_ads7950_remove(struct spi_device *spi) +{ + 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; +} + +static const struct spi_device_id ti_ads7950_id[] = { + { "ads7950", TI_ADS7950 }, + { "ads7951", TI_ADS7951 }, + { "ads7952", TI_ADS7952 }, + { "ads7953", TI_ADS7953 }, + { "ads7954", TI_ADS7954 }, + { "ads7955", TI_ADS7955 }, + { "ads7956", TI_ADS7956 }, + { "ads7957", TI_ADS7957 }, + { "ads7958", TI_ADS7958 }, + { "ads7959", TI_ADS7959 }, + { "ads7960", TI_ADS7960 }, + { "ads7961", TI_ADS7961 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ti_ads7950_id); + +static const struct of_device_id ads7950_of_table[] = { + { .compatible = "ti,ads7950", .data = &ti_ads7950_chip_info[TI_ADS7950] }, + { .compatible = "ti,ads7951", .data = &ti_ads7950_chip_info[TI_ADS7951] }, + { .compatible = "ti,ads7952", .data = &ti_ads7950_chip_info[TI_ADS7952] }, + { .compatible = "ti,ads7953", .data = &ti_ads7950_chip_info[TI_ADS7953] }, + { .compatible = "ti,ads7954", .data = &ti_ads7950_chip_info[TI_ADS7954] }, + { .compatible = "ti,ads7955", .data = &ti_ads7950_chip_info[TI_ADS7955] }, + { .compatible = "ti,ads7956", .data = &ti_ads7950_chip_info[TI_ADS7956] }, + { .compatible = "ti,ads7957", .data = &ti_ads7950_chip_info[TI_ADS7957] }, + { .compatible = "ti,ads7958", .data = &ti_ads7950_chip_info[TI_ADS7958] }, + { .compatible = "ti,ads7959", .data = &ti_ads7950_chip_info[TI_ADS7959] }, + { .compatible = "ti,ads7960", .data = &ti_ads7950_chip_info[TI_ADS7960] }, + { .compatible = "ti,ads7961", .data = &ti_ads7950_chip_info[TI_ADS7961] }, + { }, +}; +MODULE_DEVICE_TABLE(of, ads7950_of_table); + +static struct spi_driver ti_ads7950_driver = { + .driver = { + .name = "ads7950", + .of_match_table = ads7950_of_table, + }, + .probe = ti_ads7950_probe, + .remove = ti_ads7950_remove, + .id_table = ti_ads7950_id, +}; +module_spi_driver(ti_ads7950_driver); + +MODULE_AUTHOR("David Lechner <david@lechnology.com>"); +MODULE_DESCRIPTION("TI TI_ADS7950 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti-ads8344.c b/drivers/iio/adc/ti-ads8344.c new file mode 100644 index 000000000..a345a30d7 --- /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: https://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; + u8 rx_buf[3]; +}; + +#define ADS8344_VOLTAGE_CHANNEL(chan, addr) \ + { \ + .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), \ + .address = addr, \ + } + +#define ADS8344_VOLTAGE_CHANNEL_DIFF(chan1, chan2, addr) \ + { \ + .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), \ + .address = addr, \ + } + +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, sizeof(adc->rx_buf)); + if (ret) + return ret; + + return adc->rx_buf[0] << 9 | adc->rx_buf[1] << 1 | adc->rx_buf[2] >> 7; +} + +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->address, + 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->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 new file mode 100644 index 000000000..79c803537 --- /dev/null +++ b/drivers/iio/adc/ti-ads8688.c @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2015 Prevas A/S + */ + +#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/module.h> +#include <linux/of.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> + +#define ADS8688_CMD_REG(x) (x << 8) +#define ADS8688_CMD_REG_NOOP 0x00 +#define ADS8688_CMD_REG_RST 0x85 +#define ADS8688_CMD_REG_MAN_CH(chan) (0xC0 | (4 * chan)) +#define ADS8688_CMD_DONT_CARE_BITS 16 + +#define ADS8688_PROG_REG(x) (x << 9) +#define ADS8688_PROG_REG_RANGE_CH(chan) (0x05 + chan) +#define ADS8688_PROG_WR_BIT BIT(8) +#define ADS8688_PROG_DONT_CARE_BITS 8 + +#define ADS8688_REG_PLUSMINUS25VREF 0 +#define ADS8688_REG_PLUSMINUS125VREF 1 +#define ADS8688_REG_PLUSMINUS0625VREF 2 +#define ADS8688_REG_PLUS25VREF 5 +#define ADS8688_REG_PLUS125VREF 6 + +#define ADS8688_VREF_MV 4096 +#define ADS8688_REALBITS 16 +#define ADS8688_MAX_CHANNELS 8 + +/* + * enum ads8688_range - ADS8688 reference voltage range + * @ADS8688_PLUSMINUS25VREF: Device is configured for input range ±2.5 * VREF + * @ADS8688_PLUSMINUS125VREF: Device is configured for input range ±1.25 * VREF + * @ADS8688_PLUSMINUS0625VREF: Device is configured for input range ±0.625 * VREF + * @ADS8688_PLUS25VREF: Device is configured for input range 0 - 2.5 * VREF + * @ADS8688_PLUS125VREF: Device is configured for input range 0 - 1.25 * VREF + */ +enum ads8688_range { + ADS8688_PLUSMINUS25VREF, + ADS8688_PLUSMINUS125VREF, + ADS8688_PLUSMINUS0625VREF, + ADS8688_PLUS25VREF, + ADS8688_PLUS125VREF, +}; + +struct ads8688_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ads8688_state { + struct mutex lock; + const struct ads8688_chip_info *chip_info; + struct spi_device *spi; + struct regulator *reg; + unsigned int vref_mv; + enum ads8688_range range[8]; + union { + __be32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +enum ads8688_id { + ID_ADS8684, + ID_ADS8688, +}; + +struct ads8688_ranges { + enum ads8688_range range; + unsigned int scale; + int offset; + u8 reg; +}; + +static const struct ads8688_ranges ads8688_range_def[5] = { + { + .range = ADS8688_PLUSMINUS25VREF, + .scale = 76295, + .offset = -(1 << (ADS8688_REALBITS - 1)), + .reg = ADS8688_REG_PLUSMINUS25VREF, + }, { + .range = ADS8688_PLUSMINUS125VREF, + .scale = 38148, + .offset = -(1 << (ADS8688_REALBITS - 1)), + .reg = ADS8688_REG_PLUSMINUS125VREF, + }, { + .range = ADS8688_PLUSMINUS0625VREF, + .scale = 19074, + .offset = -(1 << (ADS8688_REALBITS - 1)), + .reg = ADS8688_REG_PLUSMINUS0625VREF, + }, { + .range = ADS8688_PLUS25VREF, + .scale = 38148, + .offset = 0, + .reg = ADS8688_REG_PLUS25VREF, + }, { + .range = ADS8688_PLUS125VREF, + .scale = 19074, + .offset = 0, + .reg = ADS8688_REG_PLUS125VREF, + } +}; + +static ssize_t ads8688_show_scales(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ads8688_state *st = iio_priv(dev_to_iio_dev(dev)); + + return sprintf(buf, "0.%09u 0.%09u 0.%09u\n", + ads8688_range_def[0].scale * st->vref_mv, + ads8688_range_def[1].scale * st->vref_mv, + ads8688_range_def[2].scale * st->vref_mv); +} + +static ssize_t ads8688_show_offsets(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d %d\n", ads8688_range_def[0].offset, + ads8688_range_def[3].offset); +} + +static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, + ads8688_show_scales, NULL, 0); +static IIO_DEVICE_ATTR(in_voltage_offset_available, S_IRUGO, + ads8688_show_offsets, NULL, 0); + +static struct attribute *ads8688_attributes[] = { + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage_offset_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ads8688_attribute_group = { + .attrs = ads8688_attributes, +}; + +#define ADS8688_CHAN(index) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ +} + +static const struct iio_chan_spec ads8684_channels[] = { + ADS8688_CHAN(0), + ADS8688_CHAN(1), + ADS8688_CHAN(2), + ADS8688_CHAN(3), +}; + +static const struct iio_chan_spec ads8688_channels[] = { + ADS8688_CHAN(0), + ADS8688_CHAN(1), + ADS8688_CHAN(2), + ADS8688_CHAN(3), + ADS8688_CHAN(4), + ADS8688_CHAN(5), + ADS8688_CHAN(6), + ADS8688_CHAN(7), +}; + +static int ads8688_prog_write(struct iio_dev *indio_dev, unsigned int addr, + unsigned int val) +{ + struct ads8688_state *st = iio_priv(indio_dev); + u32 tmp; + + tmp = ADS8688_PROG_REG(addr) | ADS8688_PROG_WR_BIT | val; + tmp <<= ADS8688_PROG_DONT_CARE_BITS; + st->data[0].d32 = cpu_to_be32(tmp); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ads8688_reset(struct iio_dev *indio_dev) +{ + struct ads8688_state *st = iio_priv(indio_dev); + u32 tmp; + + tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_RST); + tmp <<= ADS8688_CMD_DONT_CARE_BITS; + st->data[0].d32 = cpu_to_be32(tmp); + + return spi_write(st->spi, &st->data[0].d8[0], 4); +} + +static int ads8688_read(struct iio_dev *indio_dev, unsigned int chan) +{ + struct ads8688_state *st = iio_priv(indio_dev); + int ret; + u32 tmp; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[0], + .len = 4, + .cs_change = 1, + }, { + .tx_buf = &st->data[1].d8[0], + .rx_buf = &st->data[1].d8[0], + .len = 4, + }, + }; + + tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_MAN_CH(chan)); + tmp <<= ADS8688_CMD_DONT_CARE_BITS; + st->data[0].d32 = cpu_to_be32(tmp); + + tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_NOOP); + tmp <<= ADS8688_CMD_DONT_CARE_BITS; + st->data[1].d32 = cpu_to_be32(tmp); + + ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + return be32_to_cpu(st->data[1].d32) & 0xffff; +} + +static int ads8688_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + int ret, offset; + unsigned long scale_mv; + + struct ads8688_state *st = iio_priv(indio_dev); + + mutex_lock(&st->lock); + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ads8688_read(indio_dev, chan->channel); + mutex_unlock(&st->lock); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_mv = st->vref_mv; + scale_mv *= ads8688_range_def[st->range[chan->channel]].scale; + *val = 0; + *val2 = scale_mv; + mutex_unlock(&st->lock); + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + offset = ads8688_range_def[st->range[chan->channel]].offset; + *val = offset; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + } + mutex_unlock(&st->lock); + + return -EINVAL; +} + +static int ads8688_write_reg_range(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + enum ads8688_range range) +{ + unsigned int tmp; + int ret; + + tmp = ADS8688_PROG_REG_RANGE_CH(chan->channel); + ret = ads8688_prog_write(indio_dev, tmp, range); + + return ret; +} + +static int ads8688_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ads8688_state *st = iio_priv(indio_dev); + unsigned int scale = 0; + int ret = -EINVAL, i, offset = 0; + + mutex_lock(&st->lock); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + /* If the offset is 0 the ±2.5 * VREF mode is not available */ + offset = ads8688_range_def[st->range[chan->channel]].offset; + if (offset == 0 && val2 == ads8688_range_def[0].scale * st->vref_mv) { + mutex_unlock(&st->lock); + return -EINVAL; + } + + /* Lookup new mode */ + for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++) + if (val2 == ads8688_range_def[i].scale * st->vref_mv && + offset == ads8688_range_def[i].offset) { + ret = ads8688_write_reg_range(indio_dev, chan, + ads8688_range_def[i].reg); + break; + } + break; + case IIO_CHAN_INFO_OFFSET: + /* + * There are only two available offsets: + * 0 and -(1 << (ADS8688_REALBITS - 1)) + */ + if (!(ads8688_range_def[0].offset == val || + ads8688_range_def[3].offset == val)) { + mutex_unlock(&st->lock); + return -EINVAL; + } + + /* + * If the device are in ±2.5 * VREF mode, it's not allowed to + * switch to a mode where the offset is 0 + */ + if (val == 0 && + st->range[chan->channel] == ADS8688_PLUSMINUS25VREF) { + mutex_unlock(&st->lock); + return -EINVAL; + } + + scale = ads8688_range_def[st->range[chan->channel]].scale; + + /* Lookup new mode */ + for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++) + if (val == ads8688_range_def[i].offset && + scale == ads8688_range_def[i].scale) { + ret = ads8688_write_reg_range(indio_dev, chan, + ads8688_range_def[i].reg); + break; + } + break; + } + + if (!ret) + st->range[chan->channel] = ads8688_range_def[i].range; + + mutex_unlock(&st->lock); + + return ret; +} + +static int ads8688_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static const struct iio_info ads8688_info = { + .read_raw = &ads8688_read_raw, + .write_raw = &ads8688_write_raw, + .write_raw_get_fmt = &ads8688_write_raw_get_fmt, + .attrs = &ads8688_attribute_group, +}; + +static irqreturn_t ads8688_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + /* Ensure naturally aligned timestamp */ + u16 buffer[ADS8688_MAX_CHANNELS + sizeof(s64)/sizeof(u16)] __aligned(8); + int i, j = 0; + + for (i = 0; i < indio_dev->masklength; i++) { + if (!test_bit(i, indio_dev->active_scan_mask)) + continue; + buffer[j] = ads8688_read(indio_dev, i); + j++; + } + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct ads8688_chip_info ads8688_chip_info_tbl[] = { + [ID_ADS8684] = { + .channels = ads8684_channels, + .num_channels = ARRAY_SIZE(ads8684_channels), + }, + [ID_ADS8688] = { + .channels = ads8688_channels, + .num_channels = ARRAY_SIZE(ads8688_channels), + }, +}; + +static int ads8688_probe(struct spi_device *spi) +{ + struct ads8688_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = devm_regulator_get_optional(&spi->dev, "vref"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto err_regulator_disable; + + st->vref_mv = ret / 1000; + } else { + /* Use internal reference */ + st->vref_mv = ADS8688_VREF_MV; + } + + st->chip_info = &ads8688_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + spi->mode = SPI_MODE_1; + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = &ads8688_info; + + ads8688_reset(indio_dev); + + mutex_init(&st->lock); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, ads8688_trigger_handler, NULL); + if (ret < 0) { + dev_err(&spi->dev, "iio triggered buffer setup failed\n"); + goto err_regulator_disable; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto err_buffer_cleanup; + + return 0; + +err_buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); + +err_regulator_disable: + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); + + return ret; +} + +static int ads8688_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ads8688_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + + if (!IS_ERR(st->reg)) + regulator_disable(st->reg); + + return 0; +} + +static const struct spi_device_id ads8688_id[] = { + {"ads8684", ID_ADS8684}, + {"ads8688", ID_ADS8688}, + {} +}; +MODULE_DEVICE_TABLE(spi, ads8688_id); + +static const struct of_device_id ads8688_of_match[] = { + { .compatible = "ti,ads8684" }, + { .compatible = "ti,ads8688" }, + { } +}; +MODULE_DEVICE_TABLE(of, ads8688_of_match); + +static struct spi_driver ads8688_driver = { + .driver = { + .name = "ads8688", + }, + .probe = ads8688_probe, + .remove = ads8688_remove, + .id_table = ads8688_id, +}; +module_spi_driver(ads8688_driver); + +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 new file mode 100644 index 000000000..403b787f9 --- /dev/null +++ b/drivers/iio/adc/ti-tlc4541.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI tlc4541 ADC Driver + * + * Copyright (C) 2017 Phil Reid + * + * Datasheets can be found here: + * https://www.ti.com/lit/gpn/tlc3541 + * https://www.ti.com/lit/gpn/tlc4541 + * + * 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. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/sysfs.h> + +struct tlc4541_state { + struct spi_device *spi; + struct regulator *reg; + struct spi_transfer scan_single_xfer[3]; + struct spi_message scan_single_msg; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * 2 bytes data + 6 bytes padding + 8 bytes timestamp when + * call iio_push_to_buffers_with_timestamp. + */ + __be16 rx_buf[8] ____cacheline_aligned; +}; + +struct tlc4541_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +enum tlc4541_id { + TLC3541, + TLC4541, +}; + +#define TLC4541_V_CHAN(bits, bitshift) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = (bitshift), \ + .endianness = IIO_BE, \ + }, \ + } + +#define DECLARE_TLC4541_CHANNELS(name, bits, bitshift) \ +const struct iio_chan_spec name ## _channels[] = { \ + TLC4541_V_CHAN(bits, bitshift), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ +} + +static DECLARE_TLC4541_CHANNELS(tlc3541, 14, 2); +static DECLARE_TLC4541_CHANNELS(tlc4541, 16, 0); + +static const struct tlc4541_chip_info tlc4541_chip_info[] = { + [TLC3541] = { + .channels = tlc3541_channels, + .num_channels = ARRAY_SIZE(tlc3541_channels), + }, + [TLC4541] = { + .channels = tlc4541_channels, + .num_channels = ARRAY_SIZE(tlc4541_channels), + }, +}; + +static irqreturn_t tlc4541_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct tlc4541_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret < 0) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int tlc4541_get_range(struct tlc4541_state *st) +{ + int vref; + + vref = regulator_get_voltage(st->reg); + if (vref < 0) + return vref; + + vref /= 1000; + + return vref; +} + +static int tlc4541_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret = 0; + struct tlc4541_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 = spi_sync(st->spi, &st->scan_single_msg); + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + *val = be16_to_cpu(st->rx_buf[0]); + *val = *val >> chan->scan_type.shift; + *val &= GENMASK(chan->scan_type.realbits - 1, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = tlc4541_get_range(st); + if (ret < 0) + return ret; + *val = ret; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + +static const struct iio_info tlc4541_info = { + .read_raw = &tlc4541_read_raw, +}; + +static int tlc4541_probe(struct spi_device *spi) +{ + struct tlc4541_state *st; + struct iio_dev *indio_dev; + const struct tlc4541_chip_info *info; + int ret; + int8_t device_init = 0; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + info = &tlc4541_chip_info[spi_get_device_id(spi)->driver_data]; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = info->channels; + indio_dev->num_channels = info->num_channels; + indio_dev->info = &tlc4541_info; + + /* perform reset */ + spi_write(spi, &device_init, 1); + + /* Setup default message */ + st->scan_single_xfer[0].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[0].len = 3; + st->scan_single_xfer[1].delay.value = 3; + st->scan_single_xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS; + st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[2].len = 2; + + spi_message_init_with_transfers(&st->scan_single_msg, + st->scan_single_xfer, 3); + + st->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &tlc4541_trigger_handler, NULL); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_buffer; + + return 0; + +error_cleanup_buffer: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + regulator_disable(st->reg); + + return ret; +} + +static int tlc4541_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct tlc4541_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(st->reg); + + return 0; +} + +static const struct of_device_id tlc4541_dt_ids[] = { + { .compatible = "ti,tlc3541", }, + { .compatible = "ti,tlc4541", }, + {} +}; +MODULE_DEVICE_TABLE(of, tlc4541_dt_ids); + +static const struct spi_device_id tlc4541_id[] = { + {"tlc3541", TLC3541}, + {"tlc4541", TLC4541}, + {} +}; +MODULE_DEVICE_TABLE(spi, tlc4541_id); + +static struct spi_driver tlc4541_driver = { + .driver = { + .name = "tlc4541", + .of_match_table = tlc4541_dt_ids, + }, + .probe = tlc4541_probe, + .remove = tlc4541_remove, + .id_table = tlc4541_id, +}; +module_spi_driver(tlc4541_driver); + +MODULE_AUTHOR("Phil Reid <preid@electromag.com.au>"); +MODULE_DESCRIPTION("Texas Instruments TLC4541 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c new file mode 100644 index 000000000..0ae57d6cf --- /dev/null +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -0,0 +1,729 @@ +/* + * TI ADC MFD driver + * + * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/iio/iio.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> + +#include <linux/mfd/ti_am335x_tscadc.h> +#include <linux/iio/buffer.h> +#include <linux/iio/kfifo_buf.h> + +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> + +#define DMA_BUFFER_SIZE SZ_2K + +struct tiadc_dma { + struct dma_slave_config conf; + struct dma_chan *chan; + dma_addr_t addr; + dma_cookie_t cookie; + u8 *buf; + int current_period; + int period_size; + u8 fifo_thresh; +}; + +struct tiadc_device { + struct ti_tscadc_dev *mfd_tscadc; + struct tiadc_dma dma; + struct mutex fifo1_lock; /* to protect fifo access */ + int channels; + int total_ch_enabled; + u8 channel_line[8]; + u8 channel_step[8]; + int buffer_en_ch_steps; + u16 data[8]; + u32 open_delay[8], sample_delay[8], step_avg[8]; +}; + +static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg) +{ + return readl(adc->mfd_tscadc->tscadc_base + reg); +} + +static void tiadc_writel(struct tiadc_device *adc, unsigned int reg, + unsigned int val) +{ + writel(val, adc->mfd_tscadc->tscadc_base + reg); +} + +static u32 get_adc_step_mask(struct tiadc_device *adc_dev) +{ + u32 step_en; + + step_en = ((1 << adc_dev->channels) - 1); + step_en <<= TOTAL_STEPS - adc_dev->channels + 1; + return step_en; +} + +static u32 get_adc_chan_step_mask(struct tiadc_device *adc_dev, + struct iio_chan_spec const *chan) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) { + if (chan->channel == adc_dev->channel_line[i]) { + u32 step; + + step = adc_dev->channel_step[i]; + /* +1 for the charger */ + return 1 << (step + 1); + } + } + WARN_ON(1); + return 0; +} + +static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan) +{ + return 1 << adc_dev->channel_step[chan]; +} + +static void tiadc_step_config(struct iio_dev *indio_dev) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct device *dev = adc_dev->mfd_tscadc->dev; + unsigned int stepconfig; + int i, steps = 0; + + /* + * There are 16 configurable steps and 8 analog input + * lines available which are shared between Touchscreen and ADC. + * + * Steps forwards i.e. from 0 towards 16 are used by ADC + * depending on number of input lines needed. + * Channel would represent which analog input + * needs to be given to ADC to digitalize data. + */ + + + for (i = 0; i < adc_dev->channels; i++) { + int chan; + + chan = adc_dev->channel_line[i]; + + if (adc_dev->step_avg[i] > STEPCONFIG_AVG_16) { + dev_warn(dev, "chan %d step_avg truncating to %d\n", + chan, STEPCONFIG_AVG_16); + adc_dev->step_avg[i] = STEPCONFIG_AVG_16; + } + + if (adc_dev->step_avg[i]) + stepconfig = + STEPCONFIG_AVG(ffs(adc_dev->step_avg[i]) - 1) | + STEPCONFIG_FIFO1; + else + stepconfig = STEPCONFIG_FIFO1; + + if (iio_buffer_enabled(indio_dev)) + stepconfig |= STEPCONFIG_MODE_SWCNT; + + tiadc_writel(adc_dev, REG_STEPCONFIG(steps), + 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", + chan); + adc_dev->open_delay[i] = STEPDELAY_OPEN_MASK; + } + + if (adc_dev->sample_delay[i] > 0xFF) { + dev_warn(dev, "chan %d sample delay truncating to 0xFF\n", + chan); + adc_dev->sample_delay[i] = 0xFF; + } + + tiadc_writel(adc_dev, REG_STEPDELAY(steps), + STEPDELAY_OPEN(adc_dev->open_delay[i]) | + STEPDELAY_SAMPLE(adc_dev->sample_delay[i])); + + adc_dev->channel_step[i] = steps; + steps++; + } +} + +static irqreturn_t tiadc_irq_h(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct tiadc_device *adc_dev = iio_priv(indio_dev); + unsigned int status, config, adc_fsm; + unsigned short count = 0; + + status = tiadc_readl(adc_dev, REG_IRQSTATUS); + + /* + * ADC and touchscreen share the IRQ line. + * FIFO0 interrupts are used by TSC. Handle FIFO1 IRQs here only + */ + if (status & IRQENB_FIFO1OVRRUN) { + /* FIFO Overrun. Clear flag. Disable/Enable ADC to recover */ + config = tiadc_readl(adc_dev, REG_CTRL); + config &= ~(CNTRLREG_TSCSSENB); + tiadc_writel(adc_dev, REG_CTRL, config); + tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN + | IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES); + + /* wait for idle state. + * ADC needs to finish the current conversion + * before disabling the module + */ + do { + adc_fsm = tiadc_readl(adc_dev, REG_ADCFSM); + } while (adc_fsm != 0x10 && count++ < 100); + + tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB)); + return IRQ_HANDLED; + } else if (status & IRQENB_FIFO1THRES) { + /* Disable irq and wake worker thread */ + tiadc_writel(adc_dev, REG_IRQCLR, IRQENB_FIFO1THRES); + return IRQ_WAKE_THREAD; + } + + return IRQ_NONE; +} + +static irqreturn_t tiadc_worker_h(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct tiadc_device *adc_dev = iio_priv(indio_dev); + int i, k, fifo1count, read; + u16 *data = adc_dev->data; + + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + for (k = 0; k < fifo1count; k = k + i) { + for (i = 0; i < (indio_dev->scan_bytes)/2; i++) { + read = tiadc_readl(adc_dev, REG_FIFO1); + data[i] = read & FIFOREAD_DATA_MASK; + } + iio_push_to_buffers(indio_dev, (u8 *) data); + } + + tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES); + tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); + + return IRQ_HANDLED; +} + +static void tiadc_dma_rx_complete(void *param) +{ + struct iio_dev *indio_dev = param; + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + u8 *data; + int i; + + data = dma->buf + dma->current_period * dma->period_size; + dma->current_period = 1 - dma->current_period; /* swap the buffer ID */ + + for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) { + iio_push_to_buffers(indio_dev, data); + data += indio_dev->scan_bytes; + } +} + +static int tiadc_start_dma(struct iio_dev *indio_dev) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + struct dma_async_tx_descriptor *desc; + + dma->current_period = 0; /* We start to fill period 0 */ + /* + * Make the fifo thresh as the multiple of total number of + * channels enabled, so make sure that cyclic DMA period + * length is also a multiple of total number of channels + * enabled. This ensures that no invalid data is reported + * to the stack via iio_push_to_buffers(). + */ + dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1, + adc_dev->total_ch_enabled) - 1; + /* Make sure that period length is multiple of fifo thresh level */ + dma->period_size = rounddown(DMA_BUFFER_SIZE / 2, + (dma->fifo_thresh + 1) * sizeof(u16)); + + dma->conf.src_maxburst = dma->fifo_thresh + 1; + dmaengine_slave_config(dma->chan, &dma->conf); + + desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr, + dma->period_size * 2, + dma->period_size, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) + return -EBUSY; + + desc->callback = tiadc_dma_rx_complete; + desc->callback_param = indio_dev; + + dma->cookie = dmaengine_submit(desc); + + dma_async_issue_pending(dma->chan); + + tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh); + tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh); + tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1); + + return 0; +} + +static int tiadc_buffer_preenable(struct iio_dev *indio_dev) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + int i, fifo1count; + + tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES | + IRQENB_FIFO1OVRRUN | + IRQENB_FIFO1UNDRFLW)); + + /* Flush FIFO. Needed in corner cases in simultaneous tsc/adc use */ + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + for (i = 0; i < fifo1count; i++) + tiadc_readl(adc_dev, REG_FIFO1); + + return 0; +} + +static int tiadc_buffer_postenable(struct iio_dev *indio_dev) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + unsigned int irq_enable; + unsigned int enb = 0; + u8 bit; + + tiadc_step_config(indio_dev); + for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) { + enb |= (get_adc_step_bit(adc_dev, bit) << 1); + adc_dev->total_ch_enabled++; + } + adc_dev->buffer_en_ch_steps = enb; + + if (dma->chan) + tiadc_start_dma(indio_dev); + + am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb); + + tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES + | IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW); + + irq_enable = IRQENB_FIFO1OVRRUN; + if (!dma->chan) + irq_enable |= IRQENB_FIFO1THRES; + tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable); + + return 0; +} + +static int tiadc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + int fifo1count, i; + + tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES | + IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW)); + am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps); + adc_dev->buffer_en_ch_steps = 0; + adc_dev->total_ch_enabled = 0; + if (dma->chan) { + tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2); + dmaengine_terminate_async(dma->chan); + } + + /* Flush FIFO of leftover data in the time it takes to disable adc */ + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + for (i = 0; i < fifo1count; i++) + tiadc_readl(adc_dev, REG_FIFO1); + + return 0; +} + +static int tiadc_buffer_postdisable(struct iio_dev *indio_dev) +{ + tiadc_step_config(indio_dev); + + return 0; +} + +static const struct iio_buffer_setup_ops tiadc_buffer_setup_ops = { + .preenable = &tiadc_buffer_preenable, + .postenable = &tiadc_buffer_postenable, + .predisable = &tiadc_buffer_predisable, + .postdisable = &tiadc_buffer_postdisable, +}; + +static int tiadc_iio_buffered_hardware_setup(struct device *dev, + struct iio_dev *indio_dev, + irqreturn_t (*pollfunc_bh)(int irq, void *p), + irqreturn_t (*pollfunc_th)(int irq, void *p), + int irq, + unsigned long flags, + const struct iio_buffer_setup_ops *setup_ops) +{ + struct iio_buffer *buffer; + int ret; + + buffer = devm_iio_kfifo_allocate(dev); + if (!buffer) + return -ENOMEM; + + iio_device_attach_buffer(indio_dev, buffer); + + ret = devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh, + flags, indio_dev->name, indio_dev); + if (ret) + return ret; + + indio_dev->setup_ops = setup_ops; + indio_dev->modes |= INDIO_BUFFER_SOFTWARE; + + return 0; +} + +static const char * const chan_name_ain[] = { + "AIN0", + "AIN1", + "AIN2", + "AIN3", + "AIN4", + "AIN5", + "AIN6", + "AIN7", +}; + +static int tiadc_channel_init(struct device *dev, struct iio_dev *indio_dev, + int channels) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct iio_chan_spec *chan_array; + struct iio_chan_spec *chan; + int i; + + indio_dev->num_channels = channels; + chan_array = devm_kcalloc(dev, channels, sizeof(*chan_array), + GFP_KERNEL); + if (chan_array == NULL) + return -ENOMEM; + + chan = chan_array; + for (i = 0; i < channels; i++, chan++) { + + chan->type = IIO_VOLTAGE; + chan->indexed = 1; + chan->channel = adc_dev->channel_line[i]; + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->datasheet_name = chan_name_ain[chan->channel]; + chan->scan_index = i; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 12; + chan->scan_type.storagebits = 16; + } + + indio_dev->channels = chan_array; + + return 0; +} + +static int tiadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct tiadc_device *adc_dev = iio_priv(indio_dev); + int ret = IIO_VAL_INT; + int i, map_val; + unsigned int fifo1count, read, stepid; + bool found = false; + u32 step_en; + unsigned long timeout; + + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + step_en = get_adc_chan_step_mask(adc_dev, chan); + if (!step_en) + return -EINVAL; + + mutex_lock(&adc_dev->fifo1_lock); + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + while (fifo1count--) + tiadc_readl(adc_dev, REG_FIFO1); + + am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en); + + timeout = jiffies + msecs_to_jiffies + (IDLE_TIMEOUT * adc_dev->channels); + /* Wait for Fifo threshold interrupt */ + while (1) { + fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); + if (fifo1count) + break; + + if (time_after(jiffies, timeout)) { + am335x_tsc_se_adc_done(adc_dev->mfd_tscadc); + ret = -EAGAIN; + goto err_unlock; + } + } + map_val = adc_dev->channel_step[chan->scan_index]; + + /* + * We check the complete FIFO. We programmed just one entry but in case + * something went wrong we left empty handed (-EAGAIN previously) and + * then the value apeared somehow in the FIFO we would have two entries. + * Therefore we read every item and keep only the latest version of the + * requested channel. + */ + for (i = 0; i < fifo1count; i++) { + read = tiadc_readl(adc_dev, REG_FIFO1); + stepid = read & FIFOREAD_CHNLID_MASK; + stepid = stepid >> 0x10; + + if (stepid == map_val) { + read = read & FIFOREAD_DATA_MASK; + found = true; + *val = (u16) read; + } + } + am335x_tsc_se_adc_done(adc_dev->mfd_tscadc); + + if (!found) + ret = -EBUSY; + +err_unlock: + mutex_unlock(&adc_dev->fifo1_lock); + return ret; +} + +static const struct iio_info tiadc_info = { + .read_raw = &tiadc_read_raw, +}; + +static int tiadc_request_dma(struct platform_device *pdev, + struct tiadc_device *adc_dev) +{ + struct tiadc_dma *dma = &adc_dev->dma; + dma_cap_mask_t mask; + + /* Default slave configuration parameters */ + dma->conf.direction = DMA_DEV_TO_MEM; + dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1; + + dma_cap_zero(mask); + dma_cap_set(DMA_CYCLIC, mask); + + /* Get a channel for RX */ + dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1"); + if (IS_ERR(dma->chan)) { + int ret = PTR_ERR(dma->chan); + + dma->chan = NULL; + return ret; + } + + /* RX buffer */ + dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE, + &dma->addr, GFP_KERNEL); + if (!dma->buf) + goto err; + + return 0; +err: + dma_release_channel(dma->chan); + return -ENOMEM; +} + +static int tiadc_parse_dt(struct platform_device *pdev, + struct tiadc_device *adc_dev) +{ + struct device_node *node = pdev->dev.of_node; + struct property *prop; + const __be32 *cur; + int channels = 0; + u32 val; + + of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) { + adc_dev->channel_line[channels] = val; + + /* Set Default values for optional DT parameters */ + adc_dev->open_delay[channels] = STEPCONFIG_OPENDLY; + adc_dev->sample_delay[channels] = STEPCONFIG_SAMPLEDLY; + adc_dev->step_avg[channels] = 16; + + channels++; + } + + of_property_read_u32_array(node, "ti,chan-step-avg", + adc_dev->step_avg, channels); + of_property_read_u32_array(node, "ti,chan-step-opendelay", + adc_dev->open_delay, channels); + of_property_read_u32_array(node, "ti,chan-step-sampledelay", + adc_dev->sample_delay, channels); + + adc_dev->channels = channels; + return 0; +} + +static int tiadc_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct tiadc_device *adc_dev; + struct device_node *node = pdev->dev.of_node; + int err; + + if (!node) { + dev_err(&pdev->dev, "Could not find valid DT data.\n"); + return -EINVAL; + } + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev)); + if (indio_dev == NULL) { + dev_err(&pdev->dev, "failed to allocate iio device\n"); + return -ENOMEM; + } + adc_dev = iio_priv(indio_dev); + + adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev); + tiadc_parse_dt(pdev, adc_dev); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &tiadc_info; + + tiadc_step_config(indio_dev); + tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD); + mutex_init(&adc_dev->fifo1_lock); + + err = tiadc_channel_init(&pdev->dev, indio_dev, adc_dev->channels); + if (err < 0) + return err; + + err = tiadc_iio_buffered_hardware_setup(&pdev->dev, indio_dev, + &tiadc_worker_h, + &tiadc_irq_h, + adc_dev->mfd_tscadc->irq, + IRQF_SHARED, + &tiadc_buffer_setup_ops); + + if (err) + goto err_free_channels; + + err = iio_device_register(indio_dev); + if (err) + goto err_buffer_unregister; + + platform_set_drvdata(pdev, indio_dev); + + err = tiadc_request_dma(pdev, adc_dev); + if (err && err != -ENODEV) { + dev_err_probe(&pdev->dev, err, "DMA request failed\n"); + goto err_dma; + } + + return 0; + +err_dma: + iio_device_unregister(indio_dev); +err_buffer_unregister: +err_free_channels: + return err; +} + +static int tiadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + struct tiadc_dma *dma = &adc_dev->dma; + u32 step_en; + + if (dma->chan) { + dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE, + dma->buf, dma->addr); + dma_release_channel(dma->chan); + } + iio_device_unregister(indio_dev); + + step_en = get_adc_step_mask(adc_dev); + am335x_tsc_se_clr(adc_dev->mfd_tscadc, step_en); + + return 0; +} + +static int __maybe_unused tiadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + unsigned int idle; + + idle = tiadc_readl(adc_dev, REG_CTRL); + idle &= ~(CNTRLREG_TSCSSENB); + tiadc_writel(adc_dev, REG_CTRL, (idle | + CNTRLREG_POWERDOWN)); + + return 0; +} + +static int __maybe_unused tiadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tiadc_device *adc_dev = iio_priv(indio_dev); + unsigned int restore; + + /* Make sure ADC is powered up */ + restore = tiadc_readl(adc_dev, REG_CTRL); + restore &= ~(CNTRLREG_POWERDOWN); + tiadc_writel(adc_dev, REG_CTRL, restore); + + tiadc_step_config(indio_dev); + am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, + adc_dev->buffer_en_ch_steps); + return 0; +} + +static SIMPLE_DEV_PM_OPS(tiadc_pm_ops, tiadc_suspend, tiadc_resume); + +static const struct of_device_id ti_adc_dt_ids[] = { + { .compatible = "ti,am3359-adc", }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); + +static struct platform_driver tiadc_driver = { + .driver = { + .name = "TI-am335x-adc", + .pm = &tiadc_pm_ops, + .of_match_table = ti_adc_dt_ids, + }, + .probe = tiadc_probe, + .remove = tiadc_remove, +}; +module_platform_driver(tiadc_driver); + +MODULE_DESCRIPTION("TI ADC controller driver"); +MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c new file mode 100644 index 000000000..6ce40cc45 --- /dev/null +++ b/drivers/iio/adc/twl4030-madc.c @@ -0,0 +1,938 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * + * TWL4030 MADC module driver-This driver monitors the real time + * conversion of analog signals like battery temperature, + * battery type, battery level etc. + * + * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ + * J Keerthy <j-keerthy@ti.com> + * + * Based on twl4030-madc.c + * Copyright (C) 2008 Nokia Corporation + * Mikko Ylinen <mikko.k.ylinen@nokia.com> + * + * Amit Kucheria <amit.kucheria@canonical.com> + */ + +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/mfd/twl.h> +#include <linux/module.h> +#include <linux/stddef.h> +#include <linux/mutex.h> +#include <linux/bitops.h> +#include <linux/jiffies.h> +#include <linux/types.h> +#include <linux/gfp.h> +#include <linux/err.h> +#include <linux/regulator/consumer.h> + +#include <linux/iio/iio.h> + +#define TWL4030_MADC_MAX_CHANNELS 16 + +#define TWL4030_MADC_CTRL1 0x00 +#define TWL4030_MADC_CTRL2 0x01 + +#define TWL4030_MADC_RTSELECT_LSB 0x02 +#define TWL4030_MADC_SW1SELECT_LSB 0x06 +#define TWL4030_MADC_SW2SELECT_LSB 0x0A + +#define TWL4030_MADC_RTAVERAGE_LSB 0x04 +#define TWL4030_MADC_SW1AVERAGE_LSB 0x08 +#define TWL4030_MADC_SW2AVERAGE_LSB 0x0C + +#define TWL4030_MADC_CTRL_SW1 0x12 +#define TWL4030_MADC_CTRL_SW2 0x13 + +#define TWL4030_MADC_RTCH0_LSB 0x17 +#define TWL4030_MADC_GPCH0_LSB 0x37 + +#define TWL4030_MADC_MADCON (1 << 0) /* MADC power on */ +#define TWL4030_MADC_BUSY (1 << 0) /* MADC busy */ +/* MADC conversion completion */ +#define TWL4030_MADC_EOC_SW (1 << 1) +/* MADC SWx start conversion */ +#define TWL4030_MADC_SW_START (1 << 5) +#define TWL4030_MADC_ADCIN0 (1 << 0) +#define TWL4030_MADC_ADCIN1 (1 << 1) +#define TWL4030_MADC_ADCIN2 (1 << 2) +#define TWL4030_MADC_ADCIN3 (1 << 3) +#define TWL4030_MADC_ADCIN4 (1 << 4) +#define TWL4030_MADC_ADCIN5 (1 << 5) +#define TWL4030_MADC_ADCIN6 (1 << 6) +#define TWL4030_MADC_ADCIN7 (1 << 7) +#define TWL4030_MADC_ADCIN8 (1 << 8) +#define TWL4030_MADC_ADCIN9 (1 << 9) +#define TWL4030_MADC_ADCIN10 (1 << 10) +#define TWL4030_MADC_ADCIN11 (1 << 11) +#define TWL4030_MADC_ADCIN12 (1 << 12) +#define TWL4030_MADC_ADCIN13 (1 << 13) +#define TWL4030_MADC_ADCIN14 (1 << 14) +#define TWL4030_MADC_ADCIN15 (1 << 15) + +/* Fixed channels */ +#define TWL4030_MADC_BTEMP TWL4030_MADC_ADCIN1 +#define TWL4030_MADC_VBUS TWL4030_MADC_ADCIN8 +#define TWL4030_MADC_VBKB TWL4030_MADC_ADCIN9 +#define TWL4030_MADC_ICHG TWL4030_MADC_ADCIN10 +#define TWL4030_MADC_VCHG TWL4030_MADC_ADCIN11 +#define TWL4030_MADC_VBAT TWL4030_MADC_ADCIN12 + +/* Step size and prescaler ratio */ +#define TEMP_STEP_SIZE 147 +#define TEMP_PSR_R 100 +#define CURR_STEP_SIZE 147 +#define CURR_PSR_R1 44 +#define CURR_PSR_R2 88 + +#define TWL4030_BCI_BCICTL1 0x23 +#define TWL4030_BCI_CGAIN 0x020 +#define TWL4030_BCI_MESBAT (1 << 1) +#define TWL4030_BCI_TYPEN (1 << 4) +#define TWL4030_BCI_ITHEN (1 << 3) + +#define REG_BCICTL2 0x024 +#define TWL4030_BCI_ITHSENS 0x007 + +/* Register and bits for GPBR1 register */ +#define TWL4030_REG_GPBR1 0x0c +#define TWL4030_GPBR1_MADC_HFCLK_EN (1 << 7) + +#define TWL4030_USB_SEL_MADC_MCPC (1<<3) +#define TWL4030_USB_CARKIT_ANA_CTRL 0xBB + +struct twl4030_madc_conversion_method { + u8 sel; + u8 avg; + u8 rbase; + u8 ctrl; +}; + +/** + * struct twl4030_madc_request - madc request packet for channel conversion + * @channels: 16 bit bitmap for individual channels + * @do_avg: sample the input channel for 4 consecutive cycles + * @method: RT, SW1, SW2 + * @type: Polling or interrupt based method + * @active: Flag if request is active + * @result_pending: Flag from irq handler, that result is ready + * @raw: Return raw value, do not convert it + * @rbuf: Result buffer + */ +struct twl4030_madc_request { + unsigned long channels; + bool do_avg; + u16 method; + u16 type; + bool active; + bool result_pending; + bool raw; + int rbuf[TWL4030_MADC_MAX_CHANNELS]; +}; + +enum conversion_methods { + TWL4030_MADC_RT, + TWL4030_MADC_SW1, + TWL4030_MADC_SW2, + TWL4030_MADC_NUM_METHODS +}; + +enum sample_type { + TWL4030_MADC_WAIT, + TWL4030_MADC_IRQ_ONESHOT, + TWL4030_MADC_IRQ_REARM +}; + +/** + * struct twl4030_madc_data - a container for madc info + * @dev: Pointer to device structure for madc + * @lock: Mutex protecting this data structure + * @usb3v1: Pointer to bias regulator for madc + * @requests: Array of request struct corresponding to SW1, SW2 and RT + * @use_second_irq: IRQ selection (main or co-processor) + * @imr: Interrupt mask register of MADC + * @isr: Interrupt status register of MADC + */ +struct twl4030_madc_data { + struct device *dev; + struct mutex lock; + struct regulator *usb3v1; + struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; + bool use_second_irq; + u8 imr; + u8 isr; +}; + +static int twl4030_madc_conversion(struct twl4030_madc_request *req); + +static int twl4030_madc_read(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct twl4030_madc_data *madc = iio_priv(iio_dev); + struct twl4030_madc_request req; + int ret; + + req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1; + + req.channels = BIT(chan->channel); + req.active = false; + req.type = TWL4030_MADC_WAIT; + req.raw = !(mask == IIO_CHAN_INFO_PROCESSED); + req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW); + + ret = twl4030_madc_conversion(&req); + if (ret < 0) + return ret; + + *val = req.rbuf[chan->channel]; + + return IIO_VAL_INT; +} + +static const struct iio_info twl4030_madc_iio_info = { + .read_raw = &twl4030_madc_read, +}; + +#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \ + .type = _type, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + .datasheet_name = _name, \ + .indexed = 1, \ +} + +static const struct iio_chan_spec twl4030_madc_iio_channels[] = { + TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"), + TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"), + TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"), + TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"), + TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"), + TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"), + TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"), + TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"), + TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"), + TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"), + TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"), + TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"), + TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"), + TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"), + TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"), + TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"), +}; + +static struct twl4030_madc_data *twl4030_madc; + +struct twl4030_prescale_divider_ratios { + s16 numerator; + s16 denominator; +}; + +static const struct twl4030_prescale_divider_ratios +twl4030_divider_ratios[16] = { + {1, 1}, /* CHANNEL 0 No Prescaler */ + {1, 1}, /* CHANNEL 1 No Prescaler */ + {6, 10}, /* CHANNEL 2 */ + {6, 10}, /* CHANNEL 3 */ + {6, 10}, /* CHANNEL 4 */ + {6, 10}, /* CHANNEL 5 */ + {6, 10}, /* CHANNEL 6 */ + {6, 10}, /* CHANNEL 7 */ + {3, 14}, /* CHANNEL 8 */ + {1, 3}, /* CHANNEL 9 */ + {1, 1}, /* CHANNEL 10 No Prescaler */ + {15, 100}, /* CHANNEL 11 */ + {1, 4}, /* CHANNEL 12 */ + {1, 1}, /* CHANNEL 13 Reserved channels */ + {1, 1}, /* CHANNEL 14 Reseved channels */ + {5, 11}, /* CHANNEL 15 */ +}; + + +/* Conversion table from -3 to 55 degrees Celcius */ +static int twl4030_therm_tbl[] = { + 30800, 29500, 28300, 27100, + 26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, + 17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, + 12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280, + 8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710, + 6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920, + 4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670, + 3550 +}; + +/* + * Structure containing the registers + * of different conversion methods supported by MADC. + * Hardware or RT real time conversion request initiated by external host + * processor for RT Signal conversions. + * External host processors can also request for non RT conversions + * SW1 and SW2 software conversions also called asynchronous or GPC request. + */ +static +const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { + [TWL4030_MADC_RT] = { + .sel = TWL4030_MADC_RTSELECT_LSB, + .avg = TWL4030_MADC_RTAVERAGE_LSB, + .rbase = TWL4030_MADC_RTCH0_LSB, + }, + [TWL4030_MADC_SW1] = { + .sel = TWL4030_MADC_SW1SELECT_LSB, + .avg = TWL4030_MADC_SW1AVERAGE_LSB, + .rbase = TWL4030_MADC_GPCH0_LSB, + .ctrl = TWL4030_MADC_CTRL_SW1, + }, + [TWL4030_MADC_SW2] = { + .sel = TWL4030_MADC_SW2SELECT_LSB, + .avg = TWL4030_MADC_SW2AVERAGE_LSB, + .rbase = TWL4030_MADC_GPCH0_LSB, + .ctrl = TWL4030_MADC_CTRL_SW2, + }, +}; + +/** + * twl4030_madc_channel_raw_read() - Function to read a particular channel value + * @madc: pointer to struct twl4030_madc_data + * @reg: lsb of ADC Channel + * + * Return: 0 on success, an error code otherwise. + */ +static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) +{ + u16 val; + int ret; + /* + * For each ADC channel, we have MSB and LSB register pair. MSB address + * is always LSB address+1. reg parameter is the address of LSB register + */ + ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg); + if (ret) { + dev_err(madc->dev, "unable to read register 0x%X\n", reg); + return ret; + } + + return (int)(val >> 6); +} + +/* + * Return battery temperature in degrees Celsius + * Or < 0 on failure. + */ +static int twl4030battery_temperature(int raw_volt) +{ + u8 val; + int temp, curr, volt, res, ret; + + volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; + /* Getting and calculating the supply current in micro amperes */ + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, + REG_BCICTL2); + if (ret < 0) + return ret; + + curr = ((val & TWL4030_BCI_ITHSENS) + 1) * 10; + /* Getting and calculating the thermistor resistance in ohms */ + res = volt * 1000 / curr; + /* calculating temperature */ + for (temp = 58; temp >= 0; temp--) { + int actual = twl4030_therm_tbl[temp]; + if ((actual - res) >= 0) + break; + } + + return temp + 1; +} + +static int twl4030battery_current(int raw_volt) +{ + int ret; + u8 val; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, + TWL4030_BCI_BCICTL1); + if (ret) + return ret; + if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */ + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1; + else /* slope of 0.88 mV/mA */ + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; +} + +/* + * Function to read channel values + * @madc - pointer to twl4030_madc_data struct + * @reg_base - Base address of the first channel + * @Channels - 16 bit bitmap. If the bit is set, channel's value is read + * @buf - The channel values are stored here. if read fails error + * @raw - Return raw values without conversion + * value is stored + * Returns the number of successfully read channels. + */ +static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, + u8 reg_base, unsigned + long channels, int *buf, + bool raw) +{ + int count = 0; + int i; + u8 reg; + + for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { + reg = reg_base + (2 * i); + buf[i] = twl4030_madc_channel_raw_read(madc, reg); + if (buf[i] < 0) { + dev_err(madc->dev, "Unable to read register 0x%X\n", + reg); + return buf[i]; + } + if (raw) { + count++; + continue; + } + switch (i) { + case 10: + buf[i] = twl4030battery_current(buf[i]); + if (buf[i] < 0) { + dev_err(madc->dev, "err reading current\n"); + return buf[i]; + } else { + count++; + buf[i] = buf[i] - 750; + } + break; + case 1: + buf[i] = twl4030battery_temperature(buf[i]); + if (buf[i] < 0) { + dev_err(madc->dev, "err reading temperature\n"); + return buf[i]; + } else { + buf[i] -= 3; + count++; + } + break; + default: + count++; + /* Analog Input (V) = conv_result * step_size / R + * conv_result = decimal value of 10-bit conversion + * result + * step size = 1.5 / (2 ^ 10 -1) + * R = Prescaler ratio for input channels. + * Result given in mV hence multiplied by 1000. + */ + buf[i] = (buf[i] * 3 * 1000 * + twl4030_divider_ratios[i].denominator) + / (2 * 1023 * + twl4030_divider_ratios[i].numerator); + } + } + + return count; +} + +/* + * Disables irq. + * @madc - pointer to twl4030_madc_data struct + * @id - irq number to be disabled + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 + * corresponding to RT, SW1, SW2 conversion requests. + * Returns error if i2c read/write fails. + */ +static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id) +{ + u8 val; + int ret; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + return ret; + } + val |= (1 << id); + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); + if (ret) { + dev_err(madc->dev, + "unable to write imr register 0x%X\n", madc->imr); + return ret; + } + + return 0; +} + +static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) +{ + struct twl4030_madc_data *madc = _madc; + const struct twl4030_madc_conversion_method *method; + u8 isr_val, imr_val; + int i, ret; + struct twl4030_madc_request *r; + + mutex_lock(&madc->lock); + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr); + if (ret) { + dev_err(madc->dev, "unable to read isr register 0x%X\n", + madc->isr); + goto err_i2c; + } + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + goto err_i2c; + } + isr_val &= ~imr_val; + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + if (!(isr_val & (1 << i))) + continue; + ret = twl4030_madc_disable_irq(madc, i); + if (ret < 0) + dev_dbg(madc->dev, "Disable interrupt failed %d\n", i); + madc->requests[i].result_pending = true; + } + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + r = &madc->requests[i]; + /* No pending results for this method, move to next one */ + if (!r->result_pending) + continue; + method = &twl4030_conversion_methods[r->method]; + /* Read results */ + twl4030_madc_read_channels(madc, method->rbase, + r->channels, r->rbuf, r->raw); + /* Free request */ + r->result_pending = false; + r->active = false; + } + mutex_unlock(&madc->lock); + + return IRQ_HANDLED; + +err_i2c: + /* + * In case of error check whichever request is active + * and service the same. + */ + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + r = &madc->requests[i]; + if (!r->active) + continue; + method = &twl4030_conversion_methods[r->method]; + /* Read results */ + twl4030_madc_read_channels(madc, method->rbase, + r->channels, r->rbuf, r->raw); + /* Free request */ + r->result_pending = false; + r->active = false; + } + mutex_unlock(&madc->lock); + + return IRQ_HANDLED; +} + +/* + * Function which enables the madc conversion + * by writing to the control register. + * @madc - pointer to twl4030_madc_data struct + * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1 + * corresponding to RT SW1 or SW2 conversion methods. + * Returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, + int conv_method) +{ + const struct twl4030_madc_conversion_method *method; + int ret = 0; + + if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2) + return -ENOTSUPP; + + method = &twl4030_conversion_methods[conv_method]; + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START, + method->ctrl); + if (ret) { + dev_err(madc->dev, "unable to write ctrl register 0x%X\n", + method->ctrl); + return ret; + } + + return 0; +} + +/* + * Function that waits for conversion to be ready + * @madc - pointer to twl4030_madc_data struct + * @timeout_ms - timeout value in milliseconds + * @status_reg - ctrl register + * returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc, + unsigned int timeout_ms, + u8 status_reg) +{ + unsigned long timeout; + int ret; + + timeout = jiffies + msecs_to_jiffies(timeout_ms); + do { + u8 reg; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, ®, status_reg); + if (ret) { + dev_err(madc->dev, + "unable to read status register 0x%X\n", + status_reg); + return ret; + } + if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW)) + return 0; + usleep_range(500, 2000); + } while (!time_after(jiffies, timeout)); + dev_err(madc->dev, "conversion timeout!\n"); + + return -EAGAIN; +} + +/* + * An exported function which can be called from other kernel drivers. + * @req twl4030_madc_request structure + * req->rbuf will be filled with read values of channels based on the + * channel index. If a particular channel reading fails there will + * be a negative error value in the corresponding array element. + * returns 0 if succeeds else error value + */ +static int twl4030_madc_conversion(struct twl4030_madc_request *req) +{ + const struct twl4030_madc_conversion_method *method; + int ret; + + if (!req || !twl4030_madc) + return -EINVAL; + + mutex_lock(&twl4030_madc->lock); + if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) { + ret = -EINVAL; + goto out; + } + /* Do we have a conversion request ongoing */ + if (twl4030_madc->requests[req->method].active) { + ret = -EBUSY; + goto out; + } + method = &twl4030_conversion_methods[req->method]; + /* Select channels to be converted */ + ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write sel register 0x%X\n", method->sel); + goto out; + } + /* Select averaging for all channels if do_avg is set */ + if (req->do_avg) { + ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, + method->avg); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write avg register 0x%X\n", + method->avg); + goto out; + } + } + /* With RT method we should not be here anymore */ + if (req->method == TWL4030_MADC_RT) { + ret = -EINVAL; + goto out; + } + ret = twl4030_madc_start_conversion(twl4030_madc, req->method); + if (ret < 0) + goto out; + twl4030_madc->requests[req->method].active = true; + /* Wait until conversion is ready (ctrl register returns EOC) */ + ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl); + if (ret) { + twl4030_madc->requests[req->method].active = false; + goto out; + } + ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, + req->channels, req->rbuf, req->raw); + twl4030_madc->requests[req->method].active = false; + +out: + mutex_unlock(&twl4030_madc->lock); + + return ret; +} + +/** + * twl4030_madc_set_current_generator() - setup bias current + * + * @madc: pointer to twl4030_madc_data struct + * @chan: can be one of the two values: + * 0 - Enables bias current for main battery type reading + * 1 - Enables bias current for main battery temperature sensing + * @on: enable or disable chan. + * + * Function to enable or disable bias current for + * main battery type reading or temperature sensing + */ +static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, + int chan, int on) +{ + int ret; + int regmask; + u8 regval; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, + ®val, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", + TWL4030_BCI_BCICTL1); + return ret; + } + + regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; + if (on) + regval |= regmask; + else + regval &= ~regmask; + + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, + regval, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", + TWL4030_BCI_BCICTL1); + return ret; + } + + return 0; +} + +/* + * Function that sets MADC software power on bit to enable MADC + * @madc - pointer to twl4030_madc_data struct + * @on - Enable or disable MADC software power on bit. + * returns error if i2c read/write fails else 0 + */ +static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) +{ + u8 regval; + int ret; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, + ®val, TWL4030_MADC_CTRL1); + if (ret) { + dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", + TWL4030_MADC_CTRL1); + return ret; + } + if (on) + regval |= TWL4030_MADC_MADCON; + else + regval &= ~TWL4030_MADC_MADCON; + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1); + if (ret) { + dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n", + TWL4030_MADC_CTRL1); + return ret; + } + + return 0; +} + +/* + * Initialize MADC and request for threaded irq + */ +static int twl4030_madc_probe(struct platform_device *pdev) +{ + struct twl4030_madc_data *madc; + struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device_node *np = pdev->dev.of_node; + int irq, ret; + u8 regval; + struct iio_dev *iio_dev = NULL; + + if (!pdata && !np) { + dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n"); + return -EINVAL; + } + + iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc)); + if (!iio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + madc = iio_priv(iio_dev); + madc->dev = &pdev->dev; + + iio_dev->name = dev_name(&pdev->dev); + iio_dev->info = &twl4030_madc_iio_info; + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->channels = twl4030_madc_iio_channels; + iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels); + + /* + * Phoenix provides 2 interrupt lines. The first one is connected to + * the OMAP. The other one can be connected to the other processor such + * as modem. Hence two separate ISR and IMR registers. + */ + if (pdata) + madc->use_second_irq = (pdata->irq_line != 1); + else + madc->use_second_irq = of_property_read_bool(np, + "ti,system-uses-second-madc-irq"); + + madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 : + TWL4030_MADC_IMR1; + madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 : + TWL4030_MADC_ISR1; + + ret = twl4030_madc_set_power(madc, 1); + if (ret < 0) + return ret; + ret = twl4030_madc_set_current_generator(madc, 0, 1); + if (ret < 0) + goto err_current_generator; + + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, + ®val, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", + TWL4030_BCI_BCICTL1); + goto err_i2c; + } + regval |= TWL4030_BCI_MESBAT; + ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, + regval, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", + TWL4030_BCI_BCICTL1); + goto err_i2c; + } + + /* Check that MADC clock is on */ + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1); + if (ret) { + dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n", + TWL4030_REG_GPBR1); + goto err_i2c; + } + + /* If MADC clk is not on, turn it on */ + if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) { + dev_info(&pdev->dev, "clk disabled, enabling\n"); + regval |= TWL4030_GPBR1_MADC_HFCLK_EN; + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval, + TWL4030_REG_GPBR1); + if (ret) { + dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n", + TWL4030_REG_GPBR1); + goto err_i2c; + } + } + + platform_set_drvdata(pdev, iio_dev); + mutex_init(&madc->lock); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + twl4030_madc_threaded_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "twl4030_madc", madc); + if (ret) { + dev_err(&pdev->dev, "could not request irq\n"); + goto err_i2c; + } + twl4030_madc = madc; + + /* Configure MADC[3:6] */ + ret = twl_i2c_read_u8(TWL_MODULE_USB, ®val, + TWL4030_USB_CARKIT_ANA_CTRL); + if (ret) { + dev_err(&pdev->dev, "unable to read reg CARKIT_ANA_CTRL 0x%X\n", + TWL4030_USB_CARKIT_ANA_CTRL); + goto err_i2c; + } + regval |= TWL4030_USB_SEL_MADC_MCPC; + ret = twl_i2c_write_u8(TWL_MODULE_USB, regval, + TWL4030_USB_CARKIT_ANA_CTRL); + if (ret) { + dev_err(&pdev->dev, "unable to write reg CARKIT_ANA_CTRL 0x%X\n", + TWL4030_USB_CARKIT_ANA_CTRL); + goto err_i2c; + } + + /* Enable 3v1 bias regulator for MADC[3:6] */ + madc->usb3v1 = devm_regulator_get(madc->dev, "vusb3v1"); + if (IS_ERR(madc->usb3v1)) { + ret = -ENODEV; + goto err_i2c; + } + + ret = regulator_enable(madc->usb3v1); + if (ret) { + dev_err(madc->dev, "could not enable 3v1 bias regulator\n"); + goto err_i2c; + } + + ret = iio_device_register(iio_dev); + if (ret) { + dev_err(&pdev->dev, "could not register iio device\n"); + goto err_usb3v1; + } + + return 0; + +err_usb3v1: + regulator_disable(madc->usb3v1); +err_i2c: + twl4030_madc_set_current_generator(madc, 0, 0); +err_current_generator: + twl4030_madc_set_power(madc, 0); + return ret; +} + +static int twl4030_madc_remove(struct platform_device *pdev) +{ + struct iio_dev *iio_dev = platform_get_drvdata(pdev); + struct twl4030_madc_data *madc = iio_priv(iio_dev); + + iio_device_unregister(iio_dev); + + twl4030_madc_set_current_generator(madc, 0, 0); + twl4030_madc_set_power(madc, 0); + + regulator_disable(madc->usb3v1); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id twl_madc_of_match[] = { + { .compatible = "ti,twl4030-madc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_madc_of_match); +#endif + +static struct platform_driver twl4030_madc_driver = { + .probe = twl4030_madc_probe, + .remove = twl4030_madc_remove, + .driver = { + .name = "twl4030_madc", + .of_match_table = of_match_ptr(twl_madc_of_match), + }, +}; + +module_platform_driver(twl4030_madc_driver); + +MODULE_DESCRIPTION("TWL4030 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("J Keerthy"); +MODULE_ALIAS("platform:twl4030_madc"); diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c new file mode 100644 index 000000000..024bdc1ef --- /dev/null +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -0,0 +1,1027 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TWL6030 GPADC module driver + * + * Copyright (C) 2009-2013 Texas Instruments Inc. + * Nishant Kamat <nskamat@ti.com> + * Balaji T K <balajitk@ti.com> + * Graeme Gregory <gg@slimlogic.co.uk> + * Girish S Ghongdemath <girishsg@ti.com> + * Ambresh K <ambresh@ti.com> + * Oleksandr Kozaruk <oleksandr.kozaruk@ti.com + * + * Based on twl4030-madc.c + * Copyright (C) 2008 Nokia Corporation + * Mikko Ylinen <mikko.k.ylinen@nokia.com> + */ +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/mfd/twl.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define DRIVER_NAME "twl6030_gpadc" + +/* + * twl6030 per TRM has 17 channels, and twl6032 has 19 channels + * 2 test network channels are not used, + * 2 die temperature channels are not used either, as it is not + * defined how to convert ADC value to temperature + */ +#define TWL6030_GPADC_USED_CHANNELS 13 +#define TWL6030_GPADC_MAX_CHANNELS 15 +#define TWL6032_GPADC_USED_CHANNELS 15 +#define TWL6032_GPADC_MAX_CHANNELS 19 +#define TWL6030_GPADC_NUM_TRIM_REGS 16 + +#define TWL6030_GPADC_CTRL_P1 0x05 + +#define TWL6032_GPADC_GPSELECT_ISB 0x07 +#define TWL6032_GPADC_CTRL_P1 0x08 + +#define TWL6032_GPADC_GPCH0_LSB 0x0d +#define TWL6032_GPADC_GPCH0_MSB 0x0e + +#define TWL6030_GPADC_CTRL_P1_SP1 BIT(3) + +#define TWL6030_GPADC_GPCH0_LSB (0x29) + +#define TWL6030_GPADC_RT_SW1_EOC_MASK BIT(5) + +#define TWL6030_GPADC_TRIM1 0xCD + +#define TWL6030_REG_TOGGLE1 0x90 +#define TWL6030_GPADCS BIT(1) +#define TWL6030_GPADCR BIT(0) + +#define USB_VBUS_CTRL_SET 0x04 +#define USB_ID_CTRL_SET 0x06 + +#define TWL6030_MISC1 0xE4 +#define VBUS_MEAS 0x01 +#define ID_MEAS 0x01 + +#define VAC_MEAS 0x04 +#define VBAT_MEAS 0x02 +#define BB_MEAS 0x01 + + +/** + * struct twl6030_chnl_calib - channel calibration + * @gain: slope coefficient for ideal curve + * @gain_error: gain error + * @offset_error: offset of the real curve + */ +struct twl6030_chnl_calib { + s32 gain; + s32 gain_error; + s32 offset_error; +}; + +/** + * struct twl6030_ideal_code - GPADC calibration parameters + * GPADC is calibrated in two points: close to the beginning and + * to the and of the measurable input range + * + * @channel: channel number + * @code1: ideal code for the input at the beginning + * @code2: ideal code for at the end of the range + * @volt1: voltage input at the beginning(low voltage) + * @volt2: voltage input at the end(high voltage) + */ +struct twl6030_ideal_code { + int channel; + u16 code1; + u16 code2; + u16 volt1; + u16 volt2; +}; + +struct twl6030_gpadc_data; + +/** + * struct twl6030_gpadc_platform_data - platform specific data + * @nchannels: number of GPADC channels + * @iio_channels: iio channels + * @ideal: pointer to calibration parameters + * @start_conversion: pointer to ADC start conversion function + * @channel_to_reg: pointer to ADC function to convert channel to + * register address for reading conversion result + * @calibrate: pointer to calibration function + */ +struct twl6030_gpadc_platform_data { + const int nchannels; + const struct iio_chan_spec *iio_channels; + const struct twl6030_ideal_code *ideal; + int (*start_conversion)(int channel); + u8 (*channel_to_reg)(int channel); + int (*calibrate)(struct twl6030_gpadc_data *gpadc); +}; + +/** + * struct twl6030_gpadc_data - GPADC data + * @dev: device pointer + * @lock: mutual exclusion lock for the structure + * @irq_complete: completion to signal end of conversion + * @twl6030_cal_tbl: pointer to calibration data for each + * channel with gain error and offset + * @pdata: pointer to device specific data + */ +struct twl6030_gpadc_data { + struct device *dev; + struct mutex lock; + struct completion irq_complete; + struct twl6030_chnl_calib *twl6030_cal_tbl; + const struct twl6030_gpadc_platform_data *pdata; +}; + +/* + * channels 11, 12, 13, 15 and 16 have no calibration data + * calibration offset is same for channels 1, 3, 4, 5 + * + * The data is taken from GPADC_TRIM registers description. + * GPADC_TRIM registers keep difference between the code measured + * at volt1 and volt2 input voltages and corresponding code1 and code2 + */ +static const struct twl6030_ideal_code + twl6030_ideal[TWL6030_GPADC_USED_CHANNELS] = { + [0] = { /* ch 0, external, battery type, resistor value */ + .channel = 0, + .code1 = 116, + .code2 = 745, + .volt1 = 141, + .volt2 = 910, + }, + [1] = { /* ch 1, external, battery temperature, NTC resistor value */ + .channel = 1, + .code1 = 82, + .code2 = 900, + .volt1 = 100, + .volt2 = 1100, + }, + [2] = { /* ch 2, external, audio accessory/general purpose */ + .channel = 2, + .code1 = 55, + .code2 = 818, + .volt1 = 101, + .volt2 = 1499, + }, + [3] = { /* ch 3, external, general purpose */ + .channel = 3, + .code1 = 82, + .code2 = 900, + .volt1 = 100, + .volt2 = 1100, + }, + [4] = { /* ch 4, external, temperature measurement/general purpose */ + .channel = 4, + .code1 = 82, + .code2 = 900, + .volt1 = 100, + .volt2 = 1100, + }, + [5] = { /* ch 5, external, general purpose */ + .channel = 5, + .code1 = 82, + .code2 = 900, + .volt1 = 100, + .volt2 = 1100, + }, + [6] = { /* ch 6, external, general purpose */ + .channel = 6, + .code1 = 82, + .code2 = 900, + .volt1 = 100, + .volt2 = 1100, + }, + [7] = { /* ch 7, internal, main battery */ + .channel = 7, + .code1 = 614, + .code2 = 941, + .volt1 = 3001, + .volt2 = 4599, + }, + [8] = { /* ch 8, internal, backup battery */ + .channel = 8, + .code1 = 82, + .code2 = 688, + .volt1 = 501, + .volt2 = 4203, + }, + [9] = { /* ch 9, internal, external charger input */ + .channel = 9, + .code1 = 182, + .code2 = 818, + .volt1 = 2001, + .volt2 = 8996, + }, + [10] = { /* ch 10, internal, VBUS */ + .channel = 10, + .code1 = 149, + .code2 = 818, + .volt1 = 1001, + .volt2 = 5497, + }, + [11] = { /* ch 11, internal, VBUS charging current */ + .channel = 11, + }, + /* ch 12, internal, Die temperature */ + /* ch 13, internal, Die temperature */ + [12] = { /* ch 14, internal, USB ID line */ + .channel = 14, + .code1 = 48, + .code2 = 714, + .volt1 = 323, + .volt2 = 4800, + }, +}; + +static const struct twl6030_ideal_code + twl6032_ideal[TWL6032_GPADC_USED_CHANNELS] = { + [0] = { /* ch 0, external, battery type, resistor value */ + .channel = 0, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [1] = { /* ch 1, external, battery temperature, NTC resistor value */ + .channel = 1, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [2] = { /* ch 2, external, audio accessory/general purpose */ + .channel = 2, + .code1 = 1441, + .code2 = 3276, + .volt1 = 660, + .volt2 = 1500, + }, + [3] = { /* ch 3, external, temperature with external diode/general + purpose */ + .channel = 3, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [4] = { /* ch 4, external, temperature measurement/general purpose */ + .channel = 4, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [5] = { /* ch 5, external, general purpose */ + .channel = 5, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [6] = { /* ch 6, external, general purpose */ + .channel = 6, + .code1 = 1441, + .code2 = 3276, + .volt1 = 440, + .volt2 = 1000, + }, + [7] = { /* ch7, internal, system supply */ + .channel = 7, + .code1 = 1441, + .code2 = 3276, + .volt1 = 2200, + .volt2 = 5000, + }, + [8] = { /* ch8, internal, backup battery */ + .channel = 8, + .code1 = 1441, + .code2 = 3276, + .volt1 = 2200, + .volt2 = 5000, + }, + [9] = { /* ch 9, internal, external charger input */ + .channel = 9, + .code1 = 1441, + .code2 = 3276, + .volt1 = 3960, + .volt2 = 9000, + }, + [10] = { /* ch10, internal, VBUS */ + .channel = 10, + .code1 = 150, + .code2 = 751, + .volt1 = 1000, + .volt2 = 5000, + }, + [11] = { /* ch 11, internal, VBUS DC-DC output current */ + .channel = 11, + .code1 = 1441, + .code2 = 3276, + .volt1 = 660, + .volt2 = 1500, + }, + /* ch 12, internal, Die temperature */ + /* ch 13, internal, Die temperature */ + [12] = { /* ch 14, internal, USB ID line */ + .channel = 14, + .code1 = 1441, + .code2 = 3276, + .volt1 = 2420, + .volt2 = 5500, + }, + /* ch 15, internal, test network */ + /* ch 16, internal, test network */ + [13] = { /* ch 17, internal, battery charging current */ + .channel = 17, + }, + [14] = { /* ch 18, internal, battery voltage */ + .channel = 18, + .code1 = 1441, + .code2 = 3276, + .volt1 = 2200, + .volt2 = 5000, + }, +}; + +static inline int twl6030_gpadc_write(u8 reg, u8 val) +{ + return twl_i2c_write_u8(TWL6030_MODULE_GPADC, val, reg); +} + +static inline int twl6030_gpadc_read(u8 reg, u8 *val) +{ + + return twl_i2c_read(TWL6030_MODULE_GPADC, val, reg, 2); +} + +static int twl6030_gpadc_enable_irq(u8 mask) +{ + int ret; + + ret = twl6030_interrupt_unmask(mask, REG_INT_MSK_LINE_B); + if (ret < 0) + return ret; + + ret = twl6030_interrupt_unmask(mask, REG_INT_MSK_STS_B); + + return ret; +} + +static void twl6030_gpadc_disable_irq(u8 mask) +{ + twl6030_interrupt_mask(mask, REG_INT_MSK_LINE_B); + twl6030_interrupt_mask(mask, REG_INT_MSK_STS_B); +} + +static irqreturn_t twl6030_gpadc_irq_handler(int irq, void *indio_dev) +{ + struct twl6030_gpadc_data *gpadc = iio_priv(indio_dev); + + complete(&gpadc->irq_complete); + + return IRQ_HANDLED; +} + +static int twl6030_start_conversion(int channel) +{ + return twl6030_gpadc_write(TWL6030_GPADC_CTRL_P1, + TWL6030_GPADC_CTRL_P1_SP1); +} + +static int twl6032_start_conversion(int channel) +{ + int ret; + + ret = twl6030_gpadc_write(TWL6032_GPADC_GPSELECT_ISB, channel); + if (ret) + return ret; + + return twl6030_gpadc_write(TWL6032_GPADC_CTRL_P1, + TWL6030_GPADC_CTRL_P1_SP1); +} + +static u8 twl6030_channel_to_reg(int channel) +{ + return TWL6030_GPADC_GPCH0_LSB + 2 * channel; +} + +static u8 twl6032_channel_to_reg(int channel) +{ + /* + * for any prior chosen channel, when the conversion is ready + * the result is avalable in GPCH0_LSB, GPCH0_MSB. + */ + + return TWL6032_GPADC_GPCH0_LSB; +} + +static int twl6030_gpadc_lookup(const struct twl6030_ideal_code *ideal, + int channel, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (ideal[i].channel == channel) + break; + + return i; +} + +static int twl6030_channel_calibrated(const struct twl6030_gpadc_platform_data + *pdata, int channel) +{ + const struct twl6030_ideal_code *ideal = pdata->ideal; + int i; + + i = twl6030_gpadc_lookup(ideal, channel, pdata->nchannels); + /* not calibrated channels have 0 in all structure members */ + return pdata->ideal[i].code2; +} + +static int twl6030_gpadc_make_correction(struct twl6030_gpadc_data *gpadc, + int channel, int raw_code) +{ + const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal; + int corrected_code; + int i; + + i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels); + corrected_code = ((raw_code * 1000) - + gpadc->twl6030_cal_tbl[i].offset_error) / + gpadc->twl6030_cal_tbl[i].gain_error; + + return corrected_code; +} + +static int twl6030_gpadc_get_raw(struct twl6030_gpadc_data *gpadc, + int channel, int *res) +{ + u8 reg = gpadc->pdata->channel_to_reg(channel); + __le16 val; + int raw_code; + int ret; + + ret = twl6030_gpadc_read(reg, (u8 *)&val); + if (ret) { + dev_dbg(gpadc->dev, "unable to read register 0x%X\n", reg); + return ret; + } + + raw_code = le16_to_cpu(val); + dev_dbg(gpadc->dev, "GPADC raw code: %d", raw_code); + + if (twl6030_channel_calibrated(gpadc->pdata, channel)) + *res = twl6030_gpadc_make_correction(gpadc, channel, raw_code); + else + *res = raw_code; + + return ret; +} + +static int twl6030_gpadc_get_processed(struct twl6030_gpadc_data *gpadc, + int channel, int *val) +{ + const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal; + int corrected_code; + int channel_value; + int i; + int ret; + + ret = twl6030_gpadc_get_raw(gpadc, channel, &corrected_code); + if (ret) + return ret; + + i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels); + channel_value = corrected_code * + gpadc->twl6030_cal_tbl[i].gain; + + /* Shift back into mV range */ + channel_value /= 1000; + + dev_dbg(gpadc->dev, "GPADC corrected code: %d", corrected_code); + dev_dbg(gpadc->dev, "GPADC value: %d", channel_value); + + *val = channel_value; + + return ret; +} + +static int twl6030_gpadc_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct twl6030_gpadc_data *gpadc = iio_priv(indio_dev); + int ret; + long timeout; + + mutex_lock(&gpadc->lock); + + ret = gpadc->pdata->start_conversion(chan->channel); + if (ret) { + dev_err(gpadc->dev, "failed to start conversion\n"); + goto err; + } + /* wait for conversion to complete */ + timeout = wait_for_completion_interruptible_timeout( + &gpadc->irq_complete, msecs_to_jiffies(5000)); + if (timeout == 0) { + ret = -ETIMEDOUT; + goto err; + } else if (timeout < 0) { + ret = -EINTR; + goto err; + } + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = twl6030_gpadc_get_raw(gpadc, chan->channel, val); + ret = ret ? -EIO : IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_PROCESSED: + ret = twl6030_gpadc_get_processed(gpadc, chan->channel, val); + ret = ret ? -EIO : IIO_VAL_INT; + break; + + default: + break; + } +err: + mutex_unlock(&gpadc->lock); + + return ret; +} + +/* + * The GPADC channels are calibrated using a two point calibration method. + * The channels measured with two known values: volt1 and volt2, and + * ideal corresponding output codes are known: code1, code2. + * The difference(d1, d2) between ideal and measured codes stored in trim + * registers. + * The goal is to find offset and gain of the real curve for each calibrated + * channel. + * gain: k = 1 + ((d2 - d1) / (x2 - x1)) + * offset: b = d1 + (k - 1) * x1 + */ +static void twl6030_calibrate_channel(struct twl6030_gpadc_data *gpadc, + int channel, int d1, int d2) +{ + int b, k, gain, x1, x2, i; + const struct twl6030_ideal_code *ideal = gpadc->pdata->ideal; + + i = twl6030_gpadc_lookup(ideal, channel, gpadc->pdata->nchannels); + + /* Gain */ + gain = ((ideal[i].volt2 - ideal[i].volt1) * 1000) / + (ideal[i].code2 - ideal[i].code1); + + x1 = ideal[i].code1; + x2 = ideal[i].code2; + + /* k - real curve gain */ + k = 1000 + (((d2 - d1) * 1000) / (x2 - x1)); + + /* b - offset of the real curve gain */ + b = (d1 * 1000) - (k - 1000) * x1; + + gpadc->twl6030_cal_tbl[i].gain = gain; + gpadc->twl6030_cal_tbl[i].gain_error = k; + gpadc->twl6030_cal_tbl[i].offset_error = b; + + dev_dbg(gpadc->dev, "GPADC d1 for Chn: %d = %d\n", channel, d1); + dev_dbg(gpadc->dev, "GPADC d2 for Chn: %d = %d\n", channel, d2); + dev_dbg(gpadc->dev, "GPADC x1 for Chn: %d = %d\n", channel, x1); + dev_dbg(gpadc->dev, "GPADC x2 for Chn: %d = %d\n", channel, x2); + dev_dbg(gpadc->dev, "GPADC Gain for Chn: %d = %d\n", channel, gain); + dev_dbg(gpadc->dev, "GPADC k for Chn: %d = %d\n", channel, k); + dev_dbg(gpadc->dev, "GPADC b for Chn: %d = %d\n", channel, b); +} + +static inline int twl6030_gpadc_get_trim_offset(s8 d) +{ + /* + * XXX NOTE! + * bit 0 - sign, bit 7 - reserved, 6..1 - trim value + * though, the documentation states that trim value + * is absolute value, the correct conversion results are + * obtained if the value is interpreted as 2's complement. + */ + __u32 temp = ((d & 0x7f) >> 1) | ((d & 1) << 6); + + return sign_extend32(temp, 6); +} + +static int twl6030_calibration(struct twl6030_gpadc_data *gpadc) +{ + int ret; + int chn; + u8 trim_regs[TWL6030_GPADC_NUM_TRIM_REGS]; + s8 d1, d2; + + /* + * for calibration two measurements have been performed at + * factory, for some channels, during the production test and + * have been stored in registers. This two stored values are + * used to correct the measurements. The values represent + * offsets for the given input from the output on ideal curve. + */ + ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs, + TWL6030_GPADC_TRIM1, TWL6030_GPADC_NUM_TRIM_REGS); + if (ret < 0) { + dev_err(gpadc->dev, "calibration failed\n"); + return ret; + } + + for (chn = 0; chn < TWL6030_GPADC_MAX_CHANNELS; chn++) { + + switch (chn) { + case 0: + d1 = trim_regs[0]; + d2 = trim_regs[1]; + break; + case 1: + case 3: + case 4: + case 5: + case 6: + d1 = trim_regs[4]; + d2 = trim_regs[5]; + break; + case 2: + d1 = trim_regs[12]; + d2 = trim_regs[13]; + break; + case 7: + d1 = trim_regs[6]; + d2 = trim_regs[7]; + break; + case 8: + d1 = trim_regs[2]; + d2 = trim_regs[3]; + break; + case 9: + d1 = trim_regs[8]; + d2 = trim_regs[9]; + break; + case 10: + d1 = trim_regs[10]; + d2 = trim_regs[11]; + break; + case 14: + d1 = trim_regs[14]; + d2 = trim_regs[15]; + break; + default: + continue; + } + + d1 = twl6030_gpadc_get_trim_offset(d1); + d2 = twl6030_gpadc_get_trim_offset(d2); + + twl6030_calibrate_channel(gpadc, chn, d1, d2); + } + + return 0; +} + +static int twl6032_get_trim_value(u8 *trim_regs, unsigned int reg0, + unsigned int reg1, unsigned int mask0, unsigned int mask1, + unsigned int shift0) +{ + int val; + + val = (trim_regs[reg0] & mask0) << shift0; + val |= (trim_regs[reg1] & mask1) >> 1; + if (trim_regs[reg1] & 0x01) + val = -val; + + return val; +} + +static int twl6032_calibration(struct twl6030_gpadc_data *gpadc) +{ + int chn, d1 = 0, d2 = 0, temp; + u8 trim_regs[TWL6030_GPADC_NUM_TRIM_REGS]; + int ret; + + ret = twl_i2c_read(TWL6030_MODULE_ID2, trim_regs, + TWL6030_GPADC_TRIM1, TWL6030_GPADC_NUM_TRIM_REGS); + if (ret < 0) { + dev_err(gpadc->dev, "calibration failed\n"); + return ret; + } + + /* + * Loop to calculate the value needed for returning voltages from + * GPADC not values. + * + * gain is calculated to 3 decimal places fixed point. + */ + for (chn = 0; chn < TWL6032_GPADC_MAX_CHANNELS; chn++) { + + switch (chn) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 11: + case 14: + d1 = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, + 0x06, 2); + d2 = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f, + 0x06, 2); + break; + case 8: + temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, + 0x06, 2); + d1 = temp + twl6032_get_trim_value(trim_regs, 7, 6, + 0x18, 0x1E, 1); + + temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3F, + 0x06, 2); + d2 = temp + twl6032_get_trim_value(trim_regs, 9, 7, + 0x1F, 0x06, 2); + break; + case 9: + temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, + 0x06, 2); + d1 = temp + twl6032_get_trim_value(trim_regs, 13, 11, + 0x18, 0x1E, 1); + + temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f, + 0x06, 2); + d2 = temp + twl6032_get_trim_value(trim_regs, 15, 13, + 0x1F, 0x06, 1); + break; + case 10: + d1 = twl6032_get_trim_value(trim_regs, 10, 8, 0x0f, + 0x0E, 3); + d2 = twl6032_get_trim_value(trim_regs, 14, 12, 0x0f, + 0x0E, 3); + break; + case 7: + case 18: + temp = twl6032_get_trim_value(trim_regs, 2, 0, 0x1f, + 0x06, 2); + + d1 = (trim_regs[4] & 0x7E) >> 1; + if (trim_regs[4] & 0x01) + d1 = -d1; + d1 += temp; + + temp = twl6032_get_trim_value(trim_regs, 3, 1, 0x3f, + 0x06, 2); + + d2 = (trim_regs[5] & 0xFE) >> 1; + if (trim_regs[5] & 0x01) + d2 = -d2; + + d2 += temp; + break; + default: + /* No data for other channels */ + continue; + } + + twl6030_calibrate_channel(gpadc, chn, d1, d2); + } + + return 0; +} + +#define TWL6030_GPADC_CHAN(chn, _type, chan_info) { \ + .type = _type, \ + .channel = chn, \ + .info_mask_separate = BIT(chan_info), \ + .indexed = 1, \ +} + +static const struct iio_chan_spec twl6030_gpadc_iio_channels[] = { + TWL6030_GPADC_CHAN(0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(1, IIO_TEMP, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(3, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(4, IIO_TEMP, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(11, IIO_VOLTAGE, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), +}; + +static const struct iio_chan_spec twl6032_gpadc_iio_channels[] = { + TWL6030_GPADC_CHAN(0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(1, IIO_TEMP, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(3, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(4, IIO_TEMP, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(11, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + TWL6030_GPADC_CHAN(17, IIO_VOLTAGE, IIO_CHAN_INFO_RAW), + TWL6030_GPADC_CHAN(18, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), +}; + +static const struct iio_info twl6030_gpadc_iio_info = { + .read_raw = &twl6030_gpadc_read_raw, +}; + +static const struct twl6030_gpadc_platform_data twl6030_pdata = { + .iio_channels = twl6030_gpadc_iio_channels, + .nchannels = TWL6030_GPADC_USED_CHANNELS, + .ideal = twl6030_ideal, + .start_conversion = twl6030_start_conversion, + .channel_to_reg = twl6030_channel_to_reg, + .calibrate = twl6030_calibration, +}; + +static const struct twl6030_gpadc_platform_data twl6032_pdata = { + .iio_channels = twl6032_gpadc_iio_channels, + .nchannels = TWL6032_GPADC_USED_CHANNELS, + .ideal = twl6032_ideal, + .start_conversion = twl6032_start_conversion, + .channel_to_reg = twl6032_channel_to_reg, + .calibrate = twl6032_calibration, +}; + +static const struct of_device_id of_twl6030_match_tbl[] = { + { + .compatible = "ti,twl6030-gpadc", + .data = &twl6030_pdata, + }, + { + .compatible = "ti,twl6032-gpadc", + .data = &twl6032_pdata, + }, + { /* end */ } +}; +MODULE_DEVICE_TABLE(of, of_twl6030_match_tbl); + +static int twl6030_gpadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct twl6030_gpadc_data *gpadc; + const struct twl6030_gpadc_platform_data *pdata; + const struct of_device_id *match; + struct iio_dev *indio_dev; + int irq; + int ret; + + match = of_match_device(of_twl6030_match_tbl, dev); + if (!match) + return -EINVAL; + + pdata = match->data; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc)); + if (!indio_dev) + return -ENOMEM; + + gpadc = iio_priv(indio_dev); + + gpadc->twl6030_cal_tbl = devm_kcalloc(dev, + pdata->nchannels, + sizeof(*gpadc->twl6030_cal_tbl), + GFP_KERNEL); + if (!gpadc->twl6030_cal_tbl) + return -ENOMEM; + + gpadc->dev = dev; + gpadc->pdata = pdata; + + platform_set_drvdata(pdev, indio_dev); + mutex_init(&gpadc->lock); + init_completion(&gpadc->irq_complete); + + ret = pdata->calibrate(gpadc); + if (ret < 0) { + dev_err(&pdev->dev, "failed to read calibration registers\n"); + return ret; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + twl6030_gpadc_irq_handler, + IRQF_ONESHOT, "twl6030_gpadc", indio_dev); + if (ret) + return ret; + + ret = twl6030_gpadc_enable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable GPADC interrupt\n"); + return ret; + } + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCS, + TWL6030_REG_TOGGLE1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable GPADC module\n"); + return ret; + } + + ret = twl_i2c_write_u8(TWL_MODULE_USB, VBUS_MEAS, USB_VBUS_CTRL_SET); + if (ret < 0) { + dev_err(dev, "failed to wire up inputs\n"); + return ret; + } + + ret = twl_i2c_write_u8(TWL_MODULE_USB, ID_MEAS, USB_ID_CTRL_SET); + if (ret < 0) { + dev_err(dev, "failed to wire up inputs\n"); + return ret; + } + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID0, + VBAT_MEAS | BB_MEAS | VAC_MEAS, + TWL6030_MISC1); + if (ret < 0) { + dev_err(dev, "failed to wire up inputs\n"); + return ret; + } + + indio_dev->name = DRIVER_NAME; + indio_dev->info = &twl6030_gpadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = pdata->iio_channels; + indio_dev->num_channels = pdata->nchannels; + + return iio_device_register(indio_dev); +} + +static int twl6030_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + twl6030_gpadc_disable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK); + iio_device_unregister(indio_dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int twl6030_gpadc_suspend(struct device *pdev) +{ + int ret; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCR, + TWL6030_REG_TOGGLE1); + if (ret) + dev_err(pdev, "error resetting GPADC (%d)!\n", ret); + + return 0; +}; + +static int twl6030_gpadc_resume(struct device *pdev) +{ + int ret; + + ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCS, + TWL6030_REG_TOGGLE1); + if (ret) + dev_err(pdev, "error setting GPADC (%d)!\n", ret); + + return 0; +}; +#endif + +static SIMPLE_DEV_PM_OPS(twl6030_gpadc_pm_ops, twl6030_gpadc_suspend, + twl6030_gpadc_resume); + +static struct platform_driver twl6030_gpadc_driver = { + .probe = twl6030_gpadc_probe, + .remove = twl6030_gpadc_remove, + .driver = { + .name = DRIVER_NAME, + .pm = &twl6030_gpadc_pm_ops, + .of_match_table = of_twl6030_match_tbl, + }, +}; + +module_platform_driver(twl6030_gpadc_driver); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("Balaji T K <balajitk@ti.com>"); +MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>"); +MODULE_AUTHOR("Oleksandr Kozaruk <oleksandr.kozaruk@ti.com"); +MODULE_DESCRIPTION("twl6030 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c new file mode 100644 index 000000000..fd57fc43e --- /dev/null +++ b/drivers/iio/adc/vf610_adc.c @@ -0,0 +1,973 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Freescale Vybrid vf610 ADC driver + * + * Copyright 2013 Freescale Semiconductor, Inc. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/regulator/consumer.h> +#include <linux/of_platform.h> +#include <linux/err.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +/* This will be the driver name the kernel reports */ +#define DRIVER_NAME "vf610-adc" + +/* Vybrid/IMX ADC registers */ +#define VF610_REG_ADC_HC0 0x00 +#define VF610_REG_ADC_HC1 0x04 +#define VF610_REG_ADC_HS 0x08 +#define VF610_REG_ADC_R0 0x0c +#define VF610_REG_ADC_R1 0x10 +#define VF610_REG_ADC_CFG 0x14 +#define VF610_REG_ADC_GC 0x18 +#define VF610_REG_ADC_GS 0x1c +#define VF610_REG_ADC_CV 0x20 +#define VF610_REG_ADC_OFS 0x24 +#define VF610_REG_ADC_CAL 0x28 +#define VF610_REG_ADC_PCTL 0x30 + +/* Configuration register field define */ +#define VF610_ADC_MODE_BIT8 0x00 +#define VF610_ADC_MODE_BIT10 0x04 +#define VF610_ADC_MODE_BIT12 0x08 +#define VF610_ADC_MODE_MASK 0x0c +#define VF610_ADC_BUSCLK2_SEL 0x01 +#define VF610_ADC_ALTCLK_SEL 0x02 +#define VF610_ADC_ADACK_SEL 0x03 +#define VF610_ADC_ADCCLK_MASK 0x03 +#define VF610_ADC_CLK_DIV2 0x20 +#define VF610_ADC_CLK_DIV4 0x40 +#define VF610_ADC_CLK_DIV8 0x60 +#define VF610_ADC_CLK_MASK 0x60 +#define VF610_ADC_ADLSMP_LONG 0x10 +#define VF610_ADC_ADSTS_SHORT 0x100 +#define VF610_ADC_ADSTS_NORMAL 0x200 +#define VF610_ADC_ADSTS_LONG 0x300 +#define VF610_ADC_ADSTS_MASK 0x300 +#define VF610_ADC_ADLPC_EN 0x80 +#define VF610_ADC_ADHSC_EN 0x400 +#define VF610_ADC_REFSEL_VALT 0x800 +#define VF610_ADC_REFSEL_VBG 0x1000 +#define VF610_ADC_ADTRG_HARD 0x2000 +#define VF610_ADC_AVGS_8 0x4000 +#define VF610_ADC_AVGS_16 0x8000 +#define VF610_ADC_AVGS_32 0xC000 +#define VF610_ADC_AVGS_MASK 0xC000 +#define VF610_ADC_OVWREN 0x10000 + +/* General control register field define */ +#define VF610_ADC_ADACKEN 0x1 +#define VF610_ADC_DMAEN 0x2 +#define VF610_ADC_ACREN 0x4 +#define VF610_ADC_ACFGT 0x8 +#define VF610_ADC_ACFE 0x10 +#define VF610_ADC_AVGEN 0x20 +#define VF610_ADC_ADCON 0x40 +#define VF610_ADC_CAL 0x80 + +/* Other field define */ +#define VF610_ADC_ADCHC(x) ((x) & 0x1F) +#define VF610_ADC_AIEN (0x1 << 7) +#define VF610_ADC_CONV_DISABLE 0x1F +#define VF610_ADC_HS_COCO0 0x1 +#define VF610_ADC_CALF 0x2 +#define VF610_ADC_TIMEOUT msecs_to_jiffies(100) + +#define DEFAULT_SAMPLE_TIME 1000 + +/* V at 25°C of 696 mV */ +#define VF610_VTEMP25_3V0 950 +/* V at 25°C of 699 mV */ +#define VF610_VTEMP25_3V3 867 +/* Typical sensor slope coefficient at all temperatures */ +#define VF610_TEMP_SLOPE_COEFF 1840 + +enum clk_sel { + VF610_ADCIOC_BUSCLK_SET, + VF610_ADCIOC_ALTCLK_SET, + VF610_ADCIOC_ADACK_SET, +}; + +enum vol_ref { + VF610_ADCIOC_VR_VREF_SET, + VF610_ADCIOC_VR_VALT_SET, + VF610_ADCIOC_VR_VBG_SET, +}; + +enum average_sel { + VF610_ADC_SAMPLE_1, + VF610_ADC_SAMPLE_4, + VF610_ADC_SAMPLE_8, + VF610_ADC_SAMPLE_16, + VF610_ADC_SAMPLE_32, +}; + +enum conversion_mode_sel { + VF610_ADC_CONV_NORMAL, + VF610_ADC_CONV_HIGH_SPEED, + VF610_ADC_CONV_LOW_POWER, +}; + +enum lst_adder_sel { + VF610_ADCK_CYCLES_3, + VF610_ADCK_CYCLES_5, + VF610_ADCK_CYCLES_7, + VF610_ADCK_CYCLES_9, + VF610_ADCK_CYCLES_13, + VF610_ADCK_CYCLES_17, + VF610_ADCK_CYCLES_21, + VF610_ADCK_CYCLES_25, +}; + +struct vf610_adc_feature { + enum clk_sel clk_sel; + enum vol_ref vol_ref; + enum conversion_mode_sel conv_mode; + + int clk_div; + int sample_rate; + int res_mode; + u32 lst_adder_index; + u32 default_sample_time; + + bool calibration; + bool ovwren; +}; + +struct vf610_adc { + struct device *dev; + void __iomem *regs; + struct clk *clk; + + u32 vref_uv; + u32 value; + struct regulator *vref; + + u32 max_adck_rate[3]; + struct vf610_adc_feature adc_feature; + + u32 sample_freq_avail[5]; + + struct completion completion; + /* Ensure the timestamp is naturally aligned */ + struct { + u16 chan; + s64 timestamp __aligned(8); + } scan; +}; + +static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 }; +static const u32 vf610_lst_adder[] = { 3, 5, 7, 9, 13, 17, 21, 25 }; + +static inline void vf610_adc_calculate_rates(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &info->adc_feature; + unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk); + u32 adck_period, lst_addr_min; + int divisor, i; + + adck_rate = info->max_adck_rate[adc_feature->conv_mode]; + + if (adck_rate) { + /* calculate clk divider which is within specification */ + divisor = ipg_rate / adck_rate; + adc_feature->clk_div = 1 << fls(divisor + 1); + } else { + /* fall-back value using a safe divisor */ + adc_feature->clk_div = 8; + } + + adck_rate = ipg_rate / adc_feature->clk_div; + + /* + * Determine the long sample time adder value to be used based + * on the default minimum sample time provided. + */ + adck_period = NSEC_PER_SEC / adck_rate; + lst_addr_min = adc_feature->default_sample_time / adck_period; + for (i = 0; i < ARRAY_SIZE(vf610_lst_adder); i++) { + if (vf610_lst_adder[i] > lst_addr_min) { + adc_feature->lst_adder_index = i; + break; + } + } + + /* + * Calculate ADC sample frequencies + * Sample time unit is ADCK cycles. ADCK clk source is ipg clock, + * which is the same as bus clock. + * + * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder) + * SFCAdder: fixed to 6 ADCK cycles + * AverageNum: 1, 4, 8, 16, 32 samples for hardware average. + * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode + * LSTAdder(Long Sample Time): 3, 5, 7, 9, 13, 17, 21, 25 ADCK cycles + */ + for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++) + info->sample_freq_avail[i] = + adck_rate / (6 + vf610_hw_avgs[i] * + (25 + vf610_lst_adder[adc_feature->lst_adder_index])); +} + +static inline void vf610_adc_cfg_init(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &info->adc_feature; + + /* set default Configuration for ADC controller */ + adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET; + adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET; + + adc_feature->calibration = true; + adc_feature->ovwren = true; + + adc_feature->res_mode = 12; + adc_feature->sample_rate = 1; + + adc_feature->conv_mode = VF610_ADC_CONV_LOW_POWER; + + vf610_adc_calculate_rates(info); +} + +static void vf610_adc_cfg_post_set(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &info->adc_feature; + int cfg_data = 0; + int gc_data = 0; + + switch (adc_feature->clk_sel) { + case VF610_ADCIOC_ALTCLK_SET: + cfg_data |= VF610_ADC_ALTCLK_SEL; + break; + case VF610_ADCIOC_ADACK_SET: + cfg_data |= VF610_ADC_ADACK_SEL; + break; + default: + break; + } + + /* low power set for calibration */ + cfg_data |= VF610_ADC_ADLPC_EN; + + /* enable high speed for calibration */ + cfg_data |= VF610_ADC_ADHSC_EN; + + /* voltage reference */ + switch (adc_feature->vol_ref) { + case VF610_ADCIOC_VR_VREF_SET: + break; + case VF610_ADCIOC_VR_VALT_SET: + cfg_data |= VF610_ADC_REFSEL_VALT; + break; + case VF610_ADCIOC_VR_VBG_SET: + cfg_data |= VF610_ADC_REFSEL_VBG; + break; + default: + dev_err(info->dev, "error voltage reference\n"); + } + + /* data overwrite enable */ + if (adc_feature->ovwren) + cfg_data |= VF610_ADC_OVWREN; + + writel(cfg_data, info->regs + VF610_REG_ADC_CFG); + writel(gc_data, info->regs + VF610_REG_ADC_GC); +} + +static void vf610_adc_calibration(struct vf610_adc *info) +{ + int adc_gc, hc_cfg; + + if (!info->adc_feature.calibration) + return; + + /* enable calibration interrupt */ + hc_cfg = VF610_ADC_AIEN | VF610_ADC_CONV_DISABLE; + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + + adc_gc = readl(info->regs + VF610_REG_ADC_GC); + writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC); + + if (!wait_for_completion_timeout(&info->completion, VF610_ADC_TIMEOUT)) + dev_err(info->dev, "Timeout for adc calibration\n"); + + adc_gc = readl(info->regs + VF610_REG_ADC_GS); + if (adc_gc & VF610_ADC_CALF) + dev_err(info->dev, "ADC calibration failed\n"); + + info->adc_feature.calibration = false; +} + +static void vf610_adc_cfg_set(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &(info->adc_feature); + int cfg_data; + + cfg_data = readl(info->regs + VF610_REG_ADC_CFG); + + cfg_data &= ~VF610_ADC_ADLPC_EN; + if (adc_feature->conv_mode == VF610_ADC_CONV_LOW_POWER) + cfg_data |= VF610_ADC_ADLPC_EN; + + cfg_data &= ~VF610_ADC_ADHSC_EN; + if (adc_feature->conv_mode == VF610_ADC_CONV_HIGH_SPEED) + cfg_data |= VF610_ADC_ADHSC_EN; + + writel(cfg_data, info->regs + VF610_REG_ADC_CFG); +} + +static void vf610_adc_sample_set(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &(info->adc_feature); + int cfg_data, gc_data; + + cfg_data = readl(info->regs + VF610_REG_ADC_CFG); + gc_data = readl(info->regs + VF610_REG_ADC_GC); + + /* resolution mode */ + cfg_data &= ~VF610_ADC_MODE_MASK; + switch (adc_feature->res_mode) { + case 8: + cfg_data |= VF610_ADC_MODE_BIT8; + break; + case 10: + cfg_data |= VF610_ADC_MODE_BIT10; + break; + case 12: + cfg_data |= VF610_ADC_MODE_BIT12; + break; + default: + dev_err(info->dev, "error resolution mode\n"); + break; + } + + /* clock select and clock divider */ + cfg_data &= ~(VF610_ADC_CLK_MASK | VF610_ADC_ADCCLK_MASK); + switch (adc_feature->clk_div) { + case 1: + break; + case 2: + cfg_data |= VF610_ADC_CLK_DIV2; + break; + case 4: + cfg_data |= VF610_ADC_CLK_DIV4; + break; + case 8: + cfg_data |= VF610_ADC_CLK_DIV8; + break; + case 16: + switch (adc_feature->clk_sel) { + case VF610_ADCIOC_BUSCLK_SET: + cfg_data |= VF610_ADC_BUSCLK2_SEL | VF610_ADC_CLK_DIV8; + break; + default: + dev_err(info->dev, "error clk divider\n"); + break; + } + break; + } + + /* + * Set ADLSMP and ADSTS based on the Long Sample Time Adder value + * determined. + */ + switch (adc_feature->lst_adder_index) { + case VF610_ADCK_CYCLES_3: + break; + case VF610_ADCK_CYCLES_5: + cfg_data |= VF610_ADC_ADSTS_SHORT; + break; + case VF610_ADCK_CYCLES_7: + cfg_data |= VF610_ADC_ADSTS_NORMAL; + break; + case VF610_ADCK_CYCLES_9: + cfg_data |= VF610_ADC_ADSTS_LONG; + break; + case VF610_ADCK_CYCLES_13: + cfg_data |= VF610_ADC_ADLSMP_LONG; + break; + case VF610_ADCK_CYCLES_17: + cfg_data |= VF610_ADC_ADLSMP_LONG; + cfg_data |= VF610_ADC_ADSTS_SHORT; + break; + case VF610_ADCK_CYCLES_21: + cfg_data |= VF610_ADC_ADLSMP_LONG; + cfg_data |= VF610_ADC_ADSTS_NORMAL; + break; + case VF610_ADCK_CYCLES_25: + cfg_data |= VF610_ADC_ADLSMP_LONG; + cfg_data |= VF610_ADC_ADSTS_NORMAL; + break; + default: + dev_err(info->dev, "error in sample time select\n"); + } + + /* update hardware average selection */ + cfg_data &= ~VF610_ADC_AVGS_MASK; + gc_data &= ~VF610_ADC_AVGEN; + switch (adc_feature->sample_rate) { + case VF610_ADC_SAMPLE_1: + break; + case VF610_ADC_SAMPLE_4: + gc_data |= VF610_ADC_AVGEN; + break; + case VF610_ADC_SAMPLE_8: + gc_data |= VF610_ADC_AVGEN; + cfg_data |= VF610_ADC_AVGS_8; + break; + case VF610_ADC_SAMPLE_16: + gc_data |= VF610_ADC_AVGEN; + cfg_data |= VF610_ADC_AVGS_16; + break; + case VF610_ADC_SAMPLE_32: + gc_data |= VF610_ADC_AVGEN; + cfg_data |= VF610_ADC_AVGS_32; + break; + default: + dev_err(info->dev, + "error hardware sample average select\n"); + } + + writel(cfg_data, info->regs + VF610_REG_ADC_CFG); + writel(gc_data, info->regs + VF610_REG_ADC_GC); +} + +static void vf610_adc_hw_init(struct vf610_adc *info) +{ + /* CFG: Feature set */ + vf610_adc_cfg_post_set(info); + vf610_adc_sample_set(info); + + /* adc calibration */ + vf610_adc_calibration(info); + + /* CFG: power and speed set */ + vf610_adc_cfg_set(info); +} + +static int vf610_set_conversion_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct vf610_adc *info = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + info->adc_feature.conv_mode = mode; + vf610_adc_calculate_rates(info); + vf610_adc_hw_init(info); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int vf610_get_conversion_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct vf610_adc *info = iio_priv(indio_dev); + + return info->adc_feature.conv_mode; +} + +static const char * const vf610_conv_modes[] = { "normal", "high-speed", + "low-power" }; + +static const struct iio_enum vf610_conversion_mode = { + .items = vf610_conv_modes, + .num_items = ARRAY_SIZE(vf610_conv_modes), + .get = vf610_get_conversion_mode, + .set = vf610_set_conversion_mode, +}; + +static const struct iio_chan_spec_ext_info vf610_ext_info[] = { + IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, &vf610_conversion_mode), + {}, +}; + +#define VF610_ADC_CHAN(_idx, _chan_type) { \ + .type = (_chan_type), \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .ext_info = vf610_ext_info, \ + .scan_index = (_idx), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ +} + +#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \ + .type = (_chan_type), \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .scan_index = (_idx), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + }, \ +} + +static const struct iio_chan_spec vf610_adc_iio_channels[] = { + VF610_ADC_CHAN(0, IIO_VOLTAGE), + VF610_ADC_CHAN(1, IIO_VOLTAGE), + VF610_ADC_CHAN(2, IIO_VOLTAGE), + VF610_ADC_CHAN(3, IIO_VOLTAGE), + VF610_ADC_CHAN(4, IIO_VOLTAGE), + VF610_ADC_CHAN(5, IIO_VOLTAGE), + VF610_ADC_CHAN(6, IIO_VOLTAGE), + VF610_ADC_CHAN(7, IIO_VOLTAGE), + VF610_ADC_CHAN(8, IIO_VOLTAGE), + VF610_ADC_CHAN(9, IIO_VOLTAGE), + VF610_ADC_CHAN(10, IIO_VOLTAGE), + VF610_ADC_CHAN(11, IIO_VOLTAGE), + VF610_ADC_CHAN(12, IIO_VOLTAGE), + VF610_ADC_CHAN(13, IIO_VOLTAGE), + VF610_ADC_CHAN(14, IIO_VOLTAGE), + VF610_ADC_CHAN(15, IIO_VOLTAGE), + VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP), + IIO_CHAN_SOFT_TIMESTAMP(32), + /* sentinel */ +}; + +static int vf610_adc_read_data(struct vf610_adc *info) +{ + int result; + + result = readl(info->regs + VF610_REG_ADC_R0); + + switch (info->adc_feature.res_mode) { + case 8: + result &= 0xFF; + break; + case 10: + result &= 0x3FF; + break; + case 12: + result &= 0xFFF; + break; + default: + break; + } + + return result; +} + +static irqreturn_t vf610_adc_isr(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct vf610_adc *info = iio_priv(indio_dev); + int coco; + + coco = readl(info->regs + VF610_REG_ADC_HS); + if (coco & VF610_ADC_HS_COCO0) { + info->value = vf610_adc_read_data(info); + if (iio_buffer_enabled(indio_dev)) { + info->scan.chan = info->value; + iio_push_to_buffers_with_timestamp(indio_dev, + &info->scan, + iio_get_time_ns(indio_dev)); + iio_trigger_notify_done(indio_dev->trig); + } else + complete(&info->completion); + } + + return IRQ_HANDLED; +} + +static ssize_t vf610_show_samp_freq_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vf610_adc *info = iio_priv(dev_to_iio_dev(dev)); + size_t len = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(info->sample_freq_avail); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, + "%u ", info->sample_freq_avail[i]); + + /* replace trailing space by newline */ + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(vf610_show_samp_freq_avail); + +static struct attribute *vf610_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group vf610_attribute_group = { + .attrs = vf610_attributes, +}; + +static int vf610_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct vf610_adc *info = iio_priv(indio_dev); + unsigned int hc_cfg; + long ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&indio_dev->mlock); + if (iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + + reinit_completion(&info->completion); + hc_cfg = VF610_ADC_ADCHC(chan->channel); + hc_cfg |= VF610_ADC_AIEN; + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + ret = wait_for_completion_interruptible_timeout + (&info->completion, VF610_ADC_TIMEOUT); + if (ret == 0) { + mutex_unlock(&indio_dev->mlock); + return -ETIMEDOUT; + } + if (ret < 0) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + + switch (chan->type) { + case IIO_VOLTAGE: + *val = info->value; + break; + case IIO_TEMP: + /* + * Calculate in degree Celsius times 1000 + * Using the typical sensor slope of 1.84 mV/°C + * and VREFH_ADC at 3.3V, V at 25°C of 699 mV + */ + *val = 25000 - ((int)info->value - VF610_VTEMP25_3V3) * + 1000000 / VF610_TEMP_SLOPE_COEFF; + + break; + default: + mutex_unlock(&indio_dev->mlock); + return -EINVAL; + } + + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = info->vref_uv / 1000; + *val2 = info->adc_feature.res_mode; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = info->sample_freq_avail[info->adc_feature.sample_rate]; + *val2 = 0; + return IIO_VAL_INT; + + default: + break; + } + + return -EINVAL; +} + +static int vf610_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct vf610_adc *info = iio_priv(indio_dev); + int i; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + for (i = 0; + i < ARRAY_SIZE(info->sample_freq_avail); + i++) + if (val == info->sample_freq_avail[i]) { + info->adc_feature.sample_rate = i; + vf610_adc_sample_set(info); + return 0; + } + break; + + default: + break; + } + + return -EINVAL; +} + +static int vf610_adc_buffer_postenable(struct iio_dev *indio_dev) +{ + struct vf610_adc *info = iio_priv(indio_dev); + unsigned int channel; + int val; + + val = readl(info->regs + VF610_REG_ADC_GC); + val |= VF610_ADC_ADCON; + writel(val, info->regs + VF610_REG_ADC_GC); + + channel = find_first_bit(indio_dev->active_scan_mask, + indio_dev->masklength); + + val = VF610_ADC_ADCHC(channel); + val |= VF610_ADC_AIEN; + + writel(val, info->regs + VF610_REG_ADC_HC0); + + return 0; +} + +static int vf610_adc_buffer_predisable(struct iio_dev *indio_dev) +{ + struct vf610_adc *info = iio_priv(indio_dev); + unsigned int hc_cfg = 0; + int val; + + val = readl(info->regs + VF610_REG_ADC_GC); + val &= ~VF610_ADC_ADCON; + writel(val, info->regs + VF610_REG_ADC_GC); + + hc_cfg |= VF610_ADC_CONV_DISABLE; + hc_cfg &= ~VF610_ADC_AIEN; + + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + + return 0; +} + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .postenable = &vf610_adc_buffer_postenable, + .predisable = &vf610_adc_buffer_predisable, + .validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + +static int vf610_adc_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct vf610_adc *info = iio_priv(indio_dev); + + if ((readval == NULL) || + ((reg % 4) || (reg > VF610_REG_ADC_PCTL))) + return -EINVAL; + + *readval = readl(info->regs + reg); + + return 0; +} + +static const struct iio_info vf610_adc_iio_info = { + .read_raw = &vf610_read_raw, + .write_raw = &vf610_write_raw, + .debugfs_reg_access = &vf610_adc_reg_access, + .attrs = &vf610_attribute_group, +}; + +static const struct of_device_id vf610_adc_match[] = { + { .compatible = "fsl,vf610-adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vf610_adc_match); + +static int vf610_adc_probe(struct platform_device *pdev) +{ + struct vf610_adc *info; + struct iio_dev *indio_dev; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct vf610_adc)); + if (!indio_dev) { + dev_err(&pdev->dev, "Failed allocating iio device\n"); + return -ENOMEM; + } + + info = iio_priv(indio_dev); + info->dev = &pdev->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; + + ret = devm_request_irq(info->dev, irq, + vf610_adc_isr, 0, + dev_name(&pdev->dev), indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq); + return ret; + } + + info->clk = devm_clk_get(&pdev->dev, "adc"); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "failed getting clock, err = %ld\n", + PTR_ERR(info->clk)); + return PTR_ERR(info->clk); + } + + info->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(info->vref)) + return PTR_ERR(info->vref); + + ret = regulator_enable(info->vref); + if (ret) + return ret; + + info->vref_uv = regulator_get_voltage(info->vref); + + of_property_read_u32_array(pdev->dev.of_node, "fsl,adck-max-frequency", + info->max_adck_rate, 3); + + ret = of_property_read_u32(pdev->dev.of_node, "min-sample-time", + &info->adc_feature.default_sample_time); + if (ret) + info->adc_feature.default_sample_time = DEFAULT_SAMPLE_TIME; + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&info->completion); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &vf610_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = vf610_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(vf610_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; + } + + vf610_adc_cfg_init(info); + vf610_adc_hw_init(info); + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + NULL, &iio_triggered_buffer_setup_ops); + if (ret < 0) { + dev_err(&pdev->dev, "Couldn't initialise the buffer\n"); + goto error_iio_device_register; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't register the device.\n"); + goto error_adc_buffer_init; + } + + return 0; + +error_adc_buffer_init: + iio_triggered_buffer_cleanup(indio_dev); +error_iio_device_register: + clk_disable_unprepare(info->clk); +error_adc_clk_enable: + regulator_disable(info->vref); + + return ret; +} + +static int vf610_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct vf610_adc *info = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(info->vref); + clk_disable_unprepare(info->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int vf610_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct vf610_adc *info = iio_priv(indio_dev); + int hc_cfg; + + /* ADC controller enters to stop mode */ + hc_cfg = readl(info->regs + VF610_REG_ADC_HC0); + hc_cfg |= VF610_ADC_CONV_DISABLE; + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + + clk_disable_unprepare(info->clk); + regulator_disable(info->vref); + + return 0; +} + +static int vf610_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct vf610_adc *info = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(info->vref); + if (ret) + return ret; + + ret = clk_prepare_enable(info->clk); + if (ret) + goto disable_reg; + + vf610_adc_hw_init(info); + + return 0; + +disable_reg: + regulator_disable(info->vref); + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, vf610_adc_resume); + +static struct platform_driver vf610_adc_driver = { + .probe = vf610_adc_probe, + .remove = vf610_adc_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = vf610_adc_match, + .pm = &vf610_adc_pm_ops, + }, +}; + +module_platform_driver(vf610_adc_driver); + +MODULE_AUTHOR("Fugang Duan <B38611@freescale.com>"); +MODULE_DESCRIPTION("Freescale VF610 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c new file mode 100644 index 000000000..1028b101c --- /dev/null +++ b/drivers/iio/adc/viperboard_adc.c @@ -0,0 +1,150 @@ +// 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. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> + +#include <linux/usb.h> +#include <linux/iio/iio.h> + +#include <linux/mfd/viperboard.h> + +#define VPRBRD_ADC_CMD_GET 0x00 + +struct vprbrd_adc_msg { + u8 cmd; + u8 chan; + u8 val; +} __packed; + +struct vprbrd_adc { + struct vprbrd *vb; +}; + +#define VPRBRD_ADC_CHANNEL(_index) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +} + +static struct iio_chan_spec const vprbrd_adc_iio_channels[] = { + VPRBRD_ADC_CHANNEL(0), + VPRBRD_ADC_CHANNEL(1), + VPRBRD_ADC_CHANNEL(2), + VPRBRD_ADC_CHANNEL(3), +}; + +static int vprbrd_iio_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long info) +{ + int ret, error = 0; + struct vprbrd_adc *adc = iio_priv(iio_dev); + struct vprbrd *vb = adc->vb; + struct vprbrd_adc_msg *admsg = (struct vprbrd_adc_msg *)vb->buf; + + switch (info) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&vb->lock); + + admsg->cmd = VPRBRD_ADC_CMD_GET; + admsg->chan = chan->channel; + admsg->val = 0x00; + + ret = usb_control_msg(vb->usb_dev, + usb_sndctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC, + VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg, + sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS); + if (ret != sizeof(struct vprbrd_adc_msg)) { + dev_err(&iio_dev->dev, "usb send error on adc read\n"); + error = -EREMOTEIO; + } + + ret = usb_control_msg(vb->usb_dev, + usb_rcvctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC, + VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, admsg, + sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS); + + *val = admsg->val; + + mutex_unlock(&vb->lock); + + if (ret != sizeof(struct vprbrd_adc_msg)) { + dev_err(&iio_dev->dev, "usb recv error on adc read\n"); + error = -EREMOTEIO; + } + + if (error) + goto error; + + return IIO_VAL_INT; + default: + error = -EINVAL; + break; + } +error: + return error; +} + +static const struct iio_info vprbrd_adc_iio_info = { + .read_raw = &vprbrd_iio_read_raw, +}; + +static int vprbrd_adc_probe(struct platform_device *pdev) +{ + struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent); + struct vprbrd_adc *adc; + struct iio_dev *indio_dev; + int ret; + + /* registering iio */ + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!indio_dev) { + dev_err(&pdev->dev, "failed allocating iio device\n"); + return -ENOMEM; + } + + adc = iio_priv(indio_dev); + adc->vb = vb; + indio_dev->name = "viperboard adc"; + indio_dev->info = &vprbrd_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = vprbrd_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(vprbrd_adc_iio_channels); + + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret) { + dev_err(&pdev->dev, "could not register iio (adc)"); + return ret; + } + + return 0; +} + +static struct platform_driver vprbrd_adc_driver = { + .driver = { + .name = "viperboard-adc", + }, + .probe = vprbrd_adc_probe, +}; + +module_platform_driver(vprbrd_adc_driver); + +MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>"); +MODULE_DESCRIPTION("IIO ADC driver for Nano River Techs Viperboard"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:viperboard-adc"); diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c new file mode 100644 index 000000000..30b5a17ce --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -0,0 +1,1354 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Xilinx XADC driver + * + * Copyright 2013-2014 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Documentation for the parts can be found at: + * - XADC hardmacro: Xilinx UG480 + * - ZYNQ XADC interface: Xilinx UG585 + * - AXI XADC interface: Xilinx PG019 + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/overflow.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sysfs.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include "xilinx-xadc.h" + +static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500; + +/* ZYNQ register definitions */ +#define XADC_ZYNQ_REG_CFG 0x00 +#define XADC_ZYNQ_REG_INTSTS 0x04 +#define XADC_ZYNQ_REG_INTMSK 0x08 +#define XADC_ZYNQ_REG_STATUS 0x0c +#define XADC_ZYNQ_REG_CFIFO 0x10 +#define XADC_ZYNQ_REG_DFIFO 0x14 +#define XADC_ZYNQ_REG_CTL 0x18 + +#define XADC_ZYNQ_CFG_ENABLE BIT(31) +#define XADC_ZYNQ_CFG_CFIFOTH_MASK (0xf << 20) +#define XADC_ZYNQ_CFG_CFIFOTH_OFFSET 20 +#define XADC_ZYNQ_CFG_DFIFOTH_MASK (0xf << 16) +#define XADC_ZYNQ_CFG_DFIFOTH_OFFSET 16 +#define XADC_ZYNQ_CFG_WEDGE BIT(13) +#define XADC_ZYNQ_CFG_REDGE BIT(12) +#define XADC_ZYNQ_CFG_TCKRATE_MASK (0x3 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV2 (0x0 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV4 (0x1 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV8 (0x2 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV16 (0x3 << 8) +#define XADC_ZYNQ_CFG_IGAP_MASK 0x1f +#define XADC_ZYNQ_CFG_IGAP(x) (x) + +#define XADC_ZYNQ_INT_CFIFO_LTH BIT(9) +#define XADC_ZYNQ_INT_DFIFO_GTH BIT(8) +#define XADC_ZYNQ_INT_ALARM_MASK 0xff +#define XADC_ZYNQ_INT_ALARM_OFFSET 0 + +#define XADC_ZYNQ_STATUS_CFIFO_LVL_MASK (0xf << 16) +#define XADC_ZYNQ_STATUS_CFIFO_LVL_OFFSET 16 +#define XADC_ZYNQ_STATUS_DFIFO_LVL_MASK (0xf << 12) +#define XADC_ZYNQ_STATUS_DFIFO_LVL_OFFSET 12 +#define XADC_ZYNQ_STATUS_CFIFOF BIT(11) +#define XADC_ZYNQ_STATUS_CFIFOE BIT(10) +#define XADC_ZYNQ_STATUS_DFIFOF BIT(9) +#define XADC_ZYNQ_STATUS_DFIFOE BIT(8) +#define XADC_ZYNQ_STATUS_OT BIT(7) +#define XADC_ZYNQ_STATUS_ALM(x) BIT(x) + +#define XADC_ZYNQ_CTL_RESET BIT(4) + +#define XADC_ZYNQ_CMD_NOP 0x00 +#define XADC_ZYNQ_CMD_READ 0x01 +#define XADC_ZYNQ_CMD_WRITE 0x02 + +#define XADC_ZYNQ_CMD(cmd, addr, data) (((cmd) << 26) | ((addr) << 16) | (data)) + +/* AXI register definitions */ +#define XADC_AXI_REG_RESET 0x00 +#define XADC_AXI_REG_STATUS 0x04 +#define XADC_AXI_REG_ALARM_STATUS 0x08 +#define XADC_AXI_REG_CONVST 0x0c +#define XADC_AXI_REG_XADC_RESET 0x10 +#define XADC_AXI_REG_GIER 0x5c +#define XADC_AXI_REG_IPISR 0x60 +#define XADC_AXI_REG_IPIER 0x68 +#define XADC_AXI_ADC_REG_OFFSET 0x200 + +#define XADC_AXI_RESET_MAGIC 0xa +#define XADC_AXI_GIER_ENABLE BIT(31) + +#define XADC_AXI_INT_EOS BIT(4) +#define XADC_AXI_INT_ALARM_MASK 0x3c0f + +#define XADC_FLAGS_BUFFERED BIT(0) + +/* + * The XADC hardware supports a samplerate of up to 1MSPS. Unfortunately it does + * not have a hardware FIFO. Which means an interrupt is generated for each + * conversion sequence. At 1MSPS sample rate the CPU in ZYNQ7000 is completely + * overloaded by the interrupts that it soft-lockups. For this reason the driver + * limits the maximum samplerate 150kSPS. At this rate the CPU is fairly busy, + * but still responsive. + */ +#define XADC_MAX_SAMPLERATE 150000 + +static void xadc_write_reg(struct xadc *xadc, unsigned int reg, + uint32_t val) +{ + writel(val, xadc->base + reg); +} + +static void xadc_read_reg(struct xadc *xadc, unsigned int reg, + uint32_t *val) +{ + *val = readl(xadc->base + reg); +} + +/* + * The ZYNQ interface uses two asynchronous FIFOs for communication with the + * XADC. Reads and writes to the XADC register are performed by submitting a + * request to the command FIFO (CFIFO), once the request has been completed the + * result can be read from the data FIFO (DFIFO). The method currently used in + * this driver is to submit the request for a read/write operation, then go to + * sleep and wait for an interrupt that signals that a response is available in + * the data FIFO. + */ + +static void xadc_zynq_write_fifo(struct xadc *xadc, uint32_t *cmd, + unsigned int n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFIFO, cmd[i]); +} + +static void xadc_zynq_drain_fifo(struct xadc *xadc) +{ + uint32_t status, tmp; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &status); + + while (!(status & XADC_ZYNQ_STATUS_DFIFOE)) { + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &tmp); + xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &status); + } +} + +static void xadc_zynq_update_intmsk(struct xadc *xadc, unsigned int mask, + unsigned int val) +{ + xadc->zynq_intmask &= ~mask; + xadc->zynq_intmask |= val; + + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTMSK, + xadc->zynq_intmask | xadc->zynq_masked_alarm); +} + +static int xadc_zynq_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + uint32_t cmd[1]; + uint32_t tmp; + int ret; + + spin_lock_irq(&xadc->lock); + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, + XADC_ZYNQ_INT_DFIFO_GTH); + + reinit_completion(&xadc->completion); + + cmd[0] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_WRITE, reg, val); + xadc_zynq_write_fifo(xadc, cmd, ARRAY_SIZE(cmd)); + xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &tmp); + tmp &= ~XADC_ZYNQ_CFG_DFIFOTH_MASK; + tmp |= 0 << XADC_ZYNQ_CFG_DFIFOTH_OFFSET; + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, tmp); + + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, 0); + spin_unlock_irq(&xadc->lock); + + ret = wait_for_completion_interruptible_timeout(&xadc->completion, HZ); + if (ret == 0) + ret = -EIO; + else + ret = 0; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &tmp); + + return ret; +} + +static int xadc_zynq_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + uint32_t cmd[2]; + uint32_t resp, tmp; + int ret; + + cmd[0] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_READ, reg, 0); + cmd[1] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_NOP, 0, 0); + + spin_lock_irq(&xadc->lock); + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, + XADC_ZYNQ_INT_DFIFO_GTH); + xadc_zynq_drain_fifo(xadc); + reinit_completion(&xadc->completion); + + xadc_zynq_write_fifo(xadc, cmd, ARRAY_SIZE(cmd)); + xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &tmp); + tmp &= ~XADC_ZYNQ_CFG_DFIFOTH_MASK; + tmp |= 1 << XADC_ZYNQ_CFG_DFIFOTH_OFFSET; + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, tmp); + + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, 0); + spin_unlock_irq(&xadc->lock); + ret = wait_for_completion_interruptible_timeout(&xadc->completion, HZ); + if (ret == 0) + ret = -EIO; + if (ret < 0) + return ret; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &resp); + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &resp); + + *val = resp & 0xffff; + + return 0; +} + +static unsigned int xadc_zynq_transform_alarm(unsigned int alarm) +{ + return ((alarm & 0x80) >> 4) | + ((alarm & 0x78) << 1) | + (alarm & 0x07); +} + +/* + * The ZYNQ threshold interrupts are level sensitive. Since we can't make the + * threshold condition go way from within the interrupt handler, this means as + * soon as a threshold condition is present we would enter the interrupt handler + * again and again. To work around this we mask all active thresholds interrupts + * in the interrupt handler and start a timer. In this timer we poll the + * interrupt status and only if the interrupt is inactive we unmask it again. + */ +static void xadc_zynq_unmask_worker(struct work_struct *work) +{ + struct xadc *xadc = container_of(work, struct xadc, zynq_unmask_work.work); + unsigned int misc_sts, unmask; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &misc_sts); + + misc_sts &= XADC_ZYNQ_INT_ALARM_MASK; + + spin_lock_irq(&xadc->lock); + + /* Clear those bits which are not active anymore */ + unmask = (xadc->zynq_masked_alarm ^ misc_sts) & xadc->zynq_masked_alarm; + xadc->zynq_masked_alarm &= misc_sts; + + /* Also clear those which are masked out anyway */ + xadc->zynq_masked_alarm &= ~xadc->zynq_intmask; + + /* Clear the interrupts before we unmask them */ + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, unmask); + + xadc_zynq_update_intmsk(xadc, 0, 0); + + spin_unlock_irq(&xadc->lock); + + /* if still pending some alarm re-trigger the timer */ + if (xadc->zynq_masked_alarm) { + schedule_delayed_work(&xadc->zynq_unmask_work, + msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT)); + } + +} + +static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid) +{ + struct iio_dev *indio_dev = devid; + struct xadc *xadc = iio_priv(indio_dev); + uint32_t status; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status); + + status &= ~(xadc->zynq_intmask | xadc->zynq_masked_alarm); + + if (!status) + return IRQ_NONE; + + spin_lock(&xadc->lock); + + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, status); + + if (status & XADC_ZYNQ_INT_DFIFO_GTH) { + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, + XADC_ZYNQ_INT_DFIFO_GTH); + complete(&xadc->completion); + } + + status &= XADC_ZYNQ_INT_ALARM_MASK; + if (status) { + xadc->zynq_masked_alarm |= status; + /* + * mask the current event interrupt, + * unmask it when the interrupt is no more active. + */ + xadc_zynq_update_intmsk(xadc, 0, 0); + + xadc_handle_events(indio_dev, + xadc_zynq_transform_alarm(status)); + + /* unmask the required interrupts in timer. */ + schedule_delayed_work(&xadc->zynq_unmask_work, + msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT)); + } + spin_unlock(&xadc->lock); + + return IRQ_HANDLED; +} + +#define XADC_ZYNQ_TCK_RATE_MAX 50000000 +#define XADC_ZYNQ_IGAP_DEFAULT 20 +#define XADC_ZYNQ_PCAP_RATE_MAX 200000000 + +static int xadc_zynq_setup(struct platform_device *pdev, + struct iio_dev *indio_dev, int irq) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned long pcap_rate; + unsigned int tck_div; + unsigned int div; + unsigned int igap; + unsigned int tck_rate; + int ret; + + /* TODO: Figure out how to make igap and tck_rate configurable */ + igap = XADC_ZYNQ_IGAP_DEFAULT; + tck_rate = XADC_ZYNQ_TCK_RATE_MAX; + + xadc->zynq_intmask = ~0; + + pcap_rate = clk_get_rate(xadc->clk); + if (!pcap_rate) + return -EINVAL; + + if (pcap_rate > XADC_ZYNQ_PCAP_RATE_MAX) { + ret = clk_set_rate(xadc->clk, + (unsigned long)XADC_ZYNQ_PCAP_RATE_MAX); + if (ret) + return ret; + } + + if (tck_rate > pcap_rate / 2) { + div = 2; + } else { + div = pcap_rate / tck_rate; + if (pcap_rate / div > XADC_ZYNQ_TCK_RATE_MAX) + div++; + } + + if (div <= 3) + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV2; + else if (div <= 7) + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV4; + else if (div <= 15) + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV8; + else + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV16; + + xadc_write_reg(xadc, XADC_ZYNQ_REG_CTL, XADC_ZYNQ_CTL_RESET); + xadc_write_reg(xadc, XADC_ZYNQ_REG_CTL, 0); + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, ~0); + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTMSK, xadc->zynq_intmask); + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, XADC_ZYNQ_CFG_ENABLE | + XADC_ZYNQ_CFG_REDGE | XADC_ZYNQ_CFG_WEDGE | + tck_div | XADC_ZYNQ_CFG_IGAP(igap)); + + if (pcap_rate > XADC_ZYNQ_PCAP_RATE_MAX) { + ret = clk_set_rate(xadc->clk, pcap_rate); + if (ret) + return ret; + } + + return 0; +} + +static unsigned long xadc_zynq_get_dclk_rate(struct xadc *xadc) +{ + unsigned int div; + uint32_t val; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &val); + + switch (val & XADC_ZYNQ_CFG_TCKRATE_MASK) { + case XADC_ZYNQ_CFG_TCKRATE_DIV4: + div = 4; + break; + case XADC_ZYNQ_CFG_TCKRATE_DIV8: + div = 8; + break; + case XADC_ZYNQ_CFG_TCKRATE_DIV16: + div = 16; + break; + default: + div = 2; + break; + } + + return clk_get_rate(xadc->clk) / div; +} + +static void xadc_zynq_update_alarm(struct xadc *xadc, unsigned int alarm) +{ + unsigned long flags; + uint32_t status; + + /* Move OT to bit 7 */ + alarm = ((alarm & 0x08) << 4) | ((alarm & 0xf0) >> 1) | (alarm & 0x07); + + spin_lock_irqsave(&xadc->lock, flags); + + /* Clear previous interrupts if any. */ + xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status); + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, status & alarm); + + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_ALARM_MASK, + ~alarm & XADC_ZYNQ_INT_ALARM_MASK); + + spin_unlock_irqrestore(&xadc->lock, flags); +} + +static const struct xadc_ops xadc_zynq_ops = { + .read = xadc_zynq_read_adc_reg, + .write = xadc_zynq_write_adc_reg, + .setup = xadc_zynq_setup, + .get_dclk_rate = xadc_zynq_get_dclk_rate, + .interrupt_handler = xadc_zynq_interrupt_handler, + .update_alarm = xadc_zynq_update_alarm, +}; + +static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + uint32_t val32; + + xadc_read_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, &val32); + *val = val32 & 0xffff; + + return 0; +} + +static int xadc_axi_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + xadc_write_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, val); + + return 0; +} + +static int xadc_axi_setup(struct platform_device *pdev, + struct iio_dev *indio_dev, int irq) +{ + struct xadc *xadc = iio_priv(indio_dev); + + xadc_write_reg(xadc, XADC_AXI_REG_RESET, XADC_AXI_RESET_MAGIC); + xadc_write_reg(xadc, XADC_AXI_REG_GIER, XADC_AXI_GIER_ENABLE); + + return 0; +} + +static irqreturn_t xadc_axi_interrupt_handler(int irq, void *devid) +{ + struct iio_dev *indio_dev = devid; + struct xadc *xadc = iio_priv(indio_dev); + uint32_t status, mask; + unsigned int events; + + xadc_read_reg(xadc, XADC_AXI_REG_IPISR, &status); + xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &mask); + status &= mask; + + if (!status) + return IRQ_NONE; + + if ((status & XADC_AXI_INT_EOS) && xadc->trigger) + iio_trigger_poll(xadc->trigger); + + if (status & XADC_AXI_INT_ALARM_MASK) { + /* + * The order of the bits in the AXI-XADC status register does + * not match the order of the bits in the XADC alarm enable + * register. xadc_handle_events() expects the events to be in + * the same order as the XADC alarm enable register. + */ + events = (status & 0x000e) >> 1; + events |= (status & 0x0001) << 3; + events |= (status & 0x3c00) >> 6; + xadc_handle_events(indio_dev, events); + } + + xadc_write_reg(xadc, XADC_AXI_REG_IPISR, status); + + return IRQ_HANDLED; +} + +static void xadc_axi_update_alarm(struct xadc *xadc, unsigned int alarm) +{ + uint32_t val; + unsigned long flags; + + /* + * The order of the bits in the AXI-XADC status register does not match + * the order of the bits in the XADC alarm enable register. We get + * passed the alarm mask in the same order as in the XADC alarm enable + * register. + */ + alarm = ((alarm & 0x07) << 1) | ((alarm & 0x08) >> 3) | + ((alarm & 0xf0) << 6); + + spin_lock_irqsave(&xadc->lock, flags); + xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &val); + val &= ~XADC_AXI_INT_ALARM_MASK; + val |= alarm; + xadc_write_reg(xadc, XADC_AXI_REG_IPIER, val); + spin_unlock_irqrestore(&xadc->lock, flags); +} + +static unsigned long xadc_axi_get_dclk(struct xadc *xadc) +{ + return clk_get_rate(xadc->clk); +} + +static const struct xadc_ops xadc_axi_ops = { + .read = xadc_axi_read_adc_reg, + .write = xadc_axi_write_adc_reg, + .setup = xadc_axi_setup, + .get_dclk_rate = xadc_axi_get_dclk, + .update_alarm = xadc_axi_update_alarm, + .interrupt_handler = xadc_axi_interrupt_handler, + .flags = XADC_FLAGS_BUFFERED, +}; + +static int _xadc_update_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t mask, uint16_t val) +{ + uint16_t tmp; + int ret; + + ret = _xadc_read_adc_reg(xadc, reg, &tmp); + if (ret) + return ret; + + return _xadc_write_adc_reg(xadc, reg, (tmp & ~mask) | val); +} + +static int xadc_update_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t mask, uint16_t val) +{ + int ret; + + mutex_lock(&xadc->mutex); + ret = _xadc_update_adc_reg(xadc, reg, mask, val); + mutex_unlock(&xadc->mutex); + + return ret; +} + +static unsigned long xadc_get_dclk_rate(struct xadc *xadc) +{ + return xadc->ops->get_dclk_rate(xadc); +} + +static int xadc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *mask) +{ + struct xadc *xadc = iio_priv(indio_dev); + size_t new_size, n; + void *data; + + n = bitmap_weight(mask, indio_dev->masklength); + + if (check_mul_overflow(n, sizeof(*xadc->data), &new_size)) + return -ENOMEM; + + data = devm_krealloc(indio_dev->dev.parent, xadc->data, + new_size, GFP_KERNEL); + if (!data) + return -ENOMEM; + + memset(data, 0, new_size); + xadc->data = data; + + return 0; +} + +static unsigned int xadc_scan_index_to_channel(unsigned int scan_index) +{ + switch (scan_index) { + case 5: + return XADC_REG_VCCPINT; + case 6: + return XADC_REG_VCCPAUX; + case 7: + return XADC_REG_VCCO_DDR; + case 8: + return XADC_REG_TEMP; + case 9: + return XADC_REG_VCCINT; + case 10: + return XADC_REG_VCCAUX; + case 11: + return XADC_REG_VPVN; + case 12: + return XADC_REG_VREFP; + case 13: + return XADC_REG_VREFN; + case 14: + return XADC_REG_VCCBRAM; + default: + return XADC_REG_VAUX(scan_index - 16); + } +} + +static irqreturn_t xadc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct xadc *xadc = iio_priv(indio_dev); + unsigned int chan; + int i, j; + + if (!xadc->data) + goto out; + + j = 0; + for_each_set_bit(i, indio_dev->active_scan_mask, + indio_dev->masklength) { + chan = xadc_scan_index_to_channel(i); + xadc_read_adc_reg(xadc, chan, &xadc->data[j]); + j++; + } + + iio_push_to_buffers(indio_dev, xadc->data); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int xadc_trigger_set_state(struct iio_trigger *trigger, bool state) +{ + struct xadc *xadc = iio_trigger_get_drvdata(trigger); + unsigned long flags; + unsigned int convst; + unsigned int val; + int ret = 0; + + mutex_lock(&xadc->mutex); + + if (state) { + /* Only one of the two triggers can be active at a time. */ + if (xadc->trigger != NULL) { + ret = -EBUSY; + goto err_out; + } else { + xadc->trigger = trigger; + if (trigger == xadc->convst_trigger) + convst = XADC_CONF0_EC; + else + convst = 0; + } + ret = _xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF0_EC, + convst); + if (ret) + goto err_out; + } else { + xadc->trigger = NULL; + } + + spin_lock_irqsave(&xadc->lock, flags); + xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &val); + xadc_write_reg(xadc, XADC_AXI_REG_IPISR, XADC_AXI_INT_EOS); + if (state) + val |= XADC_AXI_INT_EOS; + else + val &= ~XADC_AXI_INT_EOS; + xadc_write_reg(xadc, XADC_AXI_REG_IPIER, val); + spin_unlock_irqrestore(&xadc->lock, flags); + +err_out: + mutex_unlock(&xadc->mutex); + + return ret; +} + +static const struct iio_trigger_ops xadc_trigger_ops = { + .set_trigger_state = &xadc_trigger_set_state, +}; + +static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev, + const char *name) +{ + struct device *dev = indio_dev->dev.parent; + struct iio_trigger *trig; + int ret; + + trig = devm_iio_trigger_alloc(dev, "%s%d-%s", indio_dev->name, + indio_dev->id, name); + if (trig == NULL) + return ERR_PTR(-ENOMEM); + + trig->dev.parent = indio_dev->dev.parent; + trig->ops = &xadc_trigger_ops; + iio_trigger_set_drvdata(trig, iio_priv(indio_dev)); + + ret = devm_iio_trigger_register(dev, trig); + if (ret) + return ERR_PTR(ret); + + return trig; +} + +static int xadc_power_adc_b(struct xadc *xadc, unsigned int seq_mode) +{ + uint16_t val; + + /* Powerdown the ADC-B when it is not needed. */ + switch (seq_mode) { + case XADC_CONF1_SEQ_SIMULTANEOUS: + case XADC_CONF1_SEQ_INDEPENDENT: + val = 0; + break; + default: + val = XADC_CONF2_PD_ADC_B; + break; + } + + return xadc_update_adc_reg(xadc, XADC_REG_CONF2, XADC_CONF2_PD_MASK, + val); +} + +static int xadc_get_seq_mode(struct xadc *xadc, unsigned long scan_mode) +{ + unsigned int aux_scan_mode = scan_mode >> 16; + + if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_DUAL) + return XADC_CONF1_SEQ_SIMULTANEOUS; + + if ((aux_scan_mode & 0xff00) == 0 || + (aux_scan_mode & 0x00ff) == 0) + return XADC_CONF1_SEQ_CONTINUOUS; + + return XADC_CONF1_SEQ_SIMULTANEOUS; +} + +static int xadc_postdisable(struct iio_dev *indio_dev) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned long scan_mask; + int ret; + int i; + + scan_mask = 1; /* Run calibration as part of the sequence */ + for (i = 0; i < indio_dev->num_channels; i++) + scan_mask |= BIT(indio_dev->channels[i].scan_index); + + /* Enable all channels and calibration */ + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff); + if (ret) + return ret; + + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(1), scan_mask >> 16); + if (ret) + return ret; + + ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, + XADC_CONF1_SEQ_CONTINUOUS); + if (ret) + return ret; + + return xadc_power_adc_b(xadc, XADC_CONF1_SEQ_CONTINUOUS); +} + +static int xadc_preenable(struct iio_dev *indio_dev) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned long scan_mask; + int seq_mode; + int ret; + + ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, + XADC_CONF1_SEQ_DEFAULT); + if (ret) + goto err; + + scan_mask = *indio_dev->active_scan_mask; + seq_mode = xadc_get_seq_mode(xadc, scan_mask); + + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff); + if (ret) + goto err; + + /* + * In simultaneous mode the upper and lower aux channels are samples at + * the same time. In this mode the upper 8 bits in the sequencer + * register are don't care and the lower 8 bits control two channels + * each. As such we must set the bit if either the channel in the lower + * group or the upper group is enabled. + */ + if (seq_mode == XADC_CONF1_SEQ_SIMULTANEOUS) + scan_mask = ((scan_mask >> 8) | scan_mask) & 0xff0000; + + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(1), scan_mask >> 16); + if (ret) + goto err; + + ret = xadc_power_adc_b(xadc, seq_mode); + if (ret) + goto err; + + ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, + seq_mode); + if (ret) + goto err; + + return 0; +err: + xadc_postdisable(indio_dev); + return ret; +} + +static const struct iio_buffer_setup_ops xadc_buffer_ops = { + .preenable = &xadc_preenable, + .postdisable = &xadc_postdisable, +}; + +static int xadc_read_samplerate(struct xadc *xadc) +{ + unsigned int div; + uint16_t val16; + int ret; + + ret = xadc_read_adc_reg(xadc, XADC_REG_CONF2, &val16); + if (ret) + return ret; + + div = (val16 & XADC_CONF2_DIV_MASK) >> XADC_CONF2_DIV_OFFSET; + if (div < 2) + div = 2; + + return xadc_get_dclk_rate(xadc) / div / 26; +} + +static int xadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct xadc *xadc = iio_priv(indio_dev); + uint16_t val16; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + ret = xadc_read_adc_reg(xadc, chan->address, &val16); + if (ret < 0) + return ret; + + val16 >>= 4; + if (chan->scan_type.sign == 'u') + *val = val16; + else + *val = sign_extend32(val16, 11); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + /* V = (val * 3.0) / 4096 */ + switch (chan->address) { + case XADC_REG_VCCINT: + case XADC_REG_VCCAUX: + case XADC_REG_VREFP: + case XADC_REG_VREFN: + case XADC_REG_VCCBRAM: + case XADC_REG_VCCPINT: + case XADC_REG_VCCPAUX: + case XADC_REG_VCCO_DDR: + *val = 3000; + break; + default: + *val = 1000; + break; + } + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + /* Temp in C = (val * 503.975) / 4096 - 273.15 */ + *val = 503975; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + /* Only the temperature channel has an offset */ + *val = -((273150 << 12) / 503975); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = xadc_read_samplerate(xadc); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int xadc_write_samplerate(struct xadc *xadc, int val) +{ + unsigned long clk_rate = xadc_get_dclk_rate(xadc); + unsigned int div; + + if (!clk_rate) + return -EINVAL; + + if (val <= 0) + return -EINVAL; + + /* Max. 150 kSPS */ + if (val > XADC_MAX_SAMPLERATE) + val = XADC_MAX_SAMPLERATE; + + val *= 26; + + /* Min 1MHz */ + if (val < 1000000) + val = 1000000; + + /* + * We want to round down, but only if we do not exceed the 150 kSPS + * limit. + */ + div = clk_rate / val; + if (clk_rate / div / 26 > XADC_MAX_SAMPLERATE) + div++; + if (div < 2) + div = 2; + else if (div > 0xff) + div = 0xff; + + return xadc_update_adc_reg(xadc, XADC_REG_CONF2, XADC_CONF2_DIV_MASK, + div << XADC_CONF2_DIV_OFFSET); +} + +static int xadc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long info) +{ + struct xadc *xadc = iio_priv(indio_dev); + + if (info != IIO_CHAN_INFO_SAMP_FREQ) + return -EINVAL; + + return xadc_write_samplerate(xadc, val); +} + +static const struct iio_event_spec xadc_temp_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +/* Separate values for upper and lower thresholds, but only a shared enabled */ +static const struct iio_event_spec xadc_voltage_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define XADC_CHAN_TEMP(_chan, _scan_index, _addr) { \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .event_spec = xadc_temp_events, \ + .num_event_specs = ARRAY_SIZE(xadc_temp_events), \ + .scan_index = (_scan_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_CPU, \ + }, \ +} + +#define XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .event_spec = (_alarm) ? xadc_voltage_events : NULL, \ + .num_event_specs = (_alarm) ? ARRAY_SIZE(xadc_voltage_events) : 0, \ + .scan_index = (_scan_index), \ + .scan_type = { \ + .sign = ((_addr) == XADC_REG_VREFN) ? 's' : 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_CPU, \ + }, \ + .extend_name = _ext, \ +} + +static const struct iio_chan_spec xadc_channels[] = { + XADC_CHAN_TEMP(0, 8, XADC_REG_TEMP), + XADC_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true), + XADC_CHAN_VOLTAGE(1, 10, XADC_REG_VCCAUX, "vccaux", true), + XADC_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true), + XADC_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true), + XADC_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true), + XADC_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true), + XADC_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false), + XADC_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false), + XADC_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false), + XADC_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false), + XADC_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false), + XADC_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false), + XADC_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false), + XADC_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false), + XADC_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false), + XADC_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false), + XADC_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false), + XADC_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false), + XADC_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false), + XADC_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false), + XADC_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false), + XADC_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false), + XADC_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false), + XADC_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false), + XADC_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false), +}; + +static const struct iio_info xadc_info = { + .read_raw = &xadc_read_raw, + .write_raw = &xadc_write_raw, + .read_event_config = &xadc_read_event_config, + .write_event_config = &xadc_write_event_config, + .read_event_value = &xadc_read_event_value, + .write_event_value = &xadc_write_event_value, + .update_scan_mode = &xadc_update_scan_mode, +}; + +static const struct of_device_id xadc_of_match_table[] = { + { .compatible = "xlnx,zynq-xadc-1.00.a", (void *)&xadc_zynq_ops }, + { .compatible = "xlnx,axi-xadc-1.00.a", (void *)&xadc_axi_ops }, + { }, +}; +MODULE_DEVICE_TABLE(of, xadc_of_match_table); + +static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, + unsigned int *conf) +{ + struct device *dev = indio_dev->dev.parent; + struct xadc *xadc = iio_priv(indio_dev); + struct iio_chan_spec *channels, *chan; + struct device_node *chan_node, *child; + unsigned int num_channels; + const char *external_mux; + u32 ext_mux_chan; + u32 reg; + int ret; + + *conf = 0; + + ret = of_property_read_string(np, "xlnx,external-mux", &external_mux); + if (ret < 0 || strcasecmp(external_mux, "none") == 0) + xadc->external_mux_mode = XADC_EXTERNAL_MUX_NONE; + else if (strcasecmp(external_mux, "single") == 0) + xadc->external_mux_mode = XADC_EXTERNAL_MUX_SINGLE; + else if (strcasecmp(external_mux, "dual") == 0) + xadc->external_mux_mode = XADC_EXTERNAL_MUX_DUAL; + else + return -EINVAL; + + if (xadc->external_mux_mode != XADC_EXTERNAL_MUX_NONE) { + ret = of_property_read_u32(np, "xlnx,external-mux-channel", + &ext_mux_chan); + if (ret < 0) + return ret; + + if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_SINGLE) { + if (ext_mux_chan == 0) + ext_mux_chan = XADC_REG_VPVN; + else if (ext_mux_chan <= 16) + ext_mux_chan = XADC_REG_VAUX(ext_mux_chan - 1); + else + return -EINVAL; + } else { + if (ext_mux_chan > 0 && ext_mux_chan <= 8) + ext_mux_chan = XADC_REG_VAUX(ext_mux_chan - 1); + else + return -EINVAL; + } + + *conf |= XADC_CONF0_MUX | XADC_CONF0_CHAN(ext_mux_chan); + } + + channels = devm_kmemdup(dev, xadc_channels, + sizeof(xadc_channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + num_channels = 9; + chan = &channels[9]; + + chan_node = of_get_child_by_name(np, "xlnx,channels"); + if (chan_node) { + for_each_child_of_node(chan_node, child) { + if (num_channels >= ARRAY_SIZE(xadc_channels)) { + of_node_put(child); + break; + } + + ret = of_property_read_u32(child, "reg", ®); + if (ret || reg > 16) + continue; + + if (of_property_read_bool(child, "xlnx,bipolar")) + chan->scan_type.sign = 's'; + + if (reg == 0) { + chan->scan_index = 11; + chan->address = XADC_REG_VPVN; + } else { + chan->scan_index = 15 + reg; + chan->address = XADC_REG_VAUX(reg - 1); + } + num_channels++; + chan++; + } + } + of_node_put(chan_node); + + indio_dev->num_channels = num_channels; + indio_dev->channels = devm_krealloc(dev, channels, + sizeof(*channels) * num_channels, + GFP_KERNEL); + /* If we can't resize the channels array, just use the original */ + if (!indio_dev->channels) + indio_dev->channels = channels; + + return 0; +} + +static void xadc_clk_disable_unprepare(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + +static void xadc_cancel_delayed_work(void *data) +{ + struct delayed_work *work = data; + + cancel_delayed_work_sync(work); +} + +static int xadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *id; + struct iio_dev *indio_dev; + unsigned int bipolar_mask; + unsigned int conf0; + struct xadc *xadc; + int ret; + int irq; + int i; + + if (!dev->of_node) + return -ENODEV; + + id = of_match_node(xadc_of_match_table, dev->of_node); + if (!id) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -ENXIO; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*xadc)); + if (!indio_dev) + return -ENOMEM; + + xadc = iio_priv(indio_dev); + xadc->ops = id->data; + xadc->irq = irq; + init_completion(&xadc->completion); + mutex_init(&xadc->mutex); + spin_lock_init(&xadc->lock); + INIT_DELAYED_WORK(&xadc->zynq_unmask_work, xadc_zynq_unmask_worker); + + xadc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(xadc->base)) + return PTR_ERR(xadc->base); + + indio_dev->name = "xadc"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &xadc_info; + + ret = xadc_parse_dt(indio_dev, dev->of_node, &conf0); + if (ret) + return ret; + + if (xadc->ops->flags & XADC_FLAGS_BUFFERED) { + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &xadc_trigger_handler, + &xadc_buffer_ops); + if (ret) + return ret; + + xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst"); + if (IS_ERR(xadc->convst_trigger)) + return PTR_ERR(xadc->convst_trigger); + + xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev, + "samplerate"); + if (IS_ERR(xadc->samplerate_trigger)) + return PTR_ERR(xadc->samplerate_trigger); + } + + xadc->clk = devm_clk_get(dev, NULL); + if (IS_ERR(xadc->clk)) + return PTR_ERR(xadc->clk); + + ret = clk_prepare_enable(xadc->clk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, + xadc_clk_disable_unprepare, xadc->clk); + if (ret) + return ret; + + /* + * Make sure not to exceed the maximum samplerate since otherwise the + * resulting interrupt storm will soft-lock the system. + */ + if (xadc->ops->flags & XADC_FLAGS_BUFFERED) { + ret = xadc_read_samplerate(xadc); + if (ret < 0) + return ret; + + if (ret > XADC_MAX_SAMPLERATE) { + ret = xadc_write_samplerate(xadc, XADC_MAX_SAMPLERATE); + if (ret < 0) + return ret; + } + } + + ret = devm_request_irq(dev, xadc->irq, xadc->ops->interrupt_handler, 0, + dev_name(dev), indio_dev); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work, + &xadc->zynq_unmask_work); + if (ret) + return ret; + + ret = xadc->ops->setup(pdev, indio_dev, xadc->irq); + if (ret) + return ret; + + for (i = 0; i < 16; i++) + xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i), + &xadc->threshold[i]); + + ret = xadc_write_adc_reg(xadc, XADC_REG_CONF0, conf0); + if (ret) + return ret; + + bipolar_mask = 0; + for (i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].scan_type.sign == 's') + bipolar_mask |= BIT(indio_dev->channels[i].scan_index); + } + + ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(0), bipolar_mask); + if (ret) + return ret; + + ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(1), + bipolar_mask >> 16); + if (ret) + return ret; + + /* Go to non-buffered mode */ + xadc_postdisable(indio_dev); + + return devm_iio_device_register(dev, indio_dev); +} + +static struct platform_driver xadc_driver = { + .probe = xadc_probe, + .driver = { + .name = "xadc", + .of_match_table = xadc_of_match_table, + }, +}; +module_platform_driver(xadc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Xilinx XADC IIO driver"); diff --git a/drivers/iio/adc/xilinx-xadc-events.c b/drivers/iio/adc/xilinx-xadc-events.c new file mode 100644 index 000000000..2357f5857 --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc-events.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Xilinx XADC driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + */ + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/kernel.h> + +#include "xilinx-xadc.h" + +static const struct iio_chan_spec *xadc_event_to_channel( + struct iio_dev *indio_dev, unsigned int event) +{ + switch (event) { + case XADC_THRESHOLD_OT_MAX: + case XADC_THRESHOLD_TEMP_MAX: + return &indio_dev->channels[0]; + case XADC_THRESHOLD_VCCINT_MAX: + case XADC_THRESHOLD_VCCAUX_MAX: + return &indio_dev->channels[event]; + default: + return &indio_dev->channels[event-1]; + } +} + +static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event) +{ + const struct iio_chan_spec *chan; + + /* Temperature threshold error, we don't handle this yet */ + if (event == 0) + return; + + chan = xadc_event_to_channel(indio_dev, event); + + if (chan->type == IIO_TEMP) { + /* + * The temperature channel only supports over-temperature + * events. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } else { + /* + * For other channels we don't know whether it is a upper or + * lower threshold event. Userspace will have to check the + * channel value if it wants to know. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + } +} + +void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events) +{ + unsigned int i; + + for_each_set_bit(i, &events, 8) + xadc_handle_event(indio_dev, i); +} + +static unsigned int xadc_get_threshold_offset(const struct iio_chan_spec *chan, + enum iio_event_direction dir) +{ + unsigned int offset; + + if (chan->type == IIO_TEMP) { + offset = XADC_THRESHOLD_OT_MAX; + } else { + if (chan->channel < 2) + offset = chan->channel + 1; + else + offset = chan->channel + 6; + } + + if (dir == IIO_EV_DIR_FALLING) + offset += 4; + + return offset; +} + +static unsigned int xadc_get_alarm_mask(const struct iio_chan_spec *chan) +{ + if (chan->type == IIO_TEMP) + return XADC_ALARM_OT_MASK; + switch (chan->channel) { + case 0: + return XADC_ALARM_VCCINT_MASK; + case 1: + return XADC_ALARM_VCCAUX_MASK; + case 2: + return XADC_ALARM_VCCBRAM_MASK; + case 3: + return XADC_ALARM_VCCPINT_MASK; + case 4: + return XADC_ALARM_VCCPAUX_MASK; + case 5: + return XADC_ALARM_VCCODDR_MASK; + default: + /* We will never get here */ + return 0; + } +} + +int xadc_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir) +{ + struct xadc *xadc = iio_priv(indio_dev); + + return (bool)(xadc->alarm_mask & xadc_get_alarm_mask(chan)); +} + +int xadc_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + unsigned int alarm = xadc_get_alarm_mask(chan); + struct xadc *xadc = iio_priv(indio_dev); + uint16_t cfg, old_cfg; + int ret; + + mutex_lock(&xadc->mutex); + + if (state) + xadc->alarm_mask |= alarm; + else + xadc->alarm_mask &= ~alarm; + + xadc->ops->update_alarm(xadc, xadc->alarm_mask); + + ret = _xadc_read_adc_reg(xadc, XADC_REG_CONF1, &cfg); + if (ret) + goto err_out; + + old_cfg = cfg; + cfg |= XADC_CONF1_ALARM_MASK; + cfg &= ~((xadc->alarm_mask & 0xf0) << 4); /* bram, pint, paux, ddr */ + cfg &= ~((xadc->alarm_mask & 0x08) >> 3); /* ot */ + cfg &= ~((xadc->alarm_mask & 0x07) << 1); /* temp, vccint, vccaux */ + if (old_cfg != cfg) + ret = _xadc_write_adc_reg(xadc, XADC_REG_CONF1, cfg); + +err_out: + mutex_unlock(&xadc->mutex); + + return ret; +} + +/* Register value is msb aligned, the lower 4 bits are ignored */ +#define XADC_THRESHOLD_VALUE_SHIFT 4 + +int xadc_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int *val, int *val2) +{ + unsigned int offset = xadc_get_threshold_offset(chan, dir); + struct xadc *xadc = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + *val = xadc->threshold[offset]; + break; + case IIO_EV_INFO_HYSTERESIS: + *val = xadc->temp_hysteresis; + break; + default: + return -EINVAL; + } + + *val >>= XADC_THRESHOLD_VALUE_SHIFT; + + return IIO_VAL_INT; +} + +int xadc_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int val, int val2) +{ + unsigned int offset = xadc_get_threshold_offset(chan, dir); + struct xadc *xadc = iio_priv(indio_dev); + int ret = 0; + + val <<= XADC_THRESHOLD_VALUE_SHIFT; + + if (val < 0 || val > 0xffff) + return -EINVAL; + + mutex_lock(&xadc->mutex); + + switch (info) { + case IIO_EV_INFO_VALUE: + xadc->threshold[offset] = val; + break; + case IIO_EV_INFO_HYSTERESIS: + xadc->temp_hysteresis = val; + break; + default: + mutex_unlock(&xadc->mutex); + return -EINVAL; + } + + if (chan->type == IIO_TEMP) { + /* + * According to the datasheet we need to set the lower 4 bits to + * 0x3, otherwise 125 degree celsius will be used as the + * threshold. + */ + val |= 0x3; + + /* + * Since we store the hysteresis as relative (to the threshold) + * value, but the hardware expects an absolute value we need to + * recalcualte this value whenever the hysteresis or the + * threshold changes. + */ + if (xadc->threshold[offset] < xadc->temp_hysteresis) + xadc->threshold[offset + 4] = 0; + else + xadc->threshold[offset + 4] = xadc->threshold[offset] - + xadc->temp_hysteresis; + ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset + 4), + xadc->threshold[offset + 4]); + if (ret) + goto out_unlock; + } + + if (info == IIO_EV_INFO_VALUE) + ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset), val); + +out_unlock: + mutex_unlock(&xadc->mutex); + + return ret; +} diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h new file mode 100644 index 000000000..25abed9c0 --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Xilinx XADC driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + */ + +#ifndef __IIO_XILINX_XADC__ +#define __IIO_XILINX_XADC__ + +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> + +struct iio_dev; +struct clk; +struct xadc_ops; +struct platform_device; + +void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events); + +int xadc_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir); +int xadc_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, int state); +int xadc_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int *val, int *val2); +int xadc_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int val, int val2); + +enum xadc_external_mux_mode { + XADC_EXTERNAL_MUX_NONE, + XADC_EXTERNAL_MUX_SINGLE, + XADC_EXTERNAL_MUX_DUAL, +}; + +struct xadc { + void __iomem *base; + struct clk *clk; + + const struct xadc_ops *ops; + + uint16_t threshold[16]; + uint16_t temp_hysteresis; + unsigned int alarm_mask; + + uint16_t *data; + + struct iio_trigger *trigger; + struct iio_trigger *convst_trigger; + struct iio_trigger *samplerate_trigger; + + enum xadc_external_mux_mode external_mux_mode; + + unsigned int zynq_masked_alarm; + unsigned int zynq_intmask; + struct delayed_work zynq_unmask_work; + + struct mutex mutex; + spinlock_t lock; + + struct completion completion; + int irq; +}; + +struct xadc_ops { + int (*read)(struct xadc *xadc, unsigned int reg, uint16_t *val); + int (*write)(struct xadc *xadc, unsigned int reg, uint16_t val); + int (*setup)(struct platform_device *pdev, struct iio_dev *indio_dev, + int irq); + void (*update_alarm)(struct xadc *xadc, unsigned int alarm); + unsigned long (*get_dclk_rate)(struct xadc *xadc); + irqreturn_t (*interrupt_handler)(int irq, void *devid); + + unsigned int flags; +}; + +static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + lockdep_assert_held(&xadc->mutex); + return xadc->ops->read(xadc, reg, val); +} + +static inline int _xadc_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + lockdep_assert_held(&xadc->mutex); + return xadc->ops->write(xadc, reg, val); +} + +static inline int xadc_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + int ret; + + mutex_lock(&xadc->mutex); + ret = _xadc_read_adc_reg(xadc, reg, val); + mutex_unlock(&xadc->mutex); + return ret; +} + +static inline int xadc_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + int ret; + + mutex_lock(&xadc->mutex); + ret = _xadc_write_adc_reg(xadc, reg, val); + mutex_unlock(&xadc->mutex); + return ret; +} + +/* XADC hardmacro register definitions */ +#define XADC_REG_TEMP 0x00 +#define XADC_REG_VCCINT 0x01 +#define XADC_REG_VCCAUX 0x02 +#define XADC_REG_VPVN 0x03 +#define XADC_REG_VREFP 0x04 +#define XADC_REG_VREFN 0x05 +#define XADC_REG_VCCBRAM 0x06 + +#define XADC_REG_VCCPINT 0x0d +#define XADC_REG_VCCPAUX 0x0e +#define XADC_REG_VCCO_DDR 0x0f +#define XADC_REG_VAUX(x) (0x10 + (x)) + +#define XADC_REG_MAX_TEMP 0x20 +#define XADC_REG_MAX_VCCINT 0x21 +#define XADC_REG_MAX_VCCAUX 0x22 +#define XADC_REG_MAX_VCCBRAM 0x23 +#define XADC_REG_MIN_TEMP 0x24 +#define XADC_REG_MIN_VCCINT 0x25 +#define XADC_REG_MIN_VCCAUX 0x26 +#define XADC_REG_MIN_VCCBRAM 0x27 +#define XADC_REG_MAX_VCCPINT 0x28 +#define XADC_REG_MAX_VCCPAUX 0x29 +#define XADC_REG_MAX_VCCO_DDR 0x2a +#define XADC_REG_MIN_VCCPINT 0x2c +#define XADC_REG_MIN_VCCPAUX 0x2d +#define XADC_REG_MIN_VCCO_DDR 0x2e + +#define XADC_REG_CONF0 0x40 +#define XADC_REG_CONF1 0x41 +#define XADC_REG_CONF2 0x42 +#define XADC_REG_SEQ(x) (0x48 + (x)) +#define XADC_REG_INPUT_MODE(x) (0x4c + (x)) +#define XADC_REG_THRESHOLD(x) (0x50 + (x)) + +#define XADC_REG_FLAG 0x3f + +#define XADC_CONF0_EC BIT(9) +#define XADC_CONF0_ACQ BIT(8) +#define XADC_CONF0_MUX BIT(11) +#define XADC_CONF0_CHAN(x) (x) + +#define XADC_CONF1_SEQ_MASK (0xf << 12) +#define XADC_CONF1_SEQ_DEFAULT (0 << 12) +#define XADC_CONF1_SEQ_SINGLE_PASS (1 << 12) +#define XADC_CONF1_SEQ_CONTINUOUS (2 << 12) +#define XADC_CONF1_SEQ_SINGLE_CHANNEL (3 << 12) +#define XADC_CONF1_SEQ_SIMULTANEOUS (4 << 12) +#define XADC_CONF1_SEQ_INDEPENDENT (8 << 12) +#define XADC_CONF1_ALARM_MASK 0x0f0f + +#define XADC_CONF2_DIV_MASK 0xff00 +#define XADC_CONF2_DIV_OFFSET 8 + +#define XADC_CONF2_PD_MASK (0x3 << 4) +#define XADC_CONF2_PD_NONE (0x0 << 4) +#define XADC_CONF2_PD_ADC_B (0x2 << 4) +#define XADC_CONF2_PD_BOTH (0x3 << 4) + +#define XADC_ALARM_TEMP_MASK BIT(0) +#define XADC_ALARM_VCCINT_MASK BIT(1) +#define XADC_ALARM_VCCAUX_MASK BIT(2) +#define XADC_ALARM_OT_MASK BIT(3) +#define XADC_ALARM_VCCBRAM_MASK BIT(4) +#define XADC_ALARM_VCCPINT_MASK BIT(5) +#define XADC_ALARM_VCCPAUX_MASK BIT(6) +#define XADC_ALARM_VCCODDR_MASK BIT(7) + +#define XADC_THRESHOLD_TEMP_MAX 0x0 +#define XADC_THRESHOLD_VCCINT_MAX 0x1 +#define XADC_THRESHOLD_VCCAUX_MAX 0x2 +#define XADC_THRESHOLD_OT_MAX 0x3 +#define XADC_THRESHOLD_TEMP_MIN 0x4 +#define XADC_THRESHOLD_VCCINT_MIN 0x5 +#define XADC_THRESHOLD_VCCAUX_MIN 0x6 +#define XADC_THRESHOLD_OT_MIN 0x7 +#define XADC_THRESHOLD_VCCBRAM_MAX 0x8 +#define XADC_THRESHOLD_VCCPINT_MAX 0x9 +#define XADC_THRESHOLD_VCCPAUX_MAX 0xa +#define XADC_THRESHOLD_VCCODDR_MAX 0xb +#define XADC_THRESHOLD_VCCBRAM_MIN 0xc +#define XADC_THRESHOLD_VCCPINT_MIN 0xd +#define XADC_THRESHOLD_VCCPAUX_MIN 0xe +#define XADC_THRESHOLD_VCCODDR_MIN 0xf + +#endif |