From mboxrd@z Thu Jan 1 00:00:00 1970 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 C6D21334C13; Fri, 20 Feb 2026 08:03:41 +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=1771574635; cv=none; b=Hf+UubSXwEU+HADsBhBo9c+teIn9vTbCXRDmKAckz0Iu3cRP5RlVQp2LOCGC7fpTcSkZ/P0FGwQz4kyF29nhod32rcRMeFVAXIFzllJFe3JxxhE2CPRswpYK4gvYyV1Of+czs3FEv5z2ZDeIvV/ZNCzvRcTueExLKVN65UR7Z6E= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771574635; c=relaxed/simple; bh=I9KTLa9PhtnGqhHuQ+TxaO42I0bPipL3UkKbCiE+s/s=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=R827bOHMJHxIhPhrKKKO+qKbKb0PKLTlYxCDtJE4HRIlMqZyVo6L2xXOHf9QiKJ7yjUfDeE2F2Q684+PqEQ6n/c10mKkZzEprWeREH7TNXgj7rfgJSSUZhK/IVOTotMdWFw9AGF9n+5zqTaZsccvelTxXrVwI5o+pY7CZ+5Q7Ak= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine 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=1OLaXANM; arc=none smtp.client-ip=148.163.135.77 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine 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="1OLaXANM" Received: from pps.filterd (m0167089.ppops.net [127.0.0.1]) by mx0a-00128a01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 61K6GTbq1910702; Fri, 20 Feb 2026 03:03:24 -0500 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=OITfq JdArWAVM0UTy1WB0fS0oOHaibOKQhGM76F2hCw=; b=1OLaXANMMLcxF65u0lEt7 rjbY/7sdDqJQja/pBb9wKN+9B+ms7EGyqR3qqgkKQ+tUBxA97a4Rm8labwfFm7O4 MhBah+zmxD7FM9OYM2TBWTXvqMgw1AbAAzft94Wz9ueaBfw9opJoOzeQyNI4nR1K 0Xku6RjJ/wqZC4/iPh8b2AI+pE7VnBT4pr88CmwS+abD95+Dyvi1BTyNMD2k6Q01 5OCI+V2Z25mLObTPLzEwyJhTB+7DKo/Mho2gFIYKt+aytNaX3qeqmsQNX6NxV+9/ INFpWLLGWJlCSGzVsZb7oSOx8glOyIctSO12FkfQ6utEjlcl+BSBEdTb5WhgKgAD w== Received: from nwd2mta4.analog.com ([137.71.173.58]) by mx0a-00128a01.pphosted.com (PPS) with ESMTPS id 4cdanw91xh-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 20 Feb 2026 03:03:23 -0500 (EST) 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 61K83MSM028671 (version=TLSv1/SSLv3 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Fri, 20 Feb 2026 03:03:22 -0500 Received: from ASHBCASHYB4.ad.analog.com (10.64.17.132) 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.1748.37; Fri, 20 Feb 2026 03:03:22 -0500 Received: from ASHBMBX8.ad.analog.com (10.64.17.5) by ASHBCASHYB4.ad.analog.com (10.64.17.132) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.37; Fri, 20 Feb 2026 03:03:22 -0500 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.1748.37 via Frontend Transport; Fri, 20 Feb 2026 03:03:22 -0500 Received: from ATORRENO-L02.ad.analog.com (GGONZAL4-D01.ad.analog.com [10.116.20.162] (may be forged)) by zeus.spd.analog.com (8.15.1/8.15.1) with ESMTP id 61K82vY9007405; Fri, 20 Feb 2026 03:03:12 -0500 From: Alexis Czezar Torreno Date: Fri, 20 Feb 2026 16:02:57 +0800 Subject: [PATCH 2/3] iio: dac: ad5706r: Add support for AD5706R DAC Precedence: bulk X-Mailing-List: linux-pwm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-ID: <20260220-dev_ad5706r-v1-2-7253bbd74889@analog.com> References: <20260220-dev_ad5706r-v1-0-7253bbd74889@analog.com> In-Reply-To: <20260220-dev_ad5706r-v1-0-7253bbd74889@analog.com> To: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , "David Lechner" , =?utf-8?q?Nuno_S=C3=A1?= , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= CC: , , , , "Alexis Czezar Torreno" X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1771574577; l=69795; i=alexisczezar.torreno@analog.com; s=20250213; h=from:subject:message-id; bh=I9KTLa9PhtnGqhHuQ+TxaO42I0bPipL3UkKbCiE+s/s=; b=A8wA4XG5H1U3C3ypGR+WEiZHFTYPJdaB46wlkq+nafues2P3G/wwILdjcdoINQB03lmjOTTwC 831bQu/GQkKC9SzN31bC7rsrx2i9SwODBCqULnmMbaWfRpEhDqMV2UF X-Developer-Key: i=alexisczezar.torreno@analog.com; a=ed25519; pk=XpXmJnRjnsKdDil6YpOlj9+44S+XYXVFnxvkbmaZ+10= X-ADIRuleOP-NewSCL: Rule Triggered X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwMjIwMDA2OCBTYWx0ZWRfX0IIOuZIFziEo 4fn7s6r8PQQVDZzKBguA4Uk4ZruwjFZSYwQ2xmFfh4o/rDuQyOISWlNMXRFJxtScskRBhp8iUU9 M7MtQ3EWK7gawOr8DIhZ3rjrHdIDMOG9RA+N+M7Uq2BkAHnzwjPmRu1tArA/+jB9EfOLmJuDDkn qO8nRp0OBPYYudUfI3NQB5nvFViTMzrj9pvLEOzuffQKuiayOVys/F2FEP2f6uJNH8QdlRdcsNp 9nROOeVILOlHU43whA7ucKPFNRHCQ8kKLw5pji7aLIeXxhI/mGUWKLj7EBJ5rsVuAa05zs2v4HK pRXV/nAn4UuoAfxGpCk1x0sZh5GIyppbGBrh33HOSvciaYORXd/4riCo0ApWFusNKp69L9Gnbhl o09zoY6ywRtp8yoqrmaaR7S9qNc28odQRu7uzKx4jWdVabRxG1LirRpqn6l0c5krF1UBwCTeehK l4qNVXo5Uq7wBVf8Nng== X-Authority-Analysis: v=2.4 cv=TtvrRTXh c=1 sm=1 tr=0 ts=6998154b cx=c_pps a=3WNzaoukacrqR9RwcOSAdA==:117 a=3WNzaoukacrqR9RwcOSAdA==:17 a=IkcTkHD0fZMA:10 a=HzLeVaNsDn8A:10 a=VkNPw1HP01LnGYTKEx00:22 a=Mpw57Om8IfrbqaoTuvik:22 a=GgsMoib0sEa3-_RKJdDe:22 a=gAnH3GRIAAAA:8 a=PqN82x2WhTNugZaSW0EA:9 a=QEXdDO2ut3YA:10 X-Proofpoint-GUID: ZeM6fjrLhUMdPYjPIlt6XbtpfQOR_D0W X-Proofpoint-ORIG-GUID: ZeM6fjrLhUMdPYjPIlt6XbtpfQOR_D0W X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1121,Hydra:6.1.51,FMLib:17.12.100.49 definitions=2026-02-19_06,2026-02-20_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 phishscore=0 clxscore=1015 impostorscore=0 adultscore=0 suspectscore=0 bulkscore=0 lowpriorityscore=0 malwarescore=0 spamscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2601150000 definitions=main-2602200068 Add support for the Analog Devices AD5706R, a 4-channel 16-bit current output digital-to-analog converter with SPI interface. Features: - 4 independent DAC channels - Hardware and software LDAC trigger - Configurable output range - PWM-based LDAC control - Dither and toggle modes - Dynamically configurable SPI speed Signed-off-by: Alexis Czezar Torreno --- drivers/iio/dac/Kconfig | 11 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad5706r.c | 2290 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2302 insertions(+) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index db9f5c711b3df90641f017652fbbef594cc1627d..20be74a2933049250bab779d12ecd2b9b1f5a2a7 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -178,6 +178,17 @@ config AD5624R_SPI Say yes here to build support for Analog Devices AD5624R, AD5644R and AD5664R converters (DAC). This driver uses the common SPI interface. +config AD5706R + tristate "Analog Devices AD5706R DAC driver" + depends on SPI + select IIO_BUFFER + help + Say yes here to build support for Analog Devices AD5706R 4-channel, + 16-bit current output DAC. + + To compile this driver as a module, choose M here: the + module will be called ad5706r. + config AD9739A tristate "Analog Devices AD9739A RF DAC spi driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 2a80bbf4e80ad557da79ed916027cedff286984b..0034317984985035f7987a744899924bfd4612e3 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_AD5449) += ad5449.o obj-$(CONFIG_AD5592R_BASE) += ad5592r-base.o obj-$(CONFIG_AD5592R) += ad5592r.o obj-$(CONFIG_AD5593R) += ad5593r.o +obj-$(CONFIG_AD5706R) += ad5706r.o obj-$(CONFIG_AD5755) += ad5755.o obj-$(CONFIG_AD5758) += ad5758.o obj-$(CONFIG_AD5761) += ad5761.o diff --git a/drivers/iio/dac/ad5706r.c b/drivers/iio/dac/ad5706r.c new file mode 100644 index 0000000000000000000000000000000000000000..2d718cf7300bcd1f599fe715aacb3170f72541af --- /dev/null +++ b/drivers/iio/dac/ad5706r.c @@ -0,0 +1,2290 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD5706R 16-bit Current Output Digital to Analog Converter + * + * Copyright 2026 Analog Devices Inc. + * + * This driver is designed for use with the AXI SPI Engine and AXI CLKGEN + * on Xilinx Zynq platforms. The 'clocks' device tree property references + * the AXI CLKGEN output clock, which is used to dynamically control the + * SPI clock rate for read and write operations independently. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SPI Defines */ +#define AD5706R_RD_MASK BIT(15) +#define AD5706R_ADDR_PIN_MASK GENMASK(14, 12) +#define AD5706R_ADDR_MASK GENMASK(11, 0) +#define AD5706R_VAL_MASK GENMASK(7, 0) + +/* Registers and Masks */ +#define AD5706R_MASK_RESET (BIT(7) | BIT(0)) +#define AD5706R_MASK_DEV_ADDR(x) ((x) & GENMASK(2, 0)) +#define AD5706R_REG_INTERFACE_CONFIG_A 0x00 +#define AD5706R_MASK_INTERFACE_CONFIG_A(x) ((x) & GENMASK(7, 0)) +#define AD5706R_MASK_ADDR_ASCENSION BIT(5) +#define AD5706R_REG_INTERFACE_CONFIG_B 0x01 +#define AD5706R_MASK_INTERFACE_CONFIG_B(x) ((x) & GENMASK(7, 0)) +#define AD5706R_MASK_SINGLE_INSTR BIT(7) +#define AD5706R_REG_MULTI_DAC_SEL_CH 0x14 +#define AD5706R_MASK_MULTI_DAC_SEL_CH(x) ((x) & GENMASK(3, 0)) +#define AD5706R_REG_LDAC_SYNC_ASYNC 0x16 +#define AD5706R_MASK_LDAC_SYNC_ASYNC(x) ((x) & GENMASK(3, 0)) +#define AD5706R_REG_LDAC_HW_SW 0x18 +#define AD5706R_MASK_LDAC_HW_SW(x) ((x) & GENMASK(3, 0)) +#define AD5706R_REG_LDAC_EDGE_SEL_CH(x) (0x1A + ((x) * 2)) +#define AD5706R_MASK_LDAC_EDGE_SEL_CH(x) ((x) & GENMASK(1, 0)) +#define AD5706R_REG_OUT_OPERATING_MODE 0x22 +#define AD5706R_MASK_OUT_OPERATING_MODE(x) ((x) & GENMASK(3, 0)) +#define AD5706R_REG_OUT_SWITCH_EN 0x24 +#define AD5706R_MASK_OUT_SWITCH_EN(x) ((x) & GENMASK(3, 0)) +#define AD5706R_REG_SHDN_EN 0x26 +#define AD5706R_MASK_SHDN_EN(x) ((x) & GENMASK(3, 0)) +#define AD5706R_REG_OUT_RANGE_CH(x) (0x28 + ((x) * 2)) +#define AD5706R_MASK_OUT_RANGE_CH(x) ((x) & GENMASK(1, 0)) +#define AD5706R_REG_FUNC_EN 0x30 +#define AD5706R_MASK_FUNC_EN(x) ((x) & GENMASK(3, 0)) +#define AD5706R_REG_FUNC_MODE_SEL_CH(x) (0x32 + ((x) * 2)) +#define AD5706R_MASK_FUNC_MODE_SEL_CH(x) ((x) & BIT(0)) +#define AD5706R_REG_FUNC_DAC_INPUT_B_CH(x) (0x3A + ((x) * 2)) +#define AD5706R_MASK_FUNC_DAC_INPUT_B_CH(x) ((x) & GENMASK(15, 0)) +#define AD5706R_REG_MUX_OUT_SEL 0x54 +#define AD5706R_MASK_MUX_OUT_SEL(x) ((x) & (BIT(7) | GENMASK(4, 0))) +#define AD5706R_REG_MUX_OUT_CONTROL 0x56 +#define AD5706R_MASK_MUX_OUT_CONTROL(x) ((x) & BIT(0)) +#define AD5706R_REG_MULTI_DAC_SW_LDAC 0x5A +#define AD5706R_MASK_MULTI_DAC_SW_LDAC BIT(0) +#define AD5706R_REG_MULTI_DAC_INPUT_A 0x5C +#define AD5706R_MASK_MULTI_DAC_INPUT_A(x) ((x) & GENMASK(15, 0)) +#define AD5706R_REG_DAC_SW_LDAC 0x5E +#define AD5706R_MASK_DAC_SW_LDAC(x) ((x) & GENMASK(3, 0)) +#define AD5706R_REG_DAC_INPUT_A_CH(x) (0x60 + ((x) * 2)) +#define AD5706R_MASK_DAC_INPUT_A_CH(x) ((x) & GENMASK(15, 0)) +#define AD5706R_REG_DAC_DATA_READBACK_CH(x) (0x68 + ((x) * 2)) +#define AD5706R_MASK_DAC_DATA_READBACK_CH(x) ((x) & GENMASK(15, 0)) +#define AD5706R_REG_BANDGAP_CONTROL 0x73 +#define AD5706R_MASK_BANDGAP_CONTROL BIT(0) + +#define NUM_CHANNELS 4 +#define SPI_MAX_SPEED_HZ (100 * HZ_PER_MHZ) /* 100 MHz */ +#define SPI_MIN_SPEED_HZ (3 * HZ_PER_MHZ) /* 3 MHz */ +#define SAMPLING_FREQUENCY_MIN_HZ 1 +#define SAMPLING_FREQUENCY_MAX_HZ 10000000 +#define AD5706R_DAC_RESOLUTION 16 +#define AD5706R_DAC_MAX_CODE BIT(AD5706R_DAC_RESOLUTION) /* 65536 */ +#define AD5706R_MULTIBYTE_REG_START 0x14 +#define AD5706R_MULTIBYTE_REG_END 0x71 +#define AD5706R_SINGLE_BYTE_LEN 1 +#define AD5706R_DOUBLE_BYTE_LEN 2 + +enum set_clk_mode_values { + CLK_MODE_UNKNOWN = 0, + CLK_MODE_CLKGEN = 1, + CLK_MODE_SPI_ENGINE = 2, +}; + +/* + * Order of attributes in code: + * + * Device Attributes: + * - dev_addr + * - addr_ascension + * - single_instr + * - hw_ldac_tg_state + * - sampling_frequency + * - hw_ldac_tg_pwm + * - mux_out_sel + * - multi_dac_input_a + * - multi_dac_sw_ldac_trigger + * - reference_volts + * - ref_select + * - hw_shutdown_state + * + * Channel Attributes: + * - raw + * - scale + * - offset + * - input_register_a + * - input_register_b + * - hw_active_edge + * - range_sel + * - output_state + * - ldac_trigger_chn + * - toggle_trigger_chn + * - dither_trigger_chn + * - multi_dac_sel_ch + */ + +struct ad5706r_state { + struct spi_device *spi; + /* Mutex lock for clock transitions and device access */ + struct mutex lock; + + __be32 tx_buf __aligned(ARCH_DMA_MINALIGN); + __be16 rx_buf; + + struct clk *reference_clk; + struct pwm_device *ldacb_pwm; + struct gpio_desc *resetb_gpio; + struct gpio_desc *shdn_gpio; + + /* Debugfs Attributes */ + u64 debug_streaming_len; + u64 debug_streaming_data; + u16 debug_streaming_addr; + u32 debug_spi_speed_hz_write; + u32 debug_spi_speed_hz_read; + + /* + * Sets SPI Frequency via Clock Generator or SPI Engine + * 0 = SPI frequency changed, recompute clock mode + * 1 = Frequency set via clock generator + * 2 = SPI Engine controls speed + */ + u8 set_clk_mode; + u32 spi_max_speed_hz; + + /* Device Attributes */ + unsigned int dev_addr; + unsigned int addr_ascension; + unsigned int single_instr; + unsigned int shift_val; + unsigned int addr_desc; + unsigned int hw_ldac_tg_state; + unsigned int sampling_frequency; + unsigned int hw_ldac_tg_pwm; + unsigned int mux_out_sel; + unsigned int multi_dac_input_a; + bool multi_dac_sw_ldac_trigger; + unsigned int reference_volts; + unsigned int ref_select; + unsigned int hw_shutdown_state; + + /* Channel Attributes */ + unsigned int hw_active_edge[NUM_CHANNELS]; + unsigned int range_sel[NUM_CHANNELS]; + unsigned int output_state[NUM_CHANNELS]; + unsigned int ldac_trigger_chn[NUM_CHANNELS]; + unsigned int toggle_trigger_chn[NUM_CHANNELS]; + unsigned int dither_trigger_chn[NUM_CHANNELS]; + unsigned int multi_dac_sel_ch[NUM_CHANNELS]; +}; + +/* ENUM Lists */ +enum addr_ascension_iio_dev_attr { + ADDR_ASCENSION_DECREMENT = 0, + ADDR_ASCENSION_INCREMENT, +}; + +static const char * const addr_ascension_iio_dev_attr_vals[] = { + [ADDR_ASCENSION_DECREMENT] = "decrement", + [ADDR_ASCENSION_INCREMENT] = "increment", +}; + +enum single_instr_iio_dev_attr { + SINGLE_INSTR_STREAMING = 0, + SINGLE_INSTR_SINGLE_INSTRUCTION, +}; + +static const char * const single_instr_iio_dev_attr_vals[] = { + [SINGLE_INSTR_STREAMING] = "streaming", + [SINGLE_INSTR_SINGLE_INSTRUCTION] = "single_instruction", +}; + +enum hw_ldac_tg_state_iio_dev_attr { + HW_LDAC_TG_STATE_LOW = 0, + HW_LDAC_TG_STATE_HIGH +}; + +static const char * const hw_ldac_tg_state_iio_dev_attr_vals[] = { + [HW_LDAC_TG_STATE_LOW] = "low", + [HW_LDAC_TG_STATE_HIGH] = "high", +}; + +enum hw_ldac_tg_pwm_iio_dev_attr { + HW_LDAC_TG_PWM_DISABLED, + HW_LDAC_TG_PWM_ENABLED, +}; + +static const char * const hw_ldac_tg_pwm_iio_dev_attr_vals[] = { + [HW_LDAC_TG_PWM_DISABLED] = "disable", + [HW_LDAC_TG_PWM_ENABLED] = "enable", +}; + +enum mux_out_sel_iio_dev_attr { + MUX_OUT_SEL_DISABLED = 0, /* Index 0 */ + MUX_OUT_SEL_AGND, /* Index 1 */ + MUX_OUT_SEL_AVDD, /* Index 2 */ + MUX_OUT_SEL_VREF, /* Index 3 */ + MUX_OUT_SEL_IOUT0_VMON, /* Index 4 */ + MUX_OUT_SEL_IOUT1_VMON, /* Index 5 */ + MUX_OUT_SEL_IOUT2_VMON, /* Index 6 */ + MUX_OUT_SEL_IOUT3_VMON, /* Index 7 */ + MUX_OUT_SEL_IOUT0_IMON, /* Index 8 */ + MUX_OUT_SEL_IOUT1_IMON, /* Index 9 */ + MUX_OUT_SEL_IOUT2_IMON, /* Index 10 */ + MUX_OUT_SEL_IOUT3_IMON, /* Index 11 */ + MUX_OUT_SEL_PVDD0, /* Index 12 */ + MUX_OUT_SEL_PVDD1, /* Index 13 */ + MUX_OUT_SEL_PVDD2, /* Index 14 */ + MUX_OUT_SEL_PVDD3, /* Index 15 */ + MUX_OUT_SEL_TEMP_SENSOR0, /* Index 16 */ + MUX_OUT_SEL_TEMP_SENSOR1, /* Index 17 */ + MUX_OUT_SEL_TEMP_SENSOR2, /* Index 18 */ + MUX_OUT_SEL_TEMP_SENSOR3, /* Index 19 */ + MUX_OUT_SEL_MUX_IN0, /* Index 20 */ + MUX_OUT_SEL_MUX_IN1, /* Index 21 */ + MUX_OUT_SEL_MUX_IN2, /* Index 22 */ + MUX_OUT_SEL_MUX_IN3, /* Index 23 */ +}; + +static const char * const mux_out_sel_iio_dev_attr_vals[] = { + [MUX_OUT_SEL_DISABLED] = "disabled", /* Index 0 */ + [MUX_OUT_SEL_AGND] = "agnd", /* Index 1 */ + [MUX_OUT_SEL_AVDD] = "avdd", /* Index 2 */ + [MUX_OUT_SEL_VREF] = "vref", /* Index 3 */ + [MUX_OUT_SEL_IOUT0_VMON] = "iout0_vmon", /* Index 4 */ + [MUX_OUT_SEL_IOUT1_VMON] = "iout1_vmon", /* Index 5 */ + [MUX_OUT_SEL_IOUT2_VMON] = "iout2_vmon", /* Index 6 */ + [MUX_OUT_SEL_IOUT3_VMON] = "iout3_vmon", /* Index 7 */ + [MUX_OUT_SEL_IOUT0_IMON] = "iout0_imon", /* Index 8 */ + [MUX_OUT_SEL_IOUT1_IMON] = "iout1_imon", /* Index 9 */ + [MUX_OUT_SEL_IOUT2_IMON] = "iout2_imon", /* Index 10 */ + [MUX_OUT_SEL_IOUT3_IMON] = "iout3_imon", /* Index 11 */ + [MUX_OUT_SEL_PVDD0] = "pvdd0", /* Index 12 */ + [MUX_OUT_SEL_PVDD1] = "pvdd1", /* Index 13 */ + [MUX_OUT_SEL_PVDD2] = "pvdd2", /* Index 14 */ + [MUX_OUT_SEL_PVDD3] = "pvdd3", /* Index 15 */ + [MUX_OUT_SEL_TEMP_SENSOR0] = "tdiode_ch0", /* Index 16 */ + [MUX_OUT_SEL_TEMP_SENSOR1] = "tdiode_ch1", /* Index 17 */ + [MUX_OUT_SEL_TEMP_SENSOR2] = "tdiode_ch2", /* Index 18 */ + [MUX_OUT_SEL_TEMP_SENSOR3] = "tdiode_ch3", /* Index 19 */ + [MUX_OUT_SEL_MUX_IN0] = "mux_in0", /* Index 20 */ + [MUX_OUT_SEL_MUX_IN1] = "mux_in1", /* Index 21 */ + [MUX_OUT_SEL_MUX_IN2] = "mux_in2", /* Index 22 */ + [MUX_OUT_SEL_MUX_IN3] = "mux_in3", /* Index 23 */ +}; + +static const u8 mux_out_sel_reg_values[] = { + [MUX_OUT_SEL_DISABLED] = 0x00, + [MUX_OUT_SEL_AGND] = 0x80, + [MUX_OUT_SEL_AVDD] = 0x81, + [MUX_OUT_SEL_VREF] = 0x82, + [MUX_OUT_SEL_IOUT0_VMON] = 0x84, + [MUX_OUT_SEL_IOUT1_VMON] = 0x85, + [MUX_OUT_SEL_IOUT2_VMON] = 0x86, + [MUX_OUT_SEL_IOUT3_VMON] = 0x87, + [MUX_OUT_SEL_IOUT0_IMON] = 0x88, + [MUX_OUT_SEL_IOUT1_IMON] = 0x89, + [MUX_OUT_SEL_IOUT2_IMON] = 0x8A, + [MUX_OUT_SEL_IOUT3_IMON] = 0x8B, + [MUX_OUT_SEL_PVDD0] = 0x8C, + [MUX_OUT_SEL_PVDD1] = 0x8D, + [MUX_OUT_SEL_PVDD2] = 0x8E, + [MUX_OUT_SEL_PVDD3] = 0x8F, + [MUX_OUT_SEL_TEMP_SENSOR0] = 0x90, + [MUX_OUT_SEL_TEMP_SENSOR1] = 0x91, + [MUX_OUT_SEL_TEMP_SENSOR2] = 0x92, + [MUX_OUT_SEL_TEMP_SENSOR3] = 0x93, + [MUX_OUT_SEL_MUX_IN0] = 0x94, + [MUX_OUT_SEL_MUX_IN1] = 0x95, + [MUX_OUT_SEL_MUX_IN2] = 0x96, + [MUX_OUT_SEL_MUX_IN3] = 0x97, +}; + +enum multi_dac_sw_ldac_trigger_iio_dev_attr { + MULTI_DAC_SW_LDAC_TRIGGER_LOW = 0, + MULTI_DAC_SW_LDAC_TRIGGER_TRIGGER, +}; + +static const char * const multi_dac_sw_ldac_trigger_iio_dev_attr_vals[] = { + [MULTI_DAC_SW_LDAC_TRIGGER_LOW] = "low", + [MULTI_DAC_SW_LDAC_TRIGGER_TRIGGER] = "trigger", +}; + +enum ref_select_iio_dev_attr { + REF_SELECT_EXTERNAL, + REF_SELECT_INTERNAL, +}; + +static const char * const ref_select_iio_dev_attr_vals[] = { + [REF_SELECT_EXTERNAL] = "external", + [REF_SELECT_INTERNAL] = "internal", +}; + +enum hw_shutdown_state_iio_dev_attr { + HW_SHUTDOWN_STATE_LOW, + HW_SHUTDOWN_STATE_HIGH, +}; + +static const char * const hw_shutdown_state_iio_dev_attr_vals[] = { + [HW_SHUTDOWN_STATE_LOW] = "low", + [HW_SHUTDOWN_STATE_HIGH] = "high", +}; + +enum hw_active_edge_iio_dev_attr { + HW_ACTIVE_EDGE_RISING_EDGE = 0, + HW_ACTIVE_EDGE_FALLING_EDGE, + HW_ACTIVE_EDGE_ANY_EDGE, +}; + +static const char * const hw_active_edge_iio_dev_attr_vals[] = { + [HW_ACTIVE_EDGE_RISING_EDGE] = "rising_edge", + [HW_ACTIVE_EDGE_FALLING_EDGE] = "falling_edge", + [HW_ACTIVE_EDGE_ANY_EDGE] = "any_edge", +}; + +enum range_sel_iio_dev_attr { + RANGE_SEL_50 = 0, + RANGE_SEL_150 = 1, + RANGE_SEL_200 = 2, + RANGE_SEL_300 = 3, +}; + +static const char * const range_sel_iio_dev_attr_vals[] = { + [RANGE_SEL_50] = "50mA", + [RANGE_SEL_150] = "150mA", + [RANGE_SEL_200] = "200mA", + [RANGE_SEL_300] = "300mA", +}; + +enum output_state_iio_dev_attr { + OUTPUT_STATE_NORMAL_SW = 0, + OUTPUT_STATE_SHUTDOWN_TO_TRISTATE_SW, + OUTPUT_STATE_SHUTDOWN_TO_GND_SW, + OUTPUT_STATE_NORMAL_HW, + OUTPUT_STATE_SHUTDOWN_TO_TRISTATE_HW, + OUTPUT_STATE_SHUTDOWN_TO_GND_HW, +}; + +static const char * const output_state_iio_dev_attr_vals[] = { + [OUTPUT_STATE_NORMAL_SW] = "normal_sw", + [OUTPUT_STATE_SHUTDOWN_TO_TRISTATE_SW] = "shutdown_to_tristate_sw", + [OUTPUT_STATE_SHUTDOWN_TO_GND_SW] = "shutdown_to_gnd_sw", + [OUTPUT_STATE_NORMAL_HW] = "normal_hw", + [OUTPUT_STATE_SHUTDOWN_TO_TRISTATE_HW] = "shutdown_to_tristate_hw", + [OUTPUT_STATE_SHUTDOWN_TO_GND_HW] = "shutdown_to_gnd_hw", +}; + +enum ldac_trigger_chn_iio_dev_attr { + LDAC_TRIGGER_CHN_NONE = 0, + LDAC_TRIGGER_CHN_HW_TRIGGER, + LDAC_TRIGGER_CHN_SW_TRIGGER, +}; + +static const char * const ldac_trigger_chn_iio_dev_attr_vals[] = { + [LDAC_TRIGGER_CHN_NONE] = "None", + [LDAC_TRIGGER_CHN_HW_TRIGGER] = "hw_ldac", + [LDAC_TRIGGER_CHN_SW_TRIGGER] = "sw_ldac", +}; + +enum toggle_trigger_chn_iio_dev_attr { + TOGGLE_TRIGGER_CHN_NONE = 0, + TOGGLE_TRIGGER_CHN_HW_TRIGGER, + TOGGLE_TRIGGER_CHN_SW_TRIGGER, +}; + +static const char * const toggle_trigger_chn_iio_dev_attr_vals[] = { + [TOGGLE_TRIGGER_CHN_NONE] = "None", + [TOGGLE_TRIGGER_CHN_HW_TRIGGER] = "hw_toggle", + [TOGGLE_TRIGGER_CHN_SW_TRIGGER] = "sw_toggle", +}; + +enum dither_trigger_chn_iio_dev_attr { + DITHER_TRIGGER_CHN_NONE = 0, + DITHER_TRIGGER_CHN_HW_TRIGGER, + DITHER_TRIGGER_CHN_SW_TRIGGER, +}; + +static const char * const dither_trigger_chn_iio_dev_attr_vals[] = { + [DITHER_TRIGGER_CHN_NONE] = "None", + [DITHER_TRIGGER_CHN_HW_TRIGGER] = "hw_dither", + [DITHER_TRIGGER_CHN_SW_TRIGGER] = "sw_dither", +}; + +enum multi_dac_sel_ch_iio_chan_attr { + MULTI_DAC_SEL_CH_EXCLUDE = 0, + MULTI_DAC_SEL_CH_INCLUDE = 1, +}; + +static const char * const multi_dac_sel_ch_iio_chan_attr_vals[] = { + [MULTI_DAC_SEL_CH_EXCLUDE] = "exclude", + [MULTI_DAC_SEL_CH_INCLUDE] = "include", +}; + +static int _ad5706r_set_clk_rate(struct ad5706r_state *st, u32 rate, u8 wr) +{ + int ret, current_rate; + + rate = clamp(rate, (u32)SPI_MIN_SPEED_HZ, (u32)SPI_MAX_SPEED_HZ); + /* If frequency is already set, do nothing */ + current_rate = DIV_ROUND_CLOSEST(clk_get_rate(st->reference_clk), 2); + if (wr && current_rate == rate) { + st->debug_spi_speed_hz_write = current_rate; + return 0; + } + if (!wr && current_rate == rate) { + st->debug_spi_speed_hz_read = current_rate; + return 0; + } + + /* Disable the clock before setting the rate */ + clk_disable_unprepare(st->reference_clk); + + /* spi engine spi clock runs at half the SPI reference clock */ + ret = clk_set_rate(st->reference_clk, rate * 2); + if (ret) + return ret; + + /* Re-enable the clock after setting the rate */ + ret = clk_prepare_enable(st->reference_clk); + if (ret) + return ret; + + /* If frequency is not a good number, save the closest possible */ + current_rate = DIV_ROUND_CLOSEST(clk_get_rate(st->reference_clk), 2); + if (wr) + st->debug_spi_speed_hz_write = current_rate; + else + st->debug_spi_speed_hz_read = current_rate; + + /* Wait for clock to stabilize */ + usleep_range(3000, 3100); + + return 0; +} + +/* + * Check if a frequency can be achieved using SPI Engine integer divider. + * SPI Engine hardware requires: + * - Even dividers only (hardware limitation) + * - Divider > 1 (divider of 1 means running at max frequency) + * - Exact frequency match after division (no rounding errors) + */ +static bool _can_use_spi_engine_divider(u32 max_freq, u32 target_freq) +{ + u32 divider = DIV_ROUND_CLOSEST(max_freq, target_freq); + u32 achieved_freq = DIV_ROUND_CLOSEST(max_freq, divider); + + if (achieved_freq != target_freq) + return false; /* Rounding error, need exact frequency */ + + if (divider <= 1) + return false; /* Already at max frequency */ + + if (divider & 1) + return false; /* SPI Engine requires even dividers */ + + return true; +} + +/* + * CLKGEN mode contains a 3ms delay after changing the clock frequency, this + * delay is avoided if frequencies can be set by SPI Engine. + */ +static int _ad5706r_compute_spi_clk(struct ad5706r_state *st, int *write_hz, + int *read_hz) +{ + int ret; + + /* Check frequencies if SPI Engine can generate them */ + if (_can_use_spi_engine_divider(st->spi_max_speed_hz, st->debug_spi_speed_hz_write) && + _can_use_spi_engine_divider(st->spi_max_speed_hz, st->debug_spi_speed_hz_read)) { + /* Disable the clock before setting the rate */ + clk_disable_unprepare(st->reference_clk); + + /* spi engine spi clock runs at half the SPI reference clock */ + ret = clk_set_rate(st->reference_clk, st->spi_max_speed_hz * 2); + if (ret) + return ret; + + /* Re-enable the clock after setting the rate */ + ret = clk_prepare_enable(st->reference_clk); + if (ret) + return ret; + + *write_hz = st->debug_spi_speed_hz_write; + *read_hz = st->debug_spi_speed_hz_read; + st->set_clk_mode = CLK_MODE_SPI_ENGINE; + + return 0; + } + + /* Otherwise, set to Clock Generator Mode */ + *write_hz = 0; + *read_hz = 0; + st->set_clk_mode = CLK_MODE_CLKGEN; + + return 0; +} + +static int ad5706r_reg_len(unsigned int reg) +{ + if (reg >= AD5706R_MULTIBYTE_REG_START && reg <= AD5706R_MULTIBYTE_REG_END) + return AD5706R_DOUBLE_BYTE_LEN; + + return AD5706R_SINGLE_BYTE_LEN; +} + +static int ad5706r_spi_write(struct ad5706r_state *st, u16 reg, u16 val) +{ + unsigned int num_bytes; + int write_hz, read_hz; + int ret; + + num_bytes = ad5706r_reg_len(reg); + + if (reg >= AD5706R_MULTIBYTE_REG_START && reg <= AD5706R_MULTIBYTE_REG_END) + reg = reg + st->addr_desc; + + if (st->set_clk_mode == CLK_MODE_UNKNOWN) { + ret = _ad5706r_compute_spi_clk(st, &write_hz, &read_hz); + if (ret) + return ret; + } + + if (st->set_clk_mode == CLK_MODE_CLKGEN) { + write_hz = 0; + ret = _ad5706r_set_clk_rate(st, st->debug_spi_speed_hz_write, 1); + if (ret) + return ret; + } else { + write_hz = st->debug_spi_speed_hz_write; + } + + struct spi_transfer xfer = { + .tx_buf = &st->tx_buf, + .len = num_bytes + 2, + .speed_hz = write_hz, + }; + + st->tx_buf = cpu_to_be32(((((st->dev_addr << 12) & AD5706R_ADDR_PIN_MASK) | + reg) << 16) | (val << (16 - (num_bytes * 8)))); + + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret) + return ret; + + return 0; +} + +static int ad5706r_spi_read(struct ad5706r_state *st, u16 reg, u16 *val) +{ + unsigned int num_bytes; + u16 cmd; + int write_hz, read_hz; + int ret; + + num_bytes = ad5706r_reg_len(reg); + + if (reg >= AD5706R_MULTIBYTE_REG_START && reg <= AD5706R_MULTIBYTE_REG_END) + reg = reg + st->addr_desc; + + if (st->set_clk_mode == CLK_MODE_UNKNOWN) { + ret = _ad5706r_compute_spi_clk(st, &write_hz, &read_hz); + if (ret) + return ret; + } + + if (st->set_clk_mode == CLK_MODE_CLKGEN) { + read_hz = 0; + ret = _ad5706r_set_clk_rate(st, st->debug_spi_speed_hz_read, 0); + if (ret) + return ret; + } else { + read_hz = st->debug_spi_speed_hz_read; + } + + struct spi_transfer xfer[] = { + { + .tx_buf = &st->tx_buf, + .rx_buf = NULL, + .len = 2, + .speed_hz = read_hz, + }, + { + .tx_buf = NULL, + .rx_buf = &st->rx_buf, + .len = num_bytes, + .speed_hz = read_hz, + }, + }; + + cmd = AD5706R_RD_MASK | + ((st->dev_addr << 12) & AD5706R_ADDR_PIN_MASK) | + (reg & AD5706R_ADDR_MASK); + + /* Convert to 32-bit big-endian (shift to upper 16 bits) */ + st->tx_buf = cpu_to_be32((u32)cmd << 16); + + ret = spi_sync_transfer(st->spi, xfer, ARRAY_SIZE(xfer)); + if (ret) + return ret; + + *val = be16_to_cpu(st->rx_buf) >> (16 - (num_bytes * 8)); + + return 0; +} + +/* debugfs Register Access */ + +static int ad5706r_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + + if (reg >= AD5706R_MULTIBYTE_REG_START && reg <= AD5706R_MULTIBYTE_REG_END) + reg = reg - st->addr_desc; + + if (readval) + return ad5706r_spi_read(st, reg, (u16 *)readval); + + return ad5706r_spi_write(st, reg, writeval); +} + +static int ad5706r_set_streaming_addr(void *arg, u64 buf) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + + st->debug_streaming_addr = buf; + + return 0; +} + +static int ad5706r_show_streaming_addr(void *arg, u64 *val) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + + *val = st->debug_streaming_addr; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(ad5706r_streaming_addr_fops, ad5706r_show_streaming_addr, + ad5706r_set_streaming_addr, "%llu\n"); + +static int ad5706r_set_streaming_len(void *arg, u64 buf) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + + st->debug_streaming_len = buf; + + return 0; +} + +static int ad5706r_show_streaming_len(void *arg, u64 *val) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + + *val = st->debug_streaming_len; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(ad5706r_streaming_len_fops, ad5706r_show_streaming_len, + ad5706r_set_streaming_len, "%llu\n"); + +static int ad5706r_set_streaming_data(void *arg, u64 buf) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + + st->debug_streaming_data = buf; + + return 0; +} + +static int ad5706r_show_streaming_data(void *arg, u64 *val) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + + *val = st->debug_streaming_data; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(ad5706r_streaming_data_fops, ad5706r_show_streaming_data, + ad5706r_set_streaming_data, "%llu\n"); + +static int ad5706r_set_streaming_reg_access(void *arg, u64 buf) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + u16 word; + __be16 write_val[5]; + int write_hz, read_hz; + int ret; + int i; + + write_val[0] = cpu_to_be16(((st->dev_addr << 12) & AD5706R_ADDR_PIN_MASK) | + st->debug_streaming_addr); + + for (i = 0; i < 4; i++) { + /* Extract as native u16 */ + word = (st->debug_streaming_data >> (i * 16)) & 0xFFFF; + + /* Byte swap in native endian */ + word = (word & 0x00FF) << 8 | (word & 0xFF00) >> 8; + + /* Convert to big-endian for transmission */ + write_val[i + 1] = cpu_to_be16(word); + } + + if (st->set_clk_mode == CLK_MODE_UNKNOWN) { + ret = _ad5706r_compute_spi_clk(st, &write_hz, &read_hz); + if (ret) + return ret; + } + + if (st->set_clk_mode == CLK_MODE_CLKGEN) { + write_hz = 0; + ret = _ad5706r_set_clk_rate(st, st->debug_spi_speed_hz_write, 1); + if (ret) + return ret; + } else { + write_hz = st->debug_spi_speed_hz_write; + } + + struct spi_transfer xfer = { + .tx_buf = write_val, + .len = st->debug_streaming_len + 2, + .speed_hz = write_hz, + }; + + ret = spi_sync_transfer(st->spi, &xfer, 1); + if (ret) + return ret; + + return 0; +} + +static int ad5706r_show_streaming_reg_access(void *arg, u64 *val) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + u64 read_val; + u16 cmd; + int write_hz, read_hz; + int ret; + + if (st->set_clk_mode == CLK_MODE_UNKNOWN) { + ret = _ad5706r_compute_spi_clk(st, &write_hz, &read_hz); + if (ret) + return ret; + } + + if (st->set_clk_mode == CLK_MODE_CLKGEN) { + read_hz = 0; + ret = _ad5706r_set_clk_rate(st, st->debug_spi_speed_hz_read, 0); + if (ret) + return ret; + } else { + read_hz = st->debug_spi_speed_hz_read; + } + + struct spi_transfer xfer[] = { + { + .tx_buf = &st->tx_buf, + .rx_buf = NULL, + .len = 2, + .speed_hz = read_hz, + }, + { + .tx_buf = NULL, + .rx_buf = &read_val, + .len = st->debug_streaming_len, + .speed_hz = read_hz, + }, + }; + + cmd = AD5706R_RD_MASK | + ((st->dev_addr << 12) & AD5706R_ADDR_PIN_MASK) | + (st->debug_streaming_addr & AD5706R_ADDR_MASK); + + /* Convert to 32-bit big-endian (shift to upper 16 bits) */ + st->tx_buf = cpu_to_be32((u32)cmd << 16); + + ret = spi_sync_transfer(st->spi, xfer, ARRAY_SIZE(xfer)); + if (ret) + return ret; + + *val = read_val; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(ad5706r_streaming_reg_access_fops, ad5706r_show_streaming_reg_access, + ad5706r_set_streaming_reg_access, "%llu\n"); + +static int ad5706r_set_spi_speed_write(void *arg, u64 buf) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + int ret; + + st->set_clk_mode = CLK_MODE_UNKNOWN; + ret = _ad5706r_set_clk_rate(st, buf, 1); + if (ret) + return ret; + + return 0; +} + +static int ad5706r_show_spi_speed_write(void *arg, u64 *val) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + + *val = st->debug_spi_speed_hz_write; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(ad5706r_spi_speed_write_fops, ad5706r_show_spi_speed_write, + ad5706r_set_spi_speed_write, "%llu\n"); + +static int ad5706r_set_spi_speed_read(void *arg, u64 buf) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + int ret; + + st->set_clk_mode = CLK_MODE_UNKNOWN; + ret = _ad5706r_set_clk_rate(st, buf, 0); + if (ret) + return ret; + + return 0; +} + +static int ad5706r_show_spi_speed_read(void *arg, u64 *val) +{ + struct iio_dev *indio_dev = arg; + struct ad5706r_state *st = iio_priv(indio_dev); + + *val = st->debug_spi_speed_hz_read; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(ad5706r_spi_speed_read_fops, ad5706r_show_spi_speed_read, + ad5706r_set_spi_speed_read, "%llu\n"); + +static void ad5706r_debugs_init(struct iio_dev *indio_dev) +{ + struct dentry *d = iio_get_debugfs_dentry(indio_dev); + + debugfs_create_file_unsafe("streaming_addr", 0600, d, + indio_dev, &ad5706r_streaming_addr_fops); + debugfs_create_file_unsafe("streaming_len", 0600, d, + indio_dev, &ad5706r_streaming_len_fops); + debugfs_create_file_unsafe("streaming_data", 0600, d, + indio_dev, &ad5706r_streaming_data_fops); + debugfs_create_file_unsafe("streaming_reg_access", 0600, d, + indio_dev, &ad5706r_streaming_reg_access_fops); + debugfs_create_file_unsafe("spi_speed_hz_write", 0600, d, + indio_dev, &ad5706r_spi_speed_write_fops); + debugfs_create_file_unsafe("spi_speed_hz_read", 0600, d, + indio_dev, &ad5706r_spi_speed_read_fops); +} + +/* Attributes */ + +static int _set_reg_channel_mode(struct ad5706r_state *st, int chan_n, + bool bool_func_en, bool bool_func_mode, + bool bool_sync_async, bool bool_hw_sw) +{ + u16 reg_val; + u16 reg_val2; + u16 reg_val3; + u16 mask_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_FUNC_EN, ®_val); + if (ret) + return ret; + ret = ad5706r_spi_read(st, AD5706R_REG_LDAC_SYNC_ASYNC, ®_val2); + if (ret) + return ret; + ret = ad5706r_spi_read(st, AD5706R_REG_LDAC_HW_SW, ®_val3); + if (ret) + return ret; + + mask_val = BIT(chan_n + st->shift_val); + reg_val = ~mask_val & reg_val; + reg_val2 = ~mask_val & reg_val2; + reg_val3 = ~mask_val & reg_val3; + + usleep_range(1, 2); /* Delay for device stability after mode change. */ + + /* Write 0 FUNC_EN - device unlock */ + ret = ad5706r_spi_write(st, AD5706R_REG_FUNC_EN, reg_val); + if (ret) + return ret; + + /* Write 1/0 to AD5706R_REG_FUNC_MODE_SEL_CH() */ + if (bool_func_mode) + ret = ad5706r_spi_write(st, AD5706R_REG_FUNC_MODE_SEL_CH(chan_n), + BIT(st->shift_val)); + else + ret = ad5706r_spi_write(st, AD5706R_REG_FUNC_MODE_SEL_CH(chan_n), + 0 << st->shift_val); + if (ret) + return ret; + + /* Write 1 to FUNC_EN */ + if (bool_func_en) + ret = ad5706r_spi_write(st, AD5706R_REG_FUNC_EN, + reg_val | mask_val); + if (ret) + return ret; + + /* Write 1/0 to LDAC_SYNC_ASYNC */ + if (bool_sync_async) + ret = ad5706r_spi_write(st, AD5706R_REG_LDAC_SYNC_ASYNC, + reg_val2 | mask_val); + else + ret = ad5706r_spi_write(st, AD5706R_REG_LDAC_SYNC_ASYNC, + reg_val2); + if (ret) + return ret; + + /* Write 1/0 to LDAC_HW_SW for HW */ + if (bool_hw_sw) + ret = ad5706r_spi_write(st, AD5706R_REG_LDAC_HW_SW, + reg_val3 | mask_val); + else + ret = ad5706r_spi_write(st, AD5706R_REG_LDAC_HW_SW, + reg_val3); + if (ret) + return ret; + + return 0; +} + +static int _set_pwm_duty_cycle(struct ad5706r_state *st, int duty_cycle) +{ + struct pwm_state ldacb_pwm_state; + int ret; + + pwm_get_state(st->ldacb_pwm, &ldacb_pwm_state); + + ldacb_pwm_state.duty_cycle = duty_cycle == 0 ? 0 : + DIV_ROUND_CLOSEST_ULL(NANO, st->sampling_frequency * 100 / duty_cycle); + + ret = pwm_apply_might_sleep(st->ldacb_pwm, &ldacb_pwm_state); + if (ret) + return ret; + + return 0; +} + +/* Device Attributes */ +static ssize_t ad5706r_dev_addr_write(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + unsigned int reg_val; + int ret; + + ret = kstrtou32(buf, 10, ®_val); + if (ret) + return ret; + + st->dev_addr = AD5706R_MASK_DEV_ADDR(reg_val); + + return ret ? ret : len; +} + +static ssize_t ad5706r_dev_addr_read(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + + return sysfs_emit(buf, "%u\n", st->dev_addr); +} + +static int ad5706r_addr_ascension_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_INTERFACE_CONFIG_A, ®_val); + if (ret) + return ret; + + reg_val = (~AD5706R_MASK_ADDR_ASCENSION) & reg_val; + reg_val = AD5706R_MASK_INTERFACE_CONFIG_A(reg_val); + + if (item == ADDR_ASCENSION_DECREMENT) { + ret = ad5706r_spi_write(st, AD5706R_REG_INTERFACE_CONFIG_A, + reg_val); + st->shift_val = 0; + st->addr_desc = 1; + } else { + ret = ad5706r_spi_write(st, AD5706R_REG_INTERFACE_CONFIG_A, + reg_val | AD5706R_MASK_ADDR_ASCENSION); + st->shift_val = 8; + st->addr_desc = 0; + } + if (ret) + return ret; + + st->addr_ascension = item; + + return 0; +} + +static int ad5706r_addr_ascension_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_INTERFACE_CONFIG_A, ®_val); + if (ret) + return ret; + + reg_val = (reg_val & AD5706R_MASK_ADDR_ASCENSION) >> 5; + st->addr_ascension = reg_val; + + if (st->addr_ascension) + st->shift_val = 8; + else + st->shift_val = 0; + + return st->addr_ascension; +} + +static const struct iio_enum ad5706r_addr_ascension_enum = { + .items = addr_ascension_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(addr_ascension_iio_dev_attr_vals), + .set = ad5706r_addr_ascension_write, + .get = ad5706r_addr_ascension_read, +}; + +static int ad5706r_single_instr_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_INTERFACE_CONFIG_B, ®_val); + if (ret) + return ret; + + reg_val = (~AD5706R_MASK_SINGLE_INSTR) & reg_val; + reg_val = AD5706R_MASK_INTERFACE_CONFIG_B(reg_val); + + if (item == SINGLE_INSTR_STREAMING) + ret = ad5706r_spi_write(st, AD5706R_REG_INTERFACE_CONFIG_B, + reg_val); + else + ret = ad5706r_spi_write(st, AD5706R_REG_INTERFACE_CONFIG_B, + reg_val | AD5706R_MASK_SINGLE_INSTR); + + if (ret) + return ret; + + st->single_instr = item; + + return 0; +} + +static int ad5706r_single_instr_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_INTERFACE_CONFIG_B, ®_val); + if (ret) + return ret; + + reg_val = (reg_val & AD5706R_MASK_SINGLE_INSTR) >> 7; + st->single_instr = reg_val; + + return st->single_instr; +} + +static const struct iio_enum ad5706r_single_instr_enum = { + .items = single_instr_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(single_instr_iio_dev_attr_vals), + .set = ad5706r_single_instr_write, + .get = ad5706r_single_instr_read, +}; + +static int ad5706r_hw_ldac_tg_state_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + int ret; + + if (item == HW_LDAC_TG_STATE_HIGH) + ret = _set_pwm_duty_cycle(st, 100); + else + ret = _set_pwm_duty_cycle(st, 0); + + if (!ret) + st->hw_ldac_tg_state = item; + + return ret; +} + +static int ad5706r_hw_ldac_tg_state_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + + return st->hw_ldac_tg_state; +} + +static const struct iio_enum ad5706r_hw_ldac_tg_state_enum = { + .items = hw_ldac_tg_state_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(hw_ldac_tg_state_iio_dev_attr_vals), + .set = ad5706r_hw_ldac_tg_state_write, + .get = ad5706r_hw_ldac_tg_state_read, +}; + +static int ad5706r_hw_ldac_tg_pwm_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + int ret; + + if (item == HW_LDAC_TG_PWM_DISABLED) + ret = _set_pwm_duty_cycle(st, 0); + else + ret = _set_pwm_duty_cycle(st, 50); + + if (!ret) + st->hw_ldac_tg_pwm = item; + + return ret; +} + +static int ad5706r_hw_ldac_tg_pwm_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + struct pwm_state ldacb_pwm_state; + + pwm_get_state(st->ldacb_pwm, &ldacb_pwm_state); + if (ldacb_pwm_state.duty_cycle == 0 || + ldacb_pwm_state.duty_cycle == DIV_ROUND_CLOSEST_ULL(NANO, + st->sampling_frequency)) + st->hw_ldac_tg_pwm = HW_LDAC_TG_PWM_DISABLED; + else + st->hw_ldac_tg_pwm = HW_LDAC_TG_PWM_ENABLED; + + return st->hw_ldac_tg_pwm; +} + +static const struct iio_enum ad5706r_hw_ldac_tg_pwm_enum = { + .items = hw_ldac_tg_pwm_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(hw_ldac_tg_pwm_iio_dev_attr_vals), + .set = ad5706r_hw_ldac_tg_pwm_write, + .get = ad5706r_hw_ldac_tg_pwm_read, +}; + +static int ad5706r_mux_out_sel_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + unsigned int reg_value; + int ret; + + /* Validate index */ + if (item >= ARRAY_SIZE(mux_out_sel_reg_values)) + return -EINVAL; + + /* Convert index to register value */ + reg_value = mux_out_sel_reg_values[item]; + + ret = ad5706r_spi_write(st, AD5706R_REG_MUX_OUT_SEL, + reg_value << st->shift_val); + if (ret) + return ret; + + st->mux_out_sel = item; + + return 0; +} + +static int ad5706r_mux_out_sel_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + u8 reg_byte; + int ret; + int i; + + ret = ad5706r_spi_read(st, AD5706R_REG_MUX_OUT_SEL, ®_val); + if (ret) + return ret; + + /* Extract the 8-bit value */ + reg_byte = (reg_val >> st->shift_val) & 0xFF; + + /* Find which index has this register value */ + for (i = 0; i < ARRAY_SIZE(mux_out_sel_reg_values); i++) { + if (mux_out_sel_reg_values[i] == reg_byte) { + st->mux_out_sel = i; + return i; /* Return index, not register value */ + } + } + + /* Unknown value - default to disabled */ + st->mux_out_sel = MUX_OUT_SEL_DISABLED; + return MUX_OUT_SEL_DISABLED; +} + +static const struct iio_enum ad5706r_mux_out_sel_enum = { + .items = mux_out_sel_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(mux_out_sel_iio_dev_attr_vals), + .set = ad5706r_mux_out_sel_write, + .get = ad5706r_mux_out_sel_read, +}; + +static ssize_t ad5706r_multi_dac_input_a_write(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + unsigned int reg_val; + int ret; + + ret = kstrtou32(buf, 16, ®_val); + if (ret) + return ret; + + ret = ad5706r_spi_write(st, AD5706R_REG_MULTI_DAC_INPUT_A, + AD5706R_MASK_MULTI_DAC_INPUT_A(reg_val)); + if (ret) + return ret; + + return ret ? ret : len; +} + +static ssize_t ad5706r_multi_dac_input_a_read(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, + char *buf) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_MULTI_DAC_INPUT_A, ®_val); + if (ret) + return ret; + + return sysfs_emit(buf, "0x%lx\n", AD5706R_MASK_MULTI_DAC_INPUT_A(reg_val)); +} + +static int ad5706r_multi_dac_sw_ldac_trigger_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + int ret; + + ret = ad5706r_spi_write(st, AD5706R_REG_MULTI_DAC_SW_LDAC, item << st->shift_val); + if (ret) + return ret; + + return 0; +} + +static int ad5706r_multi_dac_sw_ldac_trigger_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_MULTI_DAC_SW_LDAC, ®_val); + if (ret) + return ret; + + return reg_val; +} + +static const struct iio_enum ad5706r_multi_dac_sw_ldac_trigger_enum = { + .items = multi_dac_sw_ldac_trigger_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(multi_dac_sw_ldac_trigger_iio_dev_attr_vals), + .set = ad5706r_multi_dac_sw_ldac_trigger_write, + .get = ad5706r_multi_dac_sw_ldac_trigger_read, +}; + +static ssize_t ad5706r_reference_volts_write(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + unsigned int reg_val; + int ret; + + ret = kstrtou32(buf, 10, ®_val); + if (ret) + return ret; + + st->reference_volts = reg_val; + + return ret ? ret : len; +} + +static ssize_t ad5706r_reference_volts_read(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, + char *buf) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + + return sysfs_emit(buf, "%u\n", st->reference_volts); +} + +static int ad5706r_ref_select_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + int ret; + + ret = ad5706r_spi_write(st, AD5706R_REG_BANDGAP_CONTROL, item); + if (ret) + return ret; + + st->ref_select = item; + + return 0; +} + +static int ad5706r_ref_select_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_BANDGAP_CONTROL, ®_val); + if (ret) + return ret; + + if (reg_val) + st->ref_select = REF_SELECT_INTERNAL; + else + st->ref_select = REF_SELECT_EXTERNAL; + + return st->ref_select; +} + +static const struct iio_enum ad5706r_ref_select_enum = { + .items = ref_select_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(ref_select_iio_dev_attr_vals), + .set = ad5706r_ref_select_write, + .get = ad5706r_ref_select_read, +}; + +static int ad5706r_hw_shutdown_state_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + + if (item == HW_SHUTDOWN_STATE_LOW) + gpiod_set_value_cansleep(st->shdn_gpio, 0); + else + gpiod_set_value_cansleep(st->shdn_gpio, 1); + + st->hw_shutdown_state = item; + + return 0; +} + +static int ad5706r_hw_shutdown_state_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + + return st->hw_shutdown_state; +} + +static const struct iio_enum ad5706r_hw_shutdown_state_enum = { + .items = hw_shutdown_state_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(hw_shutdown_state_iio_dev_attr_vals), + .set = ad5706r_hw_shutdown_state_write, + .get = ad5706r_hw_shutdown_state_read, +}; + +/* Channel Attributes */ +static ssize_t ad5706r_input_register_a_write(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + unsigned int reg_val; + int ret; + + ret = kstrtou32(buf, 16, ®_val); + if (ret) + return ret; + + ret = ad5706r_spi_write(st, AD5706R_REG_DAC_INPUT_A_CH(chan->channel), + AD5706R_MASK_DAC_INPUT_A_CH(reg_val)); + if (ret) + return ret; + + return ret ? ret : len; +} + +static ssize_t ad5706r_input_register_a_read(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, + char *buf) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_DAC_INPUT_A_CH(chan->channel), ®_val); + + if (ret) + return ret; + + return sysfs_emit(buf, "0x%lx\n", AD5706R_MASK_DAC_INPUT_A_CH(reg_val)); +} + +static ssize_t ad5706r_input_register_b_write(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + unsigned int reg_val; + int ret; + + ret = kstrtou32(buf, 16, ®_val); + if (ret) + return ret; + + ret = ad5706r_spi_write(st, AD5706R_REG_FUNC_DAC_INPUT_B_CH(chan->channel), + AD5706R_MASK_FUNC_DAC_INPUT_B_CH(reg_val)); + + if (ret) + return ret; + + return ret ? ret : len; +} + +static ssize_t ad5706r_input_register_b_read(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, + char *buf) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_FUNC_DAC_INPUT_B_CH(chan->channel), + ®_val); + + if (ret) + return ret; + + return sysfs_emit(buf, "0x%lx\n", AD5706R_MASK_FUNC_DAC_INPUT_B_CH(reg_val)); +} + +static int ad5706r_hw_active_edge_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + int ret; + + ret = ad5706r_spi_write(st, AD5706R_REG_LDAC_EDGE_SEL_CH(chan->channel), + AD5706R_MASK_LDAC_EDGE_SEL_CH(item) << st->shift_val); + if (ret) + return ret; + + st->hw_active_edge[chan->channel] = item; + + return 0; +} + +static int ad5706r_hw_active_edge_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_LDAC_EDGE_SEL_CH(chan->channel), + ®_val); + if (ret) + return ret; + + st->hw_active_edge[chan->channel] = AD5706R_MASK_LDAC_EDGE_SEL_CH(reg_val >> st->shift_val); + + return st->hw_active_edge[chan->channel]; +} + +static const struct iio_enum ad5706r_hw_active_edge_enum = { + .items = hw_active_edge_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(hw_active_edge_iio_dev_attr_vals), + .set = ad5706r_hw_active_edge_write, + .get = ad5706r_hw_active_edge_read, +}; + +static int ad5706r_range_sel_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + int ret; + + ret = ad5706r_spi_write(st, AD5706R_REG_OUT_RANGE_CH(chan->channel), + AD5706R_MASK_OUT_RANGE_CH(item) << st->shift_val); + + if (ret) + return ret; + + st->range_sel[chan->channel] = item; + + return 0; +} + +static int ad5706r_range_sel_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_OUT_RANGE_CH(chan->channel), ®_val); + if (ret) + return ret; + + st->range_sel[chan->channel] = AD5706R_MASK_OUT_RANGE_CH(reg_val >> st->shift_val); + + return st->range_sel[chan->channel]; +} + +static const struct iio_enum ad5706r_range_sel_enum = { + .items = range_sel_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(range_sel_iio_dev_attr_vals), + .set = ad5706r_range_sel_write, + .get = ad5706r_range_sel_read, +}; + +static int ad5706r_output_state_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + u16 reg_val2; + u16 reg_val3; + u16 mask_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_OUT_OPERATING_MODE, ®_val); + if (ret) + return ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_OUT_SWITCH_EN, ®_val2); + if (ret) + return ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_SHDN_EN, ®_val3); + if (ret) + return ret; + + mask_val = 1 << (chan->channel + st->shift_val); + reg_val = ~mask_val & reg_val; + reg_val2 = ~mask_val & reg_val2; + reg_val3 = ~mask_val & reg_val3; + + switch (item) { + case OUTPUT_STATE_NORMAL_SW: + ret = ad5706r_spi_write(st, AD5706R_REG_OUT_OPERATING_MODE, + reg_val | mask_val); + if (ret) + return ret; + ret = ad5706r_spi_write(st, AD5706R_REG_SHDN_EN, reg_val3); + if (ret) + return ret; + break; + case OUTPUT_STATE_SHUTDOWN_TO_TRISTATE_SW: + ret = ad5706r_spi_write(st, AD5706R_REG_OUT_OPERATING_MODE, + reg_val); + if (ret) + return ret; + ret = ad5706r_spi_write(st, AD5706R_REG_OUT_SWITCH_EN, + reg_val2); + if (ret) + return ret; + ret = ad5706r_spi_write(st, AD5706R_REG_SHDN_EN, reg_val3); + if (ret) + return ret; + break; + case OUTPUT_STATE_SHUTDOWN_TO_GND_SW: + ret = ad5706r_spi_write(st, AD5706R_REG_OUT_OPERATING_MODE, + reg_val); + if (ret) + return ret; + ret = ad5706r_spi_write(st, AD5706R_REG_OUT_SWITCH_EN, + reg_val2 | mask_val); + if (ret) + return ret; + ret = ad5706r_spi_write(st, AD5706R_REG_SHDN_EN, reg_val3); + if (ret) + return ret; + break; + case OUTPUT_STATE_NORMAL_HW: + ret = ad5706r_spi_write(st, AD5706R_REG_OUT_OPERATING_MODE, + reg_val | mask_val); + if (ret) + return ret; + ret = ad5706r_spi_write(st, AD5706R_REG_SHDN_EN, reg_val3 | mask_val); + if (ret) + return ret; + gpiod_set_value_cansleep(st->shdn_gpio, 1); + break; + case OUTPUT_STATE_SHUTDOWN_TO_TRISTATE_HW: + ret = ad5706r_spi_write(st, AD5706R_REG_OUT_OPERATING_MODE, + reg_val | mask_val); + if (ret) + return ret; + ret = ad5706r_spi_write(st, AD5706R_REG_OUT_SWITCH_EN, + reg_val2); + if (ret) + return ret; + ret = ad5706r_spi_write(st, AD5706R_REG_SHDN_EN, reg_val3 | mask_val); + if (ret) + return ret; + gpiod_set_value_cansleep(st->shdn_gpio, 0); + break; + case OUTPUT_STATE_SHUTDOWN_TO_GND_HW: + ret = ad5706r_spi_write(st, AD5706R_REG_OUT_OPERATING_MODE, + reg_val | mask_val); + if (ret) + return ret; + ret = ad5706r_spi_write(st, AD5706R_REG_OUT_SWITCH_EN, + reg_val2 | mask_val); + if (ret) + return ret; + ret = ad5706r_spi_write(st, AD5706R_REG_SHDN_EN, reg_val3 | mask_val); + if (ret) + return ret; + gpiod_set_value_cansleep(st->shdn_gpio, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int ad5706r_output_state_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + u16 reg_val2; + u16 reg_val3; + u16 gpio_val; + u16 mask_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_OUT_OPERATING_MODE, ®_val); + if (ret) + return ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_OUT_SWITCH_EN, ®_val2); + if (ret) + return ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_SHDN_EN, ®_val3); + if (ret) + return ret; + + mask_val = 1 << (chan->channel + st->shift_val); + reg_val = mask_val & reg_val; + reg_val2 = mask_val & reg_val2; + reg_val3 = mask_val & reg_val3; + gpio_val = gpiod_get_value_cansleep(st->shdn_gpio); + + if (reg_val && !reg_val3) + st->output_state[chan->channel] = OUTPUT_STATE_NORMAL_SW; + else if (!reg_val && !reg_val2) + st->output_state[chan->channel] = OUTPUT_STATE_SHUTDOWN_TO_TRISTATE_SW; + else if (!reg_val && reg_val2) + st->output_state[chan->channel] = OUTPUT_STATE_SHUTDOWN_TO_GND_SW; + else if (reg_val && reg_val3 && gpio_val) + st->output_state[chan->channel] = OUTPUT_STATE_NORMAL_HW; + else if (reg_val && !reg_val2 && reg_val3 && !gpio_val) + st->output_state[chan->channel] = OUTPUT_STATE_SHUTDOWN_TO_TRISTATE_HW; + else if (reg_val && reg_val2 && reg_val3 && !gpio_val) + st->output_state[chan->channel] = OUTPUT_STATE_SHUTDOWN_TO_GND_HW; + + return st->output_state[chan->channel]; +} + +static const struct iio_enum ad5706r_output_state_enum = { + .items = output_state_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(output_state_iio_dev_attr_vals), + .set = ad5706r_output_state_write, + .get = ad5706r_output_state_read, +}; + +static int ad5706r_ldac_trigger_chn_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + bool val_func_en = 0; + bool val_func_mode = 0; + bool val_sync_async = 0; + bool val_hw_sw = 0; + int ret; + + guard(mutex)(&st->lock); + + st->ldac_trigger_chn[chan->channel] = LDAC_TRIGGER_CHN_NONE; + st->toggle_trigger_chn[chan->channel] = TOGGLE_TRIGGER_CHN_NONE; + st->dither_trigger_chn[chan->channel] = DITHER_TRIGGER_CHN_NONE; + + if (item != LDAC_TRIGGER_CHN_NONE) + val_sync_async = 1; /* Write 1 LDAC_SYNC_ASYNC */ + + if (item == LDAC_TRIGGER_CHN_SW_TRIGGER) + val_hw_sw = 1; /* Write 1 LDAC_HW_SW for SW */ + + ret = _set_reg_channel_mode(st, chan->channel, val_func_en, val_func_mode, + val_sync_async, val_hw_sw); + if (ret) + return ret; + + st->ldac_trigger_chn[chan->channel] = item; + + return 0; +} + +static int ad5706r_ldac_trigger_chn_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + + return st->ldac_trigger_chn[chan->channel]; +} + +static const struct iio_enum ad5706r_ldac_trigger_chn_enum = { + .items = ldac_trigger_chn_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(ldac_trigger_chn_iio_dev_attr_vals), + .set = ad5706r_ldac_trigger_chn_write, + .get = ad5706r_ldac_trigger_chn_read, +}; + +static int ad5706r_toggle_trigger_chn_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + bool val_func_en = 0; + bool val_func_mode = 0; + bool val_sync_async = 0; + bool val_hw_sw = 0; + int ret; + + guard(mutex)(&st->lock); + + st->ldac_trigger_chn[chan->channel] = LDAC_TRIGGER_CHN_NONE; + st->toggle_trigger_chn[chan->channel] = TOGGLE_TRIGGER_CHN_NONE; + st->dither_trigger_chn[chan->channel] = DITHER_TRIGGER_CHN_NONE; + + if (item != TOGGLE_TRIGGER_CHN_NONE) + val_func_en = 1; /* Write 1 FUNC_EN */ + if (item == TOGGLE_TRIGGER_CHN_SW_TRIGGER) + val_hw_sw = 1; /* Write 1 LDAC_HW_SW for SW */ + + ret = _set_reg_channel_mode(st, chan->channel, val_func_en, val_func_mode, + val_sync_async, val_hw_sw); + if (ret) + return ret; + + st->toggle_trigger_chn[chan->channel] = item; + + return 0; +} + +static int ad5706r_toggle_trigger_chn_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + + return st->toggle_trigger_chn[chan->channel]; +} + +static const struct iio_enum ad5706r_toggle_trigger_chn_enum = { + .items = toggle_trigger_chn_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(toggle_trigger_chn_iio_dev_attr_vals), + .set = ad5706r_toggle_trigger_chn_write, + .get = ad5706r_toggle_trigger_chn_read, +}; + +static int ad5706r_dither_trigger_chn_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + bool val_func_en = 0; + bool val_func_mode = 1; + bool val_sync_async = 0; + bool val_hw_sw = 0; + int ret; + + guard(mutex)(&st->lock); + + st->ldac_trigger_chn[chan->channel] = LDAC_TRIGGER_CHN_NONE; + st->toggle_trigger_chn[chan->channel] = TOGGLE_TRIGGER_CHN_NONE; + st->dither_trigger_chn[chan->channel] = DITHER_TRIGGER_CHN_NONE; + + if (item != DITHER_TRIGGER_CHN_NONE) + val_func_en = 1; /* Write 1 FUNC_EN */ + if (item == DITHER_TRIGGER_CHN_SW_TRIGGER) + val_hw_sw = 1; /* Write 1 LDAC_HW_SW for SW */ + + ret = _set_reg_channel_mode(st, chan->channel, val_func_en, val_func_mode, + val_sync_async, val_hw_sw); + if (ret) + return ret; + + st->dither_trigger_chn[chan->channel] = item; + + return 0; +} + +static int ad5706r_dither_trigger_chn_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + + return st->dither_trigger_chn[chan->channel]; +} + +static const struct iio_enum ad5706r_dither_trigger_chn_enum = { + .items = dither_trigger_chn_iio_dev_attr_vals, + .num_items = ARRAY_SIZE(dither_trigger_chn_iio_dev_attr_vals), + .set = ad5706r_dither_trigger_chn_write, + .get = ad5706r_dither_trigger_chn_read, +}; + +static int ad5706r_multi_dac_sel_ch_write(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int item) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + u16 mask_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_MULTI_DAC_SEL_CH, ®_val); + if (ret) + return ret; + + mask_val = BIT(chan->channel + st->shift_val); + reg_val = ~mask_val & reg_val; + + if (item == MULTI_DAC_SEL_CH_EXCLUDE) + ret = ad5706r_spi_write(st, AD5706R_REG_MULTI_DAC_SEL_CH, + reg_val); + else + ret = ad5706r_spi_write(st, AD5706R_REG_MULTI_DAC_SEL_CH, + reg_val | mask_val); + + if (ret) + return ret; + + return 0; +} + +static int ad5706r_multi_dac_sel_ch_read(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + u16 mask_val; + int ret; + + ret = ad5706r_spi_read(st, AD5706R_REG_MULTI_DAC_SEL_CH, ®_val); + if (ret) + return ret; + + mask_val = BIT(chan->channel + st->shift_val); + reg_val = mask_val & reg_val; + + if (reg_val) + st->multi_dac_sel_ch[chan->channel] = MULTI_DAC_SEL_CH_INCLUDE; + else + st->multi_dac_sel_ch[chan->channel] = MULTI_DAC_SEL_CH_EXCLUDE; + + return st->multi_dac_sel_ch[chan->channel]; +} + +static const struct iio_enum ad5706r_multi_dac_sel_ch_enum = { + .items = multi_dac_sel_ch_iio_chan_attr_vals, + .num_items = ARRAY_SIZE(multi_dac_sel_ch_iio_chan_attr_vals), + .set = ad5706r_multi_dac_sel_ch_write, + .get = ad5706r_multi_dac_sel_ch_read, +}; + +#define AD5706R_CHAN_EXT_INFO(_name, _what, _shared, _read, _write) { \ + .name = _name, \ + .read = (_read), \ + .write = (_write), \ + .private = (_what), \ + .shared = (_shared), \ +} + +static struct iio_chan_spec_ext_info ad5706r_ext_info[] = { + /* device_attribute */ + AD5706R_CHAN_EXT_INFO("dev_addr", 0, IIO_SHARED_BY_ALL, + ad5706r_dev_addr_read, ad5706r_dev_addr_write), + + IIO_ENUM("addr_ascension", IIO_SHARED_BY_ALL, &ad5706r_addr_ascension_enum), + IIO_ENUM_AVAILABLE("addr_ascension", IIO_SHARED_BY_ALL, &ad5706r_addr_ascension_enum), + + IIO_ENUM("single_instr", IIO_SHARED_BY_ALL, &ad5706r_single_instr_enum), + IIO_ENUM_AVAILABLE("single_instr", IIO_SHARED_BY_ALL, &ad5706r_single_instr_enum), + + IIO_ENUM("hw_ldac_tg_state", IIO_SHARED_BY_ALL, &ad5706r_hw_ldac_tg_state_enum), + IIO_ENUM_AVAILABLE("hw_ldac_tg_state", IIO_SHARED_BY_ALL, &ad5706r_hw_ldac_tg_state_enum), + + /* Sampling Frequency part of read/write RAW */ + + IIO_ENUM("hw_ldac_tg_pwm", IIO_SHARED_BY_ALL, &ad5706r_hw_ldac_tg_pwm_enum), + IIO_ENUM_AVAILABLE("hw_ldac_tg_pwm", IIO_SHARED_BY_ALL, &ad5706r_hw_ldac_tg_pwm_enum), + + IIO_ENUM("mux_out_sel", IIO_SHARED_BY_ALL, &ad5706r_mux_out_sel_enum), + IIO_ENUM_AVAILABLE("mux_out_sel", IIO_SHARED_BY_ALL, &ad5706r_mux_out_sel_enum), + + AD5706R_CHAN_EXT_INFO("multi_dac_input_a", 0, IIO_SHARED_BY_ALL, + ad5706r_multi_dac_input_a_read, ad5706r_multi_dac_input_a_write), + + IIO_ENUM("multi_dac_sw_ldac_trigger", IIO_SHARED_BY_ALL, + &ad5706r_multi_dac_sw_ldac_trigger_enum), + IIO_ENUM_AVAILABLE("multi_dac_sw_ldac_trigger", IIO_SHARED_BY_ALL, + &ad5706r_multi_dac_sw_ldac_trigger_enum), + + AD5706R_CHAN_EXT_INFO("reference_volts", 0, IIO_SHARED_BY_ALL, + ad5706r_reference_volts_read, ad5706r_reference_volts_write), + + IIO_ENUM("ref_select", IIO_SHARED_BY_ALL, &ad5706r_ref_select_enum), + IIO_ENUM_AVAILABLE("ref_select", IIO_SHARED_BY_ALL, &ad5706r_ref_select_enum), + + IIO_ENUM("hw_shutdown_state", IIO_SHARED_BY_ALL, &ad5706r_hw_shutdown_state_enum), + IIO_ENUM_AVAILABLE("hw_shutdown_state", IIO_SHARED_BY_ALL, &ad5706r_hw_shutdown_state_enum), + + /* Channel Attributes */ + AD5706R_CHAN_EXT_INFO("input_register_a", 0, IIO_SEPARATE, + ad5706r_input_register_a_read, ad5706r_input_register_a_write), + + AD5706R_CHAN_EXT_INFO("input_register_b", 0, IIO_SEPARATE, + ad5706r_input_register_b_read, ad5706r_input_register_b_write), + + IIO_ENUM("hw_active_edge", IIO_SEPARATE, &ad5706r_hw_active_edge_enum), + IIO_ENUM_AVAILABLE("hw_active_edge", IIO_SEPARATE, &ad5706r_hw_active_edge_enum), + + IIO_ENUM("range_sel", IIO_SEPARATE, &ad5706r_range_sel_enum), + IIO_ENUM_AVAILABLE("range_sel", IIO_SEPARATE, &ad5706r_range_sel_enum), + + IIO_ENUM("output_state", IIO_SEPARATE, &ad5706r_output_state_enum), + IIO_ENUM_AVAILABLE("output_state", IIO_SEPARATE, &ad5706r_output_state_enum), + + IIO_ENUM("ldac_trigger_chn", IIO_SEPARATE, &ad5706r_ldac_trigger_chn_enum), + IIO_ENUM_AVAILABLE("ldac_trigger_chn", IIO_SEPARATE, &ad5706r_ldac_trigger_chn_enum), + + IIO_ENUM("toggle_trigger_chn", IIO_SEPARATE, &ad5706r_toggle_trigger_chn_enum), + IIO_ENUM_AVAILABLE("toggle_trigger_chn", IIO_SEPARATE, &ad5706r_toggle_trigger_chn_enum), + + IIO_ENUM("dither_trigger_chn", IIO_SEPARATE, &ad5706r_dither_trigger_chn_enum), + IIO_ENUM_AVAILABLE("dither_trigger_chn", IIO_SEPARATE, &ad5706r_dither_trigger_chn_enum), + + IIO_ENUM("multi_dac_sel_ch", IIO_SEPARATE, &ad5706r_multi_dac_sel_ch_enum), + IIO_ENUM_AVAILABLE("multi_dac_sel_ch", IIO_SEPARATE, &ad5706r_multi_dac_sel_ch_enum), + + {}, +}; + +/* Channel */ +static int ad5706r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + u16 reg_val; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + scoped_guard(mutex, &st->lock) { + ret = ad5706r_spi_read(st, AD5706R_REG_DAC_DATA_READBACK_CH(chan->channel), + ®_val); + + if (ret) + return ret; + + *val = reg_val; + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (st->range_sel[chan->channel]) { + case RANGE_SEL_50: + *val = 50 * HZ_PER_MHZ / AD5706R_DAC_MAX_CODE; + break; + case RANGE_SEL_150: + *val = 150 * HZ_PER_MHZ / AD5706R_DAC_MAX_CODE; + break; + case RANGE_SEL_200: + *val = 200 * HZ_PER_MHZ / AD5706R_DAC_MAX_CODE; + break; + case RANGE_SEL_300: + default: + *val = 300 * HZ_PER_MHZ / AD5706R_DAC_MAX_CODE; + break; + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + *val = 0; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->sampling_frequency; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ad5706r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad5706r_state *st = iio_priv(indio_dev); + struct pwm_state ldacb_pwm_state; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + /* Sets minimum and maximum frequency */ + val = clamp(val, SAMPLING_FREQUENCY_MIN_HZ, SAMPLING_FREQUENCY_MAX_HZ); + + scoped_guard(mutex, &st->lock) { + pwm_get_state(st->ldacb_pwm, &ldacb_pwm_state); + ldacb_pwm_state.duty_cycle = DIV_ROUND_CLOSEST_ULL(NANO, 2 * val); + ldacb_pwm_state.period = DIV_ROUND_CLOSEST_ULL(NANO, val); + ldacb_pwm_state.enabled = true; + + ret = pwm_apply_might_sleep(st->ldacb_pwm, &ldacb_pwm_state); + if (ret) + return ret; + + st->sampling_frequency = val; + } + return 0; + } + + return -EINVAL; +} + +static const struct iio_info ad5706r_info = { + .read_raw = &ad5706r_read_raw, + .write_raw = &ad5706r_write_raw, + .debugfs_reg_access = &ad5706r_reg_access, +}; + +#define AD5706R_CHAN(_channel) { \ + .type = IIO_CURRENT, \ + .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),\ + .output = 1, \ + .indexed = 1, \ + .channel = _channel, \ + .ext_info = ad5706r_ext_info, \ +} + +static const struct iio_chan_spec ad5706r_channels[] = { + AD5706R_CHAN(0), + AD5706R_CHAN(1), + AD5706R_CHAN(2), + AD5706R_CHAN(3), +}; + +static int _ad5706r_setup(struct ad5706r_state *st) +{ + struct pwm_state ldacb_pwm_state; + struct device *dev = &st->spi->dev; + int ret; + int i; + + guard(mutex)(&st->lock); + + st->debug_streaming_len = 0; + st->debug_streaming_data = 0; + st->debug_streaming_addr = 0; + st->debug_spi_speed_hz_write = 10000000; + st->debug_spi_speed_hz_read = 10000000; + + st->dev_addr = 0x00; + st->addr_ascension = ADDR_ASCENSION_DECREMENT; + st->single_instr = SINGLE_INSTR_STREAMING; + st->shift_val = 0; + st->addr_desc = 1; + st->hw_ldac_tg_state = HW_LDAC_TG_STATE_LOW; + st->sampling_frequency = 1000000; + st->hw_ldac_tg_pwm = HW_LDAC_TG_PWM_DISABLED; + st->mux_out_sel = MUX_OUT_SEL_DISABLED; + st->multi_dac_input_a = 0; + st->reference_volts = 2500; + st->ref_select = REF_SELECT_EXTERNAL; + st->hw_shutdown_state = HW_SHUTDOWN_STATE_LOW; + + for (i = 0; i < 4; i++) { + st->hw_active_edge[i] = HW_ACTIVE_EDGE_RISING_EDGE; + st->range_sel[i] = RANGE_SEL_50; + st->output_state[i] = OUTPUT_STATE_NORMAL_SW; + st->ldac_trigger_chn[i] = LDAC_TRIGGER_CHN_HW_TRIGGER; + st->toggle_trigger_chn[i] = TOGGLE_TRIGGER_CHN_HW_TRIGGER; + st->dither_trigger_chn[i] = DITHER_TRIGGER_CHN_HW_TRIGGER; + st->multi_dac_sel_ch[i] = MULTI_DAC_SEL_CH_EXCLUDE; + } + + /* get spi_clk axi_clkgen, no enable as spi_engine driver enables it */ + st->reference_clk = devm_clk_get(dev, "spi_clk"); + if (IS_ERR(st->reference_clk)) + return dev_err_probe(dev, PTR_ERR(st->reference_clk), + "Failed to get AXI CLKGEN clock\n"); + + st->ldacb_pwm = devm_pwm_get(dev, "ad5706r_ldacb"); + if (IS_ERR(st->ldacb_pwm)) + return dev_err_probe(dev, PTR_ERR(st->ldacb_pwm), + "Failed to get LDACB PWM\n"); + pwm_get_state(st->ldacb_pwm, &ldacb_pwm_state); + ldacb_pwm_state.duty_cycle = 0; + ldacb_pwm_state.period = DIV_ROUND_CLOSEST_ULL(NANO, st->sampling_frequency); + ldacb_pwm_state.enabled = true; + ret = pwm_apply_might_sleep(st->ldacb_pwm, &ldacb_pwm_state); + if (ret) + return dev_err_probe(dev, ret, "Failed to apply PWM state\n"); + + st->resetb_gpio = devm_gpiod_get_optional(dev, "dac-resetb", GPIOD_OUT_LOW); + if (IS_ERR(st->resetb_gpio)) { + return dev_err_probe(dev, PTR_ERR(st->resetb_gpio), + "Failed to get RESET_B GPIO\n"); + } + + st->shdn_gpio = devm_gpiod_get_optional(dev, "dac-shdn", GPIOD_OUT_HIGH); + if (IS_ERR(st->shdn_gpio)) { + return dev_err_probe(dev, PTR_ERR(st->shdn_gpio), + "Failed to get SHDN GPIO\n"); + } + + /* + * Get SPI max speed from device tree. Allows up to 100MHz. + * If value is taken from spi->max_speed_hz, it is capped at 25MHz. + */ + ret = device_property_read_u32(dev, "spi-max-frequency", &st->spi_max_speed_hz); + if (ret) + return dev_err_probe(dev, ret, "Failed to set SPI Max Speed\n"); + + st->spi_max_speed_hz = clamp(st->spi_max_speed_hz, SPI_MIN_SPEED_HZ, SPI_MAX_SPEED_HZ); + + return 0; +} + +static int ad5706r_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ad5706r_state *st; + int ret; + + 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->spi = spi; + + ret = _ad5706r_setup(st); + if (ret) + return ret; + + indio_dev->name = "ad5706r"; + indio_dev->info = &ad5706r_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad5706r_channels; + indio_dev->num_channels = ARRAY_SIZE(ad5706r_channels); + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return ret; + + ad5706r_debugs_init(indio_dev); + + return 0; +} + +static const struct of_device_id ad5706r_of_match[] = { + { .compatible = "adi,ad5706r" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ad5706r_of_match); + +static const struct spi_device_id ad5706r_id[] = { + { "ad5706r", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad5706r_id); + +static struct spi_driver ad5706r_driver = { + .driver = { + .name = "ad5706r", + .of_match_table = ad5706r_of_match, + }, + .probe = ad5706r_probe, + .id_table = ad5706r_id, +}; + +module_spi_driver(ad5706r_driver); + +MODULE_AUTHOR("Alexis Czezar Torreno "); +MODULE_DESCRIPTION("AD5706R Driver"); +MODULE_LICENSE("GPL"); -- 2.34.1