From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 2045F4746B1; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=n9Un8JpFhVyV4aC/kq3JymW+FWSTv2jWWlIlTkrKSGg8FRA9dfi6vNCrXeXqYsJGXi7UkqM/FJoB5ZP1tXRJHRLgH4EuNgw//HWMf+3cJunrk5/hu6U4XjlwmfbCr2DE0d7FvPT9jviUSsxpvAUnhkCh/r9/Iwzxtn0Em9apDqQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=c2p5L1q6zxv7wWh9EpdRkFyHFnzsFh472s+WmtKCZhw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LLebX7Fhd5bR8NkTrm3kgZq4dThLVzJIOK6iald0Km2zGmN5F5E3N7lgsOWmybtmfMPrqBh1zACFUJR35F1xpTisqmbnlzkzc813jIN9C7uXSe+4D0hdNEsCU9lGGG1rE6VSY2Liau1Q/mFF+Jms1+HJE5FqkeUkxityddba+cw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=tvn3pqSK; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="tvn3pqSK" Received: by smtp.kernel.org (Postfix) with ESMTPS id BAE89C2BCFC; Wed, 22 Jul 2026 15:50:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735457; bh=c2p5L1q6zxv7wWh9EpdRkFyHFnzsFh472s+WmtKCZhw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=tvn3pqSKsFHfok9BwBydRARoJ1m04vciW0OyO6b0h7ckcA9Xm1aH5IGh8Z8HmLnQM B31TAP/xN7RdQEXO8OwYG4nX4DM1WzxupMKDhLfOzFXqfLc9cjJFmfesi116aBAW4D TMrCwSaTnyEhziLauGlXxCXHxI8UL8tdppAbopOPYpUf7yQT3m7mrTx1sSgsSHyAne Q76x8SsxvZGK1JU+KxoqbrWegp7xgqb8b9fnfnu+nMe50vSRfrLxJHHYg/Im3IV5+e Kk7KFgHIL6YVxnTkebD1ZlUqKrv0e/0SWxILq0m44yALY4Vr6T8Ay2fTir6VZvU0wC PoIF5EsYOPv0Q== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 963CEC44536; Wed, 22 Jul 2026 15:50:57 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:10 +0100 Subject: [PATCH v9 01/17] iio: ABI: add attributes for altcurrent channels Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-1-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar , Johan Hovold X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=3588; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=NMyQtwKnhP7ghLAaLd8Vv8Yqv0bWLLVXShCmZ6SK4qg=; b=jPE8V/mOW+rnINhBGXR8IHAvefsCXxeDvnqDh09fTspG4Od13kNyaJoKFtwFkGmUg8VIAHUUK FZ/kycpkdxFB1R80n7cHDkDMfV0PIpEvufvs11lh/g+7kn/fFMryqqe X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add attributes applicable to altcurrent channels, e.g., frequency, phase, scale, raw, label and powerdown. Those are going to be used by a DDS device to be introduced shortly. Signed-off-by: Rodrigo Alencar --- Documentation/ABI/testing/sysfs-bus-iio | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/te= sting/sysfs-bus-iio index 5cc2e82c4997..3e75ce1c97c6 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -478,6 +478,10 @@ What: /sys/bus/iio/devices/iio:deviceX/in_currentY_sc= ale What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_scale What: /sys/bus/iio/devices/iio:deviceX/in_current_scale What: /sys/bus/iio/devices/iio:deviceX/in_current_q_scale +What: /sys/bus/iio/devices/iio:deviceX/in_altcurrentY_scale +What: /sys/bus/iio/devices/iio:deviceX/in_altcurrent_scale +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_scale +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrent_scale What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale What: /sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_scale @@ -799,6 +803,8 @@ What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_po= werdown What: /sys/bus/iio/devices/iio:deviceX/out_voltage_powerdown What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_powerdown What: /sys/bus/iio/devices/iio:deviceX/out_altvoltage_powerdown +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_powerdown +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrent_powerdown KernelVersion: 2.6.38 Contact: linux-iio@vger.kernel.org Description: @@ -809,6 +815,7 @@ Description: together. =20 What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_frequency KernelVersion: 3.4.0 Contact: linux-iio@vger.kernel.org Description: @@ -835,8 +842,10 @@ Description: all the other channels, since it involves changing the VCO fundamental output frequency. =20 +What: /sys/bus/iio/devices/iio:deviceX/in_altcurrentY_phase What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_i_phase What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_q_phase +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_phase What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_phase What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_i_phase What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_q_phase @@ -850,6 +859,7 @@ Description: channel. =20 What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_raw Date: May 2012 KernelVersion: 3.5 Contact: Johan Hovold @@ -1736,6 +1746,7 @@ Description: =20 What: /sys/bus/iio/devices/iio:deviceX/in_currentY_raw What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_raw +What: /sys/bus/iio/devices/iio:deviceX/in_altcurrentY_raw KernelVersion: 3.17 Contact: linux-iio@vger.kernel.org Description: @@ -2126,7 +2137,9 @@ Description: =20 Raw counter device counters direction for channel Y. =20 +What: /sys/bus/iio/devices/iio:deviceX/in_altcurrentY_label What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_label +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_label What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_label KernelVersion: 5.8 Contact: linux-iio@vger.kernel.org --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 2DBF2476059; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=HSjdj1DUg0Wvdf8+NKhC00OI5CsMqRDT/AA4gq/ePCpA1XskMRvjpCdPrBNFjcOinC9LzsdRvb+IXEVGAw2xwhi+NAmbSE1+sYdHdjyF3SqEejCBmUOnL5Xd4nZEg6hAVEgWd+izaP9DYH8af2fYXPlEks2KOWFg8kd2vLJbt9g= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=y5y1Pex3r0tEmIZ3ma1DZMuFnuVLNzcCNOrpynmqk7Y=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ljw+PQ6lcQz2pScfjeM9jjHflgcRSZMAZdWcTljW/NGdgUHDkuapyZd5WlsaE/4i61tjcKjbB0D41ZPFWk+2Kk7X2eP7OOk30930Kz53OMnNhR2ePOeT+S2Qv65vJfNGo9Dcqo6WopYBbZZIRnBNDUFbfdItjYT9k2d7DaGUkqw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=RzqsapQT; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="RzqsapQT" Received: by smtp.kernel.org (Postfix) with ESMTPS id C9E7FC2BCF7; Wed, 22 Jul 2026 15:50:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735457; bh=y5y1Pex3r0tEmIZ3ma1DZMuFnuVLNzcCNOrpynmqk7Y=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=RzqsapQTlXuI1Y02yeG4uD9r/9NeUBqanDcyqmgxa+ZpquzT87pFxk0swY/JCRp8b qf5rkzt6z1XNAfnssQ/iJBnP/GtCSlmbO18DViaIm8IcfF8iW0MQJBNISAiJWaoZBN HdD+Cn8u7VeinPIcue0aWRwfjuKqEvSH6nXobZj3lcOlHHdzAjvFuwGALhE3R11WuW YnXNAUDNNXJ3Ux/7spF1Ok+h+eC9MfoZpEKo5vasMDiu7RohWze9pz9KtuQ/O7xUl9 aImrz4IFD7kUstmWk0GVB+vUw5b9PBE67nRoZIfgavzGsqOLj9fKHEyyxoJgSYLVs5 +trwj1siVtsnw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id AC02FC4453C; Wed, 22 Jul 2026 15:50:57 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:11 +0100 Subject: [PATCH v9 02/17] iio: ABI: raw, scale and offset for frequency/phase channels Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-2-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=4117; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=b0wjZEHLLiPjjpYzkara2Qcj+Q0dyqXjAs1Abo131jY=; b=7YSSiDWO+6IdTjka1AU23VETB8rsI/ffIvWiy6QG68vMvUQrjOyuJvbrRTH0/CBd6RCa4xUO2 6YPc6VRwtv5C1vnqcmhx7PJoukFsWw/aL/Rhcy5eu5ozITLS/ZeN5zw X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Consider scale and offset attributes for frequency and phase channels. Frequency channels are going to be introduced later alongside a DDS device driver which is going to leverage those new attributes. Signed-off-by: Rodrigo Alencar --- Documentation/ABI/testing/sysfs-bus-iio | 41 +++++++++++++++++++++++++++++= ++++ 1 file changed, 41 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/te= sting/sysfs-bus-iio index 3e75ce1c97c6..24712b5bd110 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -441,6 +441,14 @@ What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_= offset What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_offset What: /sys/bus/iio/devices/iio:deviceX/in_currentY_offset What: /sys/bus/iio/devices/iio:deviceX/in_current_offset +What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_offset +What: /sys/bus/iio/devices/iio:deviceX/in_phase_offset +What: /sys/bus/iio/devices/iio:deviceX/out_phaseY_offset +What: /sys/bus/iio/devices/iio:deviceX/out_phase_offset +What: /sys/bus/iio/devices/iio:deviceX/in_frequencyY_offset +What: /sys/bus/iio/devices/iio:deviceX/in_frequency_offset +What: /sys/bus/iio/devices/iio:deviceX/out_frequencyY_offset +What: /sys/bus/iio/devices/iio:deviceX/out_frequency_offset What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset @@ -474,6 +482,14 @@ What: /sys/bus/iio/devices/iio:deviceX/in_voltage_sca= le What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_scale What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_scale What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_scale +What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_scale +What: /sys/bus/iio/devices/iio:deviceX/in_phase_scale +What: /sys/bus/iio/devices/iio:deviceX/out_phaseY_scale +What: /sys/bus/iio/devices/iio:deviceX/out_phase_scale +What: /sys/bus/iio/devices/iio:deviceX/in_frequencyY_scale +What: /sys/bus/iio/devices/iio:deviceX/in_frequency_scale +What: /sys/bus/iio/devices/iio:deviceX/out_frequencyY_scale +What: /sys/bus/iio/devices/iio:deviceX/out_frequency_scale What: /sys/bus/iio/devices/iio:deviceX/in_currentY_scale What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_scale What: /sys/bus/iio/devices/iio:deviceX/in_current_scale @@ -2149,6 +2165,22 @@ Description: specific attributes. This is useful for userspace to be able to better identify an individual channel. =20 +What: /sys/bus/iio/devices/iio:deviceX/in_frequencyY_raw +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled) frequency reading from channel Y. + Units after application of scale and offset are Hz. + +What: /sys/bus/iio/devices/iio:deviceX/out_frequencyY_raw +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled, no bias etc.) output frequency for channel Y. + The number must always be specified and unique if the output + corresponds to a single channel. Units after application of + scale and offset are Hz. + What: /sys/bus/iio/devices/iio:deviceX/in_phaseY_raw KernelVersion: 4.18 Contact: linux-iio@vger.kernel.org @@ -2156,6 +2188,15 @@ Description: Raw (unscaled) phase difference reading from channel Y. Units after application of scale and offset are radians. =20 +What: /sys/bus/iio/devices/iio:deviceX/out_phaseY_raw +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled, no bias etc.) output phase for channel Y. + The number must always be specified and unique if the output + corresponds to a single channel. Units after application of + scale and offset are radians. + What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm1_input What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm1_input What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm2p5_input --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 2D93F4746C5; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=I69UKA3JfMhXtAPCVFNBLviTT+VRF8Mc8jO8qAc5kj/+CadC9ldPWZjvdUsJevTfE7oWqVWNVP35/HKJjvQ9pLXcXYSspwCruORe4nRP0x81MbYIMys2yPVYBVEMYD0jGaaJsa7l7ScsJFEU7LEWUx6sISHCr/55Y0blgSvtF+o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=8V9+BsSVbTHZfLGi06PqZVikcnksNRPWkUmpnuxzJzw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=jVhcfB8CD+H0wsAagCqAUqK2tWslSN3RpT1BiJXZb+k1zWqzUJxklHcDCz9OM1y0MLfnP7WCHOMnvruGsEyckMUxF8SlRH6DyyZWcBY2N0boe3pcBw/X8DAAbaZk1ciaxJIH9bV3pJi37uVDgWmQrUxQj6Z0qeqp5vwiRQ0MAXg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Pn4JAYna; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Pn4JAYna" Received: by smtp.kernel.org (Postfix) with ESMTPS id DDA01C2BCB3; Wed, 22 Jul 2026 15:50:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=8V9+BsSVbTHZfLGi06PqZVikcnksNRPWkUmpnuxzJzw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=Pn4JAYna8q+ncOjlZ0hH8qNZrdTt9fqF42S9CYCnMkUkJJtON5ncMJqlbGNcNNNQF L0xguONGhWmTqHnIBikAG+R9yGqxOnkhYLxuFiRNIYKyJeCLWdRQtVvbLeasPhzwqg oLzYOK6trKX54KVhvBk35SO/lZsKhi757Uy8hkZ8PDllI5riI67bIHbHFKMxXzB1xT FIC5d/6xh48WJx33RVaS6yhNV8ifx0UyIB/Ig2PxaETT0nlhsEW8+kgQp8Pph1R0w5 5ChiqYUVxYxTmdOKnibvnPGaL2gtZ4KMPOYVkEZ4jJuNBSS2Gk3i/eeaxxAendSvGF 8gS0NpZqCy1Bg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id C115BC44532; Wed, 22 Jul 2026 15:50:57 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:12 +0100 Subject: [PATCH v9 03/17] iio: ABI: add parent entry for iio channels Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-3-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=2407; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=KME4lbAmZLS+7H2VW8Y9DEQOx4E1scOeHoVVhQsAaYw=; b=L04fTjudyOWSMvXiDXgK71k2VGi0FnOVgiweMpQUpSHbEUeQToW/3UYcDpctqTRyUIT669FS4 ve7if/K2EeiBjUKoxdxVxAqD9EFha6WYkGg3BtCmXQjlccQfAdrxcN1 X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add documentation for a read-only sysfs attribute that allows to expose parent-child relationships between IIO channels. Signed-off-by: Rodrigo Alencar --- Documentation/ABI/testing/sysfs-bus-iio | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/te= sting/sysfs-bus-iio index 24712b5bd110..851887e85cb2 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -2165,6 +2165,34 @@ Description: specific attributes. This is useful for userspace to be able to better identify an individual channel. =20 +What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_parent +What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_parent +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_parent +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_parent +What: /sys/bus/iio/devices/iio:deviceX/in_currentY_parent +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_parent +What: /sys/bus/iio/devices/iio:deviceX/in_altcurrentY_parent +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_parent +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Read-only attribute containing the sysfs name prefix of the parent + channel for hierarchical channel relationships (e.g. "out_altvoltage0"). + The prefix uniquely identifies the parent channel and can be used by + userspace to organize channels in tree-like structures that reflects + virtual or logical relationships between them. + + For example, an IIO device may expose a primary output channel as + "out_voltage0" together with a set of profile-based or mode-based + subchannels used to configure or compose the output signal. These + subchannels may carry common attributes such as frequency, phase, etc. + They do not represent physical outputs, but rather logical/virtual + channels grouping configurations that affect the primary output. + Each subchannel exposes a _[Y]_parent attribute reading + "out_voltage0", allowing userspace to group controls under the output + channel they belong to rather than treating them as unrelated top-level + channels. + What: /sys/bus/iio/devices/iio:deviceX/in_frequencyY_raw KernelVersion: 7.3 Contact: linux-iio@vger.kernel.org --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 2DE3D476069; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=j8LnTGHWBp6eC+WJaZeKBVI5iu9gxAy/43qTOMoMGynjro07AKcrVvj2W1G2scsx366OqHAPFyw4ud842UdAw+2F4LLiDkVhhdPm53PgLf5DZ3ccLOp+0IWi0xaiZzrXsbynbZzwalL7cBHcxuOmwCuCm0t+sWDthrNleIuCSg0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=LkGR2S/oFgI/+IPKjfMY0lgtsj7hpBpubbqxfDZIpLs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=MhBV0Sytjm12mh7nimMXiJxYmTGWiTsMSNVj2ocvrvC6l3KZHfAzLTxxbBYj1ZPBJCZzH7qe+Qw4BEM013pW3NbvHg1RRC74we29Q6vcP5XXgH7i143cBmPpMEi6kOuEzXSUPA9dxBCduW4UU3WOg6xb/To8lpq36Yx0BdGw7m0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=eyhPE0jX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="eyhPE0jX" Received: by smtp.kernel.org (Postfix) with ESMTPS id ECC88C2BCFD; Wed, 22 Jul 2026 15:50:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=LkGR2S/oFgI/+IPKjfMY0lgtsj7hpBpubbqxfDZIpLs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=eyhPE0jXjUaPyXTYnIm0Xnf1ps4+yXfpVjmhiqePLx12VHPQTkJfrA8zss6u0IuH2 BY5Ezo0gIZ8kht55xoSCkctoCH7nBsdxqkwFqSDd5xb1BN96CDviX1ZslKiu40Sh1z Kn9zpb8c5coMlJ9f7CeI0x6iKNQFLxnEZKIN91OIhDBuFjIIL8nV1EXixqRBMS95PU CaGKToU6eye7krlyujA+h4agnKfXvxViqpAx4w+5dSeCqyVatnh7zzL6IcTyAFwA72 letaJmv+P1oUIZ384egvXolDNgVYoLOHPhUi640h1NScEj5+JZWFhxz15wcERgbU9v uQYcMwyalKurQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id D2DF1C44539; Wed, 22 Jul 2026 15:50:57 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:13 +0100 Subject: [PATCH v9 04/17] iio: add IIO_FREQUENCY channel type Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-4-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=1881; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=1IPBIAnpoq77uH/f9ncnZ8sog7Fcx5CLyDe5QVK2WqI=; b=cnrEDLZ2SbIQgytYCsLM95oa3NsRNdyWC7v1TuQ2RgwlQH2uNF51Yz9unZVFi64HAU/spwo5a mnQo8TQ+yCXCtLpxuKURLM7o/GXgos2e68GEuwmtZ9sJckum6i+Hnho X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add IIO_FREQUENCY channel type to support Direct Digital Synthesizer (DDS) devices that handles frequency values written directly to its buffered interface. Signed-off-by: Rodrigo Alencar --- drivers/iio/industrialio-core.c | 1 + include/uapi/linux/iio/types.h | 1 + tools/iio/iio_event_monitor.c | 2 ++ 3 files changed, 4 insertions(+) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-cor= e.c index bdf3d4c06331..a45277bc5d99 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -99,6 +99,7 @@ static const char * const iio_chan_type_name_spec[] =3D { [IIO_ATTENTION] =3D "attention", [IIO_ALTCURRENT] =3D "altcurrent", [IIO_COVERAGE] =3D "coverage", + [IIO_FREQUENCY] =3D "frequency", }; =20 static const char * const iio_modifier_names[] =3D { diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index c9295c707041..1996839d1fce 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -54,6 +54,7 @@ enum iio_chan_type { IIO_ATTENTION, IIO_ALTCURRENT, IIO_COVERAGE, + IIO_FREQUENCY, }; =20 enum iio_modifier { diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index bc3ef4c77c2b..7885f0f7562e 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -66,6 +66,7 @@ static const char * const iio_chan_type_name_spec[] =3D { [IIO_ATTENTION] =3D "attention", [IIO_ALTCURRENT] =3D "altcurrent", [IIO_COVERAGE] =3D "coverage", + [IIO_FREQUENCY] =3D "frequency", }; =20 static const char * const iio_ev_type_text[] =3D { @@ -196,6 +197,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_ATTENTION: case IIO_ALTCURRENT: case IIO_COVERAGE: + case IIO_FREQUENCY: break; default: return false; --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 56AFE492504; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=W9VrV58sE149b9jMtviTuHA9wDccGsp3nPCDq30XaPMVClHunlqz/1J/WZ4uQTyNzCphhG1mZMl+8vyTVo90fQw4LfGHeBppO80vUy0Cfyy+ImAvbQufQAT2pZU2zrbKfbtgZYO/LMoPVtmVSleFDZK9pqiTccd4ykfvFGs9ASw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=8MhduG14jsXaL5k4h11ocWyrck/47G7XvTTKiN8VDTM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YPH3JOb/6+prNkmOlcChTMkgS27C2t29pqmbgzKPMbQYcfcGBNZPmzPRrKJubP82DfbQEVF3Rx5mxNay8T5P+vQKz3pgUJ3qsF7e4flYJDumIFZVZBJMrxFUMndGrAR3nS+2exnkHqLVFMIdHqHj+/rux6MnsKpD+Xo5bXWp6ww= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=RTBOL+SQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="RTBOL+SQ" Received: by smtp.kernel.org (Postfix) with ESMTPS id 0525BC2BCFB; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=8MhduG14jsXaL5k4h11ocWyrck/47G7XvTTKiN8VDTM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=RTBOL+SQSL0mTv+paXGujIxM6F/tjiMT1BhFaxRgXGQYRIilFxY+PkzHu5u6ToQSs 1zrbE/HNg14T5rVJ0AkJiAmFISIsnn5B0xhieVo+PFC9TnZhDss4vo86cdRQGpM8XF inRBjOZ2KTcL/2u0lFBSsg6VMUGlRdct2tH9QP1VBD742127psJM83zHa8pCNjs3i2 qiNN35QvCtFCt6xEYvqUCfO4QgTLRHysTjWtbppUT5hWNtajGNd9g244WirvJHMt2C Chk5sjxqQBxbF056gNlBN5a051v2Hg9yLZgGtn88EG7tTHOGjqLg8nfhUAb7DDZ/Ij dyvz985VMI6cA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id E4BC7C44536; Wed, 22 Jul 2026 15:50:57 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:14 +0100 Subject: [PATCH v9 05/17] iio: core: support 64-bit register through debugfs Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-5-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=5286; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=rhtJXuF4mkm1WH5HBYUmnTDdQ7mLzj7pscCY6q7WDzI=; b=cRMGuwU/PtHZzvBnGwSJc1/X4AXGNhrM9uLQNvsLIUkXdRNb9WdFNIjH5DSpdeoi+ReQtrq4k 1cnGIHNHW+XCfcKAfhrF9wiOvnQfHdPalwDE/Kp7jBkHDnEAuGE0XDr X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add debugfs_reg64_access function pointer field into iio_info and modify file operation callbacks to favor 64-bit variant when it is available. Signed-off-by: Rodrigo Alencar --- drivers/iio/industrialio-core.c | 33 ++++++++++++++++++++++++--------- include/linux/iio/iio-opaque.h | 2 +- include/linux/iio/iio.h | 7 +++++++ 3 files changed, 32 insertions(+), 10 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-cor= e.c index a45277bc5d99..98fc00343dc5 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -388,6 +388,7 @@ static ssize_t iio_debugfs_read_reg(struct file *file, = char __user *userbuf, struct iio_dev *indio_dev =3D file->private_data; struct iio_dev_opaque *iio_dev_opaque =3D to_iio_dev_opaque(indio_dev); unsigned int val =3D 0; + u64 val64 =3D 0; int ret; =20 if (*ppos > 0) @@ -395,9 +396,17 @@ static ssize_t iio_debugfs_read_reg(struct file *file,= char __user *userbuf, iio_dev_opaque->read_buf, iio_dev_opaque->read_buf_len); =20 - ret =3D indio_dev->info->debugfs_reg_access(indio_dev, - iio_dev_opaque->cached_reg_addr, - 0, &val); + if (indio_dev->info->debugfs_reg64_access) { + ret =3D indio_dev->info->debugfs_reg64_access(indio_dev, + iio_dev_opaque->cached_reg_addr, + 0, &val64); + } else { + ret =3D indio_dev->info->debugfs_reg_access(indio_dev, + iio_dev_opaque->cached_reg_addr, + 0, &val); + val64 =3D val; + } + if (ret) { dev_err(indio_dev->dev.parent, "%s: read failed\n", __func__); return ret; @@ -405,7 +414,7 @@ static ssize_t iio_debugfs_read_reg(struct file *file, = char __user *userbuf, =20 iio_dev_opaque->read_buf_len =3D snprintf(iio_dev_opaque->read_buf, sizeof(iio_dev_opaque->read_buf), - "0x%X\n", val); + "0x%llX\n", val64); =20 return simple_read_from_buffer(userbuf, count, ppos, iio_dev_opaque->read_buf, @@ -417,8 +426,9 @@ static ssize_t iio_debugfs_write_reg(struct file *file, { struct iio_dev *indio_dev =3D file->private_data; struct iio_dev_opaque *iio_dev_opaque =3D to_iio_dev_opaque(indio_dev); - unsigned int reg, val; + unsigned int reg; char buf[80]; + u64 val64; int ret; =20 if (*ppos !=3D 0 || count >=3D sizeof(buf)) @@ -431,7 +441,7 @@ static ssize_t iio_debugfs_write_reg(struct file *file, =20 buf[ret] =3D '\0'; =20 - ret =3D sscanf(buf, "%i %i", ®, &val); + ret =3D sscanf(buf, "%i %lli", ®, &val64); =20 switch (ret) { case 1: @@ -439,8 +449,12 @@ static ssize_t iio_debugfs_write_reg(struct file *file, break; case 2: iio_dev_opaque->cached_reg_addr =3D reg; - ret =3D indio_dev->info->debugfs_reg_access(indio_dev, reg, - val, NULL); + if (indio_dev->info->debugfs_reg64_access) + ret =3D indio_dev->info->debugfs_reg64_access(indio_dev, reg, + val64, NULL); + else + ret =3D indio_dev->info->debugfs_reg_access(indio_dev, reg, + val64, NULL); if (ret) { dev_err(indio_dev->dev.parent, "%s: write failed\n", __func__); @@ -471,7 +485,8 @@ static void iio_device_register_debugfs(struct iio_dev = *indio_dev) { struct iio_dev_opaque *iio_dev_opaque; =20 - if (indio_dev->info->debugfs_reg_access =3D=3D NULL) + if (!indio_dev->info->debugfs_reg_access && + !indio_dev->info->debugfs_reg64_access) return; =20 if (!iio_debugfs_dentry) diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index b87841a355f8..98330385e08d 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -73,7 +73,7 @@ struct iio_dev_opaque { #if defined(CONFIG_DEBUG_FS) struct dentry *debugfs_dentry; unsigned int cached_reg_addr; - char read_buf[20]; + char read_buf[24]; unsigned int read_buf_len; #endif }; diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 711c00f67371..cd17703274a6 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -484,6 +484,10 @@ struct iio_trigger; /* forward declaration */ * @update_scan_mode: function to configure device and scan buffer when * channels have changed * @debugfs_reg_access: function to read or write register value of device + * @debugfs_reg64_access: function to read or write 64-bit register value = of + * device. debugfs_reg_access() and debugfs_reg64_access() + * are mutually exclusive, but the 64-bit variant has + * precedence in case both are implemented. * @fwnode_xlate: fwnode based function pointer to obtain channel specifie= r index. * @hwfifo_set_watermark: function pointer to set the current hardware * fifo watermark level; see hwfifo_* entries in @@ -572,6 +576,9 @@ struct iio_info { int (*debugfs_reg_access)(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval); + int (*debugfs_reg64_access)(struct iio_dev *indio_dev, + unsigned int reg, u64 writeval, + u64 *readval); int (*fwnode_xlate)(struct iio_dev *indio_dev, const struct fwnode_reference_args *iiospec); int (*hwfifo_set_watermark)(struct iio_dev *indio_dev, unsigned int val); --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 56A374921BC; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=Ki0jSvMgFrMWRv32SdAmdcfxDahkV4D9KCwMOxBh+R0wlj5xh+lfzvRK8SXEgrP4pEZWXh8AN7iSVH4nODzMmT5XilnHQKbDH4969mLAiGp1u2fJd3+FO/YbI4llXAu1Dyon69XLNA+8I9G4rGUipm2NaQTiDwTfS5QQXnd2AD8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=zDzo9F6BjYMLU4Bklfq3YRGbBEWoNPSpXL18gbs0G2E=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=mhHu0vtG0b3uMeG8nz4bOYYPrwkhaBUEy+gVMbV7OaX7qJRtD9cgn1Ms29dkUGESe/YGsTDheA6Ms++GGpF+aH/DI4Ncm/Bt7QZUa12pYWwmF4vi+AuRQoyztgpolKp0WR4Fdq8sj0LiwNRdPyY/kvmn3IBQPs9CabQVKHXpRKQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=meutNsE7; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="meutNsE7" Received: by smtp.kernel.org (Postfix) with ESMTPS id 14635C2BCC6; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=zDzo9F6BjYMLU4Bklfq3YRGbBEWoNPSpXL18gbs0G2E=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=meutNsE7AyxRXYMnbRuf+rl4wsVXSbGj1RUlIQFCxnF9w7qOZoxhVnjjtuz/iOyK5 DNq3tE2YpnHll99hNE6BdWfo3OPo1h16Yavr0tLD1twyIT2lJ4B4wMaWJ3AoflYpCp J3SISFy5/QJRLBiZXdrPf6keDHyCyYt2pQeFPrhLGutAjQzx4+eVj+28wwGq2U1+a0 nxvQ+Ss6mRArjtftAXn5NfuuTEr5zkii7C8bTiYYwLVGOX6l4H6K90FII58jnzalXE Gs8X2J2w+BctiDNwoKiA4VwZzYmXRjx6P4t31oKKU7qRvSEfU1y6Ua/FR1m6G2TSEf XRAzpoc1dE88g== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 00619C4453F; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:15 +0100 Subject: [PATCH v9 06/17] iio: core: create local __iio_chan_prefix_emit() for reuse Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-6-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=7174; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=oHv9yUvbBMOmb0C0IYClxjAbCiF7JNtiCFd2C4OtotY=; b=/hHqVgIM62yxFjupquvvK39UROFbdFZFBWFD0z9jnFZzHHfQ7T1pwX/z9P6JhBPZKdEWYp9hm CJkPmPmkNhZAJ1kOuCScs6O453LpDD5M9PvYNd2AL8jkVDFbuGwTD9j X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Move logic to create a channel prefix for naming attribute files into a separate __iio_chan_prefix_emit() function for reuse. The dev pointer is passed to __iio_device_attr_init() so that dev_err() replaces WARN(). The new helper is going to be used to expose channel relationships as sysfs attributes. Signed-off-by: Rodrigo Alencar --- drivers/iio/industrialio-core.c | 172 +++++++++++++++++-------------------= ---- 1 file changed, 71 insertions(+), 101 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-cor= e.c index 98fc00343dc5..23d3801887a9 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include =20 @@ -199,6 +200,61 @@ static const char * const iio_chan_info_postfix[] =3D { [IIO_CHAN_INFO_CONVDELAY] =3D "convdelay", [IIO_CHAN_INFO_POWERFACTOR] =3D "powerfactor", }; + +static ssize_t __iio_chan_prefix_emit(struct device *dev, + const struct iio_chan_spec *chan, + enum iio_shared_by shared_by, + char *buf, size_t len) +{ + const char *type =3D iio_chan_type_name_spec[chan->type]; + const char *dir =3D iio_direction[chan->output]; + struct seq_buf s; + + seq_buf_init(&s, buf, len); + + switch (shared_by) { + case IIO_SHARED_BY_ALL: + break; + case IIO_SHARED_BY_DIR: + seq_buf_printf(&s, "%s", dir); + break; + case IIO_SHARED_BY_TYPE: + seq_buf_printf(&s, "%s_%s", dir, type); + if (chan->differential) + seq_buf_printf(&s, "-%s", type); + break; + case IIO_SEPARATE: + /* Validate differential channel settings */ + if (chan->differential) { + if (!chan->indexed) { + dev_err(dev, "Differential channels must be indexed\n"); + return -EINVAL; + } + if (chan->modified) { + dev_err(dev, "Differential channels can not have modifier\n"); + return -EINVAL; + } + } + + seq_buf_printf(&s, "%s_%s", dir, type); + + if (chan->indexed) + seq_buf_printf(&s, "%d", chan->channel); + + if (chan->differential) + seq_buf_printf(&s, "-%s%d", type, chan->channel2); + + if (chan->modified) + seq_buf_printf(&s, "_%s", iio_modifier_names[chan->channel2]); + + if (chan->extend_name) + seq_buf_printf(&s, "_%s", chan->extend_name); + break; + } + + return seq_buf_has_overflowed(&s) ? -EOVERFLOW : s.len; +} + /** * iio_device_id() - query the unique ID for the device * @indio_dev: Device structure whose ID is being queried @@ -1088,7 +1144,8 @@ static ssize_t iio_write_channel_info(struct device *= dev, } =20 static -int __iio_device_attr_init(struct device_attribute *dev_attr, +int __iio_device_attr_init(struct device *dev, + struct device_attribute *dev_attr, const char *postfix, struct iio_chan_spec const *chan, ssize_t (*readfunc)(struct device *dev, @@ -1100,106 +1157,22 @@ int __iio_device_attr_init(struct device_attribute= *dev_attr, size_t len), enum iio_shared_by shared_by) { - int ret =3D 0; - char *name =3D NULL; - char *full_postfix; + char prefix[NAME_MAX + 1]; + ssize_t ret; =20 sysfs_attr_init(&dev_attr->attr); =20 - /* Build up postfix of __postfix */ - if (chan->modified && (shared_by =3D=3D IIO_SEPARATE)) { - if (chan->extend_name) - full_postfix =3D kasprintf(GFP_KERNEL, "%s_%s_%s", - iio_modifier_names[chan->channel2], - chan->extend_name, - postfix); - else - full_postfix =3D kasprintf(GFP_KERNEL, "%s_%s", - iio_modifier_names[chan->channel2], - postfix); - } else { - if (chan->extend_name =3D=3D NULL || shared_by !=3D IIO_SEPARATE) - full_postfix =3D kstrdup(postfix, GFP_KERNEL); - else - full_postfix =3D kasprintf(GFP_KERNEL, - "%s_%s", - chan->extend_name, - postfix); - } - if (full_postfix =3D=3D NULL) + ret =3D __iio_chan_prefix_emit(dev, chan, shared_by, prefix, sizeof(prefi= x)); + if (ret < 0) + return ret; + + if (ret) + dev_attr->attr.name =3D kasprintf(GFP_KERNEL, "%s_%s", prefix, postfix); + else + dev_attr->attr.name =3D kstrdup(postfix, GFP_KERNEL); + if (!dev_attr->attr.name) return -ENOMEM; =20 - if (chan->differential) { /* Differential can not have modifier */ - switch (shared_by) { - case IIO_SHARED_BY_ALL: - name =3D kasprintf(GFP_KERNEL, "%s", full_postfix); - break; - case IIO_SHARED_BY_DIR: - name =3D kasprintf(GFP_KERNEL, "%s_%s", - iio_direction[chan->output], - full_postfix); - break; - case IIO_SHARED_BY_TYPE: - name =3D kasprintf(GFP_KERNEL, "%s_%s-%s_%s", - iio_direction[chan->output], - iio_chan_type_name_spec[chan->type], - iio_chan_type_name_spec[chan->type], - full_postfix); - break; - case IIO_SEPARATE: - if (!chan->indexed) { - WARN(1, "Differential channels must be indexed\n"); - ret =3D -EINVAL; - goto error_free_full_postfix; - } - name =3D kasprintf(GFP_KERNEL, - "%s_%s%d-%s%d_%s", - iio_direction[chan->output], - iio_chan_type_name_spec[chan->type], - chan->channel, - iio_chan_type_name_spec[chan->type], - chan->channel2, - full_postfix); - break; - } - } else { /* Single ended */ - switch (shared_by) { - case IIO_SHARED_BY_ALL: - name =3D kasprintf(GFP_KERNEL, "%s", full_postfix); - break; - case IIO_SHARED_BY_DIR: - name =3D kasprintf(GFP_KERNEL, "%s_%s", - iio_direction[chan->output], - full_postfix); - break; - case IIO_SHARED_BY_TYPE: - name =3D kasprintf(GFP_KERNEL, "%s_%s_%s", - iio_direction[chan->output], - iio_chan_type_name_spec[chan->type], - full_postfix); - break; - - case IIO_SEPARATE: - if (chan->indexed) - name =3D kasprintf(GFP_KERNEL, "%s_%s%d_%s", - iio_direction[chan->output], - iio_chan_type_name_spec[chan->type], - chan->channel, - full_postfix); - else - name =3D kasprintf(GFP_KERNEL, "%s_%s_%s", - iio_direction[chan->output], - iio_chan_type_name_spec[chan->type], - full_postfix); - break; - } - } - if (name =3D=3D NULL) { - ret =3D -ENOMEM; - goto error_free_full_postfix; - } - dev_attr->attr.name =3D name; - if (readfunc) { dev_attr->attr.mode |=3D 0444; dev_attr->show =3D readfunc; @@ -1210,10 +1183,7 @@ int __iio_device_attr_init(struct device_attribute *= dev_attr, dev_attr->store =3D writefunc; } =20 -error_free_full_postfix: - kfree(full_postfix); - - return ret; + return 0; } =20 static void __iio_device_attr_deinit(struct device_attribute *dev_attr) @@ -1242,7 +1212,7 @@ int __iio_add_chan_devattr(const char *postfix, iio_attr =3D kzalloc_obj(*iio_attr); if (iio_attr =3D=3D NULL) return -ENOMEM; - ret =3D __iio_device_attr_init(&iio_attr->dev_attr, + ret =3D __iio_device_attr_init(dev, &iio_attr->dev_attr, postfix, chan, readfunc, writefunc, shared_by); if (ret) --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 79B024D98EC; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=gfiC9nr/dvoF1PeqUiXrDb/37vhukOkiui682mLn8DJKB+DWfLdiBci0+JxRgBxkoos4qCw66AsSImiKA4NaJtaQgjuE9V+9nHWOJ8BwFj3lZTa7elcRZQLshK5BLCAEXmXuitpRJfKdjaTd1Lkr9wY+zAifAppYUyX+iwb6NCs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=yW1PLHZZiYF+Epk8vTWuRrzkM/pY0d+Z/K//KPKBJvU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ja+biQ2xsRlCVn3bpkMwWSI4oalVoQEGKA9IgAe0WUg6ZiuqaCdB7p7HpKLs5AZr48TBv/FaxRmTSnxgjSFQ2t4rHCMsE24tbLZlZQk+PgPebqOILx/Qw/537rK6C6RtQpg9gAfTVTZyTtlJd7vGafZ04wYLGkU2KLutThU9JeQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dC6ywu+A; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="dC6ywu+A" Received: by smtp.kernel.org (Postfix) with ESMTPS id 27D0CC2BCF6; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=yW1PLHZZiYF+Epk8vTWuRrzkM/pY0d+Z/K//KPKBJvU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=dC6ywu+Ax4rwLPjhOCOVqd+QTzNzxW6RoNwbvGtdrXB/Dj0y2J/8YbqpoTcYO+GsI nOwk/OjnjJIc5hNwY9Kz34ZRGm93VWlK++/lC5UD8f9aYn0qIoLeIzaPKr10gx+9uD NxvIkrKka/383nHvOtQ6YUTlR+LtyKrYM9h3q686zSqlOA85ixS0EmOzAN/dnO3Vqo 5Ho+IJhPnai8CZj+AHS9x4x11PFs1YFSLA5m+MBH8VGDRxzK9mA6HvHyp3et/H9jV7 yW2LPNaZAu8tlIgk04ouOIjWUVwCxrEha8BEqBbSYoh6mMGei3Lxo7Ik5+cSigkCGe rao+1VUtamWig== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 12924C4453C; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:16 +0100 Subject: [PATCH v9 07/17] iio: test: add kunit tests for channel prefix naming generation Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-7-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=13857; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=pkAN5gKpJH+XBT6chCn1Uw/Mtvb3KtlNF8KATkxv+ds=; b=1n08IaoM+XOxrw+G1yZEJxvmAfV7yuUFPeBE05xNd+rSg4ODxYUbYmlqblq6OfWtAUcOAcO84 kgmT6g4hUAXBZMd3Xt16dczf9RPIXpIw4HnwSIwRVG/+qLAHJg/efUV X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add a KUnit test suite covering __iio_chan_prefix_emit(), the helper that builds IIO sysfs attribute name prefixes from an iio_chan_spec. The suite groups cases by the enum iio_shared_by mode it exercises: - IIO_SHARED_BY_ALL: produces an empty prefix. - IIO_SHARED_BY_DIR: emits direction only ("in" / "out"). - IIO_SHARED_BY_TYPE: emits "_" and the differential "_-" variant. - IIO_SEPARATE: covers the full matrix of indexed, differential, modified, output and extend_name combinations, plus the two documented error paths (differential without indexed, differential with modifier). A final case exercises the seq_buf overflow path by passing an undersized buffer and expects -EOVERFLOW. Also, an entry is created under MAINTAINERS dedicated to tests for IIO core helpers. Signed-off-by: Rodrigo Alencar --- MAINTAINERS | 7 + drivers/iio/iio_core.h | 7 + drivers/iio/industrialio-core.c | 13 +- drivers/iio/test/Kconfig | 14 ++ drivers/iio/test/Makefile | 1 + drivers/iio/test/iio-test-channel-prefix.c | 304 +++++++++++++++++++++++++= ++++ 6 files changed, 342 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 7077ed7c9efe..d61447c9d197 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12634,6 +12634,13 @@ F: include/dt-bindings/iio/ F: include/linux/iio/ F: tools/iio/ =20 +IIO CORE KUNIT TESTS +M: Rodrigo Alencar +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/test/iio-test-channel-prefix.c +F: drivers/iio/test/iio-test-format.c + IIO UNIT CONVERTER M: Peter Rosin L: linux-iio@vger.kernel.org diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index b7d5f4f0fada..59fa1bd9924b 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -109,4 +109,11 @@ void iio_device_wakeup_eventset(struct iio_dev *indio_= dev); struct iio_event_interface; bool iio_event_enabled(const struct iio_event_interface *ev_int); =20 +#if IS_ENABLED(CONFIG_KUNIT) +ssize_t __iio_chan_prefix_emit(struct device *dev, + const struct iio_chan_spec *chan, + enum iio_shared_by shared_by, + char *buf, size_t len); +#endif + #endif diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-cor= e.c index 23d3801887a9..aa9d6474edd9 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -9,12 +9,15 @@ =20 #define pr_fmt(fmt) "iio-core: " fmt =20 +#include + #include #include #include #include #include #include +#include #include #include #include @@ -201,10 +204,11 @@ static const char * const iio_chan_info_postfix[] =3D= { [IIO_CHAN_INFO_POWERFACTOR] =3D "powerfactor", }; =20 -static ssize_t __iio_chan_prefix_emit(struct device *dev, - const struct iio_chan_spec *chan, - enum iio_shared_by shared_by, - char *buf, size_t len) +VISIBLE_IF_KUNIT +ssize_t __iio_chan_prefix_emit(struct device *dev, + const struct iio_chan_spec *chan, + enum iio_shared_by shared_by, + char *buf, size_t len) { const char *type =3D iio_chan_type_name_spec[chan->type]; const char *dir =3D iio_direction[chan->output]; @@ -254,6 +258,7 @@ static ssize_t __iio_chan_prefix_emit(struct device *de= v, =20 return seq_buf_has_overflowed(&s) ? -EOVERFLOW : s.len; } +EXPORT_SYMBOL_IF_KUNIT(__iio_chan_prefix_emit); =20 /** * iio_device_id() - query the unique ID for the device diff --git a/drivers/iio/test/Kconfig b/drivers/iio/test/Kconfig index 4fc17dd0dcd7..7d2c29cc583b 100644 --- a/drivers/iio/test/Kconfig +++ b/drivers/iio/test/Kconfig @@ -4,6 +4,20 @@ # =20 # Keep in alphabetical order +config IIO_CHANNEL_PREFIX_KUNIT_TEST + tristate "Test IIO channel prefix" if !KUNIT_ALL_TESTS + depends on KUNIT && IIO + default KUNIT_ALL_TESTS + help + Build unit tests for __iio_chan_prefix_emit(), the helper that + builds IIO sysfs attribute name prefixes from an iio_chan_spec. + The tests are compiled into the IIO core module. + + For more information on KUnit and unit tests in general, please refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + config IIO_GTS_KUNIT_TEST tristate "Test IIO gain-time-scale helpers" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/drivers/iio/test/Makefile b/drivers/iio/test/Makefile index 0c846bc21acd..ba08afa7f3f2 100644 --- a/drivers/iio/test/Makefile +++ b/drivers/iio/test/Makefile @@ -4,6 +4,7 @@ # =20 # Keep in alphabetical order +obj-$(CONFIG_IIO_CHANNEL_PREFIX_KUNIT_TEST) +=3D iio-test-channel-prefix.o obj-$(CONFIG_IIO_RESCALE_KUNIT_TEST) +=3D iio-test-rescale.o obj-$(CONFIG_IIO_FORMAT_KUNIT_TEST) +=3D iio-test-format.o obj-$(CONFIG_IIO_GTS_KUNIT_TEST) +=3D iio-test-gts.o diff --git a/drivers/iio/test/iio-test-channel-prefix.c b/drivers/iio/test/= iio-test-channel-prefix.c new file mode 100644 index 000000000000..d049e4222c40 --- /dev/null +++ b/drivers/iio/test/iio-test-channel-prefix.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Unit tests for IIO channel prefix generation. + */ + +#include +#include + +#include +#include +#include + +#include "../iio_core.h" + +#define PREFIX_BUF_SIZE (NAME_MAX + 1) + +#define EXPECT_PREFIX(_test, _buf, _ret, _expected) do { \ + struct kunit *__test =3D (_test); \ + const char *__expected =3D (_expected); \ + \ + KUNIT_EXPECT_EQ(__test, (_ret), (ssize_t)strlen(__expected)); \ + KUNIT_EXPECT_STREQ(__test, (_buf), __expected); \ + } while (0) + +static char *iio_test_prefix_alloc(struct kunit *test) +{ + char *buf =3D kunit_kzalloc(test, PREFIX_BUF_SIZE, GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + return buf; +} + +static void iio_test_prefix_shared_by_all(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_VOLTAGE, + }; + const struct iio_chan_spec chan_noisy =3D { + .type =3D IIO_ACCEL, + .output =3D 1, + .indexed =3D 1, + .modified =3D 1, + .channel =3D 7, + .channel2 =3D IIO_MOD_Z, + .extend_name =3D "supply", + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SHARED_BY_ALL, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, ""); + + ret =3D __iio_chan_prefix_emit(NULL, &chan_noisy, IIO_SHARED_BY_ALL, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, ""); +} + +static void iio_test_prefix_shared_by_dir(struct kunit *test) +{ + const struct iio_chan_spec chan_in =3D { + .type =3D IIO_VOLTAGE, + .output =3D 0, + }; + const struct iio_chan_spec chan_out =3D { + .type =3D IIO_VOLTAGE, + .output =3D 1, + }; + const struct iio_chan_spec chan_in_noisy =3D { + .type =3D IIO_ACCEL, + .indexed =3D 1, + .modified =3D 1, + .channel =3D 5, + .channel2 =3D IIO_MOD_Y, + .extend_name =3D "supply", + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan_in, IIO_SHARED_BY_DIR, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in"); + + ret =3D __iio_chan_prefix_emit(NULL, &chan_out, IIO_SHARED_BY_DIR, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "out"); + + ret =3D __iio_chan_prefix_emit(NULL, &chan_in_noisy, IIO_SHARED_BY_DIR, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in"); +} + +static void iio_test_prefix_shared_by_type(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_VOLTAGE, + }; + const struct iio_chan_spec chan_diff =3D { + .type =3D IIO_VOLTAGE, + .differential =3D 1, + }; + const struct iio_chan_spec chan_noisy =3D { + .type =3D IIO_VOLTAGE, + .indexed =3D 1, + .modified =3D 1, + .channel =3D 4, + .channel2 =3D IIO_MOD_X, + .extend_name =3D "supply", + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SHARED_BY_TYPE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage"); + + ret =3D __iio_chan_prefix_emit(NULL, &chan_diff, IIO_SHARED_BY_TYPE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage-voltage"); + + ret =3D __iio_chan_prefix_emit(NULL, &chan_noisy, IIO_SHARED_BY_TYPE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage"); +} + +static void iio_test_prefix_separate_simple(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_TEMP, + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_temp"); +} + +static void iio_test_prefix_separate_indexed(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_VOLTAGE, + .indexed =3D 1, + .channel =3D 3, + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage3"); +} + +static void iio_test_prefix_separate_indexed_diff(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_VOLTAGE, + .indexed =3D 1, + .differential =3D 1, + .channel =3D 0, + .channel2 =3D 1, + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage0-voltage1"); +} + +static void iio_test_prefix_separate_modified(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_ACCEL, + .modified =3D 1, + .channel2 =3D IIO_MOD_X, + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_accel_x"); +} + +static void iio_test_prefix_separate_indexed_modified(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_ACCEL, + .indexed =3D 1, + .modified =3D 1, + .channel =3D 2, + .channel2 =3D IIO_MOD_Y, + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_accel2_y"); +} + +static void iio_test_prefix_separate_extend_name(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_VOLTAGE, + .indexed =3D 1, + .channel =3D 2, + .extend_name =3D "supply", + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "in_voltage2_supply"); +} + +static void iio_test_prefix_output_separate(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_VOLTAGE, + .output =3D 1, + .indexed =3D 1, + .channel =3D 0, + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + EXPECT_PREFIX(test, buf, ret, "out_voltage0"); +} + +static void iio_test_prefix_diff_unindexed_fails(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_VOLTAGE, + .differential =3D 1, + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +static void iio_test_prefix_diff_modified_fails(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_VOLTAGE, + .indexed =3D 1, + .differential =3D 1, + .modified =3D 1, + .channel =3D 0, + .channel2 =3D 1, + }; + char *buf =3D iio_test_prefix_alloc(test); + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SEPARATE, + buf, PREFIX_BUF_SIZE); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +static void iio_test_prefix_overflow(struct kunit *test) +{ + const struct iio_chan_spec chan =3D { + .type =3D IIO_VOLTAGE, + }; + char small[4]; + ssize_t ret; + + ret =3D __iio_chan_prefix_emit(NULL, &chan, IIO_SHARED_BY_TYPE, + small, sizeof(small)); + KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW); +} + +static struct kunit_case iio_chan_prefix_test_cases[] =3D { + KUNIT_CASE(iio_test_prefix_shared_by_all), + KUNIT_CASE(iio_test_prefix_shared_by_dir), + KUNIT_CASE(iio_test_prefix_shared_by_type), + KUNIT_CASE(iio_test_prefix_separate_simple), + KUNIT_CASE(iio_test_prefix_separate_indexed), + KUNIT_CASE(iio_test_prefix_separate_indexed_diff), + KUNIT_CASE(iio_test_prefix_separate_modified), + KUNIT_CASE(iio_test_prefix_separate_indexed_modified), + KUNIT_CASE(iio_test_prefix_separate_extend_name), + KUNIT_CASE(iio_test_prefix_output_separate), + KUNIT_CASE(iio_test_prefix_diff_unindexed_fails), + KUNIT_CASE(iio_test_prefix_diff_modified_fails), + KUNIT_CASE(iio_test_prefix_overflow), + { } +}; + +static struct kunit_suite iio_channel_prefix_test_suite =3D { + .name =3D "iio-channel-prefix", + .test_cases =3D iio_chan_prefix_test_cases, +}; + +kunit_test_suite(iio_channel_prefix_test_suite); + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 7712E3515FC; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=gn66PqwhMh7h42aeMUPF1Ci7mDKvKloEAG2Qak4Y1KiX9efDUBcr3JFcFOCeQ3nxkV7I30nCEwjJSfgKlngMzI7U4tJDtsWIa+pOYYccAlbotvpTMe8BRitiT+qEY9Gla2BUYqKWgEtoAY+ArbrcOF9FXOIrvf6T4OoxcwEQrL8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=Pf0sgnB2zRgSmd8CaEG1hcLLHpnKDmh2Do5uEum+5Zg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OTDMFUlavmMAySVXDRaadEhiwPrUy4OWxgFP3KLXT5SmvJ71m2mEadOUOlGcLoaB/W3g/h1IXn6Lts0zZQjGnJe9m/hoB0QHSCMwge4aNuji6OHKsFOLIJUObbmBXH+YqE4msyD5ZSap2h1mzyg+dOAsCTifwEsJVNC69uqiF1M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=MGLYeJvi; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="MGLYeJvi" Received: by smtp.kernel.org (Postfix) with ESMTPS id 39228C4AF0F; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=Pf0sgnB2zRgSmd8CaEG1hcLLHpnKDmh2Do5uEum+5Zg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=MGLYeJviBN6H6t9vu3P9LT6e3fuXTGC2hiS81vmys2xSrybR9AqQHmT02m0ZLSL/h Nh0arHlsAB0LIUKukxRoFG2M+c9uYPhtezbvogWUypbZvTNJNhNKbwsmRpB5b/4MkJ yFwJmn6NexmlKcO70As3sSzQfar2fABY7XNDTrdz7xTDzuZDDjw/JhPIaUpiOGfVeE S8I8RGzNg1+twwpSHdXOKc3nfijUKqdEaOb6mslazCphwqVimTn7Bb8GU8qmSF/hRt h2feLppZSm3Z3/FT1UZSurEyRYcyFetKy66szczmqmE8MTV8DRfJQp6ffdo1db77m5 ds+Z/Bp03Gpxw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 242AEC4453E; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:17 +0100 Subject: [PATCH v9 08/17] iio: core: add hierarchical channel relationships Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-8-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=3402; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=njI4C72Gs2/a1BSXycY8GevO618GmzzAWx+9MSybKW8=; b=WYp/6SYNpCU1qgW2E8EkvCT7BGOOgp0k52+CapOMorpLFL0sdvWcsQLWSPkaXaQNNhr/iCUWx g9ncui9Gj1TDuAZ9f5qSNC1Ym/k0LNyp7WUqCEPoFhfuW7yMTF4FW7G X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add parent-child relationship between iio channels by creating a parent pointer field in iio_chan_spec struct and exposing a sysfs attribute that returns the parent channel prefix. Signed-off-by: Rodrigo Alencar --- drivers/iio/industrialio-core.c | 40 +++++++++++++++++++++++++++++++++++++= +++ include/linux/iio/iio.h | 5 +++++ 2 files changed, 45 insertions(+) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-cor= e.c index aa9d6474edd9..f7e3048354b9 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -856,6 +856,22 @@ static ssize_t iio_read_channel_label(struct device *d= ev, to_iio_dev_attr(attr)->c, buf); } =20 +static ssize_t iio_read_channel_parent(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + const struct iio_chan_spec *chan =3D to_iio_dev_attr(attr)->c->parent; + ssize_t len; + + len =3D __iio_chan_prefix_emit(dev, chan, IIO_SEPARATE, buf, PAGE_SIZE); + if (len < 0) + return len; + + buf[len++] =3D '\n'; + + return len; +} + static ssize_t iio_read_channel_info(struct device *dev, struct device_attribute *attr, char *buf) @@ -1269,6 +1285,25 @@ static int iio_device_add_channel_label(struct iio_d= ev *indio_dev, return 1; } =20 +static int iio_device_add_channel_parent_attr(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + struct iio_dev_opaque *iio_dev_opaque =3D to_iio_dev_opaque(indio_dev); + int ret; + + if (!chan->parent) + return 0; + + ret =3D __iio_add_chan_devattr("parent", chan, + &iio_read_channel_parent, NULL, + 0, IIO_SEPARATE, &indio_dev->dev, NULL, + &iio_dev_opaque->channel_attr_list); + if (ret < 0) + return ret; + + return 1; +} + static int iio_device_add_info_mask_type(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, enum iio_shared_by shared_by, @@ -1407,6 +1442,11 @@ static int iio_device_add_channel_sysfs(struct iio_d= ev *indio_dev, return ret; attrcount +=3D ret; =20 + ret =3D iio_device_add_channel_parent_attr(indio_dev, chan); + if (ret < 0) + return ret; + attrcount +=3D ret; + if (chan->ext_info) { unsigned int i =3D 0; =20 diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index cd17703274a6..0f006b8ad555 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -264,6 +264,10 @@ struct iio_scan_type { * @ext_info: Array of extended info attributes for this channel. * The array is NULL terminated, the last element should * have its name field set to NULL. + * @parent: Optional pointer to the parent channel spec for + * hierarchical channel relationships. When set, a read-only + * "parent" sysfs attribute is created containing the + * parent channel's sysfs name prefix (e.g. "in_voltage0"). * @extend_name: Allows labeling of channel attributes with an * informative name. Note this has no effect codes etc, * unlike modifiers. @@ -309,6 +313,7 @@ struct iio_chan_spec { const struct iio_event_spec *event_spec; unsigned int num_event_specs; const struct iio_chan_spec_ext_info *ext_info; + const struct iio_chan_spec *parent; const char *extend_name; const char *datasheet_name; unsigned int modified:1; --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 909024DA546; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=rBmndz8EOBhpRep7c81Ke6sIgvflsgcGTja+IThjCUtyy9YxypKn8yK7LfU5HCUzqGCuPKK+rBpzdM3syNt6yhtSQLwpxXzrXMpS23nIWdeAJk+pakNxqgcAp5obkDUHCwC4fLmy8ldZgcs6C+3kAWTR2sWi6C0O1jQ2LuNJ47I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=BaADvXGy14XBjjlm8c6D/DORjtIqmgp3rQFS3v01stU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=pkpiqURCkcJBArzfJwjDkCHLAfvw2/e/Ig3O3AQh4fWT+4s5iOTRIBFqHukm012NfcWMt9NGT0zl8BTxXqN5SgXkr7Mo2KSuEFglgNuWQce60AseWSP67DdJuDYC7v5A5/b7AjylAWLRHkeLviEEm2SdJJ2biOsTGKcmklWu7CY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=b5lLyMKX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="b5lLyMKX" Received: by smtp.kernel.org (Postfix) with ESMTPS id 49119C32781; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=BaADvXGy14XBjjlm8c6D/DORjtIqmgp3rQFS3v01stU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=b5lLyMKXIo5vYwm/29O+vWhBTf1KKiNtnrpWyUViigVo6xWmO8hu07ektGlHjUhhs RYm7w8Mq3UDDWDzwDuChyghNWdOEujtSok/907MGvXAooD4irKngjVDOf8QaZuYQHk sKuv5D0C0R5agee1gcbJvDAQEw3e+9qkYqEmqMDWvyrHoyYblJcLyyD3lMvTqtxVRe NbVYuiq3haydUB9n0IMAfl5pH2NS6juBET8Cu/ICuEkGRfeF7T+nKIhqW5a0HeycA/ f48n0L9zg+JVzgKjLQBFwq3M5aQPnqH7DRIr0AShGe5KFjarTHINxtmcMhU4weNkrr uLInLHq23/FWQ== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 38EF0C44532; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:18 +0100 Subject: [PATCH v9 09/17] dt-bindings: iio: frequency: add ad9910 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-9-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar , Conor Dooley X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=7035; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=SQ6CCLQgIcjPZ3DKADZpMwSSa5EcEZeAQztZcKlAwiA=; b=ragTRh5j18WFp8Kc5cgkFkUJkDHyJZGGeYjjic/qgOpXfva2FtLm2cHkidDYLqiG6Yr9zRvqz g8MCyZSfgI9CGdOmVVFGmnhiSUfTMRjMBft/+0nHiAadIR15DS0H/QJ X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar DT-bindings for AD9910, a 1 GSPS DDS with 14-bit DAC. It includes configurations for clocks, PLL configs, reset and basic GPIO control. Reviewed-by: Conor Dooley Signed-off-by: Rodrigo Alencar --- .../bindings/iio/frequency/adi,ad9910.yaml | 209 +++++++++++++++++= ++++ MAINTAINERS | 7 + 2 files changed, 216 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yam= l b/Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml new file mode 100644 index 000000000000..4eebbd388b27 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml @@ -0,0 +1,209 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/frequency/adi,ad9910.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD9910 Direct Digital Synthesizer + +maintainers: + - Rodrigo Alencar + +description: > + The AD9910 is a 1 GSPS direct digital synthesizer (DDS) with an integrat= ed + 14-bit DAC. It features single tone mode with 8 configurable profiles, + a digital ramp generator, RAM control, OSK, and a parallel data port for + high-speed streaming. + + https://www.analog.com/en/products/ad9910.html + +properties: + compatible: + const: adi,ad9910 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 70000000 + + clocks: + minItems: 1 + items: + - description: Reference clock (REF_CLK). + - description: Optional synchronization clock (SYNC_IN). + + clock-names: + minItems: 1 + items: + - const: ref_clk + - const: sync_in + + '#clock-cells': + const: 1 + + clock-output-names: + items: + - const: sync_clk + - const: pdclk + - const: sync_out + + interrupts: + minItems: 1 + maxItems: 2 + description: + Requires interrupt-names property with the same number of items. The + supported interrupts are 'drover' (digital ramp generator limit) and + 'ram_swp_ovr' (end of RAM sweep). + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + enum: [ drover, ram_swp_ovr ] + + dvdd-io33-supply: + description: 3.3V Digital I/O supply. + + avdd33-supply: + description: 3.3V Analog DAC supply. + + dvdd18-supply: + description: 1.8V Digital Core supply. + + avdd18-supply: + description: 1.8V Analog Core supply. + + reset-gpios: + maxItems: 1 + description: + GPIOs controlling the Main Device reset. + + io-reset-gpios: + maxItems: 1 + description: + GPIO controlling the I/O_RESET pin. + + powerdown-gpios: + maxItems: 1 + description: + GPIO controlling the EXT_PWR_DWN pin. + + update-gpios: + maxItems: 1 + description: + GPIO controlling the I/O_UPDATE pin. + + profile-gpios: + minItems: 3 + maxItems: 3 + description: + GPIOs controlling the PROFILE[2:0] pins for profile selection. + + sync-err-gpios: + maxItems: 1 + description: + GPIO used to read SYNC_SMP_ERR pin status. + + lock-detect-gpios: + maxItems: 1 + description: + GPIO used to read PLL_LOCK pin status. + + pwms: + maxItems: 1 + description: + PWM used to drive the DRCTL pin, which controls the digital ramp + generator slope direction. + + pwm-names: + items: + - const: drctl + + adi,pll-enable: + type: boolean + description: + Enables the internal PLL. Applicable when external loop filter compo= nents + are connected to the PLL_LOOP_FILTER pin and low reference clock + frequencies are used, often provided by a crystal or by a single-end= ed + on-board oscillator. + + adi,charge-pump-current-microamp: + minimum: 212 + maximum: 387 + default: 212 + description: + PLL charge pump current in microamps. Only applicable when the inter= nal + PLL is enabled. The value is rounded to the nearest supported step. = This + value depends mostly on the loop filter design. + + adi,refclk-out-drive-strength: + $ref: /schemas/types.yaml#/definitions/string + enum: [ disabled, low, medium, high ] + default: disabled + description: + Reference clock output (DRV0) drive strength. Only applicable when + the internal PLL is enabled. + +dependencies: + adi,charge-pump-current-microamp: [ 'adi,pll-enable' ] + adi,refclk-out-drive-strength: [ 'adi,pll-enable' ] + lock-detect-gpios: [ 'adi,pll-enable' ] + interrupts: [ interrupt-names ] + pwms: [ pwm-names ] + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - dvdd-io33-supply + - avdd33-supply + - dvdd18-supply + - avdd18-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells =3D <1>; + #size-cells =3D <0>; + dds@0 { + compatible =3D "adi,ad9910"; + reg =3D <0>; + spi-max-frequency =3D <1000000>; + + #clock-cells =3D <1>; + clock-output-names =3D "sync_clk", "pdclk", "sync_out"; + + clocks =3D <&ad9910_refclk>; + clock-names =3D "ref_clk"; + + dvdd-io33-supply =3D <&vdd_io33>; + avdd33-supply =3D <&vdd_a33>; + dvdd18-supply =3D <&vdd_d18>; + avdd18-supply =3D <&vdd_a18>; + + reset-gpios =3D <&gpio 0 GPIO_ACTIVE_HIGH>; + io-reset-gpios =3D <&gpio 1 GPIO_ACTIVE_HIGH>; + powerdown-gpios =3D <&gpio 2 GPIO_ACTIVE_HIGH>; + update-gpios =3D <&gpio 3 GPIO_ACTIVE_HIGH>; + profile-gpios =3D <&gpio 4 GPIO_ACTIVE_HIGH>, + <&gpio 5 GPIO_ACTIVE_HIGH>, + <&gpio 6 GPIO_ACTIVE_HIGH>; + + pwms =3D <&pwm 0 1000000 0>; + pwm-names =3D "drctl"; + + adi,pll-enable; + adi,charge-pump-current-microamp =3D <387>; + adi,refclk-out-drive-strength =3D "disabled"; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index d61447c9d197..380455e5b7ee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1678,6 +1678,13 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml F: drivers/iio/dac/ad9739a.c =20 +ANALOG DEVICES INC AD9910 DRIVER +M: Rodrigo Alencar +L: linux-iio@vger.kernel.org +S: Supported +W: https://ez.analog.com/linux-software-drivers +F: Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml + ANALOG DEVICES INC MAX22007 DRIVER M: Janani Sunil L: linux-iio@vger.kernel.org --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 80C384DA52F; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=k01WfG+IfFoCNW79Zp/KMJ3lfx0gEyx6Z7fQ4GrkRQZuwCosPO1d1uHB9X4dXoH8W0DIumAX36EJQIHJVaAgoZ99yPEjOXrgYqlb1NN2/l1TTYmJyDbK7xcX2qZ2njLlspbzlCJmZgG2Xcpu9zR3e2YaSmC0mWCutH95LKp8ko4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=BCBBnSdHjQJf+6x3hn96YlnCd6oqtjx/BMWsoXWs0II=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=s6hGKuw2K0/lc0ul9vBRJREYcH/W4QH1JleSwOZJttpIpl23oRiSdcEAZkkylIQRuLJpt0+XBEkOaJ85KAG8IslpUrrJUSVPmWfq4oDs568QMd5AKFVS6m6CCzEuW4YznTgdinrR4AH0K+mcqPS4x10WXzKq17iZFn/XxhQ8Pi4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jQGgcDuQ; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="jQGgcDuQ" Received: by smtp.kernel.org (Postfix) with ESMTPS id 61ED0C19425; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=BCBBnSdHjQJf+6x3hn96YlnCd6oqtjx/BMWsoXWs0II=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=jQGgcDuQ906QNK/Yx95I232DJ0N2TN5cmCbNeDIyElUTMEnSjQ3SpnWAAApf8Yhk7 8tJOx1Hsr0GEbzHescE91lRkGpo/dkCqZ25Uj+IJi2wXrY87AcmWrXCuQMjwc15z6E u97mfwwDKjbDeHz+hEWGJRcBlYnu5jpxV4pJQvL1M9rGnE/gCkbc5hb+HWOFO/29Mn QxQQEitkSSGcRrvI+KwMeseJuyNWRpGNHgZIl5ssY78oo2QCaW8VRZ0Ek8vrncAckw voc6BkuEALy9v1FBSZy6XfAmCQQ6i2Y4v7Km/RRNRZOKDSOaCEO3aQXP0T3lG3oLv2 6bC1UXija/WRg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4FC59C4453F; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:19 +0100 Subject: [PATCH v9 10/17] iio: frequency: ad9910: initial driver implementation Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-10-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=37069; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=IoEOjLWIeDZDxzVawAeIQ0qlGUEAqAXwufaUzoGjws0=; b=MidtA4wgRA7VSgtBYYXMD4PVjLXwgTjfb6tvYesGWQI1bXHxqg7mnan1ue4u6kB+KxDV0bi5o w3hp0ct2GrDCGT7blUGuBBcttSmpzOZDW9SmWvnpCeWlyENXrQ5tDCF X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add the core AD9910 DDS driver infrastructure with single tone mode support. This includes SPI register access, profile management via GPIO pins, PLL/DAC configuration from firmware properties, and single tone frequency/phase/amplitude control through IIO attributes. Signed-off-by: Rodrigo Alencar --- MAINTAINERS | 1 + drivers/iio/frequency/Kconfig | 18 + drivers/iio/frequency/Makefile | 1 + drivers/iio/frequency/ad9910.c | 1142 ++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 1162 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 380455e5b7ee..83342b577aa6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1684,6 +1684,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml +F: drivers/iio/frequency/ad9910.c =20 ANALOG DEVICES INC MAX22007 DRIVER M: Janani Sunil diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 90c6304c4bcd..6033f9155b4f 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -23,6 +23,24 @@ config AD9523 =20 endmenu =20 +menu "Direct Digital Synthesis" + +config AD9910 + tristate "Analog Devices AD9910 Direct Digital Synthesizer" + depends on SPI + depends on GPIOLIB + help + Say yes here to build support for Analog Devices AD9910 + 1 GSPS, 14-Bit DDS with integrated DAC. + + Supports single tone mode with 8 configurable profiles + and digital ramp generation. + + To compile this driver as a module, choose M here: the + module will be called ad9910. + +endmenu + # # Phase-Locked Loop (PLL) frequency synthesizers # diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 53b4d01414d8..e3b1d36ed620 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -5,6 +5,7 @@ =20 # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AD9523) +=3D ad9523.o +obj-$(CONFIG_AD9910) +=3D ad9910.o obj-$(CONFIG_ADF41513) +=3D adf41513.o obj-$(CONFIG_ADF4350) +=3D adf4350.o obj-$(CONFIG_ADF4371) +=3D adf4371.o diff --git a/drivers/iio/frequency/ad9910.c b/drivers/iio/frequency/ad9910.c new file mode 100644 index 000000000000..b41b011af281 --- /dev/null +++ b/drivers/iio/frequency/ad9910.c @@ -0,0 +1,1142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD9910 SPI DDS (Direct Digital Synthesizer) driver + * + * Copyright 2026 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Register addresses */ +#define AD9910_REG_CFR1 0x00 +#define AD9910_REG_CFR2 0x01 +#define AD9910_REG_CFR3 0x02 +#define AD9910_REG_AUX_DAC 0x03 +#define AD9910_REG_IO_UPDATE_RATE 0x04 +#define AD9910_REG_FTW 0x07 +#define AD9910_REG_POW 0x08 +#define AD9910_REG_ASF 0x09 +#define AD9910_REG_MULTICHIP_SYNC 0x0A +#define AD9910_REG_DRG_LIMIT 0x0B +#define AD9910_REG_DRG_STEP 0x0C +#define AD9910_REG_DRG_RATE 0x0D +#define AD9910_REG_PROFILE0 0x0E +#define AD9910_REG_PROFILE1 0x0F +#define AD9910_REG_PROFILE2 0x10 +#define AD9910_REG_PROFILE3 0x11 +#define AD9910_REG_PROFILE4 0x12 +#define AD9910_REG_PROFILE5 0x13 +#define AD9910_REG_PROFILE6 0x14 +#define AD9910_REG_PROFILE7 0x15 +#define AD9910_REG_RAM 0x16 + +#define AD9910_REG_NUM_CACHED 0x16 +#define AD9910_REG_PROFILE(x) (AD9910_REG_PROFILE0 + (x)) + +/* CFR1 bit definitions */ +#define AD9910_CFR1_RAM_ENABLE_MSK BIT(31) +#define AD9910_CFR1_RAM_PLAYBACK_DEST_MSK GENMASK(30, 29) +#define AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK BIT(23) +#define AD9910_CFR1_INV_SINC_EN_MSK BIT(22) +#define AD9910_CFR1_INT_PROFILE_CTL_MSK GENMASK(20, 17) +#define AD9910_CFR1_SELECT_SINE_MSK BIT(16) +#define AD9910_CFR1_LOAD_LRR_IO_UPDATE_MSK BIT(15) +#define AD9910_CFR1_AUTOCLR_DIG_RAMP_ACCUM_MSK BIT(14) +#define AD9910_CFR1_AUTOCLR_PHASE_ACCUM_MSK BIT(13) +#define AD9910_CFR1_CLEAR_DIG_RAMP_ACCUM_MSK BIT(12) +#define AD9910_CFR1_CLEAR_PHASE_ACCUM_MSK BIT(11) +#define AD9910_CFR1_LOAD_ARR_IO_UPDATE_MSK BIT(10) +#define AD9910_CFR1_OSK_ENABLE_MSK BIT(9) +#define AD9910_CFR1_SELECT_AUTO_OSK_MSK BIT(8) +#define AD9910_CFR1_DIGITAL_POWER_DOWN_MSK BIT(7) +#define AD9910_CFR1_DAC_POWER_DOWN_MSK BIT(6) +#define AD9910_CFR1_REFCLK_INPUT_POWER_DOWN_MSK BIT(5) +#define AD9910_CFR1_AUX_DAC_POWER_DOWN_MSK BIT(4) +#define AD9910_CFR1_EXT_POWER_DOWN_CTL_MSK BIT(3) +#define AD9910_CFR1_SDIO_INPUT_ONLY_MSK BIT(1) +#define AD9910_CFR1_LSB_FIRST_MSK BIT(0) + +/* DIGITAL and REFCLK_INPUT powerdown bits are effective without I/O updat= e */ +#define AD9910_CFR1_SW0_POWER_DOWN_MSK (AD9910_CFR1_DIGITAL_POWER_DOWN_MSK= | \ + AD9910_CFR1_REFCLK_INPUT_POWER_DOWN_MSK) +/* DAC and AUX_DAC powerdown require I/O update */ +#define AD9910_CFR1_SW1_POWER_DOWN_MSK (AD9910_CFR1_DAC_POWER_DOWN_MSK | \ + AD9910_CFR1_AUX_DAC_POWER_DOWN_MSK) + +#define AD9910_CFR1_SW_POWER_DOWN_MSK (AD9910_CFR1_SW0_POWER_DOWN_MSK | \ + AD9910_CFR1_SW1_POWER_DOWN_MSK) + +/* CFR2 bit definitions */ +#define AD9910_CFR2_AMP_SCALE_SINGLE_TONE_MSK BIT(24) +#define AD9910_CFR2_INTERNAL_IO_UPDATE_MSK BIT(23) +#define AD9910_CFR2_SYNC_CLK_EN_MSK BIT(22) +#define AD9910_CFR2_DRG_DEST_MSK GENMASK(21, 20) +#define AD9910_CFR2_DRG_ENABLE_MSK BIT(19) +#define AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK BIT(18) +#define AD9910_CFR2_DRG_NO_DWELL_LOW_MSK BIT(17) +#define AD9910_CFR2_DRG_NO_DWELL_MSK GENMASK(18, 17) +#define AD9910_CFR2_READ_EFFECTIVE_FTW_MSK BIT(16) +#define AD9910_CFR2_IO_UPDATE_RATE_CTL_MSK GENMASK(15, 14) +#define AD9910_CFR2_PDCLK_ENABLE_MSK BIT(11) +#define AD9910_CFR2_PDCLK_INVERT_MSK BIT(10) +#define AD9910_CFR2_TXENABLE_INVERT_MSK BIT(9) +#define AD9910_CFR2_MATCHED_LATENCY_EN_MSK BIT(7) +#define AD9910_CFR2_DATA_ASM_HOLD_LAST_MSK BIT(6) +#define AD9910_CFR2_SYNC_TIMING_VAL_DISABLE_MSK BIT(5) +#define AD9910_CFR2_PARALLEL_DATA_PORT_EN_MSK BIT(4) +#define AD9910_CFR2_FM_GAIN_MSK GENMASK(3, 0) + +/* CFR3 bit definitions */ +#define AD9910_CFR3_OPEN_MSK (BIT(27) | GENMASK(18, 16)) +#define AD9910_CFR3_DRV0_MSK GENMASK(29, 28) +#define AD9910_CFR3_VCO_SEL_MSK GENMASK(26, 24) +#define AD9910_CFR3_ICP_MSK GENMASK(21, 19) +#define AD9910_CFR3_REFCLK_DIV_BYPASS_MSK BIT(15) +#define AD9910_CFR3_REFCLK_DIV_RESETB_MSK BIT(14) +#define AD9910_CFR3_PFD_RESET_MSK BIT(10) +#define AD9910_CFR3_PLL_EN_MSK BIT(8) +#define AD9910_CFR3_N_MSK GENMASK(7, 1) + +/* Auxiliary DAC Control Register Bits */ +#define AD9910_AUX_DAC_FSC_MSK GENMASK(7, 0) + +/* ASF Register Bits */ +#define AD9910_ASF_RAMP_RATE_MSK GENMASK(31, 16) +#define AD9910_ASF_SCALE_FACTOR_MSK GENMASK(15, 2) +#define AD9910_ASF_STEP_SIZE_MSK GENMASK(1, 0) + +/* Multichip Sync Register Bits */ +#define AD9910_MC_SYNC_VALIDATION_DELAY_MSK GENMASK(31, 28) +#define AD9910_MC_SYNC_RECEIVER_ENABLE_MSK BIT(27) +#define AD9910_MC_SYNC_GENERATOR_ENABLE_MSK BIT(26) +#define AD9910_MC_SYNC_GENERATOR_POLARITY_MSK BIT(25) +#define AD9910_MC_SYNC_STATE_PRESET_MSK GENMASK(23, 18) +#define AD9910_MC_SYNC_OUTPUT_DELAY_MSK GENMASK(15, 11) +#define AD9910_MC_SYNC_INPUT_DELAY_MSK GENMASK(7, 3) + +/* Profile Register Format (Single Tone Mode) */ +#define AD9910_PROFILE_ST_ASF_MSK GENMASK_ULL(61, 48) +#define AD9910_PROFILE_ST_POW_MSK GENMASK_ULL(47, 32) +#define AD9910_PROFILE_ST_FTW_MSK GENMASK_ULL(31, 0) + +/* Device constants */ +#define AD9910_PI_NANORAD 3141592653UL + +#define AD9910_MAX_SYSCLK_HZ (1000 * HZ_PER_MHZ) +#define AD9910_MAX_PHASE_MICRORAD (AD9910_PI_NANORAD / 500) + +#define AD9910_ASF_MAX FIELD_MAX(AD9910_PROFILE_ST_ASF_MSK) +#define AD9910_POW_MAX FIELD_MAX(AD9910_PROFILE_ST_POW_MSK) +#define AD9910_NUM_PROFILES 8 + +/* PLL constants */ +#define AD9910_PLL_MIN_N 12 +#define AD9910_PLL_MAX_N 127 + +#define AD9910_PLL_IN_MIN_FREQ_HZ (3307 * HZ_PER_KHZ) +#define AD9910_PLL_IN_MAX_FREQ_HZ (60 * HZ_PER_MHZ) + +#define AD9910_PLL_OUT_MIN_FREQ_HZ (420 * HZ_PER_MHZ) +#define AD9910_PLL_OUT_MAX_FREQ_HZ AD9910_MAX_SYSCLK_HZ + +#define AD9910_VCO0_RANGE_AUTO_MAX_HZ (457 * HZ_PER_MHZ) +#define AD9910_VCO1_RANGE_AUTO_MAX_HZ (530 * HZ_PER_MHZ) +#define AD9910_VCO2_RANGE_AUTO_MAX_HZ (632 * HZ_PER_MHZ) +#define AD9910_VCO3_RANGE_AUTO_MAX_HZ (775 * HZ_PER_MHZ) +#define AD9910_VCO4_RANGE_AUTO_MAX_HZ (897 * HZ_PER_MHZ) +#define AD9910_VCO_RANGE_NUM 6 + +#define AD9910_ICP_MIN_uA 212 +#define AD9910_ICP_MAX_uA 387 +#define AD9910_ICP_STEP_uA 25 + +#define AD9910_DAC_IOUT_MAX_uA 31590 +#define AD9910_DAC_IOUT_DEFAULT_uA 20070 +#define AD9910_DAC_IOUT_MIN_uA 8640 + +/* altcurrent ABI is in mA */ +#define AD9910_NANO_MILLIAMP_PER_MICROAMP 1000000UL + +#define AD9910_REFDIV2_MIN_FREQ_HZ (25 * HZ_PER_MHZ) +#define AD9910_REFDIV2_MAX_FREQ_HZ (1900 * HZ_PER_MHZ) + +#define AD9910_WAKEUP_DELAY_us 1000 /* 1ms: datasheet Table 1 */ +#define AD9910_RESET_DELAY_us 1 /* 5 sysclk cycles: < 1us */ + +#define AD9910_SPI_DATA_IDX 1 +#define AD9910_SPI_DATA_LEN_MAX sizeof(__be64) +#define AD9910_SPI_MESSAGE_LEN_MAX (AD9910_SPI_DATA_IDX + AD9910_SPI_DATA_= LEN_MAX) +#define AD9910_SPI_READ_MSK BIT(7) +#define AD9910_SPI_ADDR_MSK GENMASK(4, 0) + +/** + * enum ad9910_channel - AD9910 channel identifiers in priority order + * + * @AD9910_CHANNEL_PHY: Physical output channel + * @AD9910_CHANNEL_PROFILE_0: Profile 0 output channel + * @AD9910_CHANNEL_PROFILE_1: Profile 1 output channel + * @AD9910_CHANNEL_PROFILE_2: Profile 2 output channel + * @AD9910_CHANNEL_PROFILE_3: Profile 3 output channel + * @AD9910_CHANNEL_PROFILE_4: Profile 4 output channel + * @AD9910_CHANNEL_PROFILE_5: Profile 5 output channel + * @AD9910_CHANNEL_PROFILE_6: Profile 6 output channel + * @AD9910_CHANNEL_PROFILE_7: Profile 7 output channel + */ +enum ad9910_channel { + AD9910_CHANNEL_PHY =3D 100, + AD9910_CHANNEL_PROFILE_0 =3D 110, + AD9910_CHANNEL_PROFILE_1 =3D 111, + AD9910_CHANNEL_PROFILE_2 =3D 112, + AD9910_CHANNEL_PROFILE_3 =3D 113, + AD9910_CHANNEL_PROFILE_4 =3D 114, + AD9910_CHANNEL_PROFILE_5 =3D 115, + AD9910_CHANNEL_PROFILE_6 =3D 116, + AD9910_CHANNEL_PROFILE_7 =3D 117, +}; + +enum { + AD9910_CHAN_IDX_PHY, + AD9910_CHAN_IDX_PROFILE_0, + AD9910_CHAN_IDX_PROFILE_1, + AD9910_CHAN_IDX_PROFILE_2, + AD9910_CHAN_IDX_PROFILE_3, + AD9910_CHAN_IDX_PROFILE_4, + AD9910_CHAN_IDX_PROFILE_5, + AD9910_CHAN_IDX_PROFILE_6, + AD9910_CHAN_IDX_PROFILE_7, +}; + +enum { + AD9910_POWERDOWN, +}; + +struct ad9910_data { + u32 sysclk_freq_hz; + u32 output_current_uA; + + u16 pll_charge_pump_current; + u8 refclk_out_drv; + bool pll_enabled; +}; + +union ad9910_reg { + u64 val64; + u32 val32; + u16 val16; +}; + +struct ad9910_state { + struct spi_device *spi; + struct clk *refclk; + + struct gpio_desc *gpio_pwdown; + struct gpio_desc *gpio_update; + struct gpio_descs *gpio_profile; + + /* cached registers */ + union ad9910_reg reg[AD9910_REG_NUM_CACHED]; + + /* Lock for accessing device registers and state variables */ + struct mutex lock; + + struct ad9910_data data; + u8 profile; + + /* + * RAM loading requires a reasonable amount of bytes, at the same time + * DMA capable SPI drivers requires the transfer buffers to live in + * their own cache lines. + */ + u8 tx_buf[AD9910_SPI_MESSAGE_LEN_MAX] __aligned(IIO_DMA_MINALIGN); +}; + +/** + * ad9910_rational_scale() - Perform scaling of input given a reference. + * @input: The input value to be scaled. + * @scale: The numerator of the scaling factor. + * @reference: The denominator of the scaling factor. + * + * Closest rounding with mul_u64_add_u64_div_u64 + * + * Return: The scaled value. + */ +static inline u64 ad9910_rational_scale(u64 input, u64 scale, u64 referenc= e) +{ + return mul_u64_add_u64_div_u64(input, scale, reference >> 1, reference); +} + +static int ad9910_io_update(struct ad9910_state *st) +{ + if (st->gpio_update) { + gpiod_set_value_cansleep(st->gpio_update, 1); + fsleep(1); + gpiod_set_value_cansleep(st->gpio_update, 0); + } + + return 0; +} + +static inline int ad9910_spi_read(struct ad9910_state *st, u8 reg, void *d= ata, + size_t len) +{ + u8 inst =3D AD9910_SPI_READ_MSK | FIELD_PREP(AD9910_SPI_ADDR_MSK, reg); + + return spi_write_then_read(st->spi, &inst, sizeof(inst), data, len); +} + +static inline int ad9910_spi_write(struct ad9910_state *st, u8 reg, size_t= len, + bool update) +{ + int ret; + + st->tx_buf[0] =3D FIELD_PREP(AD9910_SPI_ADDR_MSK, reg); + ret =3D spi_write(st->spi, st->tx_buf, AD9910_SPI_DATA_IDX + len); + if (ret) + return ret; + + if (update) + return ad9910_io_update(st); + + return 0; +} + +#define AD9910_REG_READ_FN(nb) \ +static int ad9910_reg##nb##_read(struct ad9910_state *st, u8 reg, \ + u##nb * data) \ +{ \ + __be##nb be_data; \ + int ret; \ + \ + ret =3D ad9910_spi_read(st, reg, &be_data, sizeof(be_data)); \ + if (ret) \ + return ret; \ + \ + *data =3D be##nb##_to_cpu(be_data); \ + return ret; \ +} + +AD9910_REG_READ_FN(16) +AD9910_REG_READ_FN(32) +AD9910_REG_READ_FN(64) + +#define AD9910_REG_WRITE_FN(nb) \ +static int ad9910_reg##nb##_write(struct ad9910_state *st, u8 reg, \ + u##nb data, bool update) \ +{ \ + int ret; \ + \ + put_unaligned_be##nb(data, &st->tx_buf[AD9910_SPI_DATA_IDX]); \ + ret =3D ad9910_spi_write(st, reg, sizeof(data), update); \ + if (ret) \ + return ret; \ + \ + st->reg[reg].val##nb =3D data; \ + return ret; \ +} + +AD9910_REG_WRITE_FN(16) +AD9910_REG_WRITE_FN(32) +AD9910_REG_WRITE_FN(64) + +#define AD9910_REG_UPDATE_FN(nb) \ +static int ad9910_reg##nb##_update(struct ad9910_state *st, \ + u8 reg, u##nb mask, \ + u##nb data, bool update) \ +{ \ + u##nb reg_val =3D (st->reg[reg].val##nb & ~mask) | (data & mask); \ + \ + if (reg_val =3D=3D st->reg[reg].val##nb && !update) \ + return 0; \ + \ + return ad9910_reg##nb##_write(st, reg, reg_val, update); \ +} + +AD9910_REG_UPDATE_FN(16) +AD9910_REG_UPDATE_FN(32) +AD9910_REG_UPDATE_FN(64) + +static int ad9910_set_dac_current(struct ad9910_state *st, u32 val_uA, + bool update) +{ + u32 code; + + /* FSC =3D (86.4 / Rset) * (1 + CODE/96) where Rset =3D 10k ohms */ + val_uA =3D clamp(val_uA, AD9910_DAC_IOUT_MIN_uA, AD9910_DAC_IOUT_MAX_uA); + code =3D DIV_ROUND_CLOSEST(val_uA - AD9910_DAC_IOUT_MIN_uA, 90); + st->data.output_current_uA =3D AD9910_DAC_IOUT_MIN_uA + code * 90; + + return ad9910_reg32_write(st, AD9910_REG_AUX_DAC, code, update); +} + +static int ad9910_set_sysclk_freq(struct ad9910_state *st, u32 freq_hz, + bool update) +{ + struct device *dev =3D &st->spi->dev; + unsigned long refclk_freq_hz; + u32 sysclk_freq_hz; + u32 tmp32, vco_sel; + int ret; + + if (!freq_hz || freq_hz > AD9910_MAX_SYSCLK_HZ) + return -EINVAL; + + refclk_freq_hz =3D clk_get_rate(st->refclk); + if (st->data.pll_enabled) { + if (refclk_freq_hz < AD9910_PLL_IN_MIN_FREQ_HZ || + refclk_freq_hz > AD9910_PLL_IN_MAX_FREQ_HZ) { + dev_err(dev, + "REF_CLK freq %lu Hz is out of PLL input range\n", + refclk_freq_hz); + return -ERANGE; + } + + tmp32 =3D DIV_ROUND_CLOSEST(freq_hz, refclk_freq_hz); + tmp32 =3D clamp(tmp32, DIV_ROUND_UP(AD9910_PLL_OUT_MIN_FREQ_HZ, refclk_f= req_hz), + AD9910_PLL_OUT_MAX_FREQ_HZ / refclk_freq_hz); + tmp32 =3D clamp(tmp32, AD9910_PLL_MIN_N, AD9910_PLL_MAX_N); + sysclk_freq_hz =3D refclk_freq_hz * tmp32; + + if (sysclk_freq_hz <=3D AD9910_VCO0_RANGE_AUTO_MAX_HZ) + vco_sel =3D 0; + else if (sysclk_freq_hz <=3D AD9910_VCO1_RANGE_AUTO_MAX_HZ) + vco_sel =3D 1; + else if (sysclk_freq_hz <=3D AD9910_VCO2_RANGE_AUTO_MAX_HZ) + vco_sel =3D 2; + else if (sysclk_freq_hz <=3D AD9910_VCO3_RANGE_AUTO_MAX_HZ) + vco_sel =3D 3; + else if (sysclk_freq_hz <=3D AD9910_VCO4_RANGE_AUTO_MAX_HZ) + vco_sel =3D 4; + else + vco_sel =3D 5; + + ret =3D ad9910_reg32_update(st, AD9910_REG_CFR3, + AD9910_CFR3_N_MSK | AD9910_CFR3_VCO_SEL_MSK, + FIELD_PREP(AD9910_CFR3_N_MSK, tmp32) | + FIELD_PREP(AD9910_CFR3_VCO_SEL_MSK, vco_sel), + update); + if (ret) + return ret; + } else { + if (refclk_freq_hz < AD9910_REFDIV2_MIN_FREQ_HZ || + refclk_freq_hz > AD9910_REFDIV2_MAX_FREQ_HZ) { + dev_err(dev, + "REF_CLK freq %lu Hz is out of divider range\n", + refclk_freq_hz); + return -ERANGE; + } + + tmp32 =3D DIV_ROUND_CLOSEST(refclk_freq_hz, freq_hz); + tmp32 =3D clamp(tmp32, 1U, 2U); + sysclk_freq_hz =3D refclk_freq_hz / tmp32; + tmp32 =3D AD9910_CFR3_VCO_SEL_MSK | + FIELD_PREP(AD9910_CFR3_REFCLK_DIV_BYPASS_MSK, tmp32 % 2); + ret =3D ad9910_reg32_update(st, AD9910_REG_CFR3, + AD9910_CFR3_VCO_SEL_MSK | + AD9910_CFR3_REFCLK_DIV_BYPASS_MSK, + tmp32, update); + if (ret) + return ret; + } + + st->data.sysclk_freq_hz =3D sysclk_freq_hz; + + return 0; +} + +static int ad9910_profile_set(struct ad9910_state *st, u8 profile) +{ + DECLARE_BITMAP(values, BITS_PER_TYPE(profile)); + + st->profile =3D profile; + values[0] =3D profile; + gpiod_multi_set_value_cansleep(st->gpio_profile, values); + + return 0; +} + +static inline bool ad9910_sw_powerdown_get(struct ad9910_state *st) +{ + return FIELD_GET(AD9910_CFR1_SW_POWER_DOWN_MSK, + st->reg[AD9910_REG_CFR1].val32) ? true : false; +} + +static int ad9910_sw_powerdown_set(struct ad9910_state *st, bool enable) +{ + int ret; + + if (ad9910_sw_powerdown_get(st) =3D=3D enable) + return 0; + + /* + * When powering down, the DAC and AUX_DAC (SW1) must be powered down + * first, as they require an I/O update to take effect. The opposite is + * true when powering up, the DAC and AUX_DAC must be powered up last, + * i.e., after the DIGITAL and REFCLK_INPUT (SW0) power up. + */ + if (enable) { + ret =3D ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_SW1_POWER_DOWN_MSK, + AD9910_CFR1_SW1_POWER_DOWN_MSK, + true); + if (ret) + return ret; + + return ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_SW0_POWER_DOWN_MSK, + AD9910_CFR1_SW0_POWER_DOWN_MSK, + false); + } + + ret =3D ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_SW0_POWER_DOWN_MSK, 0, false); + if (ret) + return ret; + + return ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_SW1_POWER_DOWN_MSK, 0, true); +} + +static ssize_t ad9910_ext_info_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + int val; + + guard(mutex)(&st->lock); + + switch (private) { + case AD9910_POWERDOWN: + val =3D ad9910_sw_powerdown_get(st); + break; + default: + return -EINVAL; + } + + return iio_format_value(buf, IIO_VAL_INT, 1, &val); +} + +static ssize_t ad9910_ext_info_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + u32 val32; + int ret; + + ret =3D kstrtou32(buf, 10, &val32); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + switch (private) { + case AD9910_POWERDOWN: + ret =3D ad9910_sw_powerdown_set(st, val32 ? true : false); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + + return len; +} + +static const struct iio_chan_spec_ext_info ad9910_phy_ext_info[] =3D { + { + .name =3D "powerdown", + .read =3D ad9910_ext_info_read, + .write =3D ad9910_ext_info_write, + .private =3D AD9910_POWERDOWN, + .shared =3D IIO_SEPARATE, + }, + { } +}; + +#define AD9910_PROFILE_CHAN(idx) { \ + .type =3D IIO_ALTCURRENT, \ + .indexed =3D 1, \ + .output =3D 1, \ + .channel =3D AD9910_CHANNEL_PROFILE_ ## idx, \ + .address =3D AD9910_CHAN_IDX_PROFILE_ ## idx, \ + .info_mask_separate =3D BIT(IIO_CHAN_INFO_ENABLE) | \ + BIT(IIO_CHAN_INFO_FREQUENCY) | \ + BIT(IIO_CHAN_INFO_PHASE) | \ + BIT(IIO_CHAN_INFO_RAW), \ + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], \ +} + +static const struct iio_chan_spec ad9910_channels[] =3D { + [AD9910_CHAN_IDX_PHY] =3D { + .type =3D IIO_ALTCURRENT, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_PHY, + .address =3D AD9910_CHAN_IDX_PHY, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SCALE), + .ext_info =3D ad9910_phy_ext_info, + }, + [AD9910_CHAN_IDX_PROFILE_0] =3D AD9910_PROFILE_CHAN(0), + [AD9910_CHAN_IDX_PROFILE_1] =3D AD9910_PROFILE_CHAN(1), + [AD9910_CHAN_IDX_PROFILE_2] =3D AD9910_PROFILE_CHAN(2), + [AD9910_CHAN_IDX_PROFILE_3] =3D AD9910_PROFILE_CHAN(3), + [AD9910_CHAN_IDX_PROFILE_4] =3D AD9910_PROFILE_CHAN(4), + [AD9910_CHAN_IDX_PROFILE_5] =3D AD9910_PROFILE_CHAN(5), + [AD9910_CHAN_IDX_PROFILE_6] =3D AD9910_PROFILE_CHAN(6), + [AD9910_CHAN_IDX_PROFILE_7] =3D AD9910_PROFILE_CHAN(7), +}; + +static int ad9910_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + u64 tmp64; + u32 tmp32; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_ENABLE: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + if (ad9910_sw_powerdown_get(st)) { + *val =3D 0; + } else { + tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; + *val =3D (tmp32 =3D=3D st->profile); + } + break; + default: + return -EINVAL; + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_FREQUENCY: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; + tmp64 =3D FIELD_GET(AD9910_PROFILE_ST_FTW_MSK, + st->reg[AD9910_REG_PROFILE(tmp32)].val64); + break; + default: + return -EINVAL; + } + tmp64 *=3D st->data.sysclk_freq_hz; + *val =3D tmp64 >> 32; + *val2 =3D ((tmp64 & GENMASK_ULL(31, 0)) * MICRO) >> 32; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_PHASE: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; + tmp64 =3D FIELD_GET(AD9910_PROFILE_ST_POW_MSK, + st->reg[AD9910_REG_PROFILE(tmp32)].val64); + break; + default: + return -EINVAL; + } + tmp32 =3D (tmp64 * AD9910_MAX_PHASE_MICRORAD) >> 16; + *val =3D tmp32 / MICRO; + *val2 =3D tmp32 % MICRO; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_RAW: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; + *val =3D FIELD_GET(AD9910_PROFILE_ST_ASF_MSK, + st->reg[AD9910_REG_PROFILE(tmp32)].val64); + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + switch (chan->channel) { + case AD9910_CHANNEL_PHY: + *val =3D st->data.sysclk_freq_hz; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->address) { + case AD9910_CHAN_IDX_PHY: + tmp64 =3D (u64)st->data.output_current_uA * + AD9910_NANO_MILLIAMP_PER_MICROAMP; + *val =3D 0; + *val2 =3D tmp64 >> 14; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad9910_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + u64 tmp64; + u32 tmp32; + int ret; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_ENABLE: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; + if (!val) { + if (tmp32 !=3D st->profile) + return 0; /* nothing to do */ + + return ad9910_sw_powerdown_set(st, true); + } + + ret =3D ad9910_sw_powerdown_set(st, false); + if (ret) + return ret; + + return ad9910_profile_set(st, tmp32); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_FREQUENCY: + if (val < 0 || val2 < 0 || val >=3D st->data.sysclk_freq_hz / 2) + return -EINVAL; + + tmp64 =3D ad9910_rational_scale((u64)val * MICRO + val2, BIT_ULL(32), + (u64)MICRO * st->data.sysclk_freq_hz); + tmp64 =3D min_t(u64, tmp64, U32_MAX); + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; + tmp64 =3D FIELD_PREP(AD9910_PROFILE_ST_FTW_MSK, tmp64); + return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), + AD9910_PROFILE_ST_FTW_MSK, + tmp64, true); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_PHASE: + if (val < 0 || val2 < 0) + return -EINVAL; + + tmp64 =3D (u64)val * MICRO + val2; + if (tmp64 >=3D AD9910_MAX_PHASE_MICRORAD) + return -EINVAL; + + tmp64 =3D DIV_U64_ROUND_CLOSEST(tmp64 << 16, AD9910_MAX_PHASE_MICRORAD); + tmp64 =3D min(tmp64, AD9910_POW_MAX); + + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; + tmp64 =3D FIELD_PREP(AD9910_PROFILE_ST_POW_MSK, tmp64); + return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), + AD9910_PROFILE_ST_POW_MSK, + tmp64, true); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + if (val < 0) + return -EINVAL; + + tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; + tmp64 =3D FIELD_PREP(AD9910_PROFILE_ST_ASF_MSK, + min_t(u64, val, AD9910_ASF_MAX)); + return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), + AD9910_PROFILE_ST_ASF_MSK, + tmp64, true); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + return ad9910_set_sysclk_freq(st, val, true); + case IIO_CHAN_INFO_SCALE: + switch (chan->address) { + case AD9910_CHAN_IDX_PHY: + if (val !=3D 0 || val2 < 0) + return -EINVAL; + + tmp32 =3D DIV_U64_ROUND_CLOSEST((u64)val2 << 14, + AD9910_NANO_MILLIAMP_PER_MICROAMP); + return ad9910_set_dac_current(st, tmp32, true); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad9910_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_ENABLE: + return IIO_VAL_INT; + case IIO_CHAN_INFO_FREQUENCY: + case IIO_CHAN_INFO_PHASE: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_RAW: + switch (chan->channel) { + case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +} + +static int ad9910_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, u64 writeval, + u64 *readval) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + union ad9910_reg tmp; + int ret; + + if (reg >=3D AD9910_REG_RAM) + return -EINVAL; + + guard(mutex)(&st->lock); + + switch (reg) { + case AD9910_REG_DRG_LIMIT: + case AD9910_REG_DRG_STEP: + case AD9910_REG_PROFILE0 ... AD9910_REG_PROFILE7: + if (!readval) + return ad9910_reg64_write(st, reg, writeval, true); + + ret =3D ad9910_reg64_read(st, reg, &tmp.val64); + if (ret) + return ret; + *readval =3D tmp.val64; + return 0; + case AD9910_REG_POW: + if (!readval) + return ad9910_reg16_write(st, reg, writeval, true); + + ret =3D ad9910_reg16_read(st, reg, &tmp.val16); + if (ret) + return ret; + *readval =3D tmp.val16; + return 0; + default: + if (!readval) + return ad9910_reg32_write(st, reg, writeval, true); + + ret =3D ad9910_reg32_read(st, reg, &tmp.val32); + if (ret) + return ret; + *readval =3D tmp.val32; + return 0; + } +} + +static const char * const ad9910_channel_str[] =3D { + [AD9910_CHAN_IDX_PHY] =3D "phy", + [AD9910_CHAN_IDX_PROFILE_0] =3D "profile0", + [AD9910_CHAN_IDX_PROFILE_1] =3D "profile1", + [AD9910_CHAN_IDX_PROFILE_2] =3D "profile2", + [AD9910_CHAN_IDX_PROFILE_3] =3D "profile3", + [AD9910_CHAN_IDX_PROFILE_4] =3D "profile4", + [AD9910_CHAN_IDX_PROFILE_5] =3D "profile5", + [AD9910_CHAN_IDX_PROFILE_6] =3D "profile6", + [AD9910_CHAN_IDX_PROFILE_7] =3D "profile7", +}; + +static int ad9910_read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + return sysfs_emit(label, "%s\n", ad9910_channel_str[chan->address]); +} + +static const struct iio_info ad9910_info =3D { + .read_raw =3D ad9910_read_raw, + .write_raw =3D ad9910_write_raw, + .write_raw_get_fmt =3D ad9910_write_raw_get_fmt, + .read_label =3D ad9910_read_label, + .debugfs_reg64_access =3D &ad9910_debugfs_reg_access, +}; + +static int ad9910_cfg_sysclk(struct ad9910_state *st, bool update) +{ + u32 cfr3 =3D AD9910_CFR3_OPEN_MSK; + u32 tmp32; + + cfr3 |=3D FIELD_PREP(AD9910_CFR3_DRV0_MSK, st->data.refclk_out_drv); + + if (st->data.pll_enabled) { + tmp32 =3D st->data.pll_charge_pump_current - AD9910_ICP_MIN_uA; + tmp32 =3D DIV_ROUND_CLOSEST(tmp32, AD9910_ICP_STEP_uA); + cfr3 |=3D FIELD_PREP(AD9910_CFR3_ICP_MSK, tmp32) | + AD9910_CFR3_PLL_EN_MSK; + } else { + cfr3 |=3D AD9910_CFR3_ICP_MSK | + AD9910_CFR3_REFCLK_DIV_RESETB_MSK | + AD9910_CFR3_PFD_RESET_MSK; + } + st->reg[AD9910_REG_CFR3].val32 =3D cfr3; + + return ad9910_set_sysclk_freq(st, AD9910_MAX_SYSCLK_HZ, update); +} + +static int ad9910_parse_fw(struct ad9910_state *st) +{ + static const char * const refclk_out_drv0[] =3D { + "disabled", "low", "medium", "high", + }; + struct device *dev =3D &st->spi->dev; + const char *prop; + u32 tmp; + int ret; + + st->data.pll_enabled =3D device_property_read_bool(dev, "adi,pll-enable"); + if (st->data.pll_enabled) { + prop =3D "adi,charge-pump-current-microamp"; + if (device_property_present(dev, prop)) { + ret =3D device_property_read_u32(dev, prop, &tmp); + if (ret) + return dev_err_probe(dev, ret, "property read: %s\n", prop); + + if (tmp < AD9910_ICP_MIN_uA || tmp > AD9910_ICP_MAX_uA) + return dev_err_probe(dev, -ERANGE, + "invalid charge pump current %u\n", tmp); + } else { + tmp =3D AD9910_ICP_MIN_uA; + } + st->data.pll_charge_pump_current =3D tmp; + + prop =3D "adi,refclk-out-drive-strength"; + if (device_property_present(dev, prop)) { + ret =3D device_property_match_property_string(dev, prop, + refclk_out_drv0, + ARRAY_SIZE(refclk_out_drv0)); + if (ret < 0) + return dev_err_probe(dev, ret, "property read: %s\n", prop); + + st->data.refclk_out_drv =3D ret; + } + } + + return 0; +} + +static void ad9910_sw_powerdown_action(void *data) +{ + ad9910_sw_powerdown_set(data, true); +} + +static void ad9910_hw_powerdown_action(void *data) +{ + struct ad9910_state *st =3D data; + + gpiod_set_value_cansleep(st->gpio_pwdown, 1); +} + +static int ad9910_setup(struct device *dev, struct ad9910_state *st, + struct reset_control *dev_rst) +{ + int ret; + + ret =3D reset_control_assert(dev_rst); + if (ret) + return ret; + + fsleep(AD9910_RESET_DELAY_us); + + ret =3D reset_control_deassert(dev_rst); + if (ret) + return ret; + + ret =3D ad9910_reg32_write(st, AD9910_REG_CFR1, + (st->spi->mode & SPI_3WIRE ? 0 : + AD9910_CFR1_SDIO_INPUT_ONLY_MSK), false); + if (ret) + return ret; + + ret =3D devm_add_action_or_reset(dev, ad9910_sw_powerdown_action, st); + if (ret) + return ret; + + ret =3D ad9910_reg32_write(st, AD9910_REG_CFR2, + AD9910_CFR2_AMP_SCALE_SINGLE_TONE_MSK | + AD9910_CFR2_SYNC_TIMING_VAL_DISABLE_MSK | + AD9910_CFR2_DRG_NO_DWELL_MSK | + AD9910_CFR2_DATA_ASM_HOLD_LAST_MSK | + AD9910_CFR2_SYNC_CLK_EN_MSK | + AD9910_CFR2_PDCLK_ENABLE_MSK, false); + if (ret) + return ret; + + ret =3D ad9910_cfg_sysclk(st, false); + if (ret) + return ret; + + ret =3D ad9910_set_dac_current(st, AD9910_DAC_IOUT_DEFAULT_uA, false); + if (ret) + return ret; + + return ad9910_io_update(st); +} + +static int ad9910_probe(struct spi_device *spi) +{ + static const char * const supplies[] =3D { + "dvdd-io33", "avdd33", "dvdd18", "avdd18", + }; + struct device *dev =3D &spi->dev; + struct reset_control *dev_rst; + struct gpio_desc *io_rst_gpio; + struct iio_dev *indio_dev; + struct ad9910_state *st; + int ret; + + indio_dev =3D devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st =3D iio_priv(indio_dev); + st->spi =3D spi; + + indio_dev->name =3D "ad9910"; + indio_dev->info =3D &ad9910_info; + indio_dev->modes =3D INDIO_DIRECT_MODE; + indio_dev->channels =3D ad9910_channels; + indio_dev->num_channels =3D ARRAY_SIZE(ad9910_channels); + + ret =3D devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + ret =3D devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supplies), supplie= s); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + st->refclk =3D devm_clk_get_enabled(dev, "ref_clk"); + if (IS_ERR(st->refclk)) + return dev_err_probe(dev, PTR_ERR(st->refclk), + "Failed to get reference clock\n"); + + dev_rst =3D devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(dev_rst)) + return dev_err_probe(dev, PTR_ERR(dev_rst), + "failed to get device reset control\n"); + + /* + * The IO RESET pin is not used in this driver, as we assume that all + * SPI transfers are complete, but if it is wired up, we need to make + * sure it is not floating. We can use either a reset controller or a + * GPIO for this. + */ + io_rst_gpio =3D devm_gpiod_get_optional(dev, "io-reset", GPIOD_OUT_LOW); + if (IS_ERR(io_rst_gpio)) + return dev_err_probe(dev, PTR_ERR(io_rst_gpio), + "failed to get io reset gpio\n"); + + st->gpio_update =3D devm_gpiod_get_optional(dev, "update", GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_update)) + return dev_err_probe(dev, PTR_ERR(st->gpio_update), + "failed to get update gpio\n"); + + st->gpio_profile =3D devm_gpiod_get_array_optional(dev, "profile", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_profile)) + return dev_err_probe(dev, PTR_ERR(st->gpio_profile), + "failed to get profile gpios\n"); + + if (st->gpio_profile && st->gpio_profile->ndescs !=3D 3) + return dev_err_probe(dev, -EINVAL, + "invalid number of profile gpios\n"); + + st->gpio_pwdown =3D devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_LOW); + if (IS_ERR(st->gpio_pwdown)) + return dev_err_probe(dev, PTR_ERR(st->gpio_pwdown), + "failed to get powerdown gpio\n"); + + ret =3D devm_add_action_or_reset(dev, ad9910_hw_powerdown_action, st); + if (ret) + return dev_err_probe(dev, ret, + "failed to add hw powerdown action\n"); + + fsleep(AD9910_WAKEUP_DELAY_us); + + ret =3D ad9910_parse_fw(st); + if (ret) + return ret; + + ret =3D ad9910_setup(dev, st, dev_rst); + if (ret) + return dev_err_probe(dev, ret, "device setup failed\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id ad9910_id[] =3D { + { .name =3D "ad9910" }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad9910_id); + +static const struct of_device_id ad9910_of_match[] =3D { + { .compatible =3D "adi,ad9910" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad9910_of_match); + +static struct spi_driver ad9910_driver =3D { + .driver =3D { + .name =3D "ad9910", + .of_match_table =3D ad9910_of_match, + }, + .probe =3D ad9910_probe, + .id_table =3D ad9910_id, +}; +module_spi_driver(ad9910_driver); + +MODULE_AUTHOR("Rodrigo Alencar "); +MODULE_DESCRIPTION("Analog Devices AD9910 DDS driver"); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 90DDA4DA559; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=F4RrJJxr7H8YycEPPSwlmgS4HBh18iH7GAp7RAm+ex3QI8Y3Kv7X86ANG2ADYvvxvnBWMkDTP3CKdeOxutvMS0L9ssTBCyPi5bmw29hbRcsNKAI3qgnBcb3bQpe2QZETBboZ/MQy4rw1W4Iv5tKkm3x8xxOFT4j+feCz5rVh+Kw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=wjGU47dMiUmh3ewETIM8jiZt5gDJ6tkeOfiYVNEuSWQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=X+0VYMPrFGiHEm/yEmg/+PI53JN08uiRw8P6EElCUrq5LdTG1nI77FbEsiG5pxpYsTPMPUz6gZhFC5dTrEYWsPUEP8ADkuMz3VDrvJcIILQOwErOKRqAlI+sZLzXUnsmKjm8Q3UMLoRUEKb4CAqUD3oYefKG6Lq87VgGTlbgj+4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Hvuo/kYv; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Hvuo/kYv" Received: by smtp.kernel.org (Postfix) with ESMTPS id 7187BC2BCB3; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=wjGU47dMiUmh3ewETIM8jiZt5gDJ6tkeOfiYVNEuSWQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=Hvuo/kYvmcopdRrqal22J9Fag8cHSWzTpNhaujdIo0//h49RkQSZI6FDDVoiMxQQ/ mN1ZWdCrVJAPP8NuAnmh7nKOQTfrKMWrwiOdRQhhavFdBplBtsn669xO5azeUNtyx3 /zY4dkq1rodlQMyj87rUgunQZg8WkOBBSidzyFaPZJ1MEuc1YgwrPKM3MQzKZpyFCp D98eyPOtp3eoXKrJEoO35b9NIuzhcrUdL5GuaHxZYiaDqMsuRqU9HqrAV/b8cB/Onp ytBuoJ+U85jpQtUmBH3JBaJ0sSZW4MGqfZvJORv6fIWTzSmA7T7lvnyfBzFW098AxE N4vAolE5to+nA== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5F429C4453C; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:20 +0100 Subject: [PATCH v9 11/17] iio: frequency: ad9910: add basic parallel port support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-11-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=9246; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=fxc3LNHFMCvmFNXVI1Q9NxrOcuqJsc2ClYxY6gEQXnU=; b=w3fwgXreW6+vN1EFNd8eQimdKcudPB7q8sluLshz1jTJ9VVZMkem9ak9GcugyC5zbDhoHRyZQ va4c7OAC/91DdnqVXgfw+LglkDj2tTwjZk5U9wulB+Vwc0sWinMUIXd X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add parallel port support with amplitude, phase and frequency channels. Those will be buffered capable channels, but only basic control of offset and scale are implemented at this point. There are separate amplitude and phase control for polar destination, which will provide different scan types. Enabling and disabling of parallel mode will be implemented with buffer setup ops or with update_scan_mode() once IIO backend integration is in place. Signed-off-by: Rodrigo Alencar --- drivers/iio/frequency/ad9910.c | 154 +++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 154 insertions(+) diff --git a/drivers/iio/frequency/ad9910.c b/drivers/iio/frequency/ad9910.c index b41b011af281..aaeff2e7640e 100644 --- a/drivers/iio/frequency/ad9910.c +++ b/drivers/iio/frequency/ad9910.c @@ -122,9 +122,13 @@ /* Auxiliary DAC Control Register Bits */ #define AD9910_AUX_DAC_FSC_MSK GENMASK(7, 0) =20 +/* POW Register Bits */ +#define AD9910_POW_PP_LSB_MSK GENMASK(7, 0) + /* ASF Register Bits */ #define AD9910_ASF_RAMP_RATE_MSK GENMASK(31, 16) #define AD9910_ASF_SCALE_FACTOR_MSK GENMASK(15, 2) +#define AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK GENMASK(7, 2) #define AD9910_ASF_STEP_SIZE_MSK GENMASK(1, 0) =20 /* Multichip Sync Register Bits */ @@ -148,7 +152,9 @@ #define AD9910_MAX_PHASE_MICRORAD (AD9910_PI_NANORAD / 500) =20 #define AD9910_ASF_MAX FIELD_MAX(AD9910_PROFILE_ST_ASF_MSK) +#define AD9910_ASF_PP_LSB_MAX FIELD_MAX(AD9910_ASF_SCALE_FACTOR_PP_LSB_MS= K) #define AD9910_POW_MAX FIELD_MAX(AD9910_PROFILE_ST_POW_MSK) +#define AD9910_POW_PP_LSB_MAX FIELD_MAX(AD9910_POW_PP_LSB_MSK) #define AD9910_NUM_PROFILES 8 =20 /* PLL constants */ @@ -203,6 +209,8 @@ * @AD9910_CHANNEL_PROFILE_5: Profile 5 output channel * @AD9910_CHANNEL_PROFILE_6: Profile 6 output channel * @AD9910_CHANNEL_PROFILE_7: Profile 7 output channel + * @AD9910_CHANNEL_PARALLEL: Parallel Data output channel + * @AD9910_CHANNEL_PARALLEL_POLAR: Parallel Polar Data output channel */ enum ad9910_channel { AD9910_CHANNEL_PHY =3D 100, @@ -214,6 +222,8 @@ enum ad9910_channel { AD9910_CHANNEL_PROFILE_5 =3D 115, AD9910_CHANNEL_PROFILE_6 =3D 116, AD9910_CHANNEL_PROFILE_7 =3D 117, + AD9910_CHANNEL_PARALLEL =3D 120, + AD9910_CHANNEL_PARALLEL_POLAR =3D 121, }; =20 enum { @@ -226,6 +236,11 @@ enum { AD9910_CHAN_IDX_PROFILE_5, AD9910_CHAN_IDX_PROFILE_6, AD9910_CHAN_IDX_PROFILE_7, + AD9910_CHAN_IDX_PARALLEL_AMP, + AD9910_CHAN_IDX_PARALLEL_PHASE, + AD9910_CHAN_IDX_PARALLEL_FREQ, + AD9910_CHAN_IDX_PARALLEL_POLAR_AMP, + AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE, }; =20 enum { @@ -610,6 +625,53 @@ static const struct iio_chan_spec ad9910_channels[] = =3D { [AD9910_CHAN_IDX_PROFILE_5] =3D AD9910_PROFILE_CHAN(5), [AD9910_CHAN_IDX_PROFILE_6] =3D AD9910_PROFILE_CHAN(6), [AD9910_CHAN_IDX_PROFILE_7] =3D AD9910_PROFILE_CHAN(7), + [AD9910_CHAN_IDX_PARALLEL_AMP] =3D { + .type =3D IIO_ALTCURRENT, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_PARALLEL, + .address =3D AD9910_CHAN_IDX_PARALLEL_AMP, + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_PARALLEL_PHASE] =3D { + .type =3D IIO_PHASE, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_PARALLEL, + .address =3D AD9910_CHAN_IDX_PARALLEL_PHASE, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_SCALE), + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_PARALLEL_FREQ] =3D { + .type =3D IIO_FREQUENCY, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_PARALLEL, + .address =3D AD9910_CHAN_IDX_PARALLEL_FREQ, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_PARALLEL_POLAR_AMP] =3D { + .type =3D IIO_ALTCURRENT, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_PARALLEL_POLAR, + .address =3D AD9910_CHAN_IDX_PARALLEL_POLAR_AMP, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE] =3D { + .type =3D IIO_PHASE, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_PARALLEL_POLAR, + .address =3D AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, }; =20 static int ad9910_read_raw(struct iio_dev *indio_dev, @@ -691,6 +753,48 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, *val =3D 0; *val2 =3D tmp64 >> 14; return IIO_VAL_INT_PLUS_NANO; + case AD9910_CHAN_IDX_PARALLEL_PHASE: + *val =3D 0; + *val2 =3D AD9910_PI_NANORAD >> 15; + return IIO_VAL_INT_PLUS_NANO; + case AD9910_CHAN_IDX_PARALLEL_FREQ: + tmp32 =3D FIELD_GET(AD9910_CFR2_FM_GAIN_MSK, + st->reg[AD9910_REG_CFR2].val32); + tmp64 =3D (u64)st->data.sysclk_freq_hz << tmp32; + tmp64 =3D ad9910_rational_scale(tmp64, NANO, BIT_ULL(32)); + *val =3D div_s64_rem(tmp64, NANO, val2); + return IIO_VAL_INT_PLUS_NANO; + case AD9910_CHAN_IDX_PARALLEL_POLAR_AMP: + tmp64 =3D (u64)st->data.output_current_uA * + AD9910_NANO_MILLIAMP_PER_MICROAMP; + *val =3D 0; + *val2 =3D tmp64 >> 8; + return IIO_VAL_INT_PLUS_NANO; + case AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE: + *val =3D 0; + *val2 =3D AD9910_PI_NANORAD >> 7; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->address) { + case AD9910_CHAN_IDX_PARALLEL_FREQ: + tmp64 =3D (u64)st->reg[AD9910_REG_FTW].val32 * MICRO; + tmp64 >>=3D FIELD_GET(AD9910_CFR2_FM_GAIN_MSK, + st->reg[AD9910_REG_CFR2].val32); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_MICRO; + case AD9910_CHAN_IDX_PARALLEL_POLAR_AMP: + tmp32 =3D FIELD_GET(AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK, + st->reg[AD9910_REG_ASF].val32); + iio_val_s64_decompose(MICRO * tmp32 >> 6, val, val2); + return IIO_VAL_DECIMAL64_MICRO; + case AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE: + tmp32 =3D FIELD_GET(AD9910_POW_PP_LSB_MSK, + st->reg[AD9910_REG_POW].val16); + iio_val_s64_decompose(MICRO * tmp32 >> 8, val, val2); + return IIO_VAL_DECIMAL64_MICRO; default: return -EINVAL; } @@ -794,9 +898,52 @@ static int ad9910_write_raw(struct iio_dev *indio_dev, tmp32 =3D DIV_U64_ROUND_CLOSEST((u64)val2 << 14, AD9910_NANO_MILLIAMP_PER_MICROAMP); return ad9910_set_dac_current(st, tmp32, true); + case AD9910_CHAN_IDX_PARALLEL_FREQ: + if (val < 0 || val2 < 0) + return -EINVAL; + + tmp64 =3D ad9910_rational_scale((u64)val * NANO + val2, BIT_ULL(32), + (u64)st->data.sysclk_freq_hz * NANO); + tmp64 =3D roundup_pow_of_two(clamp(tmp64, 1ULL, BIT_ULL(15))); + tmp32 =3D FIELD_PREP(AD9910_CFR2_FM_GAIN_MSK, ilog2(tmp64)); + return ad9910_reg32_update(st, AD9910_REG_CFR2, + AD9910_CFR2_FM_GAIN_MSK, + tmp32, true); default: return -EINVAL; } + case IIO_CHAN_INFO_OFFSET: { + s64 val64 =3D iio_val_s64_compose(val, val2); + + if (val64 < 0) + return -EINVAL; + + tmp64 =3D val64; + switch (chan->address) { + case AD9910_CHAN_IDX_PARALLEL_FREQ: + tmp32 =3D BIT(FIELD_GET(AD9910_CFR2_FM_GAIN_MSK, + st->reg[AD9910_REG_CFR2].val32)); + tmp64 =3D ad9910_rational_scale(tmp64, tmp32, MICRO); + tmp64 =3D min_t(u64, tmp64, U32_MAX); + return ad9910_reg32_write(st, AD9910_REG_FTW, tmp64, true); + case AD9910_CHAN_IDX_PARALLEL_POLAR_AMP: + tmp64 =3D ad9910_rational_scale(tmp64, BIT(6), MICRO); + tmp64 =3D min_t(u64, tmp64, AD9910_ASF_PP_LSB_MAX); + tmp32 =3D FIELD_PREP(AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK, tmp64); + return ad9910_reg32_update(st, AD9910_REG_ASF, + AD9910_ASF_SCALE_FACTOR_PP_LSB_MSK, + tmp32, true); + case AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE: + tmp64 =3D ad9910_rational_scale(tmp64, BIT(8), MICRO); + tmp64 =3D min_t(u64, tmp64, AD9910_POW_PP_LSB_MAX); + tmp32 =3D FIELD_PREP(AD9910_POW_PP_LSB_MSK, tmp64); + return ad9910_reg16_update(st, AD9910_REG_POW, + AD9910_POW_PP_LSB_MSK, + tmp32, true); + default: + return -EINVAL; + } + } default: return -EINVAL; } @@ -823,6 +970,8 @@ static int ad9910_write_raw_get_fmt(struct iio_dev *ind= io_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + return IIO_VAL_DECIMAL64_MICRO; default: return -EINVAL; } @@ -884,6 +1033,11 @@ static const char * const ad9910_channel_str[] =3D { [AD9910_CHAN_IDX_PROFILE_5] =3D "profile5", [AD9910_CHAN_IDX_PROFILE_6] =3D "profile6", [AD9910_CHAN_IDX_PROFILE_7] =3D "profile7", + [AD9910_CHAN_IDX_PARALLEL_AMP] =3D "parallel_amplitude", + [AD9910_CHAN_IDX_PARALLEL_PHASE] =3D "parallel_phase", + [AD9910_CHAN_IDX_PARALLEL_FREQ] =3D "parallel_frequency", + [AD9910_CHAN_IDX_PARALLEL_POLAR_AMP] =3D "parallel_polar_amplitude", + [AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE] =3D "parallel_polar_phase", }; =20 static int ad9910_read_label(struct iio_dev *indio_dev, --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 AD70C4E3782; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=fKkrJj1dUUTSEh6rNyaVtogCs1XM/pGCDHQkQAXr27YuTTUwxkHHDcUrhyMKFwzi6w1xb/zxxfm1acxvvwHq3RYvE0okziubgkBv22GeULWrDIvV4c9src5Gfy17drFfdXfm55UNWLYQwvvaAuxUJNxwc15mLSqeDHXz4+vAtOk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=EBQdqatAeYwThD1Kg75N1fl4xdSgZxFed5ax1mVnjSI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=JnQiC7w2PZ4KL9zO58ZCiP72bIkztqqHIXFhBroui3coe/VOX/oOjR9/KxjAsy5W3UKGfy5ZUIOrprJsp2eyU2KOmpGPjCCu1yNakADLUz/ZS/3rImTOBI3Grw7bW10Hu2KFxMlHEImqNCCLjb4i/3yDXoUgXwBL0+g3RnZHWbI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=N+9/J3CP; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="N+9/J3CP" Received: by smtp.kernel.org (Postfix) with ESMTPS id 7F600C2BCFD; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=EBQdqatAeYwThD1Kg75N1fl4xdSgZxFed5ax1mVnjSI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=N+9/J3CP5ZOJc3eJThaEXfzhHTc9gkAYRLf1gqblpfh5Bm9Yh/5IWhD/Nc5Zp3cMX wzQrDapw84RRi02VE27kdiFx9Jpq2KRejSmuE0hlNExHhH7UL2Q1dG2JH9BGDWaHp6 W07SsnwFdBiBpLdjHnLIyePweYjh3EeLAMjuD6FZG9U9yjDdy7lvEgRALCcsGagOFn 2Z91JREB6t7LDCqPOCN1OGVdpJWHWwpFtXa4nxR/WXroriWYU1/xQgRaW1J6vwo4z0 11Mw7vQjt1NK7TOFqeCzDC+sd/a+naG4UUmPcjsG/ummJTvkQZpU5bV8KX5/b/M6qq obp1/2CKK6L/A== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6E557C44536; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:21 +0100 Subject: [PATCH v9 12/17] iio: frequency: ad9910: add digital ramp generator support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-12-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=18823; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=0DkNlFjtphp2n9PU/luo6bJ9oh2P2mCD1ZGZxRc7MoI=; b=j/XSXxFrficSZLki/XzpuzzzPh3F73IJ9OCI8EJ41aVekGwQIgu9izyX5Pv1azXW4BKjWkZm3 7FVO5eb8BynAQI3kv3o1XZ3p6DdeyprtHNF4qKsB58v6ZdR4V9XrH+i X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add Digital Ramp Generator channels with destination selection (frequency, phase, or amplitude) based on attribute writes, dwell mode control, configurable upper/lower limits, step size controlled with rate of change config, and step rate controlled as sampling frequency. Signed-off-by: Rodrigo Alencar --- drivers/iio/frequency/ad9910.c | 401 +++++++++++++++++++++++++++++++++++++= +++- 1 file changed, 399 insertions(+), 2 deletions(-) diff --git a/drivers/iio/frequency/ad9910.c b/drivers/iio/frequency/ad9910.c index aaeff2e7640e..6c794e1b4b1c 100644 --- a/drivers/iio/frequency/ad9910.c +++ b/drivers/iio/frequency/ad9910.c @@ -140,6 +140,18 @@ #define AD9910_MC_SYNC_OUTPUT_DELAY_MSK GENMASK(15, 11) #define AD9910_MC_SYNC_INPUT_DELAY_MSK GENMASK(7, 3) =20 +/* Digital Ramp Limit Register */ +#define AD9910_DRG_LIMIT_UPPER_MSK GENMASK_ULL(63, 32) +#define AD9910_DRG_LIMIT_LOWER_MSK GENMASK_ULL(31, 0) + +/* Digital Ramp Step Register */ +#define AD9910_DRG_STEP_DEC_MSK GENMASK_ULL(63, 32) +#define AD9910_DRG_STEP_INC_MSK GENMASK_ULL(31, 0) + +/* Digital Ramp Rate Register */ +#define AD9910_DRG_RATE_DEC_MSK GENMASK(31, 16) +#define AD9910_DRG_RATE_INC_MSK GENMASK(15, 0) + /* Profile Register Format (Single Tone Mode) */ #define AD9910_PROFILE_ST_ASF_MSK GENMASK_ULL(61, 48) #define AD9910_PROFILE_ST_POW_MSK GENMASK_ULL(47, 32) @@ -147,6 +159,7 @@ =20 /* Device constants */ #define AD9910_PI_NANORAD 3141592653UL +#define AD9910_PI_PICORAD 3141592653590ULL =20 #define AD9910_MAX_SYSCLK_HZ (1000 * HZ_PER_MHZ) #define AD9910_MAX_PHASE_MICRORAD (AD9910_PI_NANORAD / 500) @@ -155,6 +168,7 @@ #define AD9910_ASF_PP_LSB_MAX FIELD_MAX(AD9910_ASF_SCALE_FACTOR_PP_LSB_MS= K) #define AD9910_POW_MAX FIELD_MAX(AD9910_PROFILE_ST_POW_MSK) #define AD9910_POW_PP_LSB_MAX FIELD_MAX(AD9910_POW_PP_LSB_MSK) +#define AD9910_STEP_RATE_MAX FIELD_MAX(AD9910_DRG_RATE_DEC_MSK) #define AD9910_NUM_PROFILES 8 =20 /* PLL constants */ @@ -184,6 +198,7 @@ =20 /* altcurrent ABI is in mA */ #define AD9910_NANO_MILLIAMP_PER_MICROAMP 1000000UL +#define AD9910_PICO_MILLIAMP_PER_MICROAMP 1000000000UL =20 #define AD9910_REFDIV2_MIN_FREQ_HZ (25 * HZ_PER_MHZ) #define AD9910_REFDIV2_MAX_FREQ_HZ (1900 * HZ_PER_MHZ) @@ -211,6 +226,9 @@ * @AD9910_CHANNEL_PROFILE_7: Profile 7 output channel * @AD9910_CHANNEL_PARALLEL: Parallel Data output channel * @AD9910_CHANNEL_PARALLEL_POLAR: Parallel Polar Data output channel + * @AD9910_CHANNEL_DRG: Digital Ramp Generator output channel + * @AD9910_CHANNEL_DRG_RAMP_UP: DRG ramp up channel + * @AD9910_CHANNEL_DRG_RAMP_DOWN: DRG ramp down channel */ enum ad9910_channel { AD9910_CHANNEL_PHY =3D 100, @@ -224,6 +242,9 @@ enum ad9910_channel { AD9910_CHANNEL_PROFILE_7 =3D 117, AD9910_CHANNEL_PARALLEL =3D 120, AD9910_CHANNEL_PARALLEL_POLAR =3D 121, + AD9910_CHANNEL_DRG =3D 130, + AD9910_CHANNEL_DRG_RAMP_UP =3D 131, + AD9910_CHANNEL_DRG_RAMP_DOWN =3D 132, }; =20 enum { @@ -241,10 +262,21 @@ enum { AD9910_CHAN_IDX_PARALLEL_FREQ, AD9910_CHAN_IDX_PARALLEL_POLAR_AMP, AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE, + AD9910_CHAN_IDX_DRG_FREQ, + AD9910_CHAN_IDX_DRG_PHASE, + AD9910_CHAN_IDX_DRG_AMP, + AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP, + AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN, + AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP, + AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN, + AD9910_CHAN_IDX_DRG_AMP_RAMP_UP, + AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN, }; =20 enum { AD9910_POWERDOWN, + AD9910_DWELL_EN, + AD9910_ROC, }; =20 struct ad9910_data { @@ -547,6 +579,14 @@ static ssize_t ad9910_ext_info_read(struct iio_dev *in= dio_dev, case AD9910_POWERDOWN: val =3D ad9910_sw_powerdown_get(st); break; + case AD9910_DWELL_EN: + if (chan->channel =3D=3D AD9910_CHANNEL_DRG_RAMP_UP) + val =3D FIELD_GET(AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK, + st->reg[AD9910_REG_CFR2].val32) ? 0 : 1; + else + val =3D FIELD_GET(AD9910_CFR2_DRG_NO_DWELL_LOW_MSK, + st->reg[AD9910_REG_CFR2].val32) ? 0 : 1; + break; default: return -EINVAL; } @@ -575,6 +615,113 @@ static ssize_t ad9910_ext_info_write(struct iio_dev *= indio_dev, if (ret) return ret; break; + case AD9910_DWELL_EN: + if (chan->channel =3D=3D AD9910_CHANNEL_DRG_RAMP_UP) { + val32 =3D val32 ? 0 : AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK; + ret =3D ad9910_reg32_update(st, AD9910_REG_CFR2, + AD9910_CFR2_DRG_NO_DWELL_HIGH_MSK, + val32, true); + if (ret) + return ret; + } else { + val32 =3D val32 ? 0 : AD9910_CFR2_DRG_NO_DWELL_LOW_MSK; + ret =3D ad9910_reg32_update(st, AD9910_REG_CFR2, + AD9910_CFR2_DRG_NO_DWELL_LOW_MSK, + val32, true); + if (ret) + return ret; + } + break; + default: + return -EINVAL; + } + + return len; +} + +static ssize_t ad9910_drg_roc_read(struct iio_dev *indio_dev, uintptr_t pr= ivate, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + u64 roc64; + u32 rate; + + guard(mutex)(&st->lock); + + switch (chan->channel) { + case AD9910_CHANNEL_DRG_RAMP_UP: + roc64 =3D FIELD_GET(AD9910_DRG_STEP_INC_MSK, + st->reg[AD9910_REG_DRG_STEP].val64); + rate =3D FIELD_GET(AD9910_DRG_RATE_INC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + break; + case AD9910_CHANNEL_DRG_RAMP_DOWN: + roc64 =3D FIELD_GET(AD9910_DRG_STEP_DEC_MSK, + st->reg[AD9910_REG_DRG_STEP].val64); + rate =3D FIELD_GET(AD9910_DRG_RATE_DEC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + break; + default: + return -EINVAL; + } + + if (!rate) + return -ERANGE; + + roc64 *=3D st->data.sysclk_freq_hz; + return sysfs_emit(buf, "%llu\n", div_u64(roc64, 4 * rate)); +} + +static ssize_t ad9910_drg_roc_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + u64 tmp64; + u32 rate; + int ret; + + ret =3D kstrtou64(buf, 10, &tmp64); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + switch (chan->channel) { + case AD9910_CHANNEL_DRG_RAMP_UP: + rate =3D FIELD_GET(AD9910_DRG_RATE_INC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + if (!rate) + return -ERANGE; + + tmp64 =3D ad9910_rational_scale(tmp64, 4 * rate, st->data.sysclk_freq_hz= ); + tmp64 =3D min_t(u64, tmp64, U32_MAX); + + ret =3D ad9910_reg64_update(st, AD9910_REG_DRG_STEP, + AD9910_DRG_STEP_INC_MSK, + FIELD_PREP(AD9910_DRG_STEP_INC_MSK, tmp64), + true); + if (ret) + return ret; + break; + case AD9910_CHANNEL_DRG_RAMP_DOWN: + rate =3D FIELD_GET(AD9910_DRG_RATE_DEC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + if (!rate) + return -ERANGE; + + tmp64 =3D ad9910_rational_scale(tmp64, 4 * rate, st->data.sysclk_freq_hz= ); + tmp64 =3D min_t(u64, tmp64, U32_MAX); + + ret =3D ad9910_reg64_update(st, AD9910_REG_DRG_STEP, + AD9910_DRG_STEP_DEC_MSK, + FIELD_PREP(AD9910_DRG_STEP_DEC_MSK, tmp64), + true); + if (ret) + return ret; + break; default: return -EINVAL; } @@ -593,6 +740,24 @@ static const struct iio_chan_spec_ext_info ad9910_phy_= ext_info[] =3D { { } }; =20 +static const struct iio_chan_spec_ext_info ad9910_drg_ramp_ext_info[] =3D { + { + .name =3D "dwell_en", + .read =3D ad9910_ext_info_read, + .write =3D ad9910_ext_info_write, + .private =3D AD9910_DWELL_EN, + .shared =3D IIO_SEPARATE, + }, + { + .name =3D "raw_roc", + .read =3D ad9910_drg_roc_read, + .write =3D ad9910_drg_roc_write, + .private =3D AD9910_ROC, + .shared =3D IIO_SEPARATE, + }, + { } +}; + #define AD9910_PROFILE_CHAN(idx) { \ .type =3D IIO_ALTCURRENT, \ .indexed =3D 1, \ @@ -672,6 +837,102 @@ static const struct iio_chan_spec ad9910_channels[] = =3D { BIT(IIO_CHAN_INFO_SCALE), .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], }, + [AD9910_CHAN_IDX_DRG_FREQ] =3D { + .type =3D IIO_FREQUENCY, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_DRG, + .address =3D AD9910_CHAN_IDX_DRG_FREQ, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_SCALE), + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_DRG_PHASE] =3D { + .type =3D IIO_PHASE, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_DRG, + .address =3D AD9910_CHAN_IDX_DRG_PHASE, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_SCALE), + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_DRG_AMP] =3D { + .type =3D IIO_ALTCURRENT, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_DRG, + .address =3D AD9910_CHAN_IDX_DRG_AMP, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_SCALE), + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, + [AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP] =3D { + .type =3D IIO_FREQUENCY, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_DRG_RAMP_UP, + .address =3D AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info =3D ad9910_drg_ramp_ext_info, + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_DRG_FREQ], + }, + [AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN] =3D { + .type =3D IIO_FREQUENCY, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_DRG_RAMP_DOWN, + .address =3D AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info =3D ad9910_drg_ramp_ext_info, + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_DRG_FREQ], + }, + [AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP] =3D { + .type =3D IIO_PHASE, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_DRG_RAMP_UP, + .address =3D AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info =3D ad9910_drg_ramp_ext_info, + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_DRG_PHASE], + }, + [AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN] =3D { + .type =3D IIO_PHASE, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_DRG_RAMP_DOWN, + .address =3D AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info =3D ad9910_drg_ramp_ext_info, + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_DRG_PHASE], + }, + [AD9910_CHAN_IDX_DRG_AMP_RAMP_UP] =3D { + .type =3D IIO_ALTCURRENT, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_DRG_RAMP_UP, + .address =3D AD9910_CHAN_IDX_DRG_AMP_RAMP_UP, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info =3D ad9910_drg_ramp_ext_info, + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_DRG_AMP], + }, + [AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN] =3D { + .type =3D IIO_ALTCURRENT, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_DRG_RAMP_DOWN, + .address =3D AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info =3D ad9910_drg_ramp_ext_info, + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_DRG_AMP], + }, }; =20 static int ad9910_read_raw(struct iio_dev *indio_dev, @@ -695,6 +956,15 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, *val =3D (tmp32 =3D=3D st->profile); } break; + case AD9910_CHANNEL_DRG: + tmp32 =3D FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, + st->reg[AD9910_REG_CFR2].val32); + if (tmp32 =3D=3D (chan->address - AD9910_CHAN_IDX_DRG_FREQ)) + *val =3D FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, + st->reg[AD9910_REG_CFR2].val32); + else + *val =3D 0; + break; default: return -EINVAL; } @@ -734,6 +1004,16 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, *val =3D FIELD_GET(AD9910_PROFILE_ST_ASF_MSK, st->reg[AD9910_REG_PROFILE(tmp32)].val64); return IIO_VAL_INT; + case AD9910_CHANNEL_DRG_RAMP_UP: + tmp64 =3D FIELD_GET(AD9910_DRG_LIMIT_UPPER_MSK, + st->reg[AD9910_REG_DRG_LIMIT].val64); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_INT_64; + case AD9910_CHANNEL_DRG_RAMP_DOWN: + tmp64 =3D FIELD_GET(AD9910_DRG_LIMIT_LOWER_MSK, + st->reg[AD9910_REG_DRG_LIMIT].val64); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_INT_64; default: return -EINVAL; } @@ -742,9 +1022,23 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, case AD9910_CHANNEL_PHY: *val =3D st->data.sysclk_freq_hz; return IIO_VAL_INT; + case AD9910_CHANNEL_DRG_RAMP_UP: + tmp32 =3D FIELD_GET(AD9910_DRG_RATE_INC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + break; + case AD9910_CHANNEL_DRG_RAMP_DOWN: + tmp32 =3D FIELD_GET(AD9910_DRG_RATE_DEC_MSK, + st->reg[AD9910_REG_DRG_RATE].val32); + break; default: return -EINVAL; } + if (!tmp32) + return -ERANGE; + tmp32 *=3D 4; + *val =3D st->data.sysclk_freq_hz / tmp32; + *val2 =3D div_u64((u64)(st->data.sysclk_freq_hz % tmp32) * MICRO, tmp32); + return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SCALE: switch (chan->address) { case AD9910_CHAN_IDX_PHY: @@ -774,6 +1068,21 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, *val =3D 0; *val2 =3D AD9910_PI_NANORAD >> 7; return IIO_VAL_INT_PLUS_NANO; + case AD9910_CHAN_IDX_DRG_FREQ: + tmp64 =3D ad9910_rational_scale(st->data.sysclk_freq_hz, + PICO, BIT_ULL(32)); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_PICO; + case AD9910_CHAN_IDX_DRG_PHASE: + tmp64 =3D DIV_U64_ROUND_CLOSEST(AD9910_PI_PICORAD, BIT(31)); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_PICO; + case AD9910_CHAN_IDX_DRG_AMP: + tmp64 =3D (u64)st->data.output_current_uA * + AD9910_PICO_MILLIAMP_PER_MICROAMP; + tmp64 =3D DIV_U64_ROUND_CLOSEST(tmp64 >> 1, BIT(31)); + iio_val_s64_decompose(tmp64, val, val2); + return IIO_VAL_DECIMAL64_PICO; default: return -EINVAL; } @@ -831,6 +1140,22 @@ static int ad9910_write_raw(struct iio_dev *indio_dev, return ret; =20 return ad9910_profile_set(st, tmp32); + case AD9910_CHANNEL_DRG: + tmp32 =3D chan->address - AD9910_CHAN_IDX_DRG_FREQ; + if (val) { + tmp32 =3D AD9910_CFR2_DRG_ENABLE_MSK | + FIELD_PREP(AD9910_CFR2_DRG_DEST_MSK, tmp32); + } else { + if (tmp32 !=3D FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, + st->reg[AD9910_REG_CFR2].val32)) + return 0; /* nothing to do */ + tmp32 =3D 0; + } + + return ad9910_reg32_update(st, AD9910_REG_CFR2, + AD9910_CFR2_DRG_DEST_MSK | + AD9910_CFR2_DRG_ENABLE_MSK, + tmp32, true); default: return -EINVAL; } @@ -884,11 +1209,61 @@ static int ad9910_write_raw(struct iio_dev *indio_de= v, return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), AD9910_PROFILE_ST_ASF_MSK, tmp64, true); + case AD9910_CHANNEL_DRG_RAMP_UP: { + s64 val64 =3D iio_val_s64_compose(val, val2); + + if (val64 < 0) + return -EINVAL; + + tmp64 =3D min_t(u64, val64, U32_MAX); + tmp64 =3D FIELD_PREP(AD9910_DRG_LIMIT_UPPER_MSK, tmp64); + return ad9910_reg64_update(st, AD9910_REG_DRG_LIMIT, + AD9910_DRG_LIMIT_UPPER_MSK, + tmp64, true); + } + case AD9910_CHANNEL_DRG_RAMP_DOWN: { + s64 val64 =3D iio_val_s64_compose(val, val2); + + if (val64 < 0) + return -EINVAL; + + tmp64 =3D min_t(u64, val64, U32_MAX); + tmp64 =3D FIELD_PREP(AD9910_DRG_LIMIT_LOWER_MSK, tmp64); + return ad9910_reg64_update(st, AD9910_REG_DRG_LIMIT, + AD9910_DRG_LIMIT_LOWER_MSK, + tmp64, true); + } default: return -EINVAL; } case IIO_CHAN_INFO_SAMP_FREQ: - return ad9910_set_sysclk_freq(st, val, true); + if (chan->channel =3D=3D AD9910_CHANNEL_PHY) + return ad9910_set_sysclk_freq(st, val, true); + + if (val < 0 || val2 < 0 || val > st->data.sysclk_freq_hz / 4) + return -EINVAL; + + tmp64 =3D ((u64)val * MICRO + val2) * 4; + if (!tmp64) + return -EINVAL; + + tmp64 =3D DIV64_U64_ROUND_CLOSEST((u64)st->data.sysclk_freq_hz * MICRO, = tmp64); + tmp32 =3D clamp(tmp64, 1U, AD9910_STEP_RATE_MAX); + + switch (chan->channel) { + case AD9910_CHANNEL_DRG_RAMP_UP: + tmp32 =3D FIELD_PREP(AD9910_DRG_RATE_INC_MSK, tmp32); + return ad9910_reg32_update(st, AD9910_REG_DRG_RATE, + AD9910_DRG_RATE_INC_MSK, + tmp32, true); + case AD9910_CHANNEL_DRG_RAMP_DOWN: + tmp32 =3D FIELD_PREP(AD9910_DRG_RATE_DEC_MSK, tmp32); + return ad9910_reg32_update(st, AD9910_REG_DRG_RATE, + AD9910_DRG_RATE_DEC_MSK, + tmp32, true); + default: + return -EINVAL; + } case IIO_CHAN_INFO_SCALE: switch (chan->address) { case AD9910_CHAN_IDX_PHY: @@ -963,11 +1338,16 @@ static int ad9910_write_raw_get_fmt(struct iio_dev *= indio_dev, switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: return IIO_VAL_INT; + case AD9910_CHANNEL_DRG_RAMP_UP: + case AD9910_CHANNEL_DRG_RAMP_DOWN: + return IIO_VAL_INT_64; default: return -EINVAL; } case IIO_CHAN_INFO_SAMP_FREQ: - return IIO_VAL_INT; + if (chan->channel =3D=3D AD9910_CHANNEL_PHY) + return IIO_VAL_INT; + return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SCALE: return IIO_VAL_INT_PLUS_NANO; case IIO_CHAN_INFO_OFFSET: @@ -1038,6 +1418,15 @@ static const char * const ad9910_channel_str[] =3D { [AD9910_CHAN_IDX_PARALLEL_FREQ] =3D "parallel_frequency", [AD9910_CHAN_IDX_PARALLEL_POLAR_AMP] =3D "parallel_polar_amplitude", [AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE] =3D "parallel_polar_phase", + [AD9910_CHAN_IDX_DRG_FREQ] =3D "drg_frequency", + [AD9910_CHAN_IDX_DRG_PHASE] =3D "drg_phase", + [AD9910_CHAN_IDX_DRG_AMP] =3D "drg_amplitude", + [AD9910_CHAN_IDX_DRG_FREQ_RAMP_UP] =3D "drg_rising_frequency", + [AD9910_CHAN_IDX_DRG_FREQ_RAMP_DOWN] =3D "drg_falling_frequency", + [AD9910_CHAN_IDX_DRG_PHASE_RAMP_UP] =3D "drg_rising_phase", + [AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN] =3D "drg_falling_phase", + [AD9910_CHAN_IDX_DRG_AMP_RAMP_UP] =3D "drg_rising_amplitude", + [AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN] =3D "drg_falling_amplitude", }; =20 static int ad9910_read_label(struct iio_dev *indio_dev, @@ -1173,6 +1562,14 @@ static int ad9910_setup(struct device *dev, struct a= d9910_state *st, if (ret) return ret; =20 + /* configure step rate with default values */ + ret =3D ad9910_reg32_write(st, AD9910_REG_DRG_RATE, + FIELD_PREP(AD9910_DRG_RATE_DEC_MSK, 1) | + FIELD_PREP(AD9910_DRG_RATE_INC_MSK, 1), + false); + if (ret) + return ret; + return ad9910_io_update(st); } =20 --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 BC54C3AB466; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=gV4CWvubDyKwU60gRLkV4pvjUXlOFgZEPMhSut9FlagY7941PnDuepuiX4OhtGMwKqn9unqSSdRiPmW1hjcOjKZckd0YECUhBpervwJ5SqH0tbLvlrQbSzgcg1IGrKmxLxzNc7we9K4HtzR6+r3wpz+FbD1MkU3SGTjtEI6J8Gw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=shReiqsP/G6KDG0J+oKUj4daOiyW0p/dJB6h4hU8Otg=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=DdMMFUo+JB2JsQ8beNSiso4Z3mb1USyCm6cxR1c3R8eVzZ/FYK0CldRwpXj3wQ8uzJPGNM7VC5kwzBq82+OIHs6+wn7cLJDbfFqE6BYYHLd1FuUnFufOHSvA3F11nh3fT6MECoc7QFMKdan/1YVefFwuyJNa7wj9xrZmF+JVf3M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WQYapOOO; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WQYapOOO" Received: by smtp.kernel.org (Postfix) with ESMTPS id 9675CC32786; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=shReiqsP/G6KDG0J+oKUj4daOiyW0p/dJB6h4hU8Otg=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=WQYapOOO9QD3Zjil9d7rHB7/Ia6KvWv0AetcHC0MkGlkOJ+CJBqCGXFr09KNRfXX0 5/kYRFqEWj68XJf6cjksVUG1nYEaY/44I7mG7GMyy/Z4K7KlkvK2XmOvWBlj2LBp54 iVVuxuCPNL1WGg0JQ5/meVYX7ttK5/yvoia1xExm2J2sYd6dbiCXuAUupMSm0a/b5m U8cNQQTMsGrvUewTnu/1aIgZrSCcwQZjqaYjA5TkIYuD3LD88Qad4tULwcQbHTxbFv 4MEerVV5wmxPkJn87JoyvpgiNR39BJAh/9LrqjXOXy3wAGV6XkjJfvRhlWv71mxeMU YXKCVfleG4oMg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 83D11C44532; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:22 +0100 Subject: [PATCH v9 13/17] iio: frequency: ad9910: add RAM mode support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-13-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=19963; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=x91goLxOizXgRQfFJJYYiGzZpKAboID1nbAoprAi3lY=; b=g0vOhD6is2y3TXXBxNWKYarUsUzxngibn0Pzvf1sLz03WwdwNQip1rEZQZGnuvkpF7XcxqNhS XRAAdbdx4cLALCimRrw7QZHAcjREDG41WxhvyrEn9dergScA9s4xLRs X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add RAM control channel, which includes: - RAM data loading via firmware upload interface; - Per-profile configuration and DDS core parameter destination as firmware metadata; - Profile switching relying on profile channels; - Sampling frequency control of the active profile; - ram-enable-aware read/write paths that redirect single tone frequency/phase/amplitude access through reg_profile cache when RAM is active; When RAM is enabled, the DDS profile parameters (frequency, phase, amplitude) for the single tone mode are sourced from a shadow register cache (reg_profile[]) since the profile registers are repurposed for RAM control. Signed-off-by: Rodrigo Alencar --- drivers/iio/frequency/Kconfig | 3 + drivers/iio/frequency/ad9910.c | 341 +++++++++++++++++++++++++++++++++++++= +++- 2 files changed, 337 insertions(+), 7 deletions(-) diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 6033f9155b4f..994d199080eb 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -29,6 +29,9 @@ config AD9910 tristate "Analog Devices AD9910 Direct Digital Synthesizer" depends on SPI depends on GPIOLIB + select CRC32 + select FW_LOADER + select FW_UPLOAD help Say yes here to build support for Analog Devices AD9910 1 GSPS, 14-Bit DDS with integrated DAC. diff --git a/drivers/iio/frequency/ad9910.c b/drivers/iio/frequency/ad9910.c index 6c794e1b4b1c..844cc0cc8f3e 100644 --- a/drivers/iio/frequency/ad9910.c +++ b/drivers/iio/frequency/ad9910.c @@ -8,11 +8,14 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -157,6 +160,15 @@ #define AD9910_PROFILE_ST_POW_MSK GENMASK_ULL(47, 32) #define AD9910_PROFILE_ST_FTW_MSK GENMASK_ULL(31, 0) =20 +/* Profile Register Format (RAM Mode) */ +#define AD9910_PROFILE_RAM_OPEN_MSK GENMASK_ULL(61, 56) +#define AD9910_PROFILE_RAM_STEP_RATE_MSK GENMASK_ULL(55, 40) +#define AD9910_PROFILE_RAM_END_ADDR_MSK GENMASK_ULL(39, 30) +#define AD9910_PROFILE_RAM_START_ADDR_MSK GENMASK_ULL(23, 14) +#define AD9910_PROFILE_RAM_NO_DWELL_HIGH_MSK BIT_ULL(5) +#define AD9910_PROFILE_RAM_ZERO_CROSSING_MSK BIT_ULL(3) +#define AD9910_PROFILE_RAM_MODE_CONTROL_MSK GENMASK_ULL(2, 0) + /* Device constants */ #define AD9910_PI_NANORAD 3141592653UL #define AD9910_PI_PICORAD 3141592653590ULL @@ -171,6 +183,16 @@ #define AD9910_STEP_RATE_MAX FIELD_MAX(AD9910_DRG_RATE_DEC_MSK) #define AD9910_NUM_PROFILES 8 =20 +#define AD9910_RAM_FW_MAGIC 0x00AD9910 +#define AD9910_RAM_FW_V1 0x0001 +#define AD9910_RAM_SIZE_MAX_WORDS 1024 +#define AD9910_RAM_WORD_SIZE sizeof(u32) +#define AD9910_RAM_SIZE_MAX_BYTES (AD9910_RAM_SIZE_MAX_WORDS * AD9910_RAM_= WORD_SIZE) +#define AD9910_RAM_ADDR_MAX (AD9910_RAM_SIZE_MAX_WORDS - 1) + +#define AD9910_RAM_ENABLED(st) \ + FIELD_GET(AD9910_CFR1_RAM_ENABLE_MSK, (st)->reg[AD9910_REG_CFR1].val32) + /* PLL constants */ #define AD9910_PLL_MIN_N 12 #define AD9910_PLL_MAX_N 127 @@ -207,7 +229,7 @@ #define AD9910_RESET_DELAY_us 1 /* 5 sysclk cycles: < 1us */ =20 #define AD9910_SPI_DATA_IDX 1 -#define AD9910_SPI_DATA_LEN_MAX sizeof(__be64) +#define AD9910_SPI_DATA_LEN_MAX AD9910_RAM_SIZE_MAX_BYTES #define AD9910_SPI_MESSAGE_LEN_MAX (AD9910_SPI_DATA_IDX + AD9910_SPI_DATA_= LEN_MAX) #define AD9910_SPI_READ_MSK BIT(7) #define AD9910_SPI_ADDR_MSK GENMASK(4, 0) @@ -229,6 +251,7 @@ * @AD9910_CHANNEL_DRG: Digital Ramp Generator output channel * @AD9910_CHANNEL_DRG_RAMP_UP: DRG ramp up channel * @AD9910_CHANNEL_DRG_RAMP_DOWN: DRG ramp down channel + * @AD9910_CHANNEL_RAM: RAM control output channel */ enum ad9910_channel { AD9910_CHANNEL_PHY =3D 100, @@ -245,8 +268,32 @@ enum ad9910_channel { AD9910_CHANNEL_DRG =3D 130, AD9910_CHANNEL_DRG_RAMP_UP =3D 131, AD9910_CHANNEL_DRG_RAMP_DOWN =3D 132, + AD9910_CHANNEL_RAM =3D 140, }; =20 +/** + * struct ad9910_ram_fw - AD9910 RAM firmware format + * @magic: Magic number for RAM firmware validation + * @version: Firmware version number + * @wcount: Number of RAM words to be written + * @crc: CRC32 checksum of the RAM data for integrity verification + * @cfr1: Value of CFR1 register to be configured (not all fields are + * used, but this is included here for convenience) + * @profiles: Array of RAM profile configurations + * @words: Array of RAM words to be written. Data pattern should be set in + * reverse order and wcount specifies the number of words in this + * array + */ +struct ad9910_ram_fw { + __be32 magic; + __be16 version; + __be16 wcount; + __be32 crc; + __be32 cfr1; + __be64 profiles[AD9910_NUM_PROFILES]; + __be32 words[] __counted_by_be(wcount); +} __packed; + enum { AD9910_CHAN_IDX_PHY, AD9910_CHAN_IDX_PROFILE_0, @@ -271,6 +318,7 @@ enum { AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN, AD9910_CHAN_IDX_DRG_AMP_RAMP_UP, AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN, + AD9910_CHAN_IDX_RAM, }; =20 enum { @@ -297,6 +345,7 @@ union ad9910_reg { struct ad9910_state { struct spi_device *spi; struct clk *refclk; + struct fw_upload *ram_fwu; =20 struct gpio_desc *gpio_pwdown; struct gpio_desc *gpio_update; @@ -305,12 +354,22 @@ struct ad9910_state { /* cached registers */ union ad9910_reg reg[AD9910_REG_NUM_CACHED]; =20 + /* + * alternate profile registers used to store RAM profile settings when + * RAM mode is disabled and Single Tone profile settings when RAM mode + * is enabled. + */ + u64 reg_profile[AD9910_NUM_PROFILES]; + /* Lock for accessing device registers and state variables */ struct mutex lock; =20 struct ad9910_data data; u8 profile; =20 + bool ram_fwu_cancel; + char ram_fwu_name[20]; + /* * RAM loading requires a reasonable amount of bytes, at the same time * DMA capable SPI drivers requires the transfer buffers to live in @@ -334,6 +393,22 @@ static inline u64 ad9910_rational_scale(u64 input, u64= scale, u64 reference) return mul_u64_add_u64_div_u64(input, scale, reference >> 1, reference); } =20 +static inline u64 ad9910_ram_profile_val(struct ad9910_state *st) +{ + if (AD9910_RAM_ENABLED(st)) + return st->reg[AD9910_REG_PROFILE(st->profile)].val64; + else + return st->reg_profile[st->profile]; +} + +static inline u64 ad9910_st_profile_val(struct ad9910_state *st, u8 profil= e) +{ + if (AD9910_RAM_ENABLED(st)) + return st->reg_profile[profile]; + else + return st->reg[AD9910_REG_PROFILE(profile)].val64; +} + static int ad9910_io_update(struct ad9910_state *st) { if (st->gpio_update) { @@ -933,6 +1008,18 @@ static const struct iio_chan_spec ad9910_channels[] = =3D { .ext_info =3D ad9910_drg_ramp_ext_info, .parent =3D &ad9910_channels[AD9910_CHAN_IDX_DRG_AMP], }, + [AD9910_CHAN_IDX_RAM] =3D { + .type =3D IIO_ALTCURRENT, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_RAM, + .address =3D AD9910_CHAN_IDX_RAM, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_FREQUENCY) | + BIT(IIO_CHAN_INFO_PHASE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, }; =20 static int ad9910_read_raw(struct iio_dev *indio_dev, @@ -965,6 +1052,10 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, else *val =3D 0; break; + case AD9910_CHANNEL_RAM: + *val =3D FIELD_GET(AD9910_CFR1_RAM_ENABLE_MSK, + st->reg[AD9910_REG_CFR1].val32); + break; default: return -EINVAL; } @@ -974,7 +1065,10 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; tmp64 =3D FIELD_GET(AD9910_PROFILE_ST_FTW_MSK, - st->reg[AD9910_REG_PROFILE(tmp32)].val64); + ad9910_st_profile_val(st, tmp32)); + break; + case AD9910_CHANNEL_RAM: + tmp64 =3D st->reg[AD9910_REG_FTW].val32; break; default: return -EINVAL; @@ -988,7 +1082,10 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; tmp64 =3D FIELD_GET(AD9910_PROFILE_ST_POW_MSK, - st->reg[AD9910_REG_PROFILE(tmp32)].val64); + ad9910_st_profile_val(st, tmp32)); + break; + case AD9910_CHANNEL_RAM: + tmp64 =3D st->reg[AD9910_REG_POW].val16; break; default: return -EINVAL; @@ -1002,7 +1099,7 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; *val =3D FIELD_GET(AD9910_PROFILE_ST_ASF_MSK, - st->reg[AD9910_REG_PROFILE(tmp32)].val64); + ad9910_st_profile_val(st, tmp32)); return IIO_VAL_INT; case AD9910_CHANNEL_DRG_RAMP_UP: tmp64 =3D FIELD_GET(AD9910_DRG_LIMIT_UPPER_MSK, @@ -1030,6 +1127,10 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, tmp32 =3D FIELD_GET(AD9910_DRG_RATE_DEC_MSK, st->reg[AD9910_REG_DRG_RATE].val32); break; + case AD9910_CHANNEL_RAM: + tmp32 =3D FIELD_GET(AD9910_PROFILE_RAM_STEP_RATE_MSK, + ad9910_ram_profile_val(st)); + break; default: return -EINVAL; } @@ -1119,7 +1220,7 @@ static int ad9910_write_raw(struct iio_dev *indio_dev, struct ad9910_state *st =3D iio_priv(indio_dev); u64 tmp64; u32 tmp32; - int ret; + int ret, i; =20 guard(mutex)(&st->lock); =20 @@ -1156,6 +1257,41 @@ static int ad9910_write_raw(struct iio_dev *indio_de= v, AD9910_CFR2_DRG_DEST_MSK | AD9910_CFR2_DRG_ENABLE_MSK, tmp32, true); + case AD9910_CHANNEL_RAM: + if (AD9910_RAM_ENABLED(st) =3D=3D !!val) + return 0; + + /* swap profile configs */ + for (i =3D 0; i < AD9910_NUM_PROFILES; i++) { + tmp64 =3D st->reg[AD9910_REG_PROFILE(i)].val64; + ret =3D ad9910_reg64_write(st, + AD9910_REG_PROFILE(i), + st->reg_profile[i], + false); + if (ret) + break; + st->reg_profile[i] =3D tmp64; + } + + if (ret) { + /* + * After the write failure, profiles 0..i-1 were + * already swapped in SW, but Hw registers are + * still pending an IO update, so swap them back + * in SW to keep the state consistent. + */ + while (i--) { + tmp64 =3D st->reg[AD9910_REG_PROFILE(i)].val64; + st->reg[AD9910_REG_PROFILE(i)].val64 =3D st->reg_profile[i]; + st->reg_profile[i] =3D tmp64; + } + return ret; + } + + tmp32 =3D FIELD_PREP(AD9910_CFR1_RAM_ENABLE_MSK, !!val); + return ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_RAM_ENABLE_MSK, + tmp32, true); default: return -EINVAL; } @@ -1169,10 +1305,17 @@ static int ad9910_write_raw(struct iio_dev *indio_d= ev, switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; + if (AD9910_RAM_ENABLED(st)) { + FIELD_MODIFY(AD9910_PROFILE_ST_FTW_MSK, + &st->reg_profile[tmp32], tmp64); + return 0; + } tmp64 =3D FIELD_PREP(AD9910_PROFILE_ST_FTW_MSK, tmp64); return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), AD9910_PROFILE_ST_FTW_MSK, tmp64, true); + case AD9910_CHANNEL_RAM: + return ad9910_reg32_write(st, AD9910_REG_FTW, tmp64, true); default: return -EINVAL; } @@ -1190,10 +1333,19 @@ static int ad9910_write_raw(struct iio_dev *indio_d= ev, switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; + + if (AD9910_RAM_ENABLED(st)) { + FIELD_MODIFY(AD9910_PROFILE_ST_POW_MSK, + &st->reg_profile[tmp32], tmp64); + return 0; + } + tmp64 =3D FIELD_PREP(AD9910_PROFILE_ST_POW_MSK, tmp64); return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), AD9910_PROFILE_ST_POW_MSK, tmp64, true); + case AD9910_CHANNEL_RAM: + return ad9910_reg16_write(st, AD9910_REG_POW, tmp64, true); default: return -EINVAL; } @@ -1204,8 +1356,15 @@ static int ad9910_write_raw(struct iio_dev *indio_de= v, return -EINVAL; =20 tmp32 =3D chan->channel - AD9910_CHANNEL_PROFILE_0; - tmp64 =3D FIELD_PREP(AD9910_PROFILE_ST_ASF_MSK, - min_t(u64, val, AD9910_ASF_MAX)); + tmp64 =3D min_t(u64, val, AD9910_ASF_MAX); + + if (AD9910_RAM_ENABLED(st)) { + FIELD_MODIFY(AD9910_PROFILE_ST_ASF_MSK, + &st->reg_profile[tmp32], tmp64); + return 0; + } + + tmp64 =3D FIELD_PREP(AD9910_PROFILE_ST_ASF_MSK, tmp64); return ad9910_reg64_update(st, AD9910_REG_PROFILE(tmp32), AD9910_PROFILE_ST_ASF_MSK, tmp64, true); @@ -1261,6 +1420,17 @@ static int ad9910_write_raw(struct iio_dev *indio_de= v, return ad9910_reg32_update(st, AD9910_REG_DRG_RATE, AD9910_DRG_RATE_DEC_MSK, tmp32, true); + case AD9910_CHANNEL_RAM: + if (!AD9910_RAM_ENABLED(st)) { + FIELD_MODIFY(AD9910_PROFILE_RAM_STEP_RATE_MSK, + &st->reg_profile[st->profile], tmp32); + return 0; + } + + tmp64 =3D FIELD_PREP(AD9910_PROFILE_RAM_STEP_RATE_MSK, tmp32); + return ad9910_reg64_update(st, AD9910_REG_PROFILE(st->profile), + AD9910_PROFILE_RAM_STEP_RATE_MSK, + tmp64, true); default: return -EINVAL; } @@ -1427,6 +1597,7 @@ static const char * const ad9910_channel_str[] =3D { [AD9910_CHAN_IDX_DRG_PHASE_RAMP_DOWN] =3D "drg_falling_phase", [AD9910_CHAN_IDX_DRG_AMP_RAMP_UP] =3D "drg_rising_amplitude", [AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN] =3D "drg_falling_amplitude", + [AD9910_CHAN_IDX_RAM] =3D "ram", }; =20 static int ad9910_read_label(struct iio_dev *indio_dev, @@ -1436,6 +1607,142 @@ static int ad9910_read_label(struct iio_dev *indio_= dev, return sysfs_emit(label, "%s\n", ad9910_channel_str[chan->address]); } =20 +static enum fw_upload_err ad9910_ram_fwu_prepare(struct fw_upload *fw_uplo= ad, + const u8 *data, u32 size) +{ + struct ad9910_state *st =3D fw_upload->dd_handle; + const struct ad9910_ram_fw *fw_data =3D (const struct ad9910_ram_fw *)dat= a; + size_t wcount, bcount; + + if (size < sizeof(struct ad9910_ram_fw)) + return FW_UPLOAD_ERR_INVALID_SIZE; + + if (get_unaligned_be32(&fw_data->magic) !=3D AD9910_RAM_FW_MAGIC) + return FW_UPLOAD_ERR_FW_INVALID; + + if (get_unaligned_be16(&fw_data->version) !=3D AD9910_RAM_FW_V1) + return FW_UPLOAD_ERR_FW_INVALID; + + wcount =3D get_unaligned_be16(&fw_data->wcount); + bcount =3D size - sizeof(struct ad9910_ram_fw); + if (wcount > AD9910_RAM_SIZE_MAX_WORDS || + bcount !=3D (wcount * AD9910_RAM_WORD_SIZE)) + return FW_UPLOAD_ERR_INVALID_SIZE; + + bcount +=3D sizeof(fw_data->cfr1) + sizeof(fw_data->profiles); + if (crc32(0, &fw_data->cfr1, bcount) !=3D get_unaligned_be32(&fw_data->cr= c)) + return FW_UPLOAD_ERR_FW_INVALID; + + guard(mutex)(&st->lock); + if (st->ram_fwu_cancel) { + /* cleanup() is not called when prepare fails */ + st->ram_fwu_cancel =3D false; + return FW_UPLOAD_ERR_CANCELED; + } + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err ad9910_ram_fwu_write(struct fw_upload *fw_upload, + const u8 *data, u32 offset, + u32 size, u32 *written) +{ + const struct ad9910_ram_fw *fw_data =3D (const struct ad9910_ram_fw *)dat= a; + struct ad9910_state *st =3D fw_upload->dd_handle; + int ret, ret2, idx, wcount; + u64 tmp64, backup; + + if (offset !=3D 0) + return FW_UPLOAD_ERR_INVALID_SIZE; + + guard(mutex)(&st->lock); + + if (st->ram_fwu_cancel) + return FW_UPLOAD_ERR_CANCELED; + + if (AD9910_RAM_ENABLED(st)) + return FW_UPLOAD_ERR_HW_ERROR; + + for (idx =3D 0; idx < AD9910_NUM_PROFILES; idx++) + st->reg_profile[idx] =3D get_unaligned_be64(&fw_data->profiles[idx]) | + AD9910_PROFILE_RAM_OPEN_MSK; + + ret =3D ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_RAM_PLAYBACK_DEST_MSK | + AD9910_CFR1_INT_PROFILE_CTL_MSK, + get_unaligned_be32(&fw_data->cfr1), true); + if (ret) + return FW_UPLOAD_ERR_RW_ERROR; + + wcount =3D get_unaligned_be16(&fw_data->wcount); + if (!wcount) { + *written =3D size; + return FW_UPLOAD_ERR_NONE; /* nothing else to write */ + } + + ret =3D ad9910_profile_set(st, st->profile); + if (ret) + return FW_UPLOAD_ERR_HW_ERROR; + + /* backup profile register and update it with required address range */ + backup =3D st->reg[AD9910_REG_PROFILE(st->profile)].val64; + tmp64 =3D AD9910_PROFILE_RAM_STEP_RATE_MSK | + FIELD_PREP(AD9910_PROFILE_RAM_START_ADDR_MSK, 0) | + FIELD_PREP(AD9910_PROFILE_RAM_END_ADDR_MSK, wcount - 1); + ret =3D ad9910_reg64_write(st, AD9910_REG_PROFILE(st->profile), tmp64, tr= ue); + if (ret) + return FW_UPLOAD_ERR_RW_ERROR; + + /* copy data words into spi buffer and transfer */ + memcpy(&st->tx_buf[1], fw_data->words, wcount * AD9910_RAM_WORD_SIZE); + ret =3D ad9910_spi_write(st, AD9910_REG_RAM, + wcount * AD9910_RAM_WORD_SIZE, false); + + /* restore active single tone profile regardless */ + st->reg[AD9910_REG_PROFILE(st->profile)].val64 =3D backup; + ret2 =3D ad9910_reg64_write(st, AD9910_REG_PROFILE(st->profile), backup, = true); + if (ret || ret2) + return FW_UPLOAD_ERR_RW_ERROR; + + *written =3D size; + + return FW_UPLOAD_ERR_NONE; +} + +static enum fw_upload_err ad9910_ram_fwu_poll_complete(struct fw_upload *f= w_upload) +{ + return FW_UPLOAD_ERR_NONE; +} + +static void ad9910_ram_fwu_cancel(struct fw_upload *fw_upload) +{ + struct ad9910_state *st =3D fw_upload->dd_handle; + + guard(mutex)(&st->lock); + st->ram_fwu_cancel =3D true; +} + +static void ad9910_ram_fwu_cleanup(struct fw_upload *fw_upload) +{ + struct ad9910_state *st =3D fw_upload->dd_handle; + + guard(mutex)(&st->lock); + st->ram_fwu_cancel =3D false; +} + +static void ad9910_ram_fwu_unregister(void *data) +{ + firmware_upload_unregister(data); +} + +static const struct fw_upload_ops ad9910_ram_fwu_ops =3D { + .prepare =3D ad9910_ram_fwu_prepare, + .write =3D ad9910_ram_fwu_write, + .poll_complete =3D ad9910_ram_fwu_poll_complete, + .cancel =3D ad9910_ram_fwu_cancel, + .cleanup =3D ad9910_ram_fwu_cleanup, +}; + static const struct iio_info ad9910_info =3D { .read_raw =3D ad9910_read_raw, .write_raw =3D ad9910_write_raw, @@ -1570,6 +1877,13 @@ static int ad9910_setup(struct device *dev, struct a= d9910_state *st, if (ret) return ret; =20 + for (unsigned int i =3D 0; i < AD9910_NUM_PROFILES; i++) { + st->reg_profile[i] =3D AD9910_PROFILE_RAM_OPEN_MSK; + st->reg_profile[i] |=3D FIELD_PREP(AD9910_PROFILE_RAM_STEP_RATE_MSK, 1); + st->reg_profile[i] |=3D FIELD_PREP(AD9910_PROFILE_RAM_END_ADDR_MSK, + AD9910_RAM_ADDR_MAX); + } + return ad9910_io_update(st); } =20 @@ -1663,6 +1977,19 @@ static int ad9910_probe(struct spi_device *spi) if (ret) return dev_err_probe(dev, ret, "device setup failed\n"); =20 + snprintf(st->ram_fwu_name, sizeof(st->ram_fwu_name), "%s:ram", + dev_name(&indio_dev->dev)); + st->ram_fwu =3D firmware_upload_register(THIS_MODULE, dev, st->ram_fwu_na= me, + &ad9910_ram_fwu_ops, st); + if (IS_ERR(st->ram_fwu)) + return dev_err_probe(dev, PTR_ERR(st->ram_fwu), + "failed to register ram upload ops\n"); + + ret =3D devm_add_action_or_reset(dev, ad9910_ram_fwu_unregister, st->ram_= fwu); + if (ret) + return dev_err_probe(dev, ret, + "failed to add ram upload unregister action\n"); + return devm_iio_device_register(dev, indio_dev); } =20 --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 CBDCE4EA362; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; cv=none; b=UMLnUUi4RBuUrfYP82++Moa62j+rZMYvQaxA6I46BuyvqIyvZ452kT10iRcAJ4ibUde2yAPYK+70l1OzUPMrGGc1wxXkZv60mEE0sFvHgx3ewr6tBjmKFgm8RsvCZwiIiFcovCPdsBatqhvLD1TuXW+lm7N/J47HiQ4gQwpER+s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735458; c=relaxed/simple; bh=amGj+VKSPI8w4AIHmjsgR46NsKz8RkT6GR/k+spQj60=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=avtVRrS8xl0v6sogNU56401Cp6T5GXXVL1YASsJbu2zzgDzOdf4ZNfqBB9Gkh27d9J9oFFstbecarm5IMt8XCd75thC8Jhi7hX23UrB7PFhD9nbtIQaVlFe4NUH+R7aapo0M6GYPJLgnQY0RFrXe/DpeGQYFo48ltxeZ9NLLfag= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=WE/lQCK4; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="WE/lQCK4" Received: by smtp.kernel.org (Postfix) with ESMTPS id AB856C2BCC6; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=amGj+VKSPI8w4AIHmjsgR46NsKz8RkT6GR/k+spQj60=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=WE/lQCK4UeT9IoODfCmGaWOiQpUNJFgWh+IPvBrOmngOtpYnCY6vmUwOPSAVP5db0 A1QoqkW/kuvAtikEXqfIqNHVIXRYm6zFf9AWbPC+AUR/8UiKsnEVCNxd6hKI5pHuIA YK+noSGUXKN7gHntCVMqFJ+iLdLsxLzVDQzb7mNAh6yqH+ECGZlvSrsgn64T80TVmF Nq7bkp1qMdpHkgIF7T8IpxFSXKofcLlMzo1rV+Kq500KB7I2IyPMalCh0uH8RiWfO0 JNGI0e7HFtHjM47ximGR9OGJnSoIW8Bf7elqwfI0n+gRcMss3VXa5agcEMdaJYkjdX JgUdsK1cK+Mrg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 96FD8C4453F; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:23 +0100 Subject: [PATCH v9 14/17] iio: frequency: ad9910: add output shift keying support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-14-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=10549; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=deapLg4tAOp/8NASE+fXtZFf2LYABUHicsk93OJxWd8=; b=4YXZ9QOTOTnwhG6cUTtQEuY44QMA43NgXMCx747VrIJnbXC6nyXac9Hel8UeN0tbpTrxpfrbt IMP2WIbz8AWDiI9TAzM+W7jBypRkoAy7N6plvZjiQPz/zreMBxnp/3n X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add OSK channel with amplitude envelope control capabilities: - OSK enable/disable via IIO_CHAN_INFO_ENABLE; - Amplitude ramp rate control via IIO_CHAN_INFO_SAMP_FREQ; - Amplitude scale factor readback via IIO_CHAN_INFO_RAW (ASF register); - Automatic OSK step size configurable through the raw_roc extended attribute, which allows for selectable step sizes in raw units: - 0: no step, means manual mode (NOT pin controlled) - 1: I_FS / 2^14 step, automatic mode (pin controlled) - 2: 2 I_FS / 2^14 step, automatic mode (pin controlled) - 4: 4 I_FS / 2^14 step, automatic mode (pin controlled) - 8: 8 I_FS / 2^14 step, automatic mode (pin controlled) - 16383: I_FS step (max), manual mode (pin controlled) The ASF register is initialized with a default amplitude ramp rate during device setup to ensure valid readback. Signed-off-by: Rodrigo Alencar --- drivers/iio/frequency/ad9910.c | 191 +++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 191 insertions(+) diff --git a/drivers/iio/frequency/ad9910.c b/drivers/iio/frequency/ad9910.c index 844cc0cc8f3e..49a70909bf5f 100644 --- a/drivers/iio/frequency/ad9910.c +++ b/drivers/iio/frequency/ad9910.c @@ -252,6 +252,7 @@ * @AD9910_CHANNEL_DRG_RAMP_UP: DRG ramp up channel * @AD9910_CHANNEL_DRG_RAMP_DOWN: DRG ramp down channel * @AD9910_CHANNEL_RAM: RAM control output channel + * @AD9910_CHANNEL_OSK: Output Shift Keying output channel */ enum ad9910_channel { AD9910_CHANNEL_PHY =3D 100, @@ -269,6 +270,7 @@ enum ad9910_channel { AD9910_CHANNEL_DRG_RAMP_UP =3D 131, AD9910_CHANNEL_DRG_RAMP_DOWN =3D 132, AD9910_CHANNEL_RAM =3D 140, + AD9910_CHANNEL_OSK =3D 150, }; =20 /** @@ -319,12 +321,14 @@ enum { AD9910_CHAN_IDX_DRG_AMP_RAMP_UP, AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN, AD9910_CHAN_IDX_RAM, + AD9910_CHAN_IDX_OSK, }; =20 enum { AD9910_POWERDOWN, AD9910_DWELL_EN, AD9910_ROC, + AD9910_ROC_AVAIL, }; =20 struct ad9910_data { @@ -804,6 +808,125 @@ static ssize_t ad9910_drg_roc_write(struct iio_dev *i= ndio_dev, return len; } =20 +static const u32 ad9910_osk_raw_step[] =3D { + 0, /* no step: manual mode (NOT pin controlled) */ + 1, /* step size 1: automatic mode (pin controlled) */ + 2, /* step size 2: automatic mode (pin controlled) */ + 4, /* step size 4: automatic mode (pin controlled) */ + 8, /* step size 8: automatic mode (pin controlled) */ + GENMASK(13, 0), /* max step: manual mode (pin controlled) */ +}; + +static ssize_t ad9910_osk_attrs_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + bool auto_en, pinctrl_en; + u32 rate, step; + u64 roc64; + + guard(mutex)(&st->lock); + + rate =3D FIELD_GET(AD9910_ASF_RAMP_RATE_MSK, st->reg[AD9910_REG_ASF].val3= 2); + if (!rate) + return -ERANGE; + + switch (private) { + case AD9910_ROC: + auto_en =3D FIELD_GET(AD9910_CFR1_SELECT_AUTO_OSK_MSK, + st->reg[AD9910_REG_CFR1].val32); + pinctrl_en =3D FIELD_GET(AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK, + st->reg[AD9910_REG_CFR1].val32); + if (auto_en) { + step =3D FIELD_GET(AD9910_ASF_STEP_SIZE_MSK, + st->reg[AD9910_REG_ASF].val32); + step =3D ad9910_osk_raw_step[step + 1]; + } else if (pinctrl_en) { + step =3D ad9910_osk_raw_step[ARRAY_SIZE(ad9910_osk_raw_step) - 1]; + } else { + step =3D ad9910_osk_raw_step[0]; + } + + roc64 =3D div_u64((u64)step * st->data.sysclk_freq_hz, 4 * rate); + return sysfs_emit(buf, "%llu\n", roc64); + case AD9910_ROC_AVAIL: { + ssize_t len =3D 0; + + for (unsigned int i =3D 0; i < ARRAY_SIZE(ad9910_osk_raw_step); i++) { + roc64 =3D div_u64((u64)ad9910_osk_raw_step[i] * st->data.sysclk_freq_hz, + 4 * rate); + len +=3D sysfs_emit_at(buf, len, "%llu ", roc64); + } + + buf[len - 1] =3D '\n'; /* replace last space with a newline */ + return len; + } + default: + return -EINVAL; + } +} + +static ssize_t ad9910_osk_attrs_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + u32 idx, reg_val, rate; + u64 step; + int ret; + + ret =3D kstrtou64(buf, 10, &step); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + rate =3D FIELD_GET(AD9910_ASF_RAMP_RATE_MSK, st->reg[AD9910_REG_ASF].val3= 2); + if (!rate) + return -ERANGE; + + switch (private) { + case AD9910_ROC: + step =3D ad9910_rational_scale(step, 4 * rate, + st->data.sysclk_freq_hz); + step =3D min(step, AD9910_ASF_MAX); + idx =3D find_closest(step, ad9910_osk_raw_step, + ARRAY_SIZE(ad9910_osk_raw_step)); + if (idx =3D=3D ARRAY_SIZE(ad9910_osk_raw_step) - 1) { + /* manual mode: pin-controlled */ + reg_val =3D AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK; + } else if (idx =3D=3D 0) { + /* manual mode, NOT pin-controlled */ + reg_val =3D 0; + } else { + /* auto mode: pin-controlled */ + reg_val =3D FIELD_PREP(AD9910_ASF_STEP_SIZE_MSK, idx - 1); + ret =3D ad9910_reg32_update(st, AD9910_REG_ASF, + AD9910_ASF_STEP_SIZE_MSK, + reg_val, false); + if (ret) + return ret; + + reg_val =3D AD9910_CFR1_SELECT_AUTO_OSK_MSK; + } + + ret =3D ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_SELECT_AUTO_OSK_MSK | + AD9910_CFR1_OSK_MANUAL_EXT_CTL_MSK, + reg_val, true); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + + return len; +} + static const struct iio_chan_spec_ext_info ad9910_phy_ext_info[] =3D { { .name =3D "powerdown", @@ -833,6 +956,23 @@ static const struct iio_chan_spec_ext_info ad9910_drg_= ramp_ext_info[] =3D { { } }; =20 +static const struct iio_chan_spec_ext_info ad9910_osk_ext_info[] =3D { + { + .name =3D "raw_roc", + .read =3D ad9910_osk_attrs_read, + .write =3D ad9910_osk_attrs_write, + .private =3D AD9910_ROC, + .shared =3D IIO_SEPARATE, + }, + { + .name =3D "raw_roc_available", + .read =3D ad9910_osk_attrs_read, + .private =3D AD9910_ROC_AVAIL, + .shared =3D IIO_SEPARATE, + }, + { } +}; + #define AD9910_PROFILE_CHAN(idx) { \ .type =3D IIO_ALTCURRENT, \ .indexed =3D 1, \ @@ -1020,6 +1160,18 @@ static const struct iio_chan_spec ad9910_channels[] = =3D { BIT(IIO_CHAN_INFO_SAMP_FREQ), .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], }, + [AD9910_CHAN_IDX_OSK] =3D { + .type =3D IIO_ALTCURRENT, + .indexed =3D 1, + .output =3D 1, + .channel =3D AD9910_CHANNEL_OSK, + .address =3D AD9910_CHAN_IDX_OSK, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .ext_info =3D ad9910_osk_ext_info, + .parent =3D &ad9910_channels[AD9910_CHAN_IDX_PHY], + }, }; =20 static int ad9910_read_raw(struct iio_dev *indio_dev, @@ -1056,6 +1208,10 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, *val =3D FIELD_GET(AD9910_CFR1_RAM_ENABLE_MSK, st->reg[AD9910_REG_CFR1].val32); break; + case AD9910_CHANNEL_OSK: + *val =3D FIELD_GET(AD9910_CFR1_OSK_ENABLE_MSK, + st->reg[AD9910_REG_CFR1].val32); + break; default: return -EINVAL; } @@ -1111,6 +1267,10 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, st->reg[AD9910_REG_DRG_LIMIT].val64); iio_val_s64_decompose(tmp64, val, val2); return IIO_VAL_INT_64; + case AD9910_CHANNEL_OSK: + *val =3D FIELD_GET(AD9910_ASF_SCALE_FACTOR_MSK, + st->reg[AD9910_REG_ASF].val32); + return IIO_VAL_INT; default: return -EINVAL; } @@ -1131,6 +1291,10 @@ static int ad9910_read_raw(struct iio_dev *indio_dev, tmp32 =3D FIELD_GET(AD9910_PROFILE_RAM_STEP_RATE_MSK, ad9910_ram_profile_val(st)); break; + case AD9910_CHANNEL_OSK: + tmp32 =3D FIELD_GET(AD9910_ASF_RAMP_RATE_MSK, + st->reg[AD9910_REG_ASF].val32); + break; default: return -EINVAL; } @@ -1292,6 +1456,11 @@ static int ad9910_write_raw(struct iio_dev *indio_de= v, return ad9910_reg32_update(st, AD9910_REG_CFR1, AD9910_CFR1_RAM_ENABLE_MSK, tmp32, true); + case AD9910_CHANNEL_OSK: + tmp32 =3D FIELD_PREP(AD9910_CFR1_OSK_ENABLE_MSK, !!val); + return ad9910_reg32_update(st, AD9910_REG_CFR1, + AD9910_CFR1_OSK_ENABLE_MSK, + tmp32, true); default: return -EINVAL; } @@ -1392,6 +1561,15 @@ static int ad9910_write_raw(struct iio_dev *indio_de= v, AD9910_DRG_LIMIT_LOWER_MSK, tmp64, true); } + case AD9910_CHANNEL_OSK: + if (val < 0) + return -EINVAL; + + tmp32 =3D min_t(u32, val, AD9910_ASF_MAX); + tmp32 =3D FIELD_PREP(AD9910_ASF_SCALE_FACTOR_MSK, tmp32); + return ad9910_reg32_update(st, AD9910_REG_ASF, + AD9910_ASF_SCALE_FACTOR_MSK, + tmp32, true); default: return -EINVAL; } @@ -1431,6 +1609,11 @@ static int ad9910_write_raw(struct iio_dev *indio_de= v, return ad9910_reg64_update(st, AD9910_REG_PROFILE(st->profile), AD9910_PROFILE_RAM_STEP_RATE_MSK, tmp64, true); + case AD9910_CHANNEL_OSK: + return ad9910_reg32_update(st, AD9910_REG_ASF, + AD9910_ASF_RAMP_RATE_MSK, + FIELD_PREP(AD9910_ASF_RAMP_RATE_MSK, tmp32), + true); default: return -EINVAL; } @@ -1507,6 +1690,7 @@ static int ad9910_write_raw_get_fmt(struct iio_dev *i= ndio_dev, case IIO_CHAN_INFO_RAW: switch (chan->channel) { case AD9910_CHANNEL_PROFILE_0 ... AD9910_CHANNEL_PROFILE_7: + case AD9910_CHANNEL_OSK: return IIO_VAL_INT; case AD9910_CHANNEL_DRG_RAMP_UP: case AD9910_CHANNEL_DRG_RAMP_DOWN: @@ -1598,6 +1782,7 @@ static const char * const ad9910_channel_str[] =3D { [AD9910_CHAN_IDX_DRG_AMP_RAMP_UP] =3D "drg_rising_amplitude", [AD9910_CHAN_IDX_DRG_AMP_RAMP_DOWN] =3D "drg_falling_amplitude", [AD9910_CHAN_IDX_RAM] =3D "ram", + [AD9910_CHAN_IDX_OSK] =3D "osk", }; =20 static int ad9910_read_label(struct iio_dev *indio_dev, @@ -1870,6 +2055,12 @@ static int ad9910_setup(struct device *dev, struct a= d9910_state *st, return ret; =20 /* configure step rate with default values */ + ret =3D ad9910_reg32_write(st, AD9910_REG_ASF, + FIELD_PREP(AD9910_ASF_RAMP_RATE_MSK, 1), + false); + if (ret) + return ret; + ret =3D ad9910_reg32_write(st, AD9910_REG_DRG_RATE, FIELD_PREP(AD9910_DRG_RATE_DEC_MSK, 1) | FIELD_PREP(AD9910_DRG_RATE_INC_MSK, 1), --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 E1FF24F797F; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735459; cv=none; b=fRXmjDig7sbtR/8TNmhL6bWDK0bFCtVS1JrKujHIkfhyvW7IunKVibwydyXQfimiBHGu+t/KCiCLdW41kwF/J+VhyJW5oQOSNsMnRVDzFoST7/12uUXz81Pu3YK2vKloJ3YOEg80GOI8xyPAB1Haft0jiSGIygHzOFyYHoPFfTg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735459; c=relaxed/simple; bh=zH3d6UhbgF+wymh0lvUZxUx9swJDZRjdYxVWK4uY9nk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Rmb1lstcsj+LeuvxSQACYQ6p6ZpJ4GZTP/iODC39k4tYBvFThoPBOj/nCFy0TfIE/xfv5VSWsyYfUyJXOe5PESFNPSJ3JiuqBDrGhn7McRNYhiJV7JfbXkwZJf8zi+CbMK1yPvzNZOffTf1ZjPxa9H0wHnpHaHdzJBzuq7ObucM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=rSIBtIPo; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="rSIBtIPo" Received: by smtp.kernel.org (Postfix) with ESMTPS id B87F3C4AF12; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=zH3d6UhbgF+wymh0lvUZxUx9swJDZRjdYxVWK4uY9nk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=rSIBtIPoDwvBtEEqwAZuvlBBE7b7XJYfo/zh+Cuqic7qPA/QDvkpQMYhT5SvxY70U Ra4bflNozGvUFHGRkufffloxCTbAsujpu+7tx+2kejxR6m0PSAHr6BtIcpAPK7CBTr R7Zzzvq5hPWmDJQjToCVBFS/H7BkVVEVmB1/xmQlCXwA4yuasrgdBpLNc+hy6GNSUp fv1BOzmNqP5qEv2mWk77acVhlU8DHlxDJJWZ+Ct7PoYZJnLmUT7sMmoYxGPcle3DRy 9CLExbuV1Lg+4a9UyI+SX+/xhq+o610KN2a8xxovLhS1eYc9zTs17NCVLnVmSD/gVR MgVDvkeJwqv5A== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id A8908C4453C; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:24 +0100 Subject: [PATCH v9 15/17] iio: frequency: ad9910: show channel priority in debugfs Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-15-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=8242; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=JzyGelxx1+H9uoBGvlYHJJDtGuyMvfdmciRAwzTYHBI=; b=YpQheCGhaBkjBtqSyK+hvIcsnE65MhJ3gEqMJce/1Cn4+3aTyuIGvec2gDTaSnPGGGyeMJFjU 0xVbKduCOOGC+f5+8Ng2bIg1/2g0pbSrQtOi3C+D+JzluQh6hO9sxFH X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Expose frequency_source, phase_source and amplitude_source attributes in debugfs. Those indicate from which channel the specific DDS parameter is being sourced by returning its label. The implementation follows the priority table found in the datasheet. Signed-off-by: Rodrigo Alencar --- drivers/iio/frequency/ad9910.c | 187 +++++++++++++++++++++++++++++++++++++= +++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/drivers/iio/frequency/ad9910.c b/drivers/iio/frequency/ad9910.c index 49a70909bf5f..32bd1e6cf14f 100644 --- a/drivers/iio/frequency/ad9910.c +++ b/drivers/iio/frequency/ad9910.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -193,6 +194,11 @@ #define AD9910_RAM_ENABLED(st) \ FIELD_GET(AD9910_CFR1_RAM_ENABLE_MSK, (st)->reg[AD9910_REG_CFR1].val32) =20 +#define AD9910_DEST_FREQUENCY 0 +#define AD9910_DEST_PHASE 1 +#define AD9910_DEST_AMPLITUDE 2 +#define AD9910_DEST_POLAR 3 + /* PLL constants */ #define AD9910_PLL_MIN_N 12 #define AD9910_PLL_MAX_N 127 @@ -324,6 +330,14 @@ enum { AD9910_CHAN_IDX_OSK, }; =20 +enum { + AD9910_SCAN_IDX_AMP, + AD9910_SCAN_IDX_PHASE, + AD9910_SCAN_IDX_FREQ, + AD9910_SCAN_IDX_POLAR_AMP, + AD9910_SCAN_IDX_POLAR_PHASE, +}; + enum { AD9910_POWERDOWN, AD9910_DWELL_EN, @@ -2078,6 +2092,171 @@ static int ad9910_setup(struct device *dev, struct = ad9910_state *st, return ad9910_io_update(st); } =20 +static inline const char *ad9910_frequency_source_get(struct iio_dev *indi= o_dev) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + bool ram_en, mode_en; + + guard(mutex)(&st->lock); + + /* RAM enabled and data destination is frequency */ + ram_en =3D AD9910_RAM_ENABLED(st); + if (ram_en && AD9910_DEST_FREQUENCY =3D=3D + FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK, + st->reg[AD9910_REG_CFR1].val32)) + return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; + + /* DRG enabled and data destination is frequency */ + mode_en =3D FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, + st->reg[AD9910_REG_CFR2].val32); + if (mode_en && AD9910_DEST_FREQUENCY =3D=3D + FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, + st->reg[AD9910_REG_CFR2].val32)) + return ad9910_channel_str[AD9910_CHAN_IDX_DRG_FREQ]; + + /* Parallel data port enabled and data destination is frequency */ + mode_en =3D FIELD_GET(AD9910_CFR2_PARALLEL_DATA_PORT_EN_MSK, + st->reg[AD9910_REG_CFR2].val32); + if (mode_en && indio_dev->active_scan_mask && + test_bit(AD9910_SCAN_IDX_FREQ, indio_dev->active_scan_mask)) + return ad9910_channel_str[AD9910_CHAN_IDX_PARALLEL_FREQ]; + + /* FTW: RAM enabled and data destination is phase, amplitude, or polar */ + if (ram_en) + return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; + + /* single tone profiles */ + return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile]; +} + +static int ad9910_frequency_source_show(struct seq_file *s, void *ignored) +{ + seq_printf(s, "%s\n", ad9910_frequency_source_get(s->private)); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ad9910_frequency_source); + +static inline const char *ad9910_phase_source_get(struct iio_dev *indio_de= v) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + bool ram_en, mode_en; + u32 destination; + + guard(mutex)(&st->lock); + + /* RAM enabled and data destination is phase or polar */ + ram_en =3D AD9910_RAM_ENABLED(st); + if (ram_en) { + destination =3D FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK, + st->reg[AD9910_REG_CFR1].val32); + if (destination =3D=3D AD9910_DEST_PHASE || + destination =3D=3D AD9910_DEST_POLAR) + return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; + } + + /* DRG enabled and data destination is phase */ + mode_en =3D FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, + st->reg[AD9910_REG_CFR2].val32); + if (mode_en && AD9910_DEST_PHASE =3D=3D + FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, + st->reg[AD9910_REG_CFR2].val32)) + return ad9910_channel_str[AD9910_CHAN_IDX_DRG_PHASE]; + + /* Parallel data port enabled and data destination is phase */ + mode_en =3D FIELD_GET(AD9910_CFR2_PARALLEL_DATA_PORT_EN_MSK, + st->reg[AD9910_REG_CFR2].val32); + if (mode_en && indio_dev->active_scan_mask) { + if (test_bit(AD9910_SCAN_IDX_PHASE, indio_dev->active_scan_mask)) + return ad9910_channel_str[AD9910_CHAN_IDX_PARALLEL_PHASE]; + if (test_bit(AD9910_SCAN_IDX_POLAR_PHASE, indio_dev->active_scan_mask)) + return ad9910_channel_str[AD9910_CHAN_IDX_PARALLEL_POLAR_PHASE]; + } + + /* POW: RAM enabled and data destination is frequency or amplitude */ + if (ram_en) + return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; + + /* single tone profiles */ + return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile]; +} + +static int ad9910_phase_source_show(struct seq_file *s, void *ignored) +{ + seq_printf(s, "%s\n", ad9910_phase_source_get(s->private)); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ad9910_phase_source); + +static inline const char *ad9910_amplitude_source_get(struct iio_dev *indi= o_dev) +{ + struct ad9910_state *st =3D iio_priv(indio_dev); + bool ram_en, mode_en; + u32 destination; + + guard(mutex)(&st->lock); + + /* OSK enabled */ + mode_en =3D FIELD_GET(AD9910_CFR1_OSK_ENABLE_MSK, + st->reg[AD9910_REG_CFR1].val32); + if (mode_en) + return ad9910_channel_str[AD9910_CHAN_IDX_OSK]; + + /* RAM enabled and data destination is amplitude or polar */ + ram_en =3D AD9910_RAM_ENABLED(st); + if (ram_en) { + destination =3D FIELD_GET(AD9910_CFR1_RAM_PLAYBACK_DEST_MSK, + st->reg[AD9910_REG_CFR1].val32); + if (destination =3D=3D AD9910_DEST_AMPLITUDE || + destination =3D=3D AD9910_DEST_POLAR) + return ad9910_channel_str[AD9910_CHAN_IDX_RAM]; + } + + /* DRG enabled and data destination is amplitude */ + mode_en =3D FIELD_GET(AD9910_CFR2_DRG_ENABLE_MSK, + st->reg[AD9910_REG_CFR2].val32); + if (mode_en && AD9910_DEST_AMPLITUDE =3D=3D + FIELD_GET(AD9910_CFR2_DRG_DEST_MSK, + st->reg[AD9910_REG_CFR2].val32)) + return ad9910_channel_str[AD9910_CHAN_IDX_DRG_AMP]; + + /* Parallel data port enabled and data destination is amplitude */ + mode_en =3D FIELD_GET(AD9910_CFR2_PARALLEL_DATA_PORT_EN_MSK, + st->reg[AD9910_REG_CFR2].val32); + if (mode_en && indio_dev->active_scan_mask) { + if (test_bit(AD9910_SCAN_IDX_AMP, indio_dev->active_scan_mask)) + return ad9910_channel_str[AD9910_CHAN_IDX_PARALLEL_AMP]; + if (test_bit(AD9910_SCAN_IDX_POLAR_AMP, indio_dev->active_scan_mask)) + return ad9910_channel_str[AD9910_CHAN_IDX_PARALLEL_POLAR_AMP]; + } + + /* only way to control amplitude at this point is through OSK */ + if (ram_en) + return ad9910_channel_str[AD9910_CHAN_IDX_OSK]; + + /* single tone profiles */ + return ad9910_channel_str[AD9910_CHAN_IDX_PROFILE_0 + st->profile]; +} + +static int ad9910_amplitude_source_show(struct seq_file *s, void *ignored) +{ + seq_printf(s, "%s\n", ad9910_amplitude_source_get(s->private)); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ad9910_amplitude_source); + +static inline void ad9910_debugfs_init(struct ad9910_state *st, + struct iio_dev *indio_dev) +{ + struct dentry *d =3D iio_get_debugfs_dentry(indio_dev); + + debugfs_create_file("frequency_source", 0400, d, indio_dev, + &ad9910_frequency_source_fops); + debugfs_create_file("phase_source", 0400, d, indio_dev, + &ad9910_phase_source_fops); + debugfs_create_file("amplitude_source", 0400, d, indio_dev, + &ad9910_amplitude_source_fops); +} + static int ad9910_probe(struct spi_device *spi) { static const char * const supplies[] =3D { @@ -2181,7 +2360,13 @@ static int ad9910_probe(struct spi_device *spi) return dev_err_probe(dev, ret, "failed to add ram upload unregister action\n"); =20 - return devm_iio_device_register(dev, indio_dev); + ret =3D devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + ad9910_debugfs_init(st, indio_dev); + + return 0; } =20 static const struct spi_device_id ad9910_id[] =3D { --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 039D14F7998; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735459; cv=none; b=SG5PPXL9NFrZ8d63VnOM20uwrFX81poWbHgbEO8R8L9gxTpdqtNbk2fpIVXAQG7/8Aa0NVgBvVPlyMxL4vm36p+eNjkh8zwKfMW0S35+wBXclXHeIs4CqcFXl2HYJR8FTxlZ6rNVhaQQaeAcehWAKGuG0DZx0JSt8vsKCKHgGJ4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735459; c=relaxed/simple; bh=5+f9zGuJaDqs3Qd5BGYpu7W0XnuOiVpM1LuzsifS1ag=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=L/u4XgJkxjjTacplqcMAtkHT14PJOBQwDL7C7Izx8U3F/okDRxi3oUzSGSdjOMgwvr2eNQ/gq7dXOIQ3zvlOjVymLdpSFwy7oT30sxztSnEoiDIOwdporFG6BkPJEwI7SZL0rq2L5VVEw6SjoJIWycSxJc3UllNoH/MLv8qO4pI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=EKun//7n; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="EKun//7n" Received: by smtp.kernel.org (Postfix) with ESMTPS id CCD86C4AF11; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735458; bh=5+f9zGuJaDqs3Qd5BGYpu7W0XnuOiVpM1LuzsifS1ag=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=EKun//7ny5WZhFLxu1Ern4r7/NaKX/gr/EfhP/aQn/7mznOkB1wbyYAmZZzRPTNYu Wcu8/PqicCu2K/gqLEpblh3IsR3/5KK0sjt5mG1N/rWHkQAzsDr/ob3Kc8MNB160Eg +CbQ4EEDD5lyKQ/NojCovp5UbiW6QbtXWIUiCGMvGuWzOwadPNq7ezXoqHFylIO4zM 1S7lWIoJCLmyAqYw2uB32VGqrZgkNFA2JQObe+1/BGUQopq6UZQ06Ek+5m3PjdxjQ5 WZPvNdxl5HqK531W/RGYg7tVrTj+eviZLWWEQQANr98/K4uESBqvSWzdE+FyKBrATg U/+VCo0As8hhw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id B99CFC4453E; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:25 +0100 Subject: [PATCH v9 16/17] iio: ABI: add docs for ad9910 sysfs and debugfs entries Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-16-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=4220; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=ODvLxJPODA5YQ+w5a0lwK7q83k4NXyONagfc42uDQZ8=; b=jN4Kxsc4z+pToJcmX5xQ0n1lmnChabpn46djbYi5EJiKfcildlt9aqqpBZy7T+enlRoreOJ21 cLEoD/zVVwgB8FbFOTA3hzLK68kVD4U+myYyiKIMDethOi4dKoDhn5e X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add custom ABI documentation files for the DDS AD9910 with sysfs and debugfs entries. The sysfs ones control parameters from the Digital Ramp Generator and OSK engine. The debugfs attributes are read-only and evalutate into source channel labels for each DDS core parameter. Signed-off-by: Rodrigo Alencar --- Documentation/ABI/testing/debugfs-iio-ad9910 | 23 ++++++++++++++++ .../ABI/testing/sysfs-bus-iio-frequency-ad9910 | 31 ++++++++++++++++++= ++++ MAINTAINERS | 2 ++ 3 files changed, 56 insertions(+) diff --git a/Documentation/ABI/testing/debugfs-iio-ad9910 b/Documentation/A= BI/testing/debugfs-iio-ad9910 new file mode 100644 index 000000000000..7083a20d5a40 --- /dev/null +++ b/Documentation/ABI/testing/debugfs-iio-ad9910 @@ -0,0 +1,23 @@ +What: /sys/kernel/debug/iio/iio:deviceX/frequency_source +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Read-only. Reports the channel label that is read from the + _[Y]_label attribute, which corresponds to the on-chip + signal generator that is currently driving the output frequency. + +What: /sys/kernel/debug/iio/iio:deviceX/phase_source +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Read-only. Reports the channel label that is read from the + _[Y]_label attribute, which corresponds to the on-chip + signal generator that is currently driving the output phase. + +What: /sys/kernel/debug/iio/iio:deviceX/amplitude_source +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Read-only. Reports the channel label that is read from the + _[Y]_label attribute, which corresponds to the on-chip + signal generator that is currently driving the output amplitude. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9910 b/Doc= umentation/ABI/testing/sysfs-bus-iio-frequency-ad9910 new file mode 100644 index 000000000000..4e25f538af4a --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9910 @@ -0,0 +1,31 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_dwell_en +What: /sys/bus/iio/devices/iio:deviceX/out_frequencyY_dwell_en +What: /sys/bus/iio/devices/iio:deviceX/out_phaseY_dwell_en +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + For a channel that produces parametric sweeps, this attribute controls + the sweep behavior at the configured limit. It enables dwell mode at a + sweep limit when set to 1, i.e., after the sweep is complete the output + value stays at the limit. Otherwise (setting this value to 0), the sweep + may stop or restart from the initial position, or even continue by + reversing the sweep direction. + +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_raw_roc +What: /sys/bus/iio/devices/iio:deviceX/out_frequencyY_raw_roc +What: /sys/bus/iio/devices/iio:deviceX/out_phaseY_raw_roc +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + For a channel that produces parametric sweeps, this attribute controls + the rate of change of the parameter in raw units per second (slope). + This value may be influenced by the channel sampling_frequency attribute + if available. Multiplying this value by the scale attribute of the + channel yields the rate of change in physical units per second. + +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_raw_roc_available +KernelVersion: 7.3 +Contact: linux-iio@vger.kernel.org +Description: + Lists the available roc values for the channel. Values are + space-separated in ascending order. diff --git a/MAINTAINERS b/MAINTAINERS index 83342b577aa6..182f7db0010a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1683,6 +1683,8 @@ M: Rodrigo Alencar L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers +F: Documentation/ABI/testing/debugfs-iio-ad9910 +F: Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9910 F: Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml F: drivers/iio/frequency/ad9910.c =20 --=20 2.43.0 From nobody Fri Jul 24 22:57:06 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (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 16186361940; Wed, 22 Jul 2026 15:50:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735459; cv=none; b=ADDVF0YqC2OfKWSJkHOTclg0DIbacIbdIVR2aVwOl/S02zSa2FGxtisqRaOeVV5YGq0IuDVWikip36jSgShNhqH63eVpSIowdAM8YVQaBFVVtqVf+mrpfhwExskVwyBNpPHwkJER0S+cxrnbhubwJgNz0wtQBkuHe5Ym6PY2M3c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784735459; c=relaxed/simple; bh=EORHad00TXc1emOKiAC1DaLpezn5+vnLBKWnTSkkqj4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=UIImrQ8OKO180CEuEYivpz2zw+SoUR5WUNBUt4zU0YnnMT/WON52n3zsa6V4mArm5Qwm9AgFmvD8N6JhSg6lsaoYn+2RaciG10mUC+NqRCqmbHeberxtgyxSdaQj7LG/y9kU6glgK3fbge6bKR1rH5J7bY2Z6KjepawzatNof6c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Op+M76sr; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Op+M76sr" Received: by smtp.kernel.org (Postfix) with ESMTPS id DF440C4AF0C; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784735459; bh=EORHad00TXc1emOKiAC1DaLpezn5+vnLBKWnTSkkqj4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=Op+M76sreYOIzDrxfG8xphzk6g5nrFIph+cjQmBSaIjxTnuKh/bRol84/5oVatO2H GlMj4uzooQeZ5vNy1mZE6eMlrgT/FrjevYsthhcdrVZRVLBovmI6Y057IFc4CYpChX gcnL5kDfxY9GjsDXVnrMCUNwpb/aP75bHDyNWQHU7QCYaIj8PiolaiNrZaoRxXoK8v gCap34o5hok1vi66GGY/XN6F0fu7nfUvY99KJd1rXRLyj8p3Ki+MbK8TSf/zqDmZvs eYEbuy8pFT3ORjoT9KVu4QfquE049hZy3yWkX3O0FFxZ62phSo7bGZBhuaCXfJwlhL fvPN7GB3EbDyw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id C99C8C44532; Wed, 22 Jul 2026 15:50:58 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Wed, 22 Jul 2026 16:50:26 +0100 Subject: [PATCH v9 17/17] docs: iio: add documentation for ad9910 driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260722-ad9910-iio-driver-v9-17-459d1df5ac56@analog.com> References: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> In-Reply-To: <20260722-ad9910-iio-driver-v9-0-459d1df5ac56@analog.com> To: linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-hardening@vger.kernel.org Cc: Lars-Peter Clausen , Michael Hennerich , Jonathan Cameron , David Lechner , Andy Shevchenko , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Philipp Zabel , Jonathan Corbet , Shuah Khan , Kees Cook , "Gustavo A. R. Silva" , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1784735456; l=26543; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=F6FUKEIzQkViObFGfKMrkh2+glYvete8gVuEleZRdr4=; b=g1yjbi7g7NKLbj7G4CD/47qvoBFzBrNXhDMrW2QsBZeD5xKrICNZ5kaIkx2gLtLTH9hm15cg1 2Pp1opVvcPgADSibkkc3gPqd1A7T7OwHdJRLtBH6hm2PE4Uqa1rgvTh X-Developer-Key: i=rodrigo.alencar@analog.com; a=ed25519; pk=ULeHbgU/OYh/PG/4anHDfLgldFItQHAhOktYRVLMFRo= X-Endpoint-Received: by B4 Relay for rodrigo.alencar@analog.com/default with auth_id=561 X-Original-From: Rodrigo Alencar Reply-To: rodrigo.alencar@analog.com From: Rodrigo Alencar Add documentation for the AD9910 DDS IIO driver, which describes channels, DDS modes, attributes and ABI usage examples. Signed-off-by: Rodrigo Alencar --- Documentation/iio/ad9910.rst | 792 +++++++++++++++++++++++++++++++++++++++= ++++ Documentation/iio/index.rst | 1 + MAINTAINERS | 1 + 3 files changed, 794 insertions(+) diff --git a/Documentation/iio/ad9910.rst b/Documentation/iio/ad9910.rst new file mode 100644 index 000000000000..aab569f561db --- /dev/null +++ b/Documentation/iio/ad9910.rst @@ -0,0 +1,792 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +AD9910 driver +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Direct Digital Synthesizer (DDS) driver for the Analog Devices Inc. AD9910. +The module name is ``ad9910``. + +* `AD9910 `_ + +The AD9910 is a 1 GSPS DDS with a 14-bit DAC, controlled over SPI. The dri= ver +exposes the device through a hierarchy of typed IIO output channels. The r= oot +``phy`` channel controls the system clock and full-scale output current. +Sub-channels provide independent control over single tone profiles, parall= el +port modulation, digital ramp generation (DRG), RAM playback and output sh= ift +keying (OSK). + + +Channel hierarchy +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The driver exposes the following IIO output channels, each identified by a +unique channel number and a human-readable label. The ``phy`` channel is t= he +root of the hierarchy. Changing its ``sampling_frequency`` reconfigures the +system clock (SYSCLK) which affects all other channels. + +.. flat-table:: + :header-rows: 1 + + * - Channel + - Label + - Parent + - Description + + * - ``out_altcurrent100`` + - ``phy`` + - + - Physical output: system clock and full-scale output current (:math:= `I_{FS}`). + See `Physical channel`_. + + * - ``out_altcurrent110`` ... ``out_altcurrent117`` + - ``profile0`` ... ``profile7`` + - ``phy`` + - Single tone control: frequency, phase, amplitude, enable. + See `Single Tone mode`_. + + * - ``out_altcurrent120`` + - ``parallel_amplitude`` + - ``phy`` + - Parallel port amplitude channel. + See `Parallel Port mode`_. + + * - ``out_phase120`` + - ``parallel_phase`` + - ``phy`` + - Parallel port phase channel. + + * - ``out_frequency120`` + - ``parallel_frequency`` + - ``phy`` + - Parallel port frequency channel: ``scale`` sets the FM gain + (power-of-2 multiplier), ``offset`` sets the base FTW. + + * - ``out_altcurrent121`` + - ``parallel_polar_amplitude`` + - ``phy`` + - Parallel polar amplitude channel: ``scale`` is the amplitude + resolution, ``offset`` is the amplitude bias (lower 6 bits of ASF). + + * - ``out_phase121`` + - ``parallel_polar_phase`` + - ``phy`` + - Parallel polar phase channel: ``scale`` is the phase resolution, + ``offset`` is the phase bias (lower 8 bits of POW). + + * - ``out_frequency130`` + - ``drg_frequency`` + - ``phy`` + - DRG frequency channel: ``en`` selects and enables the DRG with + frequency as the ramp target. + See `Digital ramp generator (DRG)`_. + + * - ``out_frequency131`` + - ``drg_rising_frequency`` + - ``drg_frequency`` + - DRG frequency rising-ramp parameters: limit code, dwell enable, + ramp clock, rate of change. + + * - ``out_frequency132`` + - ``drg_falling_frequency`` + - ``drg_frequency`` + - DRG frequency falling-ramp parameters: limit code, dwell enable, + ramp clock, rate of change. + + * - ``out_phase130`` + - ``drg_phase`` + - ``phy`` + - DRG phase channel: ``en`` selects and enables the DRG with phase + as the ramp target. + See `Digital ramp generator (DRG)`_. + + * - ``out_phase131`` + - ``drg_rising_phase`` + - ``drg_phase`` + - DRG phase rising-ramp parameters: limit code, dwell enable, ramp cl= ock, + rate of change. + + * - ``out_phase132`` + - ``drg_falling_phase`` + - ``drg_phase`` + - DRG phase falling-ramp parameters: limit code, dwell enable, ramp c= lock, + rate of change. + + * - ``out_altcurrent130`` + - ``drg_amplitude`` + - ``phy`` + - DRG amplitude channel: ``en`` selects and enables the DRG with + amplitude as the ramp target. + See `Digital ramp generator (DRG)`_. + + * - ``out_altcurrent131`` + - ``drg_rising_amplitude`` + - ``drg_amplitude`` + - DRG amplitude rising-ramp parameters: limit code, dwell enable, + ramp clock, rate of change. + + * - ``out_altcurrent132`` + - ``drg_falling_amplitude`` + - ``drg_amplitude`` + - DRG amplitude falling-ramp parameters: limit code, dwell enable, + ramp clock, rate of change. + + * - ``out_altcurrent140`` + - ``ram`` + - ``phy`` + - RAM playback: enable, frequency, phase and sampling frequency for + the active profile. See `RAM mode`_. + + * - ``out_altcurrent150`` + - ``osk`` + - ``phy`` + - Output shift keying (OSK): enable, amplitude code, ramp rate, + rate of change. See `Output Shift Keying (OSK)`_. + +DDS modes +=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The AD9910 supports multiple modes of operation that can be configured +independently or in combination. Each DDS core parameter (frequency, phase +and amplitude) can come from different sources, but only one is active at a +time. This activation depends on a priority list, which is based on the en= able +and destination configurations for such modes. The following tables are +extracted from the AD9910 datasheet and summarize the control parameters f= or +each mode and their priority when multiple sources are enabled simultaneou= sly: + +.. flat-table:: DDS Frequency Control + :header-rows: 1 + + * - Priority + - Data Source + - Conditions + + * - Highest Priority + - RAM + - RAM enabled and data destination is frequency + + * - + - DRG + - DRG enabled and data destination is frequency + + * - + - Parallel data and Frequency Tuning Word, FTW (frequency_offset) + - Parallel data port enabled and data destination is frequency + + * - + - FTW register (frequency) + - RAM enabled and data destination is not frequency + + * - Lowest Priority + - FTW (frequency) in single tone channel for the active profile + - All other cases + +.. flat-table:: DDS Phase Control + :header-rows: 1 + + * - Priority + - Data Source + - Conditions + + * - Highest Priority + - RAM + - RAM enabled and data destination is phase or polar + + * - + - DRG + - DRG enabled and data destination is phase + + * - + - Parallel data port + - Parallel data port enabled and data destination is phase + + * - + - Parallel data port and Phase Offset Word, POW register LSBs (phase_= offset) + - Parallel data port enabled and data destination is polar + + * - + - POW register (phase) + - RAM enabled and destination is not phase nor polar + + * - Lowest Priority + - POW (phase) in single tone channel for the active profile + - All other cases + +.. flat-table:: DDS Amplitude Control + :header-rows: 1 + + * - Priority + - Data Source + - Conditions + + * - Highest Priority + - Amplitude Scale Factor, ASF register and OSK generator + - OSK enabled + + * - + - RAM + - RAM enabled and data destination is amplitude or polar + + * - + - DRG + - DRG enabled and data destination is amplitude + + * - + - Parallel data port + - Parallel data port enabled and data destination is amplitude + + * - + - Parallel data port and ASF register LSBs (scale_offset) + - Parallel data port enabled and data destination is polar + + * - Lowest Priority + - ASF (scale) in single tone channel for the active profile + - (Amplitude scale is already enabled by default) + +While debugging or testing, the debug attributes ``frequency_source``, +``phase_source`` and ``amplitude_source`` can be used to read the label of +the channel that is actively controlling the correspondent DDS parameter, +which reflects the priority list described above. + +Single Tone mode +---------------- + +Single tone is the baseline operating mode. The ``profileY`` channels +provide enable, frequency, phase and amplitude control: + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``en`` + - boolean (0 or 1) + - Enable/disable profile Y. Only one profile can be active at a + time. When enabling a profile it disables the current active profil= e. + Disabling an active profile brings the device to a powered down sta= te. + + * - ``frequency`` + - Hz + - Output frequency. Range :math:`[0, f_{SYSCLK} / 2)`. Stored in the + profile's frequency tuning word (FTW). + + * - ``phase`` + - rad + - Phase offset. Range :math:`[0, 2\pi)`. Stored in the profile's phase + offset word (POW). + + * - ``raw`` + - integer + - Amplitude scale factor code. Range :math:`[0, 16383]`. Stored in the + profile's amplitude scale factor (ASF) register. The physical output + amplitude is ``raw * scale`` where ``scale`` is read from the ``phy= `` + channel. + +Profile switching is allowed while RAM mode is enabled. In that case single +tone parameters are stored in a shadow register and are not written to +hardware until RAM mode is disabled. + +Usage examples +^^^^^^^^^^^^^^ + +Configure a 100 MHz tone in profile 2 and set it as the active profile: + +.. code-block:: bash + + echo 100000000 > /sys/bus/iio/devices/iio\:device0/out_altcurrent112_fre= quency + echo 0 > /sys/bus/iio/devices/iio\:device0/out_altcurrent112_phase + echo 16383 > /sys/bus/iio/devices/iio\:device0/out_altcurrent112_raw + + # Activate profile 2 + echo 1 > /sys/bus/iio/devices/iio\:device0/out_altcurrent112_en + +Read back the current single tone frequency: + +.. code-block:: bash + + cat /sys/bus/iio/devices/iio\:device0/out_altcurrent112_frequency + +Parallel Port mode +------------------ + +The parallel port allows real-time modulation of DDS parameters through a +16-bit external data bus. The driver exposes separate typed channels for e= ach +modulation target. + +Non-polar modulation +^^^^^^^^^^^^^^^^^^^^ + +In non-polar mode each DDS parameter is controlled by an independent 16-bi= t bus +input. The parallel port channels expose the resolution and base offset of= their +respective bus inputs: + +.. flat-table:: + :header-rows: 1 + + * - Channel + - Attribute + - Unit + - Description + + * - ``out_phase120`` + - ``scale`` + - rad + - Phase resolution per parallel bus LSB. Fixed at :math:`\pi / 2^{15}` + rad/LSB (full 16-bit POW resolution). + + * - ``out_frequency120`` + - ``scale`` + - Hz + - Outputs the frequency scale evaluated as + :math:`f_{SYSCLK} \cdot FM / 2^{32}`. Assuming that :math:`f_{SYSCL= K}` is + fixed, it is used to configure the modulation gain :math:`FM`, whic= h is a + power-of-2 multiplier in range :math:`[1, 32768]`. Writing to this + attribute rounds up to the nearest :math:`FM` power of 2. + + * - ``out_frequency120`` + - ``offset`` + - Hz + - Outputs the frequency offset in raw units evaluated as :math:`FTW /= FM`. + Assuming that :math:`FM` is fixed, it is used to configure the FTW + register, which is a 32-bit unsigned integer. + +Polar modulation +^^^^^^^^^^^^^^^^ + +In polar mode a single 16-bit bus word carries both amplitude (high byte) +and phase (low byte). The ``parallel_polar_amplitude`` and +``parallel_polar_phase`` channels configure the bias applied to the low-or= der +bits of the ASF and POW registers respectively: + +.. flat-table:: + :header-rows: 1 + + * - Channel + - Attribute + - Unit + - Description + + * - ``out_altcurrent121`` + - ``scale`` + - mA/LSB + - Full-scale amplitude resolution (read-only, fixed at :math:`I_{FS} = / 2^{8}`). + + * - ``out_altcurrent121`` + - ``offset`` + - fractional (raw units) + - Amplitude bias. Lower 6 bits of ASF register. Range :math:`[0, 1)`. + + * - ``out_phase121`` + - ``scale`` + - rad/LSB + - Phase resolution (read-only, fixed at :math:`\pi / 2^{7}`). + + * - ``out_phase121`` + - ``offset`` + - fractional (raw units) + - Phase bias. Lower 8 bits of POW register. Range :math:`[0, 1)`. + +Usage examples +^^^^^^^^^^^^^^ + +Set parallel port frequency modulation with a modulation gain of 16 and a = 50 MHz +offset: + +.. code-block:: bash + + # f_SYSCLK =3D 1 GHz, FM =3D 16 + # frequency scale =3D f_SYSCLK * FM / 2^32 =3D 3.725290298 + echo 3.725290298 > /sys/bus/iio/devices/iio\:device0/out_frequency120_sc= ale + # frequency offset =3D 50 MHz / scale =3D 50e6 / 13421772.8 + echo 13421772.8 > /sys/bus/iio/devices/iio\:device0/out_frequency120_off= set + +One should choose a frequency scale that allows all the desired frequencies +to be represented in the 16-bit bus range, i.e., +:math:`scale =3D (f_{max} - f_{min}) / 2^{16}`. + + +Digital ramp generator (DRG) +---------------------------- + +The DRG produces linear frequency, phase or amplitude sweeps using dedicat= ed +hardware. The active ramp target (destination) is selected by enabling the +corresponding typed channel at channel number 130: + +- ``out_frequency130`` (label ``drg_frequency``) =E2=80=94 ramp targets fr= equency +- ``out_phase130`` (label ``drg_phase``) =E2=80=94 ramp targets phase +- ``out_altcurrent130`` (label ``drg_amplitude``) =E2=80=94 ramp targets a= mplitude + +Writing ``en=3D1`` to one of these channels enables the DRG and switches i= ts +destination. Writing ``en=3D0`` disables the DRG if the channel is the cur= rent +active destination; writing to an already-inactive destination is a no-op. + +Each destination channel also exposes a read-only ``scale`` attribute +reporting the physical quantity per ramp register LSB, which allows conver= ting +raw limit codes to physical values. + +The six ramp channels: + +- ``out_frequency131`` (label ``drg_rising_frequency``) +- ``out_frequency132`` (label ``drg_falling_frequency``) +- ``out_phase131`` (label ``drg_rising_phase``) +- ``out_phase132`` (label ``drg_falling_phase``) +- ``out_altcurrent131`` (label ``drg_rising_amplitude``) +- ``out_altcurrent132`` (label ``drg_falling_amplitude``) + +configure ascending and descending ramp parameters. + +Destination channel attributes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``en`` + - boolean + - Enable the DRG with this channel as the active destination. Only one + destination can be active at a time. + + * - ``scale`` + - Hz/LSB, rad/LSB or mA/LSB + - Read-only. Physical quantity per raw units. Multiply a ramp + rising/falling channel ``raw`` value by this scale to get the physi= cal + ramp target. + +Ramp channel attributes +^^^^^^^^^^^^^^^^^^^^^^^ + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``dwell_en`` + - boolean + - Enable dwell at the ramp limit. When disabled, the ramp + auto-transitions at this limit without waiting for the DRCTL pin. + Disabling both creates a bidirectional continuous ramp (triangular + pattern). Other combinations create single-shot ramps at the DRCTL + pin transition. + + * - ``raw`` + - integer (64-bit) + - Ramp limit expressed as a raw DRG register code in + :math:`[0, 2^{32}-1]`. The physical value is ``raw * scale`` where + ``scale`` is read from the active destination channel. + + * - ``sampling_frequency`` + - Hz + - Ramp clock rate. Controlled by an integer divider; the written value + is adjusted to the nearest supported rate. + + * - ``raw_roc`` + - /s + - Rate of change. Number of register codes advanced per second, compu= ted + from the hardware step size and the current ramp clock. Writing + requires ``sampling_frequency`` to be configured first. + +Usage examples +^^^^^^^^^^^^^^ + +Configure a frequency sweep from 40 MHz to 60 MHz with a rate of change of +25 GHz/s: + +.. code-block:: bash + + # Disable dwell on both limits for a bidirectional continuous ramp + echo 0 > /sys/bus/iio/devices/iio\:device0/out_frequency131_dwell_en + echo 0 > /sys/bus/iio/devices/iio\:device0/out_frequency132_dwell_en + + # Set ramp rate at 250 MHz + echo 250000000 > /sys/bus/iio/devices/iio\:device0/out_frequency131_samp= ling_frequency + echo 250000000 > /sys/bus/iio/devices/iio\:device0/out_frequency132_samp= ling_frequency + + # read the frequency scale to convert physical values to raw units + cat /sys/bus/iio/devices/iio\:device0/out_frequency130_scale + 0.232830643650 + + # 40 MHz / 0.232830643650 =3D 171798692 + echo 171798692 > /sys/bus/iio/devices/iio\:device0/out_frequency131_raw + # 60 MHz / 0.232830643650 =3D 257698038 + echo 257698038 > /sys/bus/iio/devices/iio\:device0/out_frequency132_raw + + # 25 GHz/s / 0.232830643650 =3D 107374182402 + echo 107374182402 > /sys/bus/iio/devices/iio\:device0/out_frequency131_r= aw_roc + echo 107374182402 > /sys/bus/iio/devices/iio\:device0/out_frequency132_r= aw_roc + + # Enable the DRG with frequency as the destination + echo 1 > /sys/bus/iio/devices/iio\:device0/out_frequency130_en + +RAM mode +-------- + +The AD9910 contains a 1024 x 32-bit RAM that can be loaded with waveform d= ata +and played back to modulate frequency, phase, amplitude, or polar (phase + +amplitude) parameters. + +RAM control channel attributes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``en`` + - boolean + - Enable/disable RAM playback. Toggling swaps profile registers betwe= en + single tone and RAM configurations across all 8 profiles. + + * - ``frequency`` + - Hz + - Frequency tuning word used as the single tone frequency when + RAM destination is not ``frequency``. Range: :math:`[0, f_{SYSCLK} = / 2)`. + + * - ``phase`` + - rad + - Phase offset word used as the single tone phase when RAM destination + is not ``phase``. Range: :math:`[0, 2\pi)`. + + * - ``sampling_frequency`` + - Hz + - RAM playback step rate of the active profile, which controls how fa= st + the address counter advances. Controlled by an integer divider; the + written value is adjusted to the nearest supported rate. + +Loading RAM data +^^^^^^^^^^^^^^^^ + +RAM data is loaded through the firmware upload framework. The driver regis= ters +a firmware upload sysfs entry named ``iio_deviceX:ram``. The firmware data +follows a binary format (version 1) with an 80-byte header followed by data +words. All fields are big-endian. + +.. flat-table:: RAM firmware header (80 bytes) + :header-rows: 1 + + * - Offset + - Size + - Field + - Description + + * - 0 + - 4 + - ``magic`` + - Magic number: ``0x00AD9910`` + + * - 4 + - 2 + - ``version`` + - Format version: ``0x0001`` + + * - 6 + - 2 + - ``wcount`` + - Number of 32-bit RAM data words (0--1024) + + * - 8 + - 4 + - ``crc`` + - CRC32 checksum over ``cfr1``, ``profiles`` and ``words`` + + * - 12 + - 4 + - ``cfr1`` + - CFR1 register value. Only RAM-relevant bits are used: + bits [30:29] set data destination (00: frequency, 01: phase, + 10: amplitude, 11: polar); bits [20:17] set internal profile + control (see datasheet Table 14) + + * - 16 + - 64 + - ``profiles[0..7]`` + - 8 sets of 8-byte RAM profile configurations (see below) + + * - 80 + - 4 x wcount + - ``words[]`` + - RAM data words in reverse order + +Each 8-byte profile entry contains: + +.. flat-table:: RAM profile entry (8 bytes) + :header-rows: 1 + + * - Bits + - Field + - Description + + * - [55:40] + - Address step rate + - Controls playback speed for this profile + + * - [39:30] + - End address + - Last RAM address for this profile + + * - [23:14] + - Start address + - First RAM address for this profile + + * - [5] + - No-dwell high + - No-dwell at high limit (ramp-up mode) + + * - [3] + - Zero-crossing + - Zero-crossing enable (direct-switch mode) + + * - [2:0] + - Operating mode + - 000: direct switch, 001: ramp-up, 010: bidirectional, + 011: bidirectional continuous, 100: ramp-up continuous + +Usage examples +^^^^^^^^^^^^^^ + +Configure RAM mode with firmware data and enable it: + +.. code-block:: bash + + # Load RAM data via firmware upload + echo 1 > /sys/class/firmware/iio\:device0\:ram/loading + cat ad9910-ram.bin > /sys/class/firmware/iio\:device0\:ram/data + echo 0 > /sys/class/firmware/iio\:device0\:ram/loading + + # Enable RAM mode + echo 1 > /sys/bus/iio/devices/iio\:device0/out_altcurrent140_en + +Output Shift Keying (OSK) +------------------------- + +OSK controls the output amplitude envelope, allowing the output to be ramp= ed +on/off rather than switched abruptly. + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``en`` + - boolean (0 or 1) + - Enable/disable OSK. + + * - ``raw`` + - integer + - Target amplitude code. 14-bit ASF field. Range: :math:`[0, 16383]`. + The physical output amplitude is ``raw * scale`` where ``scale`` is= read + from the ``phy`` channel. + + * - ``raw_roc`` + - /s + - Amplitude ramp rate. Writing a non-zero value enables automatic OSK + and selects the closest hardware step size. Writing ``0`` disables + automatic ramping (manual control via ``raw``). Writing the maximum + available value enables pin-controlled immediate transition. + + * - ``raw_roc_available`` + - /s + - Lists the available ``raw_roc`` values based on the current + ``sampling_frequency``. The first value is always ``0`` (disabled) = and + the last value corresponds to pin-controlled immediate mode. + + * - ``sampling_frequency`` + - Hz + - OSK ramp clock. Controlled by an integer divider; the written value + is adjusted to the nearest supported rate. + +Usage examples +^^^^^^^^^^^^^^ + +Enable OSK with automatic ramping: + +.. code-block:: bash + + # Set ramp rate 1MHz + echo 1000000 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_sampl= ing_frequency + + # Check available rate of change values + cat /sys/bus/iio/devices/iio\:device0/out_altcurrent150_raw_roc_available + 0 1000000 2000000 4000000 8000000 16383000000 + + # Enable automatic OSK with a rate of change of 8000000 raw units/s + echo 8000000 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_raw_r= oc + + # Enable OSK + echo 1 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_en + +Enable pin-controlled immediate OSK: + +.. code-block:: bash + + # Enable OSK in manual mode (no ramp) + echo 0 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_raw_roc + echo 1 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_en + + # Set target amplitude to full scale + echo 16383 > /sys/bus/iio/devices/iio\:device0/out_altcurrent150_raw + +Physical channel +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The ``phy`` channel provides device-level control: + +.. flat-table:: + :header-rows: 1 + + * - Attribute + - Unit + - Description + + * - ``sampling_frequency`` + - Hz + - System clock (SYSCLK) frequency. When the internal PLL is enabled + (via the ``adi,pll-enable`` devicetree property), configures the PLL + multiplier (Range: :math:`[420, 1000]` MHz). Without PLL, the refer= ence + clock can only be divided by 2. + + * - ``scale`` + - mA/LSB + - Full-scale DAC output current per amplitude code LSB, which is eval= uated + as :math:`I_{FS} / 2^{14}`. Shared across all ``altcurrent`` channe= ls. + Setting this attribute reconfigures the auxiliary DAC full-scale co= de and + updates the effective amplitude resolution for single tone profiles, + DRG amplitude ramps and OSK. + + * - ``powerdown`` + - boolean (0 or 1) + - Software power-down. Writing 1 powers down the digital core, DAC, + reference clock input and auxiliary DAC simultaneously. + +Usage examples +-------------- + +Set the system clock to 1 GHz: + +.. code-block:: bash + + echo 1000000000 > /sys/bus/iio/devices/iio\:device0/out_altcurrent100_sa= mpling_frequency + +Read current system clock frequency: + +.. code-block:: bash + + cat /sys/bus/iio/devices/iio\:device0/out_altcurrent100_sampling_frequen= cy + +Power down the device: + +.. code-block:: bash + + echo 1 > /sys/bus/iio/devices/iio\:device0/out_altcurrent100_powerdown diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index b02b879b053a..4c30ef033685 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -30,6 +30,7 @@ Industrial I/O Kernel Drivers ad7606 ad7625 ad7944 + ad9910 ade9000 adf41513 adis16475 diff --git a/MAINTAINERS b/MAINTAINERS index 182f7db0010a..7743d11de6e7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1686,6 +1686,7 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/ABI/testing/debugfs-iio-ad9910 F: Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9910 F: Documentation/devicetree/bindings/iio/frequency/adi,ad9910.yaml +F: Documentation/iio/ad9910.rst F: drivers/iio/frequency/ad9910.c =20 ANALOG DEVICES INC MAX22007 DRIVER --=20 2.43.0