From nobody Sat Feb 7 09:29:40 2026 Received: from mx0a-00128a01.pphosted.com (mx0a-00128a01.pphosted.com [148.163.135.77]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F028C25DCEC; Wed, 9 Apr 2025 12:25:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=148.163.135.77 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744201514; cv=none; b=dSgPufgIiePgPBTzEfsRZPkgC9A+lsfBUKosVOiNSs9dUviY9eejecfT4KS3cl8QtO8IxSOtsDHUgAL8k4ZVj8gnlCMgVvneulIje+IwJazuO/uUsTr1NB9v4e0xUitMWVPnQr8Wfa6haExcSU1ff3kN5UDtFwfGZZ1NifFnMUw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744201514; c=relaxed/simple; bh=uhBmUVX0AYwE4hMIedYNF8Pm5cKtjvdQRdaGuN3jakM=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=HZPqEX7DADILOPj69E+MmPET1Pegq2dIkVWxWSXYTMBPw/ZLIDUfEpZTk3uDzWVofhB83K15LaYkjBNkPMmJ+AnBU8LjscD+crWH1O6AsmyCTAU+F8bSZ/af19FClOTt7ANXw2803qmYY87eOZsEgitRaGDkXAbbMvWXe2xkyfo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com; spf=pass smtp.mailfrom=analog.com; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b=1c3luJFg; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=analog.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=analog.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=analog.com header.i=@analog.com header.b="1c3luJFg" Received: from pps.filterd (m0167089.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 5398xUcv002664; Wed, 9 Apr 2025 08:24:53 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=analog.com; h=cc :content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=DKIM; bh=iLuJW bQtbv81DewzJuR6JygT18UBAvnVPsK/+Vx7sX8=; b=1c3luJFgv9L3jiwhQ3bxU 0XamjhPP3Vg7uoUUPo856PLP9GLAbbk40eWQvWxj21Q2E5Qwa1kKmdoV8EOTAiiH r/s2k2Yr65xMLXM06+CWDB2ID7eUhKeya2pYCpzD+faS+cUBHh1efQrhdy4fEthV EmYeiqD0D4hRgreMzkoj+4VgbLk3/nss+ZCtVRFvfdSjLjkvr4Vf28pUjJ8NCAI9 bshqC6m4txKcyrf0h2j1UNG6A5YpRQvvI7ftQeRX+Uf6lGot1gKECV9oF4EKS/j4 HgFnBFyPfIH8lWmN1V62NeV4i3/vVSSzjc4XW22/snXIP3lWDERbOI35cDktAPfH g== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 45u1e6dqqw-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 09 Apr 2025 08:24:52 -0400 (EDT) Received: from ASHBMBX8.ad.analog.com (ASHBMBX8.ad.analog.com [10.64.17.5]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 539COpx6041835 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 9 Apr 2025 08:24:51 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBMBX8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.14; Wed, 9 Apr 2025 08:24:51 -0400 Received: from zeus.spd.analog.com (10.66.68.11) by ashbmbx8.ad.analog.com (10.64.17.5) with Microsoft SMTP Server id 15.2.986.14 via Frontend Transport; Wed, 9 Apr 2025 08:24:51 -0400 Received: from work.ad.analog.com (HYB-hERzalRezfV.ad.analog.com [10.65.205.9]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 539CObcs016906; Wed, 9 Apr 2025 08:24:39 -0400 From: Marcelo Schmitt To: , , CC: Ana-Maria Cusco , , , , , , , , , , Subject: [PATCH v1 2/7] iio: adc: Add basic support for AD4170 Date: Wed, 9 Apr 2025 09:24:35 -0300 Message-ID: <5f79007f0b9f9f67360d04fb904b6a59111a4ebe.1744200264.git.marcelo.schmitt@analog.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: gEQSAuoTH85ll8nIaBIWZJ4gr7X-4Ohj X-Authority-Analysis: v=2.4 cv=cdjSrmDM c=1 sm=1 tr=0 ts=67f66714 cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=IkcTkHD0fZMA:10 a=XR8D0OoHHMoA:10 a=gAnH3GRIAAAA:8 a=VwQbUJbxAAAA:8 a=Aqqe9QBHYUSGchtk9PwA:9 a=adnMvLaHktd4d8bb:21 a=3ZKOabzyN94A:10 a=QEXdDO2ut3YA:10 X-Proofpoint-GUID: gEQSAuoTH85ll8nIaBIWZJ4gr7X-4Ohj X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1095,Hydra:6.0.680,FMLib:17.12.68.34 definitions=2025-04-09_04,2025-04-08_04,2024-11-22_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 suspectscore=0 lowpriorityscore=0 impostorscore=0 spamscore=0 malwarescore=0 priorityscore=1501 adultscore=0 mlxscore=0 clxscore=1015 mlxlogscore=999 phishscore=0 bulkscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2502280000 definitions=main-2504090073 From: Ana-Maria Cusco Add support for the AD4170 ADC with the following features: - Single-shot read. - Analog front end PGA configuration. - Digital filter and sampling frequency configuration. - Calibration gain and offset configuration. - Differential and pseudo-differential input configuration. Signed-off-by: Ana-Maria Cusco Co-developed-by: Marcelo Schmitt Signed-off-by: Marcelo Schmitt --- MAINTAINERS | 1 + drivers/iio/adc/Kconfig | 16 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad4170.c | 1950 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 1968 insertions(+) create mode 100644 drivers/iio/adc/ad4170.c diff --git a/MAINTAINERS b/MAINTAINERS index 991b6e2e373a..56cd87028dfd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1343,6 +1343,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad4170.yaml +F: drivers/iio/adc/ad4170.c =20 ANALOG DEVICES INC AD4695 DRIVER M: Michael Hennerich diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 636469392945..de7139fc2a1f 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -70,6 +70,22 @@ config AD4130 To compile this driver as a module, choose M here: the module will be called ad4130. =20 + +config AD4170 + tristate "Analog Device AD4170 ADC Driver" + depends on SPI + depends on GPIOLIB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_SPI + depends on COMMON_CLK + help + Say yes here to build support for Analog Devices AD4170 SPI analog + to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad4170. + config AD4695 tristate "Analog Device AD4695 ADC Driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 07d4b832c42e..d3a1376d1f96 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_AD_SIGMA_DELTA) +=3D ad_sigma_delta.o obj-$(CONFIG_AD4000) +=3D ad4000.o obj-$(CONFIG_AD4030) +=3D ad4030.o obj-$(CONFIG_AD4130) +=3D ad4130.o +obj-$(CONFIG_AD4170) +=3D ad4170.o obj-$(CONFIG_AD4695) +=3D ad4695.o obj-$(CONFIG_AD4851) +=3D ad4851.o obj-$(CONFIG_AD7091R) +=3D ad7091r-base.o diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c new file mode 100644 index 000000000000..0d24286ac2ab --- /dev/null +++ b/drivers/iio/adc/ad4170.c @@ -0,0 +1,1950 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Analog Devices, Inc. + * Author: Ana-Maria Cusco + * Author: Marcelo Schmitt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * AD4170 registers + * Multibyte register addresses point to the most significant byte which i= s the + * address to use to get the most significant byte first (address accessed= is + * decremented by one for each data byte) + * + * Each register address define follows the AD4170__REG form. + * Each mask follows the AD4170__ form. + * E.g. AD4170_PIN_MUXING_DIG_AUX2_CTRL_MSK is for accessing DIG_AUX2_CTRL= field + * of PIN_MUXING_REG. + * Each constant follows the AD4170___ for= m. + * E.g. AD4170_PIN_MUXING_DIG_AUX2_DISABLED is the value written to + * DIG_AUX2_CTRL field of PIN_MUXING register to disable DIG_AUX2 pin. + * Some register names and register field names are shortened versions of + * their datasheet counterpart names to provide better code readability. + */ +#define AD4170_CONFIG_A_REG 0x00 +#define AD4170_DEV_CONFIG_REG 0x02 +#define AD4170_CHIP_TYPE_REG 0x03 +#define AD4170_PROD_ID_L_REG 0x04 +#define AD4170_PROD_ID_H_REG 0x05 +#define AD4170_CHIP_GRADE_REG 0x06 +#define AD4170_SPI_REV_REG 0x07 +#define AD4170_VENDOR_L_REG 0x08 +#define AD4170_VENDOR_H_REG 0x09 +#define AD4170_SCRATCH_PAD_REG 0x0A +#define AD4170_CONFIG_C_REG 0x0A +#define AD4170_IF_STATUS_A_REG 0x11 +#define AD4170_STATUS_REG 0x15 +#define AD4170_DATA_16B_REG 0x17 +#define AD4170_DATA_16B_STATUS_REG 0x1A +#define AD4170_DATA_24B_REG 0x1E +#define AD4170_PIN_MUXING_REG 0x69 +#define AD4170_ADC_CTRL_REG 0x71 +#define AD4170_CHAN_EN_REG 0x79 +#define AD4170_CHAN_SETUP_REG(x) (0x81 + 4 * (x)) +#define AD4170_CHAN_MAP_REG(x) (0x83 + 4 * (x)) +#define AD4170_MISC_REG(x) (0xC1 + 14 * (x)) +#define AD4170_AFE_REG(x) (0xC3 + 14 * (x)) +#define AD4170_FILTER_REG(x) (0xC5 + 14 * (x)) +#define AD4170_FILTER_FS_REG(x) (0xC7 + 14 * (x)) +#define AD4170_OFFSET_REG(x) (0xCA + 14 * (x)) +#define AD4170_GAIN_REG(x) (0xCD + 14 * (x)) +#define AD4170_V_BIAS_REG 0x135 +#define AD4170_FIR_CTRL 0x141 +#define AD4170_COEFF_DATA_REG 0x14A +#define AD4170_COEFF_ADDR_REG 0x14C +#define AD4170_GPIO_OUTPUT_REG 0x193 +#define AD4170_GPIO_INPUT_REG 0x195 + +#define AD4170_REG_READ_MASK BIT(14) + +/* AD4170_CONFIG_A_REG - INTERFACE_CONFIG_A REGISTER */ +#define AD4170_SW_RESET_MSK (BIT(7) | BIT(0)) + +/* AD4170_PIN_MUXING_REG */ +#define AD4170_PIN_MUXING_DIG_AUX2_CTRL_MSK GENMASK(7, 6) +#define AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK GENMASK(5, 4) +#define AD4170_PIN_MUXING_SYNC_CTRL_MSK GENMASK(3, 2) + +/* AD4170_ADC_CTRL_REG */ +#define AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK BIT(7) +#define AD4170_ADC_CTRL_CONT_READ_MSK GENMASK(5, 4) +#define AD4170_ADC_CTRL_MODE_MSK GENMASK(3, 0) + +/* AD4170_CHAN_EN_REG */ +#define AD4170_CHAN_EN(ch) BIT(ch) + +/* AD4170_CHAN_SETUP_REG */ +#define AD4170_CHAN_SETUP_SETUP_MSK GENMASK(2, 0) + +/* AD4170_CHAN_MAP_REG */ +#define AD4170_CHAN_MAP_AINP_MSK GENMASK(12, 8) +#define AD4170_CHAN_MAP_AINM_MSK GENMASK(4, 0) + +/* AD4170_MISC_REG */ +#define AD4170_MISC_CHOP_IEXC_MSK GENMASK(15, 14) +#define AD4170_MISC_CHOP_ADC_MSK GENMASK(9, 8) +#define AD4170_MISC_BURNOUT_MSK GENMASK(1, 0) + +/* AD4170_AFE_REG */ +#define AD4170_AFE_REF_BUF_M_MSK GENMASK(11, 10) +#define AD4170_AFE_REF_BUF_P_MSK GENMASK(9, 8) +#define AD4170_AFE_REF_SELECT_MSK GENMASK(6, 5) +#define AD4170_AFE_BIPOLAR_MSK BIT(4) +#define AD4170_AFE_PGA_GAIN_MSK GENMASK(3, 0) + +/* AD4170_FILTER_REG */ +#define AD4170_FILTER_FILTER_TYPE_MSK GENMASK(3, 0) + +/* AD4170 register constants */ + +/* AD4170_CHAN_MAP_REG constants */ +#define AD4170_CHAN_MAP_AIN0 0 +#define AD4170_CHAN_MAP_AIN1 1 +#define AD4170_CHAN_MAP_AIN2 2 +#define AD4170_CHAN_MAP_AIN3 3 +#define AD4170_CHAN_MAP_AIN4 4 +#define AD4170_CHAN_MAP_AIN5 5 +#define AD4170_CHAN_MAP_AIN6 6 +#define AD4170_CHAN_MAP_AIN7 7 +#define AD4170_CHAN_MAP_AIN8 8 +#define AD4170_CHAN_MAP_TEMP_SENSOR 17 +#define AD4170_CHAN_MAP_AVDD_AVSS_P 18 +#define AD4170_CHAN_MAP_AVDD_AVSS_N 18 +#define AD4170_CHAN_MAP_IOVDD_DGND_P 19 +#define AD4170_CHAN_MAP_IOVDD_DGND_N 19 +#define AD4170_CHAN_MAP_DAC_P 20 +#define AD4170_CHAN_MAP_DAC_N 20 +#define AD4170_CHAN_MAP_ALDO 21 +#define AD4170_CHAN_MAP_DLDO 22 +#define AD4170_CHAN_MAP_AVSS 23 +#define AD4170_CHAN_MAP_DGND 24 +#define AD4170_CHAN_MAP_REFIN1_P 25 +#define AD4170_CHAN_MAP_REFIN1_N 26 +#define AD4170_CHAN_MAP_REFIN2_P 27 +#define AD4170_CHAN_MAP_REFIN2_N 28 +#define AD4170_CHAN_MAP_REFOUT 29 + +/* AD4170_PIN_MUXING_REG constants */ +#define AD4170_PIN_MUXING_DIG_AUX1_DISABLED 0x0 +#define AD4170_PIN_MUXING_DIG_AUX1_RDY 0x1 +#define AD4170_PIN_MUXING_DIG_AUX1_SYNC 0x2 + +#define AD4170_PIN_MUXING_DIG_AUX2_DISABLED 0x0 +#define AD4170_PIN_MUXING_DIG_AUX2_LDAC 0x1 +#define AD4170_PIN_MUXING_DIG_AUX2_SYNC 0x2 + +#define AD4170_PIN_MUXING_SYNC_DISABLED 0x0 +#define AD4170_PIN_MUXING_SYNC_STANDARD 0x1 +#define AD4170_PIN_MUXING_SYNC_ALTERNATE 0x2 + +/* AD4170_ADC_CTRL_REG constants */ +#define AD4170_ADC_CTRL_CONT_READ_DISABLE 0x0 +#define AD4170_ADC_CTRL_CONT_READ_ENABLE 0x1 + +#define AD4170_ADC_CTRL_MODE_CONT 0x0 +#define AD4170_ADC_CTRL_MODE_SINGLE 0x4 +#define AD4170_ADC_CTRL_MODE_IDLE 0x7 + +/* AD4170_FILTER_REG constants */ +#define AD4170_FILTER_FILTER_TYPE_SINC5_AVG 0x0 +#define AD4170_FILTER_FILTER_TYPE_SINC5 0x4 +#define AD4170_FILTER_FILTER_TYPE_SINC3 0x6 + +/* Device properties and auxiliary constants */ + +#define AD4170_NUM_ANALOG_PINS 9 +#define AD4170_MAX_CHANNELS 16 +#define AD4170_MAX_ANALOG_PINS 8 +#define AD4170_MAX_SETUPS 8 +#define AD4170_INVALID_SETUP 9 +#define AD4170_NUM_CURRENT_SRC 4 +#define AD4170_DEFAULT_SAMP_RATE (125 * KILO) + +#define AD4170_INT_REF_2_5V (2500 * MILLI) + +/* Internal and external clock properties */ +#define AD4170_INT_CLOCK_16MHZ (16 * MEGA) +#define AD4170_EXT_CLOCK_MHZ_MIN (1 * MEGA) +#define AD4170_EXT_CLOCK_MHZ_MAX (17 * MEGA) + +#define AD4170_NUM_PGA_OPTIONS 10 + +/* Digital filter properties */ +#define AD4170_SINC3_MIN_FS 4 +#define AD4170_SINC3_MAX_FS 65532 +#define AD4170_SINC5_MIN_FS 1 +#define AD4170_SINC5_MAX_FS 256 + +#define AD4170_GAIN_REG_DEFAULT 0x555555 + +#define AD4170_ADC_CTRL_CONT_READ_EXIT 0xA5 + +/* Analog pin functions */ +#define AD4170_PIN_UNASIGNED 0x00 +#define AD4170_PIN_ANALOG_IN 0x01 +#define AD4170_PIN_CURRENT_OUT 0x02 + +enum ad4170_ref_buf { + AD4170_REF_BUF_PRE, /* Pre-charge referrence buffer */ + AD4170_REF_BUF_FULL, /* Full referrence buffering */ + AD4170_REF_BUF_BYPASS /* Bypass referrence buffering */ +}; + +enum ad4170_ref_select { + AD4170_REF_REFIN1, + AD4170_REF_REFIN2, + AD4170_REF_REFOUT, + AD4170_REF_AVDD +}; + +enum ad4170_filter_type { + AD4170_SINC5_AVG, + AD4170_SINC5, + AD4170_SINC3, +}; + +enum ad4170_regulator { + AD4170_AVDD_SUP, + AD4170_AVSS_SUP, + AD4170_IOVDD_SUP, + AD4170_REFIN1P_SUP, + AD4170_REFIN1N_SUP, + AD4170_REFIN2P_SUP, + AD4170_REFIN2N_SUP, + AD4170_MAX_SUP +}; + +enum ad4170_int_pin_sel { + AD4170_INT_PIN_SDO, + AD4170_INT_PIN_DIG_AUX1, +}; + +static const char * const ad4170_int_pin_names[] =3D { + [AD4170_INT_PIN_SDO] =3D "sdo", + [AD4170_INT_PIN_DIG_AUX1] =3D "dig_aux1", +}; + +static const unsigned int ad4170_sinc3_filt_fs_tbl[] =3D { + 4, 8, 12, 16, 20, 40, 48, 80, 100, 256, 500, 1000, 5000, 8332, 10000, + 25000, 50000, 65532 +}; + +#define AD4170_MAX_FS_TBL_SIZE ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl) + +static const unsigned int ad4170_sinc5_filt_fs_tbl[] =3D { + 1, 2, 4, 8, 12, 16, 20, 40, 48, 80, 100, 256 +}; + +static const char * const ad4170_chip_names[] =3D { + "ad4170", + "ad4190", + "ad4195" +}; + +/* + * There are 8 of each MISC, AFE, FILTER, FILTER_FS, OFFSET, and GAIN + * configuration registers. That is, there are 8 miscellaneous registers, = MISC0 + * to MISC7. Each MISC register is associated with a setup; MISCN is assoc= iated + * with setup number N. The other 5 above mentioned types of registers have + * analogous structure. A setup is a set of those registers. For example, + * setup 1 comprises of MISC1, AFE1, FILTER1, FILTER_FS1, OFFSET1, and GAI= N1 + * registers. Also, there are 16 CHANNEL_SETUP registers (CHANNEL_SETUP0 to + * CHANNEL_SETUP15). Each channel setup is associated with one of the 8 po= ssible + * setups. Thus, AD4170 can support up to 16 channels but, since there are= only + * 8 available setups, channels must share settings if more than 8 channel= s are + * configured. + */ +struct ad4170_setup { + u16 misc; + u16 afe; + u16 filter; + u16 filter_fs; + u32 offset; /* For calibration purposes */ + u32 gain; /* For calibration purposes */ +}; + +struct ad4170_setup_info { + struct ad4170_setup setup; + unsigned int enabled_channels; + unsigned int channels; +}; + +struct ad4170_chan_info { + int setup_num; /* Index to access state setup_infos array */ + struct ad4170_setup setup; /* cached setup */ + int input_range_uv; + u32 scale_tbl[10][2]; + int offset_tbl[10]; + bool initialized; + bool enabled; +}; + +static const char * const ad4170_filt_names[] =3D { + [AD4170_SINC5_AVG] =3D "sinc5+avg", + [AD4170_SINC5] =3D "sinc5", + [AD4170_SINC3] =3D "sinc3", +}; + +struct ad4170_state { + struct regmap *regmap8; + struct regmap *regmap16; + struct regmap *regmap24; + struct spi_device *spi; + int vrefs_uv[AD4170_MAX_SUP]; + struct mutex lock; /* Protect read-modify-write and multi write sequences= */ + struct iio_chan_spec chans[AD4170_MAX_CHANNELS]; + struct ad4170_chan_info chan_infos[AD4170_MAX_CHANNELS]; + struct ad4170_setup_info setup_infos[AD4170_MAX_SETUPS]; + u32 mclk_hz; + int pins_fn[AD4170_NUM_ANALOG_PINS]; + u32 int_pin_sel; + int sps_tbl[ARRAY_SIZE(ad4170_filt_names)][AD4170_MAX_FS_TBL_SIZE][2]; + struct completion completion; + struct iio_trigger *trig; +}; + +static void ad4170_fill_sps_tbl(struct ad4170_state *st) +{ + unsigned int tmp0, tmp1, i; + + /* + * The ODR can be calculated the same way for sinc5+avg, sinc5, and + * sinc3 filter types with the exception that sinc5 filter has a + * narrowed range of allowed FILTER_FS values. + */ + for (i =3D 0; i < ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl); i++) { + tmp0 =3D div_u64_rem(st->mclk_hz, 32 * ad4170_sinc3_filt_fs_tbl[i], + &tmp1); + tmp1 =3D mult_frac(tmp1, MICRO, 32 * ad4170_sinc3_filt_fs_tbl[i]); + /* Fill sinc5+avg filter SPS table */ + st->sps_tbl[AD4170_SINC5_AVG][i][0] =3D tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC5_AVG][i][1] =3D tmp1; /* Fractional part */ + + /* Fill sinc3 filter SPS table */ + st->sps_tbl[AD4170_SINC3][i][0] =3D tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC3][i][1] =3D tmp1; /* Fractional part */ + } + /* Sinc5 filter ODR doesn't use all FILTER_FS bits */ + for (i =3D 0; i < ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl); i++) { + tmp0 =3D div_u64_rem(st->mclk_hz, 32 * ad4170_sinc5_filt_fs_tbl[i], + &tmp1); + tmp1 =3D mult_frac(tmp1, MICRO, 32 * ad4170_sinc5_filt_fs_tbl[i]); + /* Fill sinc5 filter SPS table */ + st->sps_tbl[AD4170_SINC5][i][0] =3D tmp0; /* Integer part */ + st->sps_tbl[AD4170_SINC5][i][1] =3D tmp1; /* Fractional part */ + } +} + +static const struct regmap_range ad4170_8bit_rd_reg_range[] =3D { + regmap_reg_range(AD4170_CONFIG_A_REG, AD4170_IF_STATUS_A_REG), +}; + +static const struct regmap_access_table ad4170_regmap8_rd_table =3D { + .yes_ranges =3D ad4170_8bit_rd_reg_range, + .n_yes_ranges =3D ARRAY_SIZE(ad4170_8bit_rd_reg_range), +}; + +static const struct regmap_range ad4170_8bit_wr_reg_range[] =3D { + regmap_reg_range(AD4170_CONFIG_A_REG, AD4170_DEV_CONFIG_REG), + regmap_reg_range(AD4170_SCRATCH_PAD_REG, AD4170_SCRATCH_PAD_REG), + regmap_reg_range(AD4170_CONFIG_C_REG, AD4170_IF_STATUS_A_REG), +}; + +static const struct regmap_access_table ad4170_regmap8_wr_table =3D { + .yes_ranges =3D ad4170_8bit_wr_reg_range, + .n_yes_ranges =3D ARRAY_SIZE(ad4170_8bit_wr_reg_range), +}; + +static const struct regmap_range ad4170_16bit_rd_reg_range[] =3D { + regmap_reg_range(AD4170_STATUS_REG, AD4170_DATA_16B_REG), + regmap_reg_range(AD4170_PIN_MUXING_REG, + AD4170_CHAN_MAP_REG(AD4170_MAX_CHANNELS)), + regmap_reg_range(AD4170_MISC_REG(0), AD4170_FILTER_FS_REG(0)), + regmap_reg_range(AD4170_MISC_REG(1), AD4170_FILTER_FS_REG(1)), + regmap_reg_range(AD4170_MISC_REG(2), AD4170_FILTER_FS_REG(2)), + regmap_reg_range(AD4170_MISC_REG(3), AD4170_FILTER_FS_REG(3)), + regmap_reg_range(AD4170_MISC_REG(4), AD4170_FILTER_FS_REG(4)), + regmap_reg_range(AD4170_MISC_REG(5), AD4170_FILTER_FS_REG(5)), + regmap_reg_range(AD4170_MISC_REG(6), AD4170_FILTER_FS_REG(6)), + regmap_reg_range(AD4170_MISC_REG(7), AD4170_FILTER_FS_REG(7)), + regmap_reg_range(AD4170_V_BIAS_REG, AD4170_FIR_CTRL), + regmap_reg_range(AD4170_COEFF_ADDR_REG, AD4170_GPIO_INPUT_REG), +}; + +static const struct regmap_access_table ad4170_regmap16_rd_table =3D { + .yes_ranges =3D ad4170_16bit_rd_reg_range, + .n_yes_ranges =3D ARRAY_SIZE(ad4170_16bit_rd_reg_range), +}; + +static const struct regmap_range ad4170_16bit_wr_reg_range[] =3D { + regmap_reg_range(AD4170_PIN_MUXING_REG, + AD4170_CHAN_MAP_REG(AD4170_MAX_CHANNELS)), + regmap_reg_range(AD4170_MISC_REG(0), AD4170_FILTER_FS_REG(0)), + regmap_reg_range(AD4170_MISC_REG(1), AD4170_FILTER_FS_REG(1)), + regmap_reg_range(AD4170_MISC_REG(2), AD4170_FILTER_FS_REG(2)), + regmap_reg_range(AD4170_MISC_REG(3), AD4170_FILTER_FS_REG(3)), + regmap_reg_range(AD4170_MISC_REG(4), AD4170_FILTER_FS_REG(4)), + regmap_reg_range(AD4170_MISC_REG(5), AD4170_FILTER_FS_REG(5)), + regmap_reg_range(AD4170_MISC_REG(6), AD4170_FILTER_FS_REG(6)), + regmap_reg_range(AD4170_MISC_REG(7), AD4170_FILTER_FS_REG(7)), + regmap_reg_range(AD4170_V_BIAS_REG, AD4170_FIR_CTRL), + regmap_reg_range(AD4170_COEFF_ADDR_REG, AD4170_GPIO_OUTPUT_REG), +}; + +static const struct regmap_access_table ad4170_regmap16_wr_table =3D { + .yes_ranges =3D ad4170_16bit_wr_reg_range, + .n_yes_ranges =3D ARRAY_SIZE(ad4170_16bit_wr_reg_range), +}; + +static const struct regmap_range ad4170_24bit_rd_reg_range[] =3D { + regmap_reg_range(AD4170_DATA_16B_STATUS_REG, AD4170_DATA_24B_REG), + regmap_reg_range(AD4170_OFFSET_REG(0), AD4170_GAIN_REG(0)), + regmap_reg_range(AD4170_OFFSET_REG(1), AD4170_GAIN_REG(1)), + regmap_reg_range(AD4170_OFFSET_REG(2), AD4170_GAIN_REG(2)), + regmap_reg_range(AD4170_OFFSET_REG(3), AD4170_GAIN_REG(3)), + regmap_reg_range(AD4170_OFFSET_REG(4), AD4170_GAIN_REG(4)), + regmap_reg_range(AD4170_OFFSET_REG(5), AD4170_GAIN_REG(5)), + regmap_reg_range(AD4170_OFFSET_REG(6), AD4170_GAIN_REG(6)), + regmap_reg_range(AD4170_OFFSET_REG(7), AD4170_GAIN_REG(7)), +}; + +static const struct regmap_access_table ad4170_regmap24_rd_table =3D { + .yes_ranges =3D ad4170_24bit_rd_reg_range, + .n_yes_ranges =3D ARRAY_SIZE(ad4170_24bit_rd_reg_range), +}; + +static const struct regmap_range ad4170_24bit_wr_reg_range[] =3D { + regmap_reg_range(AD4170_OFFSET_REG(0), AD4170_GAIN_REG(0)), + regmap_reg_range(AD4170_OFFSET_REG(1), AD4170_GAIN_REG(1)), + regmap_reg_range(AD4170_OFFSET_REG(2), AD4170_GAIN_REG(2)), + regmap_reg_range(AD4170_OFFSET_REG(3), AD4170_GAIN_REG(3)), + regmap_reg_range(AD4170_OFFSET_REG(4), AD4170_GAIN_REG(4)), + regmap_reg_range(AD4170_OFFSET_REG(5), AD4170_GAIN_REG(5)), + regmap_reg_range(AD4170_OFFSET_REG(6), AD4170_GAIN_REG(6)), + regmap_reg_range(AD4170_OFFSET_REG(7), AD4170_GAIN_REG(7)), +}; + +static const struct regmap_access_table ad4170_regmap24_wr_table =3D { + .yes_ranges =3D ad4170_24bit_wr_reg_range, + .n_yes_ranges =3D ARRAY_SIZE(ad4170_24bit_wr_reg_range), +}; + +static int ad4170_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + int ret =3D -EINVAL; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + if (readval) { + if (regmap_check_range_table(st->regmap8, reg, + &ad4170_regmap8_rd_table)) + ret =3D regmap_read(st->regmap8, reg, readval); + + if (regmap_check_range_table(st->regmap16, reg, + &ad4170_regmap16_rd_table)) + ret =3D regmap_read(st->regmap16, reg, readval); + + if (regmap_check_range_table(st->regmap24, reg, + &ad4170_regmap24_rd_table)) + ret =3D regmap_read(st->regmap24, reg, readval); + } else { + if (regmap_check_range_table(st->regmap8, reg, + &ad4170_regmap8_wr_table)) + ret =3D regmap_write(st->regmap8, reg, writeval); + + if (regmap_check_range_table(st->regmap16, reg, + &ad4170_regmap16_wr_table)) + ret =3D regmap_write(st->regmap16, reg, writeval); + + if (regmap_check_range_table(st->regmap24, reg, + &ad4170_regmap24_wr_table)) + ret =3D regmap_write(st->regmap24, reg, writeval); + } + iio_device_release_direct(indio_dev); + + return ret; +} + +static const struct regmap_config ad4170_regmap8_config =3D { + .name =3D "ad4170-8", + .reg_bits =3D 16, + .val_bits =3D 8, + .max_register =3D AD4170_IF_STATUS_A_REG, + .rd_table =3D &ad4170_regmap8_rd_table, + .wr_table =3D &ad4170_regmap8_wr_table, + .read_flag_mask =3D BIT(6), + .zero_flag_mask =3D BIT(7), + .reg_format_endian =3D REGMAP_ENDIAN_BIG, + .val_format_endian =3D REGMAP_ENDIAN_BIG, +}; + +static const struct regmap_config ad4170_regmap16_config =3D { + .name =3D "ad4170-16", + .reg_bits =3D 16, + .val_bits =3D 16, + .rd_table =3D &ad4170_regmap16_rd_table, + .wr_table =3D &ad4170_regmap16_wr_table, + .read_flag_mask =3D BIT(6), + .zero_flag_mask =3D BIT(7), + .reg_format_endian =3D REGMAP_ENDIAN_BIG, + .val_format_endian =3D REGMAP_ENDIAN_BIG, +}; + +static const struct regmap_config ad4170_regmap24_config =3D { + .name =3D "ad4170-24", + .reg_bits =3D 16, + .val_bits =3D 24, + .rd_table =3D &ad4170_regmap24_rd_table, + .wr_table =3D &ad4170_regmap24_wr_table, + .read_flag_mask =3D BIT(6), + .zero_flag_mask =3D BIT(7), + .reg_format_endian =3D REGMAP_ENDIAN_BIG, + .val_format_endian =3D REGMAP_ENDIAN_BIG, +}; + +static int ad4170_find_setup(struct ad4170_state *st, + struct ad4170_setup *target_setup, + unsigned int *setup_num, bool *overwrite) +{ + unsigned int i; + + *setup_num =3D AD4170_INVALID_SETUP; + *overwrite =3D false; + + for (i =3D 0; i < AD4170_MAX_SETUPS; i++) { + struct ad4170_setup_info *setup_info =3D &st->setup_infos[i]; + + /* Immediately accept a matching setup. */ + if (!memcmp(target_setup, &setup_info->setup, + sizeof(*target_setup))) { + *setup_num =3D i; + return 0; + } + + /* Ignore all setups which are used by enabled channels. */ + if (setup_info->enabled_channels) + continue; + + /* Find the least used slot. */ + if (*setup_num =3D=3D AD4170_INVALID_SETUP || + setup_info->channels < st->setup_infos[*setup_num].channels) + *setup_num =3D i; + } + + if (*setup_num =3D=3D AD4170_INVALID_SETUP) + return -EINVAL; + + *overwrite =3D true; + return 0; +} + +static void ad4170_unlink_channel(struct ad4170_state *st, unsigned int ch= annel) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[channel]; + struct ad4170_setup_info *setup_info =3D &st->setup_infos[chan_info->setu= p_num]; + + chan_info->setup_num =3D AD4170_INVALID_SETUP; + setup_info->channels--; +} + +static int ad4170_unlink_setup(struct ad4170_state *st, unsigned int setup= _num) +{ + unsigned int i; + + for (i =3D 0; i < AD4170_MAX_CHANNELS; i++) { + struct ad4170_chan_info *chan_info =3D &st->chan_infos[i]; + + if (!chan_info->initialized || chan_info->setup_num !=3D setup_num) + continue; + + ad4170_unlink_channel(st, i); + } + return 0; +} + +static int ad4170_link_channel_setup(struct ad4170_state *st, + unsigned int chan_addr, + unsigned int setup_num) +{ + struct ad4170_setup_info *setup_info =3D &st->setup_infos[setup_num]; + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan_addr]; + int ret; + + ret =3D regmap_update_bits(st->regmap16, AD4170_CHAN_SETUP_REG(chan_addr), + AD4170_CHAN_SETUP_SETUP_MSK, + FIELD_PREP(AD4170_CHAN_SETUP_SETUP_MSK, + setup_num)); + if (ret) + return ret; + + chan_info->setup_num =3D setup_num; + setup_info->channels++; + return 0; +} + +/* + * Sets the ADC operating mode. Supported modes are + * - Continuous conversion mode (default) + * - Single conversion mode + * - Idle mode + */ +static int ad4170_set_mode(struct ad4170_state *st, unsigned int mode) +{ + return regmap_update_bits(st->regmap16, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MODE_MSK, + FIELD_PREP(AD4170_ADC_CTRL_MODE_MSK, mode)); +} + +static int ad4170_write_setup(struct ad4170_state *st, unsigned int setup_= num, + struct ad4170_setup *setup) +{ + int ret; + + /* + * It is recommended to place the ADC in standby mode or idle mode to + * write to OFFSET and GAIN registers. + */ + ret =3D ad4170_set_mode(st, AD4170_ADC_CTRL_MODE_IDLE); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap16, AD4170_MISC_REG(setup_num), setup->mis= c); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap16, AD4170_AFE_REG(setup_num), setup->afe); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap16, AD4170_FILTER_REG(setup_num), + setup->filter); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap16, AD4170_FILTER_FS_REG(setup_num), + setup->filter_fs); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap24, AD4170_OFFSET_REG(setup_num), + setup->offset); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap24, AD4170_GAIN_REG(setup_num), setup->gai= n); + if (ret) + return ret; + + memcpy(&st->setup_infos[setup_num].setup, setup, sizeof(*setup)); + return 0; +} + +static int ad4170_write_channel_setup(struct ad4170_state *st, + unsigned int chan_addr, bool on_enable) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan_addr]; + bool overwrite; + int setup_num; + int ret; + + /* + * Similar to AD4130 driver, the following cases need to be handled. + * + * 1. Enabled and linked channel with setup changes: + * - Find a setup. If not possible, return error. + * - Unlink channel from current setup. + * - If the setup found has only disabled channels linked to it, + * unlink all channels, and write the new setup to it. + * - Link channel to new setup. + * + * 2. Soon to be enabled and unlinked channel: + * - Find a setup. If not possible, return error. + * - If the setup found has only disabled channels linked to it, + * unlink all channels, and write the new setup to it. + * - Link channel to the setup. + * + * 3. Disabled and linked channel with setup changes: + * - Unlink channel from current setup. + * + * 4. Soon to be enabled and linked channel: + * 5. Disabled and unlinked channel with setup changes: + * - Do nothing. + */ + + /* Case 4 */ + if (on_enable && chan_info->setup_num !=3D AD4170_INVALID_SETUP) + return 0; + + if (!on_enable && !chan_info->enabled) { + if (chan_info->setup_num !=3D AD4170_INVALID_SETUP) + /* Case 3 */ + ad4170_unlink_channel(st, chan_addr); + + /* Cases 3 & 5 */ + return 0; + } + + /* Cases 1 & 2 */ + ret =3D ad4170_find_setup(st, &chan_info->setup, &setup_num, &overwrite); + if (ret) + return ret; + + if (chan_info->setup_num !=3D AD4170_INVALID_SETUP) + /* Case 1 */ + ad4170_unlink_channel(st, chan_addr); + + if (overwrite) { + ret =3D ad4170_unlink_setup(st, setup_num); + if (ret) + return ret; + + ret =3D ad4170_write_setup(st, setup_num, &chan_info->setup); + if (ret) + return ret; + } + + return ad4170_link_channel_setup(st, chan_addr, setup_num); +} + +static int ad4170_set_channel_enable(struct ad4170_state *st, + unsigned int chan_addr, bool status) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan_addr]; + struct ad4170_setup_info *setup_info; + int ret; + + if (chan_info->enabled =3D=3D status) + return 0; + + if (status) { + ret =3D ad4170_write_channel_setup(st, chan_addr, true); + if (ret) + return ret; + } + + setup_info =3D &st->setup_infos[chan_info->setup_num]; + + ret =3D regmap_update_bits(st->regmap16, AD4170_CHAN_EN_REG, + AD4170_CHAN_EN(chan_addr), + status ? AD4170_CHAN_EN(chan_addr) : 0); + if (ret) + return ret; + + setup_info->enabled_channels +=3D status ? 1 : -1; + chan_info->enabled =3D status; + return 0; +} + +static int __ad4170_get_filter_type(unsigned int filter) +{ + u16 f_conf =3D FIELD_GET(AD4170_FILTER_FILTER_TYPE_MSK, filter); + + switch (f_conf) { + case AD4170_FILTER_FILTER_TYPE_SINC5_AVG: + return AD4170_SINC5_AVG; + case AD4170_FILTER_FILTER_TYPE_SINC5: + return AD4170_SINC5; + case AD4170_FILTER_FILTER_TYPE_SINC3: + return AD4170_SINC3; + default: + return -EINVAL; + } +} + +static int ad4170_set_filter_type(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + unsigned int val) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + unsigned int old_filter_fs, old_filter, filter_type_conf; + int ret =3D 0; + + switch (val) { + case AD4170_SINC5_AVG: + filter_type_conf =3D AD4170_FILTER_FILTER_TYPE_SINC5_AVG; + break; + case AD4170_SINC5: + filter_type_conf =3D AD4170_FILTER_FILTER_TYPE_SINC5; + break; + case AD4170_SINC3: + filter_type_conf =3D AD4170_FILTER_FILTER_TYPE_SINC3; + break; + default: + return -EINVAL; + } + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + /* + * The filters provide the same ODR for a given filter_fs value but + * there are different minimum and maximum filter_fs limits for each + * filter. The filter_fs value will be adjusted if the current filter_fs + * is out of the limits of the just requested filter. Since the + * filter_fs value affects the ODR (sampling_frequency), changing the + * filter may lead to a change in the sampling frequency. + */ + old_filter =3D setup->filter; + old_filter_fs =3D setup->filter_fs; + if (val =3D=3D AD4170_SINC5_AVG || val =3D=3D AD4170_SINC3) { + if (setup->filter_fs < AD4170_SINC3_MIN_FS) + setup->filter_fs =3D AD4170_SINC3_MIN_FS; + if (setup->filter_fs > AD4170_SINC3_MAX_FS) + setup->filter_fs =3D AD4170_SINC3_MAX_FS; + + } else if (val =3D=3D AD4170_SINC5) { + if (setup->filter_fs < AD4170_SINC5_MIN_FS) + setup->filter_fs =3D AD4170_SINC5_MIN_FS; + if (setup->filter_fs > AD4170_SINC5_MAX_FS) + setup->filter_fs =3D AD4170_SINC5_MAX_FS; + } + + setup->filter &=3D ~AD4170_FILTER_FILTER_TYPE_MSK; + setup->filter |=3D FIELD_PREP(AD4170_FILTER_FILTER_TYPE_MSK, + filter_type_conf); + + guard(mutex)(&st->lock); + ret =3D ad4170_write_channel_setup(st, chan->address, false); + if (ret) { + setup->filter =3D old_filter; + setup->filter_fs =3D old_filter_fs; + } + + iio_device_release_direct(indio_dev); + return ret; +} + +static int ad4170_get_filter_type(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + + return __ad4170_get_filter_type(setup->filter); +} + +static const struct iio_enum ad4170_filter_type_enum =3D { + .items =3D ad4170_filt_names, + .num_items =3D ARRAY_SIZE(ad4170_filt_names), + .get =3D ad4170_get_filter_type, + .set =3D ad4170_set_filter_type, +}; + +static const struct iio_chan_spec_ext_info ad4170_filter_type_ext_info[] = =3D { + IIO_ENUM("filter_type", IIO_SEPARATE, &ad4170_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE, + &ad4170_filter_type_enum), + { } +}; + +static const struct iio_chan_spec ad4170_channel_template =3D { + .type =3D IIO_VOLTAGE, + .indexed =3D 1, + .differential =3D 1, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available =3D BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info =3D ad4170_filter_type_ext_info, + .scan_type =3D { + .realbits =3D 24, + .storagebits =3D 32, + .endianness =3D IIO_BE, + }, +}; + +/* + * Receives the number of a multiplexed AD4170 input (ain_n), and stores t= he + * voltage (in =C2=B5V) of the specified input into ain_voltage. If the in= put number + * is a ordinary analog input (AIN0 to AIN8), stores zero into ain_voltage. + * If a voltage regulator required by a special input is unavailable, retu= rn + * error code. Return 0 on success. + */ +static int ad4170_get_ain_voltage_uv(struct ad4170_state *st, int ain_n, + int *ain_voltage) +{ + struct device *dev =3D &st->spi->dev; + + *ain_voltage =3D 0; + if (ain_n <=3D AD4170_CHAN_MAP_TEMP_SENSOR) + return 0; + + switch (ain_n) { + case AD4170_CHAN_MAP_AVDD_AVSS_N: + *ain_voltage =3D (st->vrefs_uv[AD4170_AVDD_SUP] + - st->vrefs_uv[AD4170_AVSS_SUP]) / 5; + return 0; + case AD4170_CHAN_MAP_IOVDD_DGND_N: + *ain_voltage =3D st->vrefs_uv[AD4170_IOVDD_SUP] / 5; + return 0; + case AD4170_CHAN_MAP_AVSS: + *ain_voltage =3D st->vrefs_uv[AD4170_AVSS_SUP]; + return 0; + case AD4170_CHAN_MAP_DGND: + *ain_voltage =3D 0; + return 0; + case AD4170_CHAN_MAP_REFIN1_P: + if (st->vrefs_uv[AD4170_REFIN1P_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN+ but ref not provided\n"); + + *ain_voltage =3D st->vrefs_uv[AD4170_REFIN1P_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN1_N: + if (st->vrefs_uv[AD4170_REFIN1N_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN- but ref not provided\n"); + + *ain_voltage =3D st->vrefs_uv[AD4170_REFIN1N_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN2_P: + if (st->vrefs_uv[AD4170_REFIN2P_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN2+ but ref not provided\n"); + + *ain_voltage =3D st->vrefs_uv[AD4170_REFIN2P_SUP]; + return 0; + case AD4170_CHAN_MAP_REFIN2_N: + if (st->vrefs_uv[AD4170_REFIN2N_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "input set to REFIN2- but ref not provided\n"); + + *ain_voltage =3D st->vrefs_uv[AD4170_REFIN2N_SUP]; + return 0; + case AD4170_CHAN_MAP_REFOUT: + /* REFOUT is 2.5V relative to AVSS so take that into account */ + *ain_voltage =3D st->vrefs_uv[AD4170_AVSS_SUP] + AD4170_INT_REF_2_5V; + return 0; + } + return -EINVAL; +} + +static int ad4170_validate_analog_input(struct ad4170_state *st, int pin) +{ + if (pin <=3D AD4170_MAX_ANALOG_PINS) { + if (st->pins_fn[pin] & AD4170_PIN_CURRENT_OUT) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Pin %d already used with fn %u.\n", + pin, st->pins_fn[pin]); + + st->pins_fn[pin] |=3D AD4170_PIN_ANALOG_IN; + } + return 0; +} + +static int ad4170_validate_channel_input(struct ad4170_state *st, int pin,= bool com) +{ + /* Check common-mode input pin is mapped to a special input. */ + if (com && (pin < AD4170_CHAN_MAP_AVDD_AVSS_P || pin > AD4170_CHAN_MAP_RE= FOUT)) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid common-mode input pin number. %d\n", + pin); + + /* Check differential input pin is mapped to a analog input pin. */ + if (!com && pin > AD4170_MAX_ANALOG_PINS) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Invalid analog input pin number. %d\n", + pin); + + return ad4170_validate_analog_input(st, pin); +} + +/* + * Verifies whether the channel input configuration is valid by checking t= he + * input numbers. + * Returns 0 on valid channel input configuration. -EINVAL otherwise. + */ +static int ad4170_validate_channel(struct ad4170_state *st, + struct iio_chan_spec const *chan) +{ + int ret; + + ret =3D ad4170_validate_channel_input(st, chan->channel, false); + if (ret < 0) + return ret; + + return ad4170_validate_channel_input(st, chan->channel2, + !chan->differential); +} + +/* + * Verifies whether the channel configuration is valid by checking the pro= vided + * input type, polarity, and voltage references result in a sane input ran= ge. + * Returns negative error code on failure. + */ +static int ad4170_get_input_range(struct ad4170_state *st, + struct iio_chan_spec const *chan, + unsigned int ch_reg, unsigned int ref_sel) +{ + bool bipolar =3D chan->scan_type.sign =3D=3D 's'; + struct device *dev =3D &st->spi->dev; + int refp, refn, ain_voltage, ret; + + switch (ref_sel) { + case AD4170_REF_REFIN1: + if (st->vrefs_uv[AD4170_REFIN1P_SUP] =3D=3D -ENODEV || + st->vrefs_uv[AD4170_REFIN1N_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "REFIN+, REFIN=E2=88=92 selected but not provided\n"); + + refp =3D st->vrefs_uv[AD4170_REFIN1P_SUP]; + refn =3D st->vrefs_uv[AD4170_REFIN1N_SUP]; + break; + case AD4170_REF_REFIN2: + if (st->vrefs_uv[AD4170_REFIN2P_SUP] =3D=3D -ENODEV || + st->vrefs_uv[AD4170_REFIN2N_SUP] =3D=3D -ENODEV) + return dev_err_probe(dev, -ENODEV, + "REFIN2+, REFIN2=E2=88=92 selected but not provided\n"); + + refp =3D st->vrefs_uv[AD4170_REFIN2P_SUP]; + refn =3D st->vrefs_uv[AD4170_REFIN2N_SUP]; + break; + case AD4170_REF_AVDD: + refp =3D st->vrefs_uv[AD4170_AVDD_SUP]; + refn =3D st->vrefs_uv[AD4170_AVSS_SUP]; + break; + case AD4170_REF_REFOUT: + /* REFOUT is 2.5 V relative to AVSS */ + refp =3D st->vrefs_uv[AD4170_AVSS_SUP] + AD4170_INT_REF_2_5V; + refn =3D st->vrefs_uv[AD4170_AVSS_SUP]; + break; + default: + return -EINVAL; + } + + /* + * Find out the analog input range from the channel type, polarity, and + * voltage reference selection. + * AD4170 channels are either differential or pseudo-differential. + * Diff input voltage range: =E2=88=92VREF/gain to +VREF/gain (datasheet = page 6) + * Pseudo-diff input voltage range: 0 to VREF/gain (datasheet page 6) + */ + if (chan->differential) { + if (!bipolar) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Channel %u differential unipolar\n", + ch_reg); + + /* + * Differential bipolar channel. + * avss-supply is never above 0V. + * Assuming refin1n-supply not above 0V. + * Assuming refin2n-supply not above 0V. + */ + return refp + abs(refn); + } + /* + * Some configurations can lead to invalid setups. + * For example, if AVSS =3D -2.5V, REF_SELECT set to REFOUT (REFOUT/AVSS), + * and pseudo-diff channel configuration set, then the input range + * should go from 0V to +VREF (single-ended - datasheet pg 10), but + * REFOUT/AVSS range would be -2.5V to 0V. + * Check the positive reference is higher than 0V for pseudo-diff + * channels. + */ + if (refp <=3D 0) + return dev_err_probe(&st->spi->dev, -EINVAL, + "REF+ <=3D GND for pseudo-diff chan %u\n", + ch_reg); + + if (bipolar) + return refp; + + /* + * Pseudo-differential unipolar channel. + * Input expected to swing from IN- to +VREF. + */ + ret =3D ad4170_get_ain_voltage_uv(st, chan->channel2, &ain_voltage); + if (ret < 0) + return ret; + + if (refp - ain_voltage <=3D 0) + return dev_err_probe(&st->spi->dev, -EINVAL, + "Negative input >=3D REF+ for pseudo-diff chan %u\n", + ch_reg); + + return refp - ain_voltage; +} + +static int ad4170_read_sample(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + int settling_time_ms, ret; + + guard(mutex)(&st->lock); + /* + * The ADC sequences through all enabled channels. That can lead to + * incorrect channel being sampled if a previous read would have left a + * different channel enabled. Thus, always enable and disable the + * channel on single-shot read. + */ + ret =3D ad4170_set_channel_enable(st, chan->address, true); + if (ret) + return ret; + + reinit_completion(&st->completion); + + ret =3D ad4170_set_mode(st, AD4170_ADC_CTRL_MODE_SINGLE); + if (ret) + goto err_disable; + + /* + * When a channel is manually selected by the user, the ADC needs an + * extra time to provide the first stable conversion. The ADC settling + * time depends on the filter type, filter frequency, and ADC clock + * frequency (see datasheet page 53). The maximum settling time among + * all filter configurations is 6291164 / fCLK. Use that formula to wait + * for sufficient time whatever the filter configuration may be. + */ + settling_time_ms =3D DIV_ROUND_UP(6291164 * MILLI, st->mclk_hz); + ret =3D wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(settling_time_ms)); + if (!ret) + dev_dbg(&st->spi->dev, + "No Data Ready signal. Reading after delay.\n"); + + ret =3D regmap_read(st->regmap24, AD4170_DATA_24B_REG, val); + if (ret) + goto err_disable; + + if (chan->scan_type.sign =3D=3D 's') + *val =3D sign_extend32(*val, chan->scan_type.realbits - 1); + +err_disable: + ret =3D ad4170_set_channel_enable(st, chan->address, false); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad4170_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + enum ad4170_filter_type f_type; + unsigned int pga, fs_idx; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret =3D ad4170_read_sample(indio_dev, chan, val); + iio_device_release_direct(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: + pga =3D FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + *val =3D chan_info->scale_tbl[pga][0]; + *val2 =3D chan_info->scale_tbl[pga][1]; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + pga =3D FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + *val =3D chan_info->offset_tbl[pga]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + f_type =3D __ad4170_get_filter_type(setup->filter); + switch (f_type) { + case AD4170_SINC5_AVG: + fallthrough; + case AD4170_SINC3: + fs_idx =3D find_closest(setup->filter_fs, + ad4170_sinc3_filt_fs_tbl, + ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl)); + *val =3D st->sps_tbl[f_type][fs_idx][0]; + *val2 =3D st->sps_tbl[f_type][fs_idx][1]; + break; + case AD4170_SINC5: + fs_idx =3D find_closest(setup->filter_fs, + ad4170_sinc5_filt_fs_tbl, + ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl)); + *val =3D st->sps_tbl[f_type][fs_idx][0]; + *val2 =3D st->sps_tbl[f_type][fs_idx][1]; + break; + default: + return -EINVAL; + } + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + *val =3D setup->offset; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBSCALE: + *val =3D setup->gain; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4170_fill_scale_tbl(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + int bipolar =3D chan->scan_type.sign =3D=3D 's' ? 1 : 0; + int precision_bits =3D chan->scan_type.realbits; + int pga, ainm_voltage, ret; + unsigned long long offset; + + ainm_voltage =3D 0; + ret =3D ad4170_get_ain_voltage_uv(st, chan->channel2, &ainm_voltage); + if (ret < 0) + return dev_err_probe(&st->spi->dev, ret, + "Failed to fill scale table\n"); + + for (pga =3D 0; pga < AD4170_NUM_PGA_OPTIONS; pga++) { + u64 nv; + unsigned int lshift, rshift; + + /* + * The scale factor to get ADC output codes to values in mV + * units is given by: + * _scale =3D (input_range / gain) / 2^precision + * AD4170 gain is a power of 2 so the above can be written as + * _scale =3D input_range / 2^(precision + gain) + * Keep the input range in =C2=B5V to avoid truncating the less + * significan bits when right shifting it so to preserve scale + * precision. + */ + nv =3D (u64)chan_info->input_range_uv * NANO; + lshift =3D (pga >> 3 & 1); /* handle cases 8 and 9 */ + rshift =3D precision_bits - bipolar + (pga & 0x7) - lshift; + chan_info->scale_tbl[pga][0] =3D 0; + chan_info->scale_tbl[pga][1] =3D div_u64(nv >> rshift, MILLI); + + /* + * If the negative input is not at GND, the conversion result + * (which is relative to IN-) will be offset by the level at IN-. + * Use the scale factor the other way around to go from a known + * voltage to the corresponding ADC output code. + * With that, we are able to get to what would be the output + * code for the voltage at the negative input. + * If the negative input is not fixed, there is no offset. + */ + offset =3D ((unsigned long long)abs(ainm_voltage)) * MICRO; + offset =3D DIV_ROUND_CLOSEST_ULL(offset, chan_info->scale_tbl[pga][1]); + + /* + * After divided by the scale, offset will always fit into 31 + * bits. For _raw + _offset to be relative to GND, the value + * provided as _offset is of opposite sign than the real offset. + */ + if (ainm_voltage > 0) + chan_info->offset_tbl[pga] =3D -(int)(offset); + else + chan_info->offset_tbl[pga] =3D (int)(offset); + } + return 0; +} + +static int ad4170_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + enum ad4170_filter_type f_type; + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *vals =3D (int *)chan_info->scale_tbl; + *length =3D ARRAY_SIZE(chan_info->scale_tbl) * 2; + *type =3D IIO_VAL_INT_PLUS_NANO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + f_type =3D ad4170_get_filter_type(indio_dev, chan); + switch (f_type) { + case AD4170_SINC5_AVG: + fallthrough; + case AD4170_SINC3: + *vals =3D (int *)st->sps_tbl[f_type]; + *length =3D ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl) * 2; + break; + case AD4170_SINC5: + *vals =3D (int *)st->sps_tbl[f_type]; + *length =3D ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl) * 2; + break; + default: + return -EINVAL; + } + *type =3D IIO_VAL_INT_PLUS_MICRO; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ad4170_set_pga(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val, int val2) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + unsigned int old_pga =3D FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe); + unsigned int pga; + int ret =3D 0; + + for (pga =3D 0; pga < AD4170_NUM_PGA_OPTIONS; pga++) { + if (val =3D=3D chan_info->scale_tbl[pga][0] && + val2 =3D=3D chan_info->scale_tbl[pga][1]) + break; + } + + if (pga =3D=3D AD4170_NUM_PGA_OPTIONS) + return -EINVAL; + + if (pga =3D=3D old_pga) + return 0; + + setup->afe &=3D ~AD4170_AFE_PGA_GAIN_MSK; + setup->afe |=3D FIELD_PREP(AD4170_AFE_PGA_GAIN_MSK, pga); + + guard(mutex)(&st->lock); + ret =3D ad4170_write_channel_setup(st, chan->address, false); + if (ret) { + setup->afe &=3D ~AD4170_AFE_PGA_GAIN_MSK; + setup->afe |=3D FIELD_PREP(AD4170_AFE_PGA_GAIN_MSK, old_pga); + } + + return ret; +} + +static int ad4170_set_channel_freq(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val, + int val2) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + enum ad4170_filter_type f_type =3D __ad4170_get_filter_type(setup->filter= ); + int filt_fs_tbl_size, i, ret =3D 0; + unsigned int old_filter_fs; + + switch (f_type) { + case AD4170_SINC5_AVG: + fallthrough; + case AD4170_SINC3: + filt_fs_tbl_size =3D ARRAY_SIZE(ad4170_sinc3_filt_fs_tbl); + break; + case AD4170_SINC5: + filt_fs_tbl_size =3D ARRAY_SIZE(ad4170_sinc5_filt_fs_tbl); + break; + } + + for (i =3D 0; i < filt_fs_tbl_size; i++) { + if (st->sps_tbl[f_type][i][0] =3D=3D val && + st->sps_tbl[f_type][i][1] =3D=3D val2) + break; + } + if (i >=3D filt_fs_tbl_size) + return -EINVAL; + + old_filter_fs =3D setup->filter_fs; + if (f_type =3D=3D AD4170_SINC5) + setup->filter_fs =3D ad4170_sinc5_filt_fs_tbl[i]; + else + setup->filter_fs =3D ad4170_sinc3_filt_fs_tbl[i]; + + guard(mutex)(&st->lock); + ret =3D ad4170_write_channel_setup(st, chan->address, false); + if (ret) + setup->filter_fs =3D old_filter_fs; + + return ret; +} + +static int ad4170_set_calib_offset(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + u32 old_offset; + int ret; + + old_offset =3D setup->offset; + setup->offset =3D val; + + guard(mutex)(&st->lock); + ret =3D ad4170_write_channel_setup(st, chan->address, false); + if (ret) + setup->offset =3D old_offset; + + return ret; +} + +static int ad4170_set_calib_gain(struct ad4170_state *st, + struct iio_chan_spec const *chan, int val) +{ + struct ad4170_chan_info *chan_info =3D &st->chan_infos[chan->address]; + struct ad4170_setup *setup =3D &chan_info->setup; + u32 old_gain; + int ret; + + old_gain =3D setup->gain; + setup->gain =3D val; + + guard(mutex)(&st->lock); + ret =3D ad4170_write_channel_setup(st, chan->address, false); + if (ret) + setup->gain =3D old_gain; + + return ret; +} + +static int __ad4170_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + return ad4170_set_pga(st, chan, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad4170_set_channel_freq(st, chan, val, val2); + case IIO_CHAN_INFO_CALIBBIAS: + return ad4170_set_calib_offset(st, chan, val); + case IIO_CHAN_INFO_CALIBSCALE: + return ad4170_set_calib_gain(st, chan, val); + default: + return -EINVAL; + } +} + +static int ad4170_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long info) +{ + int ret; + + if (!iio_device_claim_direct(indio_dev)) + return -EBUSY; + + ret =3D __ad4170_write_raw(indio_dev, chan, val, val2, info); + iio_device_release_direct(indio_dev); + return ret; +} + +static int ad4170_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + case IIO_CHAN_INFO_CALIBSCALE: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info ad4170_info =3D { + .read_raw =3D ad4170_read_raw, + .read_avail =3D ad4170_read_avail, + .write_raw =3D ad4170_write_raw, + .write_raw_get_fmt =3D ad4170_write_raw_get_fmt, + .debugfs_reg_access =3D ad4170_debugfs_reg_access, +}; + +static int ad4170_soft_reset(struct ad4170_state *st) +{ + int ret; + + ret =3D regmap_write(st->regmap8, AD4170_CONFIG_A_REG, + AD4170_SW_RESET_MSK); + if (ret) + return ret; + + /* AD4170-4 requires 1 ms between reset and any register access. */ + fsleep(MILLI); + + return 0; +} + +static int ad4170_parse_reference(struct ad4170_state *st, + struct fwnode_handle *child, + struct ad4170_setup *setup) +{ + struct device *dev =3D &st->spi->dev; + int ret; + u8 aux; + + /* Positive reference buffer setup */ + aux =3D AD4170_REF_BUF_PRE; /* Default to have precharge buffer enabled. = */ + ret =3D fwnode_property_read_u8(child, "adi,buffered-positive", &aux); + if (ret) { + if (aux < AD4170_REF_BUF_PRE || aux > AD4170_REF_BUF_BYPASS) + return dev_err_probe(dev, -EINVAL, + "Invalid adi,buffered-positive: %u\n", + aux); + } + setup->afe |=3D FIELD_PREP(AD4170_AFE_REF_BUF_P_MSK, aux); + + /* Negative reference buffer setup */ + aux =3D AD4170_REF_BUF_PRE; /* Default to have precharge buffer enabled. = */ + ret =3D fwnode_property_read_u8(child, "adi,buffered-negative", &aux); + if (ret) { + if (aux < AD4170_REF_BUF_PRE || aux > AD4170_REF_BUF_BYPASS) + return dev_err_probe(dev, -EINVAL, + "Invalid adi,buffered-negative: %u\n", + aux); + } + setup->afe |=3D FIELD_PREP(AD4170_AFE_REF_BUF_M_MSK, aux); + + /* Voltage reference selection */ + aux =3D AD4170_REF_REFOUT; /* Default reference selection. */ + fwnode_property_read_u8(child, "adi,reference-select", &aux); + if (aux > AD4170_REF_AVDD) + return dev_err_probe(dev, -EINVAL, + "Invalid reference selected %u\n", aux); + setup->afe |=3D FIELD_PREP(AD4170_AFE_REF_SELECT_MSK, aux); + + return 0; +} + +static int ad4170_parse_adc_channel_type(struct device *dev, + struct fwnode_handle *child, + struct iio_chan_spec *chan) +{ + u32 pins[2]; + int ret; + + ret =3D fwnode_property_read_u32_array(child, "diff-channels", pins, + ARRAY_SIZE(pins)); + if (!ret) { + chan->differential =3D true; + chan->channel =3D pins[0]; + chan->channel2 =3D pins[1]; + return 0; + } + ret =3D fwnode_property_read_u32(child, "single-channel", &pins[0]); + if (!ret) { + chan->differential =3D false; + chan->channel =3D pins[0]; + + ret =3D fwnode_property_read_u32(child, "common-mode-channel", + &pins[1]); + if (ret) + return dev_err_probe(dev, ret, + "single-ended channels must define common-mode-channel\n"); + + chan->channel2 =3D pins[1]; + return 0; + } + return dev_err_probe(dev, ret, + "Channel must define one of diff-channels or single-channel.\n"); +} + +static int ad4170_parse_channel_node(struct iio_dev *indio_dev, + struct fwnode_handle *child, + unsigned int chan_num) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D &st->spi->dev; + struct ad4170_chan_info *chan_info; + struct ad4170_setup *setup; + struct iio_chan_spec *chan; + unsigned int ch_reg; + u8 ref_select; + bool bipolar; + int ret; + + ret =3D fwnode_property_read_u32(child, "reg", &ch_reg); + if (ret) + return ret; + + if (ch_reg >=3D AD4170_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, + "Channel idx greater than no of channels\n"); + + chan =3D &st->chans[chan_num]; + *chan =3D ad4170_channel_template; + + chan->address =3D ch_reg; + chan->scan_index =3D ch_reg; + chan_info =3D &st->chan_infos[chan->address]; + + chan_info->setup_num =3D AD4170_INVALID_SETUP; + chan_info->initialized =3D true; + + setup =3D &chan_info->setup; + ret =3D ad4170_parse_reference(st, child, setup); + if (ret) + return ret; + + ret =3D ad4170_parse_adc_channel_type(dev, child, chan); + if (ret < 0) + return ret; + + bipolar =3D fwnode_property_read_bool(child, "bipolar"); + setup->afe |=3D FIELD_PREP(AD4170_AFE_BIPOLAR_MSK, bipolar); + if (bipolar) + chan->scan_type.sign =3D 's'; + else + chan->scan_type.sign =3D 'u'; + + ref_select =3D FIELD_GET(AD4170_AFE_REF_SELECT_MSK, setup->afe); + ret =3D ad4170_validate_channel(st, chan); + if (ret < 0) + return ret; + + ret =3D ad4170_get_input_range(st, chan, ch_reg, ref_select); + if (ret < 0) + return dev_err_probe(dev, ret, "Invalid input config\n"); + + chan_info->input_range_uv =3D ret; + return 0; +} + +static int ad4170_parse_channels(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D &st->spi->dev; + unsigned int num_channels; + unsigned int chan_num; + int ret; + + num_channels =3D device_get_child_node_count(dev); + + if (num_channels > AD4170_MAX_CHANNELS) + return dev_err_probe(dev, -EINVAL, "Too many channels\n"); + + device_for_each_child_node_scoped(dev, child) { + ret =3D ad4170_parse_channel_node(indio_dev, child, chan_num++); + if (ret) + return ret; + } + + indio_dev->num_channels =3D num_channels; + indio_dev->channels =3D st->chans; + return 0; +} + +static int ad4170_parse_firmware(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D &st->spi->dev; + int reg_data, ret, i; + + st->mclk_hz =3D AD4170_INT_CLOCK_16MHZ; + + for (i =3D 0; i < AD4170_NUM_ANALOG_PINS; i++) + st->pins_fn[i] =3D AD4170_PIN_UNASIGNED; + + /* On power on, device defaults to using SDO pin for data ready signal */ + st->int_pin_sel =3D AD4170_INT_PIN_SDO; + ret =3D device_property_match_property_string(dev, "interrupt-names", + ad4170_int_pin_names, + ARRAY_SIZE(ad4170_int_pin_names)); + if (ret >=3D 0) + st->int_pin_sel =3D ret; + + reg_data =3D FIELD_PREP(AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK, + st->int_pin_sel =3D=3D AD4170_INT_PIN_DIG_AUX1 ? + AD4170_PIN_MUXING_DIG_AUX1_RDY : + AD4170_PIN_MUXING_DIG_AUX1_DISABLED); + + ret =3D regmap_update_bits(st->regmap16, AD4170_PIN_MUXING_REG, + AD4170_PIN_MUXING_DIG_AUX1_CTRL_MSK, reg_data); + if (ret) + return ret; + + return ad4170_parse_channels(indio_dev); +} + +static int ad4170_initial_config(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + struct device *dev =3D &st->spi->dev; + int i, ret; + + ad4170_fill_sps_tbl(st); + + ret =3D ad4170_set_mode(st, AD4170_ADC_CTRL_MODE_IDLE); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set ADC mode to idle\n"); + + for (i =3D 0; i < indio_dev->num_channels; i++) { + struct ad4170_chan_info *chan_info; + struct iio_chan_spec const *chan; + struct ad4170_setup *setup; + unsigned int val; + + chan =3D &indio_dev->channels[i]; + chan_info =3D &st->chan_infos[chan->address]; + + setup =3D &chan_info->setup; + setup->gain =3D AD4170_GAIN_REG_DEFAULT; + ret =3D ad4170_write_channel_setup(st, chan->address, false); + if (ret) + return dev_err_probe(dev, ret, + "Failed to write channel setup\n"); + + val =3D FIELD_PREP(AD4170_CHAN_MAP_AINP_MSK, chan->channel) | + FIELD_PREP(AD4170_CHAN_MAP_AINM_MSK, chan->channel2); + + ret =3D regmap_write(st->regmap16, AD4170_CHAN_MAP_REG(i), val); + if (ret) + return dev_err_probe(dev, ret, + "Failed to write CHAN_MAP_REG\n"); + + ret =3D ad4170_set_channel_freq(st, chan, + AD4170_DEFAULT_SAMP_RATE, 0); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set channel freq\n"); + + ret =3D ad4170_fill_scale_tbl(indio_dev, chan); + if (ret) + return dev_err_probe(dev, ret, + "Failed to fill scale tbl\n"); + } + + /* Disable all channels to avoid reading from unexpected channel */ + ret =3D regmap_write(st->regmap16, AD4170_CHAN_EN_REG, 0); + if (ret) + return dev_err_probe(dev, ret, + "Failed to disable channels\n"); + + /* + * Configure channels to share the same data output register, i.e. data + * can be read from the same register address regardless of channel + * number. + */ + return regmap_update_bits(st->regmap16, AD4170_ADC_CTRL_REG, + AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK, + AD4170_ADC_CTRL_MULTI_DATA_REG_SEL_MSK); +} + +static const struct iio_trigger_ops ad4170_trigger_ops =3D { + .validate_device =3D iio_trigger_validate_own_device, +}; + +static irqreturn_t ad4170_irq_handler(int irq, void *dev_id) +{ + struct iio_dev *indio_dev =3D dev_id; + struct ad4170_state *st =3D iio_priv(indio_dev); + + complete(&st->completion); + + return IRQ_HANDLED; +}; + +static int ad4170_trigger_setup(struct iio_dev *indio_dev) +{ + struct ad4170_state *st =3D iio_priv(indio_dev); + int ret; + + st->trig =3D devm_iio_trigger_alloc(indio_dev->dev.parent, "%s-trig%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops =3D &ad4170_trigger_ops; + st->trig->dev.parent =3D indio_dev->dev.parent; + + iio_trigger_set_drvdata(st->trig, indio_dev); + ret =3D devm_iio_trigger_register(indio_dev->dev.parent, st->trig); + if (ret) + return ret; + + indio_dev->trig =3D iio_trigger_get(st->trig); + + return request_irq(st->spi->irq, &ad4170_irq_handler, IRQF_ONESHOT, + indio_dev->name, indio_dev); +} + +static int ad4170_regulator_setup(struct ad4170_state *st) +{ + struct device *dev =3D &st->spi->dev; + int ret; + + /* Required regulators */ + ret =3D devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get AVDD voltage.\n"); + + st->vrefs_uv[AD4170_AVDD_SUP] =3D ret; + + ret =3D devm_regulator_get_enable_read_voltage(dev, "iovdd"); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get IOVDD voltage.\n"); + + st->vrefs_uv[AD4170_IOVDD_SUP] =3D ret; + + /* Optional regulators */ + ret =3D devm_regulator_get_enable_read_voltage(dev, "avss"); + if (ret < 0 && ret !=3D -ENODEV) + return dev_err_probe(dev, ret, "Failed to get AVSS voltage.\n"); + + /* Assume AVSS at GND (0V) if not provided */ + st->vrefs_uv[AD4170_AVSS_SUP] =3D ret =3D=3D -ENODEV ? 0 : -ret; + + ret =3D devm_regulator_get_enable_read_voltage(dev, "refin1p"); + if (ret < 0 && ret !=3D -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN+ voltage.\n"); + + st->vrefs_uv[AD4170_REFIN1P_SUP] =3D ret; + + ret =3D devm_regulator_get_enable_read_voltage(dev, "refin1n"); + if (ret < 0 && ret !=3D -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN- voltage.\n"); + + /* Negative supplies are assumed to provide negative voltage */ + st->vrefs_uv[AD4170_REFIN1N_SUP] =3D ret =3D=3D -ENODEV ? -ENODEV : -ret; + + ret =3D devm_regulator_get_enable_read_voltage(dev, "refin2p"); + if (ret < 0 && ret !=3D -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN2+ voltage.\n"); + + st->vrefs_uv[AD4170_REFIN2P_SUP] =3D ret; + + ret =3D devm_regulator_get_enable_read_voltage(dev, "refin2n"); + if (ret < 0 && ret !=3D -ENODEV) + return dev_err_probe(dev, ret, "Failed to get REFIN2- voltage.\n"); + + /* Negative supplies are assumed to provide negative voltage */ + st->vrefs_uv[AD4170_REFIN2N_SUP] =3D ret =3D=3D -ENODEV ? -ENODEV : -ret; + + return 0; +} + +static int ad4170_probe(struct spi_device *spi) +{ + struct device *dev =3D &spi->dev; + struct iio_dev *indio_dev; + struct ad4170_state *st; + const char *dev_name; + int ret; + + indio_dev =3D devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st =3D iio_priv(indio_dev); + devm_mutex_init(dev, &st->lock); + + dev_name =3D spi_get_device_match_data(spi); + if (!dev_name) + return -EINVAL; + + indio_dev->name =3D dev_name; + indio_dev->info =3D &ad4170_info; + + st->spi =3D spi; + st->regmap8 =3D devm_regmap_init_spi(spi, &ad4170_regmap8_config); + if (IS_ERR(st->regmap8)) + return dev_err_probe(dev, PTR_ERR(st->regmap8), + "Failed to initialize regmap8\n"); + + st->regmap16 =3D devm_regmap_init_spi(spi, &ad4170_regmap16_config); + if (IS_ERR(st->regmap16)) + return dev_err_probe(dev, PTR_ERR(st->regmap16), + "Failed to initialize regmap16\n"); + + st->regmap24 =3D devm_regmap_init_spi(spi, &ad4170_regmap24_config); + if (IS_ERR(st->regmap24)) + return dev_err_probe(dev, PTR_ERR(st->regmap24), + "Failed to initialize regmap24\n"); + + ret =3D ad4170_regulator_setup(st); + if (ret) + return ret; + + ret =3D ad4170_soft_reset(st); + if (ret) + return ret; + + ret =3D ad4170_parse_firmware(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to parse firmware\n"); + + ret =3D ad4170_initial_config(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup device\n"); + + init_completion(&st->completion); + + if (spi->irq) { + ret =3D ad4170_trigger_setup(indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup trigger\n"); + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad4170_id_table[] =3D { + { "ad4170", (kernel_ulong_t)ad4170_chip_names[0] }, + { "ad4190", (kernel_ulong_t)ad4170_chip_names[1] }, + { "ad4195", (kernel_ulong_t)ad4170_chip_names[2] }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4170_id_table); + +static const struct of_device_id ad4170_of_match[] =3D { + { .compatible =3D "adi,ad4170", .data =3D ad4170_chip_names[0] }, + { .compatible =3D "adi,ad4190", .data =3D ad4170_chip_names[1] }, + { .compatible =3D "adi,ad4195", .data =3D ad4170_chip_names[2] }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4170_of_match); + +static struct spi_driver ad4170_driver =3D { + .driver =3D { + .name =3D "ad4170", + .of_match_table =3D ad4170_of_match, + }, + .probe =3D ad4170_probe, + .id_table =3D ad4170_id_table, +}; +module_spi_driver(ad4170_driver); + +MODULE_AUTHOR("Ana-Maria Cusco "); +MODULE_AUTHOR("Marcelo Schmitt "); +MODULE_DESCRIPTION("Analog Devices AD4170 SPI driver"); +MODULE_LICENSE("GPL"); --=20 2.47.2