From nobody Mon Feb 9 23:57:53 2026 Received: from mx0b-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 55A8524EAAF; Fri, 25 Apr 2025 11:26: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=1745580372; cv=none; b=G8NXi3AFKwBx6wJalvWrhTVRiWxw7XXfeVSRffzR5gZI1QQxXvgV/t+yXTuLYTOtQ883AH7wR4Bvkh2pvIURbLdiE1lJdFm3s+n4Wll1fzCZmsCul3a1NlA0ZiDOXCChaYWYbc/wEersrJy2HiruOPMRmpvjFlQlOwpKAo47q7o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745580372; c=relaxed/simple; bh=8UkEEbhL1s79Vw5IwC5Ld8PX0aC0CvK5Fu8IaH5sBzA=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=EQBBbsdXi86RssrRDcWNWY4ty2s6U7W/sLRSXh/kfNyW8hZbvpJCQblZ5sHxdh0c4+TfeZ0u/dGuvD5W42Ww2+ZH0xzZFij0fcDJ/RFQvktx9P26oBS9tVHfwGyeb35rWWsHTx5rd5isXg5SUglxbBzWBreebNcMfF6nR1iCKR8= 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=f+AI/IUE; 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="f+AI/IUE" Received: from pps.filterd (m0375855.ppops.net [127.0.0.1]) by mx0b-00128a01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 53P8g6NN016641; Fri, 25 Apr 2025 07:26:07 -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=gq+l5 CuX7xPpACZ2N7XjAq8g3H8yeK+V+kdvFHp9+Rc=; b=f+AI/IUEwrA9kcqlZpLF9 oNDW3E1jRHTrA58URqNjD23yqBfeFBpbMwM7W9Sd1xMSm9XjfNTtLizCfOY00dBv pcV1pkCtmicS0euL6e/NIKgPUUjA9sXLtW5v4UsGZ22h//mxrwCIJR81LDLTkQSK fGdaIBaXq+XLkYV0KXNIJbctv5tObuTz2X1Abc2jEGd0n0vpzycXBOclMjcPfPT7 58+t1qJAN5JKAWEzeHJIX+Ntm4AfzhZQ2x+ynfloMhc0XC1NiYfAW2qBph5w1kz7 m4b5z1tevNjnXVRA3pgaimwoxRVUcDxibN+Gx3Kenv9KUXO7aepPdlyutN+NzB7n w== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0b-00128a01.pphosted.com (PPS) with ESMTPS id 46833j9qu7-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 25 Apr 2025 07:26:06 -0400 (EDT) Received: from ASHBMBX9.ad.analog.com (ASHBMBX9.ad.analog.com [10.64.17.10]) by nwd2mta4.analog.com (8.14.7/8.14.7) with ESMTP id 53PBQ5k6049245 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Fri, 25 Apr 2025 07:26:05 -0400 Received: from ASHBCASHYB5.ad.analog.com (10.64.17.133) by ASHBMBX9.ad.analog.com (10.64.17.10) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.14; Fri, 25 Apr 2025 07:26:05 -0400 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBCASHYB5.ad.analog.com (10.64.17.133) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.14; Fri, 25 Apr 2025 07:26:05 -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; Fri, 25 Apr 2025 07:26:05 -0400 Received: from amiclaus-VirtualBox.ad.analog.com (AMICLAUS-L02.ad.analog.com [10.48.65.211]) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 53PBPjc3003881; Fri, 25 Apr 2025 07:26:02 -0400 From: Antoniu Miclaus To: , , , , , CC: Antoniu Miclaus Subject: [PATCH v3 11/11] iio: adc: ad4080: add driver support Date: Fri, 25 Apr 2025 14:25:38 +0300 Message-ID: <20250425112538.59792-12-antoniu.miclaus@analog.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250425112538.59792-1-antoniu.miclaus@analog.com> References: <20250425112538.59792-1-antoniu.miclaus@analog.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-ORIG-GUID: 9BnDYB3nwgQvSMH-aBo_bUjVvot-bIUF X-Proofpoint-GUID: 9BnDYB3nwgQvSMH-aBo_bUjVvot-bIUF X-Proofpoint-Spam-Details-Enc: AW1haW4tMjUwNDI1MDA4MiBTYWx0ZWRfX5+BVnyYjzl7d TNHLOkZQFQ2YzP0VFi9tDxKSNJIrMMg7ytLDmwaM/QN2B0HolXElnn8lY+g8OZDmeCsFGl6Ednz 8gE78A6r/6jKDHjO7dvOfPmFPVZRqZvzfRRq1mrOKiVOfzi12qYE4spmIY96IWjrFTRwRW/D7EY BNE+iWugA1RoHuIW9rWU4/dkIMd7vRNdTEn7myxkSw/HUyrvUzizV8geHmFuZzkwxjnIRuROI61 nz28QeZybrPzYaaX7lvMOdznLn+E4fe4A4tYFm/SElszYEdz0H4xP7F0nMVVfwF2JPsdcc/Ag88 6CmemraCtVWMF0mj1sP/PDOvPQuLyzcNFVK4Hf1TYMP92IdOxTBN1d94CzlilgTD0fyNJT3K4ig wajHKQ69gu8PYg+NAeLB9lzk7P9hoZ97ULe4P7Buu4oPL7YFiGyWMq9UlvFb7Jo+LZDYwUHh X-Authority-Analysis: v=2.4 cv=Hu52G1TS c=1 sm=1 tr=0 ts=680b714e cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=XR8D0OoHHMoA:10 a=gAnH3GRIAAAA:8 a=XWQM0tir05fXBLfBid0A:9 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1099,Hydra:6.0.736,FMLib:17.12.80.40 definitions=2025-04-25_03,2025-04-24_02,2025-02-21_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 mlxscore=0 malwarescore=0 priorityscore=1501 spamscore=0 suspectscore=0 lowpriorityscore=0 mlxlogscore=999 impostorscore=0 bulkscore=0 phishscore=0 adultscore=0 classifier=spam authscore=0 authtc=n/a authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.19.0-2504070000 definitions=main-2504250082 Content-Type: text/plain; charset="utf-8" Add support for AD4080 high-speed, low noise, low distortion, 20-bit, Easy Drive, successive approximation register (SAR) analog-to-digital converter (ADC). Signed-off-by: Antoniu Miclaus --- changes in v3: - name the defines to make associacion with register name. - drop redundant defines. - add trailing comma when needed. - drop explicit masking and use field_prep instead - add fsleep during sync process. - do not wrap where 80 chars is not exceeded. - use space for { } - drop channel definition macro - return dev_info on chip id mismatch. - flip expression to `if (!st->lvds_cnv_en)` - rework num_lanes property parse. - update the driver based on the new edits on the backend interface relate= d to this part and the last disscussions in v2. drivers/iio/adc/Kconfig | 14 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ad4080.c | 618 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 633 insertions(+) create mode 100644 drivers/iio/adc/ad4080.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 27413516216c..17df328f5322 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -47,6 +47,20 @@ config AD4030 To compile this driver as a module, choose M here: the module will be called ad4030. =20 +config AD4080 + tristate "Analog Devices AD4080 high speed ADC" + depends on SPI + select REGMAP_SPI + select IIO_BACKEND + help + Say yes here to build support for Analog Devices AD4080 + high speed, low noise, low distortion, 20-bit, Easy Drive, + successive approximation register (SAR) analog-to-digital + converter (ADC). Supports iio_backended devices for AD4080. + + To compile this driver as a module, choose M here: the module will be + called ad4080. + config AD4130 tristate "Analog Device AD4130 ADC Driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 9f26d5eca822..e6efed5b4e7a 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_AB8500_GPADC) +=3D ab8500-gpadc.o obj-$(CONFIG_AD_SIGMA_DELTA) +=3D ad_sigma_delta.o obj-$(CONFIG_AD4000) +=3D ad4000.o obj-$(CONFIG_AD4030) +=3D ad4030.o +obj-$(CONFIG_AD4080) +=3D ad4080.o obj-$(CONFIG_AD4130) +=3D ad4130.o obj-$(CONFIG_AD4695) +=3D ad4695.o obj-$(CONFIG_AD4851) +=3D ad4851.o diff --git a/drivers/iio/adc/ad4080.c b/drivers/iio/adc/ad4080.c new file mode 100644 index 000000000000..b51893253941 --- /dev/null +++ b/drivers/iio/adc/ad4080.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD4080 SPI ADC driver + * + * Copyright 2025 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* Register Definition */ +#define AD4080_REG_INTERFACE_CONFIG_A 0x00 +#define AD4080_REG_INTERFACE_CONFIG_B 0x01 +#define AD4080_REG_DEVICE_CONFIG 0x02 +#define AD4080_REG_CHIP_TYPE 0x03 +#define AD4080_REG_PRODUCT_ID_L 0x04 +#define AD4080_REG_PRODUCT_ID_H 0x05 +#define AD4080_REG_CHIP_GRADE 0x06 +#define AD4080_REG_SCRATCH_PAD 0x0A +#define AD4080_REG_SPI_REVISION 0x0B +#define AD4080_REG_VENDOR_L 0x0C +#define AD4080_REG_VENDOR_H 0x0D +#define AD4080_REG_STREAM_MODE 0x0E +#define AD4080_REG_TRANSFER_CONFIG 0x0F +#define AD4080_REG_INTERFACE_CONFIG_C 0x10 +#define AD4080_REG_INTERFACE_STATUS_A 0x11 +#define AD4080_REG_DEVICE_STATUS 0x14 +#define AD4080_REG_ADC_DATA_INTF_CONFIG_A 0x15 +#define AD4080_REG_ADC_DATA_INTF_CONFIG_B 0x16 +#define AD4080_REG_ADC_DATA_INTF_CONFIG_C 0x17 +#define AD4080_REG_PWR_CTRL 0x18 +#define AD4080_REG_GPIO_CONFIG_A 0x19 +#define AD4080_REG_GPIO_CONFIG_B 0x1A +#define AD4080_REG_GPIO_CONFIG_C 0x1B +#define AD4080_REG_GENERAL_CONFIG 0x1C +#define AD4080_REG_FIFO_WATERMARK_LSB 0x1D +#define AD4080_REG_FIFO_WATERMARK_MSB 0x1E +#define AD4080_REG_EVENT_HYSTERESIS_LSB 0x1F +#define AD4080_REG_EVENT_HYSTERESIS_MSB 0x20 +#define AD4080_REG_EVENT_DETECTION_HI_LSB 0x21 +#define AD4080_REG_EVENT_DETECTION_HI_MSB 0x22 +#define AD4080_REG_EVENT_DETECTION_LO_LSB 0x23 +#define AD4080_REG_EVENT_DETECTION_LO_MSB 0x24 +#define AD4080_REG_OFFSET_LSB 0x25 +#define AD4080_REG_OFFSET_MSB 0x26 +#define AD4080_REG_GAIN_LSB 0x27 +#define AD4080_REG_GAIN_MSB 0x28 +#define AD4080_REG_FILTER_CONFIG 0x29 + +/* AD4080_REG_INTERFACE_CONFIG_A Bit Definition */ +#define AD4080_INTERFACE_CONFIG_A_SW_RESET_MSK (BIT(7) | BIT(0)) +#define AD4080_INTERFACE_CONFIG_A_ADDR_ASC_MSK BIT(5) +#define AD4080_INTERFACE_CONFIG_A_SDO_ENABLE_MSK BIT(4) + +/* AD4080_REG_INTERFACE_CONFIG_B Bit Definition */ +#define AD4080_INTERFACE_CONFIG_B_SINGLE_INST_MSK BIT(7) +#define AD4080_INTERFACE_CONFIG_B_SHORT_INST_MSK BIT(3) + +/* AD4080_REG_DEVICE_CONFIG Bit Definition */ +#define AD4080_DEVICE_CONFIG_OPERATING_MODES_MSK GENMASK(1, 0) + +/* AD4080_REG_TRANSFER_CONFIG Bit Definition */ +#define AD4080_TRANSFER_CONFIG_KEEP_STREAM_LENGTH_VAL_MSK BIT(2) + +/* AD4080_REG_INTERFACE_CONFIG_C Bit Definition */ +#define AD4080_INTERFACE_CONFIG_C_STRICT_REG_ACCESS_MSK BIT(5) + +/* AD4080_REG_ADC_DATA_INTF_CONFIG_A Bit Definition */ +#define AD4080_ADC_DATA_INTF_CONFIG_A_RESERVED_CONFIG_A_MSK BIT(6) +#define AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN_MSK BIT(4) +#define AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES_MSK BIT(2) +#define AD4080_ADC_DATA_INTF_CONFIG_A_DATA_INTF_MODE_MSK BIT(0) + +/* AD4080_REG_ADC_DATA_INTF_CONFIG_B Bit Definition */ +#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK GENMASK(7, 4) +#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_SELF_CLK_MODE_MSK BIT(3) +#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_EN_MSK BIT(0) + +/* AD4080_REG_ADC_DATA_INTF_CONFIG_C Bit Definition */ +#define AD4080_ADC_DATA_INTF_CONFIG_C_LVDS_VOD_MSK GENMASK(6, 4) + +/* AD4080_REG_PWR_CTRL Bit Definition */ +#define AD4080_PWR_CTRL_ANA_DIG_LDO_PD_MSK BIT(1) +#define AD4080_PWR_CTRL_INTF_LDO_PD_MSK BIT(0) + +/* AD4080_REG_GPIO_CONFIG_A Bit Definition */ +#define AD4080_GPIO_CONFIG_A_GPO_1_EN_MSK BIT(1) +#define AD4080_GPIO_CONFIG_A_GPO_0_EN_MSK BIT(0) + +/* AD4080_REG_GPIO_CONFIG_B Bit Definition */ +#define AD4080_GPIO_CONFIG_B_GPIO_1_SEL GENMASK(7, 4) +#define AD4080_GPIO_CONFIG_B_GPIO_0_SEL GENMASK(3, 0) + +/* AD4080_REG_FIFO_CONFIG Bit Definition */ +#define AD4080_FIFO_CONFIG_FIFO_MODE_MSK GENMASK(1, 0) + +/* AD4080_REG_FILTER_CONFIG Bit Definition */ +#define AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK GENMASK(6, 3) +#define AD4080_FILTER_CONFIG_FILTER_SEL_MSK GENMASK(1, 0) + +/* Miscellaneous Definitions */ +#define AD4080_SPI_READ BIT(7) +#define AD4080_CHIP_ID GENMASK(2, 0) + +#define AD4080_MAX_SAMP_FREQ 40000000 +#define AD4080_MIN_SAMP_FREQ 1250000 + +enum ad4080_filter_type { + FILTER_DISABLE, + SINC_1, + SINC_5, + SINC_5_COMP +}; + +static const unsigned int ad4080_scale_table[][2] =3D { + { 6000, 0}, +}; + +static const char *const ad4080_filter_type_iio_enum[] =3D { + [FILTER_DISABLE] =3D "disabled", + [SINC_1] =3D "sinc1", + [SINC_5] =3D "sinc5", + [SINC_5_COMP] =3D "sinc5_plus_compensation", +}; + +static const int ad4080_dec_rate_iio_enum[] =3D { + 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, +}; + +static const char * const ad4080_power_supplies[] =3D { + "vdd33", "vdd11", "vddldo", "iovdd", "vrefin", +}; + +struct ad4080_chip_info { + const char *name; + unsigned int product_id; + int num_scales; + const unsigned int (*scale_table)[2]; + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ad4080_state { + struct spi_device *spi; + struct regmap *regmap; + struct clk *clk; + struct iio_backend *back; + const struct ad4080_chip_info *info; + /* + * Synchronize access to members the of driver state, and ensure + * atomicity of consecutive regmap operations. + */ + struct mutex lock; + unsigned int num_lanes; + unsigned int dec_rate; + enum ad4080_filter_type filter_type; + bool lvds_cnv_en; +}; + +static const struct regmap_config ad4080_regmap_config =3D { + .reg_bits =3D 16, + .val_bits =3D 8, + .read_flag_mask =3D BIT(7), + .max_register =3D 0x29, +}; + +static int ad4080_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad4080_state *st =3D iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + + return regmap_write(st->regmap, reg, writeval); +} + +static int ad4080_get_scale(struct ad4080_state *st, int *val, int *val2) +{ + unsigned int tmp; + + tmp =3D (st->info->scale_table[0][0] * 1000000ULL) >> + st->info->channels[0].scan_type.realbits; + *val =3D tmp / 1000000; + *val2 =3D tmp % 1000000; + + return IIO_VAL_INT_PLUS_NANO; +} + +static unsigned int ad4080_get_dec_rate(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ad4080_state *st =3D iio_priv(dev); + int ret; + unsigned int data; + + ret =3D regmap_read(st->regmap, AD4080_REG_FILTER_CONFIG, &data); + if (ret) + return ret; + + return (1 << (FIELD_GET(AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, data) + 1= )); +} + +static int ad4080_set_dec_rate(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad4080_state *st =3D iio_priv(dev); + int ret; + + if (st->filter_type >=3D SINC_5 && mode >=3D 512) + return -EINVAL; + + guard(mutex)(&st->lock); + ret =3D regmap_update_bits(st->regmap, AD4080_REG_FILTER_CONFIG, + AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, + FIELD_PREP(AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, + (ilog2(mode) - 1))); + if (ret) + return ret; + + st->dec_rate =3D mode; + + return 0; +} + +static int ad4080_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ad4080_state *st =3D iio_priv(indio_dev); + int dec_rate; + + switch (m) { + case IIO_CHAN_INFO_SCALE: + return ad4080_get_scale(st, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + if (st->filter_type =3D=3D SINC_5_COMP) + dec_rate =3D st->dec_rate * 2; + else + dec_rate =3D st->dec_rate; + if (st->filter_type) + *val =3D DIV_ROUND_CLOSEST(clk_get_rate(st->clk), dec_rate); + else + *val =3D clk_get_rate(st->clk); + return IIO_VAL_INT; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val =3D ad4080_get_dec_rate(indio_dev, chan); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad4080_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return -EINVAL; + case IIO_CHAN_INFO_SAMP_FREQ: + return -EINVAL; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return ad4080_set_dec_rate(indio_dev, chan, val); + default: + return -EINVAL; + } +} + +static int ad4080_lvds_sync_write(struct ad4080_state *st) +{ + unsigned int timeout =3D 100; + bool sync_en; + int ret; + + guard(mutex)(&st->lock); + if (st->num_lanes =3D=3D 1) + ret =3D regmap_write(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_RESERVED_CONFIG_A_MSK | + AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN_MSK); + else + ret =3D regmap_write(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_RESERVED_CONFIG_A_MSK | + AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN_MSK | + AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES_MSK); + if (ret) + return ret; + + ret =3D iio_backend_data_alignment_enable(st->back); + if (ret) + return ret; + + do { + ret =3D iio_backend_sync_status_get(st->back, &sync_en); + if (ret) + return ret; + + if (!sync_en) + dev_dbg(&st->spi->dev, "Not Locked: Running Bit Slip\n"); + + fsleep(500); + } while (--timeout && !sync_en); + + if (timeout) { + dev_info(&st->spi->dev, "Success: Pattern correct and Locked!\n"); + if (st->num_lanes =3D=3D 1) + return regmap_write(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_RESERVED_CONFIG_A_MSK); + else + return regmap_write(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_RESERVED_CONFIG_A_MSK | + AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES_MSK); + } else { + dev_info(&st->spi->dev, "LVDS Sync Timeout.\n"); + if (st->num_lanes =3D=3D 1) { + ret =3D regmap_write(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_RESERVED_CONFIG_A_MSK); + if (ret) + return ret; + } else { + ret =3D regmap_write(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A, + AD4080_ADC_DATA_INTF_CONFIG_A_RESERVED_CONFIG_A_MSK | + AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES_MSK); + if (ret) + return ret; + } + + return -ETIMEDOUT; + } +} + +static ssize_t ad4080_get_filter_type(struct iio_dev *dev, + const struct iio_chan_spec *chan) +{ + struct ad4080_state *st =3D iio_priv(dev); + unsigned int data; + int ret; + + ret =3D regmap_read(st->regmap, AD4080_REG_FILTER_CONFIG, &data); + if (ret) + return ret; + + return FIELD_GET(AD4080_FILTER_CONFIG_FILTER_SEL_MSK, data); +} + +static int ad4080_set_filter_type(struct iio_dev *dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ad4080_state *st =3D iio_priv(dev); + int ret; + + if (mode >=3D SINC_5 && st->dec_rate >=3D 512) + return -EINVAL; + + guard(mutex)(&st->lock); + ret =3D iio_backend_filter_type_set(st->back, mode); + if (ret) + return ret; + + ret =3D regmap_update_bits(st->regmap, AD4080_REG_FILTER_CONFIG, + AD4080_FILTER_CONFIG_FILTER_SEL_MSK, + FIELD_PREP(AD4080_FILTER_CONFIG_FILTER_SEL_MSK, + mode)); + if (ret) + return ret; + + st->filter_type =3D mode; + + return 0; +} + +static int ad4080_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_OVERSAMPLING_RATIO: + *vals =3D ad4080_dec_rate_iio_enum; + *length =3D ARRAY_SIZE(ad4080_dec_rate_iio_enum); + *type =3D IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_info ad4080_iio_info =3D { + .debugfs_reg_access =3D ad4080_reg_access, + .read_raw =3D ad4080_read_raw, + .write_raw =3D ad4080_write_raw, + .read_avail =3D ad4080_read_avail, +}; + +static const struct iio_enum ad4080_filter_type_enum =3D { + .items =3D ad4080_filter_type_iio_enum, + .num_items =3D ARRAY_SIZE(ad4080_filter_type_iio_enum), + .set =3D ad4080_set_filter_type, + .get =3D ad4080_get_filter_type, +}; + +static struct iio_chan_spec_ext_info ad4080_ext_info[] =3D { + IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad4080_filter_type_enum), + IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL, + &ad4080_filter_type_enum), + { } +}; + +static const struct iio_chan_spec ad4080_channels[] =3D { + { + .type =3D IIO_VOLTAGE, + .indexed =3D 1, + .channel =3D 0, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all =3D BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available =3D + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .ext_info =3D ad4080_ext_info, + .scan_index =3D 0, + .scan_type =3D { + .sign =3D 's', + .realbits =3D 20, + .storagebits =3D 32, + .shift =3D 0, + }, + } +}; + +static const struct ad4080_chip_info ad4080_chip_info =3D { + .name =3D "AD4080", + .product_id =3D AD4080_CHIP_ID, + .scale_table =3D ad4080_scale_table, + .num_scales =3D ARRAY_SIZE(ad4080_scale_table), + .num_channels =3D 1, + .channels =3D ad4080_channels, +}; + +static int ad4080_setup(struct iio_dev *indio_dev) +{ + struct ad4080_state *st =3D iio_priv(indio_dev); + unsigned int id; + int ret; + + ret =3D regmap_write(st->regmap, AD4080_REG_INTERFACE_CONFIG_A, + AD4080_INTERFACE_CONFIG_A_SW_RESET_MSK); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap, AD4080_REG_INTERFACE_CONFIG_A, + AD4080_INTERFACE_CONFIG_A_SDO_ENABLE_MSK); + if (ret) + return ret; + + ret =3D regmap_read(st->regmap, AD4080_REG_CHIP_TYPE, &id); + if (ret) + return ret; + + if (id !=3D AD4080_CHIP_ID) + dev_info(&st->spi->dev, "Unrecognized CHIP_ID 0x%X\n", id); + + ret =3D regmap_set_bits(st->regmap, AD4080_REG_GPIO_CONFIG_A, + AD4080_GPIO_CONFIG_A_GPO_1_EN_MSK); + if (ret) + return ret; + + ret =3D regmap_write(st->regmap, AD4080_REG_GPIO_CONFIG_B, + FIELD_PREP(AD4080_GPIO_CONFIG_B_GPIO_1_SEL, 3)); + if (ret) + return ret; + + ret =3D iio_backend_num_lanes_set(st->back, st->num_lanes); + if (ret) + return ret; + + if (!st->lvds_cnv_en) + return 0; + + if (st->num_lanes) { + ret =3D regmap_update_bits(st->regmap, + AD4080_REG_ADC_DATA_INTF_CONFIG_B, + AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK, + FIELD_PREP(AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK, + 7)); + if (ret) + return ret; + } + + ret =3D regmap_set_bits(st->regmap, + AD4080_REG_ADC_DATA_INTF_CONFIG_B, + AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_EN_MSK); + if (ret) + return ret; + + return ad4080_lvds_sync_write(st); +} + +static void ad4080_properties_parse(struct ad4080_state *st) +{ + st->lvds_cnv_en =3D device_property_read_bool(&st->spi->dev, + "adi,lvds-cnv-enable"); + + st->num_lanes =3D 1; + device_property_read_u32(&st->spi->dev, "adi,num_lanes", &st->num_lanes); +} + +static int ad4080_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct device *dev =3D &spi->dev; + struct ad4080_state *st; + int ret; + + indio_dev =3D devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st =3D iio_priv(indio_dev); + st->spi =3D spi; + + ret =3D devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad4080_power_supplies), + ad4080_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable supplies\n"); + + st->regmap =3D devm_regmap_init_spi(spi, &ad4080_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + st->info =3D spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + + ret =3D devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + st->info =3D spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + + indio_dev->name =3D st->info->name; + indio_dev->channels =3D st->info->channels; + indio_dev->num_channels =3D st->info->num_channels; + indio_dev->info =3D &ad4080_iio_info; + indio_dev->modes =3D INDIO_DIRECT_MODE; + + ad4080_properties_parse(st); + + st->clk =3D devm_clk_get_enabled(&spi->dev, "cnv"); + if (IS_ERR(st->clk)) + return PTR_ERR(st->clk); + + st->back =3D devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return PTR_ERR(st->back); + + ret =3D devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret =3D devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + ret =3D ad4080_setup(indio_dev); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad4080_id[] =3D { + { "ad4080", (kernel_ulong_t)&ad4080_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad4080_id); + +static const struct of_device_id ad4080_of_match[] =3D { + { .compatible =3D "adi,ad4080", &ad4080_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad4080_of_match); + +static struct spi_driver ad4080_driver =3D { + .driver =3D { + .name =3D "ad4080", + .of_match_table =3D ad4080_of_match, + }, + .probe =3D ad4080_probe, + .id_table =3D ad4080_id, +}; +module_spi_driver(ad4080_driver); + +MODULE_AUTHOR("Antoniu Miclaus