From nobody Sat Jul 25 19:29:10 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 B4BC837DAAE; Tue, 14 Jul 2026 11:02:53 +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=1784026973; cv=none; b=K+KiRH69+1KlblA0tt2vV7j6mHAY5VNSk7/hcxxMUrzgKzPDVsZwRPUnogfRzR26msrDszfzk65qUuvj4sxwC4A4DdkJfdyYhTz+U6c9w33bTfjdtVfdW0rSMBsn+GZWh7JqaN8bNsJwVcxG+hNetPZTbvSOXITSmaF9khkAdpw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026973; c=relaxed/simple; bh=UtKYoW5FDSxLKqqbjmW8abMzfHP00hnJo7tRGqvTRQI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=GF/+elbYqpwpWMm2YUCP9aBAsdwmXwTdSDJ33CqMLiwVYaAE8jPL4Ts+3MFOqVbP/NOt08FJhGvAFNUwuuwUMI9VHXp3YXYm57TKc00Y/TR/MXAD0lfDau4cEAr5rM94rQzUEKkTuXmu9CGJsseK0EZuqlc2i2kar/KDYzoREbo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=TuRNftWH; 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="TuRNftWH" Received: by smtp.kernel.org (Postfix) with ESMTPS id A4FF0C2BCB9; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026972; bh=UtKYoW5FDSxLKqqbjmW8abMzfHP00hnJo7tRGqvTRQI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=TuRNftWHE2o0JZ2c+05yPnk7NsE8A4AOEjFOg8Ds/6PdHqXu7/P2W3c6MlkCp7Yx0 /c+qxc1tZbRLpUcXaAJ1DLRpTWTHIpdhAz4ZPFZCjG6BtjBWZAMzaZSP1oCcQEiV53 bmgA5vhNFZoUgSshqFWmEqqoEZ/w4xODsEtOLAZxqXMA6O/eEf8l7fEVVswAA0HB5v JkmOCCdBAHGsnm0EwxsJWaP5w2V9nbkJ/14vXaljAeUpN4uSkJxthiwvgB9ZVLS9Cy yYcJ8050UvAPxHW28X6g1V0gGUY4P74TC4+7xirY3jxFZSl3dTb4GfETYnGQSiMa/X T4AqWxzXl2B7w== 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 804A2C44501; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:41 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-1-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=3268; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=ZMU9cnovPYUzplaaXs4eCd0C1BPEa1nJaR4itGOwIM8=; b=gJbb8BTSXDrN4L0kp6TPjco2CLWdGSBDoFVnknic2nfr0sN6my7Hifp9cl8VM+cCXkG7bELew ubpkoOCSVDeDfVtBO6s3EU/xZXxm3ZbKltzPBIEZFPLVraLMxz4fXFN 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 | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/te= sting/sysfs-bus-iio index 5cc2e82c4997..90047189c3eb 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -478,6 +478,8 @@ What: /sys/bus/iio/devices/iio:deviceX/in_currentY_sca= le 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/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 +801,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 +813,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 +840,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 @@ -1736,6 +1743,8 @@ 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 +What: /sys/bus/iio/devices/iio:deviceX/out_altcurrentY_raw KernelVersion: 3.17 Contact: linux-iio@vger.kernel.org Description: @@ -2126,7 +2135,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 Sat Jul 25 19:29:10 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 AD81F37A856; Tue, 14 Jul 2026 11:02:53 +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=1784026973; cv=none; b=OjWDi1IkzH+WT3dh2hFK/ma2EJtas/LE1wUhLbNiuMJH9Bf6LltDC9aQbw78fHIF8/V+CdGWeHXwW2r7rFWAQs1tIjy8bc22PMgLXS19D3ThBVinZl4dw4LroPlw75VStClcFxfYgP+LGFPzwT/R7E4p01LP5dYaUWa4KEh7f+U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026973; c=relaxed/simple; bh=dR1AV8RzdBbRxhwpqT/7HpEbkG0q6lG9Y4EeHP7f4R0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=CYtCCypkTbB/M9ujtE9e11WxNkvf4knrLZ44mruGdve3b8PoTTCnZdolwj8uuFhivsF9Z5iIhdNY9aiQIzk615IQl8ef7Ru2nN+4MW+GFHGNpUcuMyCB3+7GWCtw8ETxxqFq9lgJMTvvFxfsHJvCqLddGxrMkIa3BakVroB+9cY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=NmrKhnRw; 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="NmrKhnRw" Received: by smtp.kernel.org (Postfix) with ESMTPS id B7D5AC2BCC9; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026972; bh=dR1AV8RzdBbRxhwpqT/7HpEbkG0q6lG9Y4EeHP7f4R0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=NmrKhnRwMIjQLpd9VXFKEQ+5bfsmJUV+HLm8g0n+r4vUIIBhvMftyADm4Ym9yZHaO JcFnj3VE7jy3j4Uq4NNYSgE3DpqzzsErR1AwWOBpZlLh+8CQdZpPWmO+9qW36m6w3V Ahs8rDFWQOvEDPElfkMfiJBAUpS5I0Bqd8hn/teDcs42fI1nPzCahCQ2t1dXei4eXz CUiTeCm6qT1oKEbDjTbj0FiOG5QyypQHoWKr6Nse98iGsHgNh9hP96yKCcUJTcXqoX gcY7ouCwATlxzBWcyjvRK0HuIQFAt+aBeNvOUk8mXVvbNO8oTpSUDEN7/68IS36dEA FXjcUSOQQwJYQ== 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 9262BC44507; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:42 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-2-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=2654; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=WbFwCkkJVUsU+INjO/fRC/McrYPJtkf/NCh+2X6x1A8=; b=An2muswWCP9b/2a57tZTiLHelYlOSP1bFOiZEjf9M9dc7447EOuKkdWUlcGI24k2VL6jkZb0x ctYKpqLf7BuAag712N2BWDBmHwKp0fSd50UMRCe3APd/DT2c4iTed7t 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 | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/te= sting/sysfs-bus-iio index 90047189c3eb..7ef0db8dad40 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -441,6 +441,10 @@ 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/out_phaseY_offset +What: /sys/bus/iio/devices/iio:deviceX/out_phase_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 +478,10 @@ 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/out_phaseY_scale +What: /sys/bus/iio/devices/iio:deviceX/out_phase_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 @@ -2147,7 +2155,16 @@ 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 +What: /sys/bus/iio/devices/iio:deviceX/out_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/in_phaseY_raw +What: /sys/bus/iio/devices/iio:deviceX/out_phaseY_raw KernelVersion: 4.18 Contact: linux-iio@vger.kernel.org Description: --=20 2.43.0 From nobody Sat Jul 25 19:29:10 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 BE75B380FE7; Tue, 14 Jul 2026 11:02:53 +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=1784026973; cv=none; b=TZsE7HnDUUbEshU5ORLOCE1nGt0jNbF7hFZ3lBNvP9DOcj5Tf96cntmCNFN85wicpXM8fWJmh0gs7G6DF7UN7Dm2mAV71lrfzyjVFXmMCFU5xpMjfW8jypTcpZ4NVpLRdfaWCUqtddeXy13vlSX+5gSaj2vN2CQtu3cMkzSX0/E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026973; c=relaxed/simple; bh=5X2Ye7V+h+u4pOI03KSzCN+e5fKvlRFKmRzAEhOXp7Y=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Y+uS9O9src0kxI9a051VnPuUX9oTA6j/sNXkIPO11nAZjE71TziWWDdUQf+O0I2F0IXh26k1BYnug3Brpe8K3quqv7bL999zj1iSqMcVPtYWV8y+PYFLY0tQQfPhmECTHaVJcD1NkI5mE2R8mmwXalxGyH1E7Mz6slC957blHLA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=azp+4xt/; 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="azp+4xt/" Received: by smtp.kernel.org (Postfix) with ESMTPS id C34ECC2BCF6; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026972; bh=5X2Ye7V+h+u4pOI03KSzCN+e5fKvlRFKmRzAEhOXp7Y=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=azp+4xt/ov0JBSofcVEuV6j5IZvY1aWDtangB0x59hVf5kAjfv0aQBz1h8rtReweY dbKcWum4z4vSRuPnfUb35j3sEBnqwcmLNg+jITD6VSsSa/AhoDQ40IQ3nQS1u3mFIA lpI3BrqbyI2GOlzgGDceCDT/H1B8aqN6M9b/ifHUx21mB70zwA/ZjDfd11coBsFAFD M0RU0Q7e12fSBZsWO1molFKBTchwoT2dFyV25okZXTno6Z1PBQw8qq8hfHFGcod4r3 y2Ss1ifal0ouR1aui86y9r+2YxbRptRBQC2eVyQLWPPoLAAfWYMSzntEiGz5+zX0f7 u5OTq2PABax+Q== 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 A304CC44509; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:43 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-3-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=2431; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=5nFgyUfllJljSZOQwdty2yaZdYRSoVEDNbvhxaPGuQ4=; b=+sZGmCIyOZ0XlZx02qZz/e0hX2dfkibia59XxSpalI66NtGBbE8Kqp09EbtgURJnYp7HljDjb sF1J7XnEFkxDTAIRHrl8w04kp4anSARdWCsoE71DSQsJHJWZd+5yXlW 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 7ef0db8dad40..96a7991c0723 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -2155,6 +2155,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 What: /sys/bus/iio/devices/iio:deviceX/out_frequencyY_raw KernelVersion: 7.3 --=20 2.43.0 From nobody Sat Jul 25 19:29:10 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 BA27437FF75; Tue, 14 Jul 2026 11:02:53 +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=1784026973; cv=none; b=rVAbOpqGA3e22nkbDR8eOkZF3vKI1KtNsiwkGKMDnHOKxk6LOerrSld5XHSvTMIYpRhr3Wv0hPsxrPgOKaHWcX3iawZibZi1l+vs9pKjiVfQYcVhINzdMO0zH18DbLVfmmk8tm9kXOdMRmtRyAYwfQKQiRSy/TPsw/3a/KU3WtQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026973; c=relaxed/simple; bh=LkGR2S/oFgI/+IPKjfMY0lgtsj7hpBpubbqxfDZIpLs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=hwh3mZLVSRHCxgRve2RxWVG4xj6/AoW/WCUVrdBnXME1uBM0TtZyaMLBoOhh0VKhPyU4GQHDn8zDfsUoQcHHRXFb1xet/xrCeNsEUIQxKBR1H9gZ0iuYGBWPoPRCY4Xhsl58Phh3X2Ww3XeEH8rnXd4QyLG5JUtQnvyyT3ltuIY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=C/6UYlSw; 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="C/6UYlSw" Received: by smtp.kernel.org (Postfix) with ESMTPS id CCE94C2BCFF; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026972; bh=LkGR2S/oFgI/+IPKjfMY0lgtsj7hpBpubbqxfDZIpLs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=C/6UYlSwClXIs81nRMWAJVW1BKpx38TOquqYk5CP7/JZtnRnjlMqS4CBGYyLI/8O/ uUOsI7JIMq/y1uzDbNDYvYbexvJxn9QSAMiFbiqExUuo06aA7Moh0Psdqlocjj9bB5 8grsBwU7dvHgpdgzXbMIGY7SxR1PuPoEu98/dKMHVcTFADH4iPzdiim1v2ZtKCPKCs YNsAWKCeqzKmtCOpU0Gg7y+9AicxYcA4JFUnUnw1wSTJJuBCA5gynOqGtkz8aA4Rr2 fAopuMm+x/z6f/fBNIZoMnZx38qbWS8VRpWhO7wefw8udzT9hnu5VYIpTRwSKoixkH V3X3a1rrJoObQ== 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 B378FC44508; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:44 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-4-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=1881; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=1IPBIAnpoq77uH/f9ncnZ8sog7Fcx5CLyDe5QVK2WqI=; b=Hh0U0aJccVFe4NQUgS0JeTmEw8mq9gt3x8+qE/+Jy3iK6KQVx1F6Yl5ukbFN9z+TpRABLwDGz HGigzqvoW3eDGXKTVl33FgsQFtOccEyRCmvPCYsFZx0JRxmOJG3JBWZ 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 Sat Jul 25 19:29:10 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 10ECE3815E8; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=slWm8hO+uqrX/AjpBsBXLPvFUjS5WZLnIoj5H2wlvtWvI1i5yJSfA5fGek6KZFTUC/Qn2ygdJFz0slZ3mMGb17536iIQbxrBwT947ocu9LwdfXzW8IQWMEtbx2iN8M3LPsyMoP3iV5fJhFS2vl+2NSYGswGNe3hqAHtgBM3yxDk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=8MhduG14jsXaL5k4h11ocWyrck/47G7XvTTKiN8VDTM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=IMbZcid8uSj0I3TCS3iLWbeotw0J3r7SXqyF6ASwRnkcRILkRnx245SOwzOyOtKZ/7a/WxemXhFZdgs1d0hZs8bHbuo/I2C/IaihXZrAvnbtmHvsKCNWWskdZGNiO8eGrfbBRvOEtSxQcg/BT49XnGIMPP2s/+q08TCW3RZjyDk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Jog3ZZRb; 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="Jog3ZZRb" Received: by smtp.kernel.org (Postfix) with ESMTPS id E522FC2BD05; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=8MhduG14jsXaL5k4h11ocWyrck/47G7XvTTKiN8VDTM=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=Jog3ZZRbZuUfdxFSbv8h1DGU2iGwdwAhFpaLXK2/XCcwMnJWG2VtduhOMKhlJQlg9 pV6dOy5rYQNJlcSb/Jn57B4IqHjZ9PI3ikH5TJk6xh69nR+DFkZsf4Th2CEhEUfeI4 ZX7eJBPE5tpIugb1zHiLzpU78masGDBksdrkehcIqQwjjp6bMXAjJGl5Wy8koYycI2 dHSA5VFpxLC3PDnq8uezsGx8X2cIcu9XAdvg8pSm6y5nU7N9DAzNPvkhubaP0rIXRO kJecauMN3LDBvQRWeuEAyzuvbJQlblkc+opQb2Ru3hYTuCIpar8NAn7/NaECBI+JGq zaeOF2UCcQClg== 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 C8D90C43458; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:45 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-5-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=5286; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=rhtJXuF4mkm1WH5HBYUmnTDdQ7mLzj7pscCY6q7WDzI=; b=YAxE9pn6u6bhZrcLTw8MHFRH3h+kwoKvhGQSG6whC7jj0LiaeZourqWryPgsnfXbUCR41x+C+ C8cqka5RpmGBuPsOo1i+1mN7EYxDmZFhL6AO+rJ10VYfv12Z9NcnQXd 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 Sat Jul 25 19:29:10 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 258A0381EA1; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=suQFwJrL4ty8sWwxhieRo2xT/xPmyRASZuYHQLvTi1rqZx6jeKr4xFmxduHNY8OQjU5juPgE4oRRrkmFyWidQ94P1vbQ9dChmmyr73miNTAM/hsvsmCk8ufs/KwdlMAYyR8cIfUlCpxnbotHrNd9/cK4VcZ5x4gcCPZyhT/JLRk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=ClRBhh0tGOXqqTYpWvY4XA/CGkS0cXbN3Pcfu9w8KF0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=N7Z0w9Ij32Skhk7htb+299Jdz+NUd0ucpsDNDD/MEwskxz2sV5lIuT22C968q1wQyAmcmiU/fTs+pH4RJGFMEKkjvYfS+z46ncERRYSpLN6yv9jd/gHfUmJ7xhB72N7z9jukXQtEFpUNSoVP/vIY4ZC1AzvL6YhR9YxN5azIRj0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=RT5wEPey; 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="RT5wEPey" Received: by smtp.kernel.org (Postfix) with ESMTPS id F0795C4AF17; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=ClRBhh0tGOXqqTYpWvY4XA/CGkS0cXbN3Pcfu9w8KF0=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=RT5wEPeyGmXIaJgSU1qpW+8LFnF0MXlukBb9w2B1RfPwm2knmX87mbEkstkikaGF9 P1JZOCYtAzRvt/G6ic23eABk/EyVsAgbJpgQ6P6yePuUljKay2G95PoQQuhj74Jhiv PZmBZUo8GjgaEx5iwDAgViLJDsrccpYISRQsGHcS3grpd149iTM95B17pzlBbYEq2K CCIGtV9BZKWiDMqlyLJJDb9xcMwzV/EKuLALRxTfBSb1gdNEjQn52zUPF60TkYotRO Kea+bjlmrpGk+PaimxKuUx8YfwzrAVxcKpgsoj69C8LFybtwWzXfJQYYZCV75SghtZ eRY6bHznMJyTg== 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 DAD7CC4450A; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:46 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-6-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=7103; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=Cf5KZC7lI5UDQIeW/RGJX7Fxdw5QzQyuUSqfO/q7ZWk=; b=TSxZEIxuvpJSri+d6UL2BUrv5Mos9YFVh1qCv34Uixnn6SCtu+LIanqtf+E8KtXFZ0Nk+tV+w TdQybq9jvUEBbHxvkMOGiFICWk3jRq4Uno1nSZP1N3+i1zRNcP6DJ3J 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 | 167 ++++++++++++++++--------------------= ---- 1 file changed, 66 insertions(+), 101 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-cor= e.c index 98fc00343dc5..b365922d8481 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,56 @@ 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: + 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); + } else if (chan->differential) { + dev_err(dev, "Differential channels must be indexed\n"); + return -EINVAL; + } + + if (chan->modified) { + if (chan->differential) { + dev_err(dev, "Differential channels can not have modifier\n"); + return -EINVAL; + } + 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 +1139,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 +1152,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 +1178,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 +1207,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 Sat Jul 25 19:29:10 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 360EF3876D7; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=HX8aJeaBhxdAQHgQA1oKl8fVPDylJd8UkEqUkDCvOqUAhxMgTvnJ/V2ycN4RlClpz/okMcT4SMN0tYCxZF3tBUwwdnBhxNwt44npUQ3w+Vfl6tDPSkDpzWdpUx+XO/BJNMl5bQ1j0lNt0wfmL7PCeVhVqVvG31jpu3GnM8Lb1xA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=kqbLfRqrQe70WtV9A1wHM2UX90jMCw35sdfgI1Op+Ww=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=IOFZk+L+ZoSBg+VTz26aZeNaGV8kE0HOqEhNrtxRAc7yz4HYec3Q2u8wViUodVXDgez0rSSsRpMK5xcs+wdFmtCvbmgkN/h4pafGY4Ld5I0ITob+z64ybz2ygj6sC5fSkNaYOT7diwbQITVORGAdDNFDxWaG4ok/TEm9WacCTfc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=AO9QRmsj; 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="AO9QRmsj" Received: by smtp.kernel.org (Postfix) with ESMTPS id 0CB06C4AF11; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=kqbLfRqrQe70WtV9A1wHM2UX90jMCw35sdfgI1Op+Ww=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=AO9QRmsjVCwM3esv6sjPyGPOi5SGfh9GHr4dorkpYuDH+6Mb4Yc64QK6ZgZ92c2Nn +uzOe5k+jY2dQuX4S9HXhQyQoKuXcWqpTc3VHvPIpyVfY60vhrVEP1I8t6zyno5q+i B0+x7dKw27CqcAUxLgMQpOlG8GRRYBDFodp13XcHpO16EEKQ6VhVnOql5+V68ielTW vEFZ86NtN+UpEPPk+llQnTWIcDqUSPJ1WiLyi5YVVMKWPqcbqH1ivMKvihyVGnFl1t ChB3rwaCltvYW+xRxkGbbe2LHoPNs3L3k4o6cj0/qSjTFVggycDax0CRU9EbanTvq5 A6C7p2tGK/INA== 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 EC7C2C44507; Tue, 14 Jul 2026 11:02:52 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:47 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-7-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=11782; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=cLsar4g6ywEsFfBe5Wt3Znzrh2jrG/2Jj2nW3Bwx/Rs=; b=pPGs4QuH28V1qiWC1C9rvBJyWe9S1IEDDX1ybZjrov+7GGsfEReEOZhoY6vNgo6MRYp7NHeN3 SiqHXjl2YIsA+JoAbcLL11z6xYXCVed11d3I7YLj9yZcWr/DBfngE0Q 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. Because __iio_chan_prefix_emit() is static, the test translation unit is pulled into industrialio-core.c. Also, an entry is created under MAINTAINERS dedicated to tests for IIO core helpers. Signed-off-by: Rodrigo Alencar --- MAINTAINERS | 7 + drivers/iio/industrialio-core.c | 4 + drivers/iio/test/Kconfig | 14 ++ drivers/iio/test/iio-test-channel-prefix.c | 299 +++++++++++++++++++++++++= ++++ 4 files changed, 324 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2b1ec46c5919..ffeee7c1ffa8 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/industrialio-core.c b/drivers/iio/industrialio-cor= e.c index b365922d8481..3909255212f5 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -2230,6 +2230,10 @@ EXPORT_SYMBOL_GPL(iio_device_get_current_mode); subsys_initcall(iio_init); module_exit(iio_exit); =20 +#if IS_ENABLED(CONFIG_IIO_CHANNEL_PREFIX_KUNIT_TEST) +#include "test/iio-test-channel-prefix.c" +#endif + MODULE_AUTHOR("Jonathan Cameron "); MODULE_DESCRIPTION("Industrial I/O core"); MODULE_LICENSE("GPL"); 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/iio-test-channel-prefix.c b/drivers/iio/test/= iio-test-channel-prefix.c new file mode 100644 index 000000000000..4326d9bc466d --- /dev/null +++ b/drivers/iio/test/iio-test-channel-prefix.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Unit tests for IIO channel prefix generation. + */ + +#include + +#include +#include +#include + +#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); --=20 2.43.0 From nobody Sat Jul 25 19:29:10 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 28A9937F8B3; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=kRN/8sm2nu1+o4r/NrN8fFiWEtWiJRgqmk72oid6ki46+1MG3uxML1N47b1dpenH4d3sPpyhxGJ3PQVB4uZIq1Y5LrHDrCLTDulGh9Plri4p/Nltu41bYMm7gyuPf39cwPGl+mUUGh8rFUCBP83kVdFiAPRWb/x7ac/qGz96DX0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=hCIe40KJp0DVG0loqRy/4JNLZyr3rW4LLxYL/bwapJQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=IAjumK4tq1M0hc3ThgAT7PuntVJZmjHNaOliIMgCaskq9bMi6Ic8GiIm7Zmbt2Uy99IDCkrkPsgB7UCnGvx2f4ZfuERQ6hCnLqkOryrdKX8ES9JuzMDD8i9KMQcMKZB1EGtlI2L1oIf/WPREZjkd2Sak1E24rLt0eeGQfSqNMC0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=tIVFp9xR; 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="tIVFp9xR" Received: by smtp.kernel.org (Postfix) with ESMTPS id 1DB5BC2BD01; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=hCIe40KJp0DVG0loqRy/4JNLZyr3rW4LLxYL/bwapJQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=tIVFp9xRUjz24rtjBVFkBemL9XVikjWD0kNEayYQ/Neml4i4Bxerml1p+YflEvpKo +lrQCOIbPsM1O5d5tDrSNtnokFdB6aVWWPj8vzJAQ201r4wWmhja6YAgG5DDGghX8p exTc6gEdXjA/JD80dA4qow93pidVSJOmKv6DoZXSBsk24W5llHaXDqUv2OzwDY9UVc 6nXnqGzOSoazIU6UyfpeYekoay32DRCE5rurq0fgtQIsbHva3QuKR9OID2K1M7q6cv sdCel+1iZZSLnDLVJGtSRFqq+/vivnv/stqa2T+zDUzgnuQkuCvC1KDK61cr+DZc/3 xDBCRvti5AbCg== 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 07525C44501; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:48 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-8-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=3402; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=x2FDIaEA9EK129rBESxX3qw+hnanTxkIq1mgZqUl04g=; b=t5cyglnEOhY1/cCW2Jfa7WgdzDm43mqBGThJJtysyulDgVUDzutnKIFphWvjH/NMcSIDvfy8/ 67r+w5N1BpQC0tVRT+CyqwanDSUjD6v2OeGfPI7N2QjilJmvaU0VNGp 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 3909255212f5..5e28b50f143d 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -846,6 +846,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) @@ -1259,6 +1275,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, @@ -1397,6 +1432,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 Sat Jul 25 19:29:10 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 1BC11381E86; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=i63n5yOXDZiAcp7eJ+AI+Sfwclins805fJ9G/7yXUR70ODn+V/WDrL42hjEz53NVdGmGbKRLhMODTAO4ihCZHXrzxGygxiJDpuowBuOX+tFhcPoUCOkvPf2Mj4DKqwYyb6Wpr6P42v6fUyYs6MJR4+jKKzdab8EWvW1T17wSAaA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=c1JdUD8PchaBo1bs7Ioiz8H7o58rPjkNesf4pl2eSXs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=OeXuLxyKgBeByh0bD4h1IUvdYcJn6hbvCFTToothtY0BKKjK4Kq84iRf8oUGCJy2RwewRdIn7pED16lRw2ZP0eh1K3Pyc0cggzXOstecf+J1oNUr+tdQEH/uY2HjXhAdlSFY1EUQUnT0RjRCx3Sw2Nge/D1TZmNjvh++HVAOWA4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BKJ7Ho6j; 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="BKJ7Ho6j" Received: by smtp.kernel.org (Postfix) with ESMTPS id 2DF8DC4AF52; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=c1JdUD8PchaBo1bs7Ioiz8H7o58rPjkNesf4pl2eSXs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=BKJ7Ho6jzAjpeoL/GsN0V7TCOhMLkYb9OOdMpCjXsik11B2aX19urwpSDytWAR9JL 7aclhPkLmUCMyUp5PB2DUcJpTwWnhhXvmYzRp+niURQhlFhVS3QqnZ1noEfXR8HUCJ gC+au1wrdgFCFsf7k05LVoIcDJ4Aoi9zsBQLD24iwe54QudfF9Pjn5iojkCDYVQ2IZ G6NHHw5j8+cdeTJCFHqknl2gMu416ksmsbIMB3kLiT3OT9AhIvTkfwp9prnBSZVCx8 igEPqaRBJOOIpTgEhU5x06FXCRkDGz434wOz4lF/SwOJv4yMvWtfPJaEqnhNU6JAor L7gH7kVdbf7FQ== 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 172C5C44509; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:49 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-9-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=7035; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=XNIjlGZaPxI2Wi7H1IltvCAZo5/CDJaXWkRhFfShqrg=; b=5VPjkaSOxGx2GISohGLmiC2LLxMYPjs8s0/v9WYtXsmW0tJGoPZFHWq8K3lVN0mGsZflGcl5p 9QpdI3Y6SHsAdltA8nglUQR96DHe+sh5iWkiq+NKsU50OjHRHQJ8oGt 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 ffeee7c1ffa8..2bafb6c32b3c 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 Sat Jul 25 19:29:10 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 2BC8E381EB5; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=sxL+b5AMxQM7D3t5NEJX0uuQVZwAX79iBJhKHbanCFysdRc7u6ChGUuwEtP52HF0z2Gz/cam+Y0pshfTE6P9EuOecdw75N5piIqj40NWztVLLisfKbOB/tmjFPOicPUvKpyw0W7gU5/HfPopYk3iGNS2rU1O6XdK+u8DBOr/EUU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=9PZV2oaUAHNabR3o2Wqz9RCVTRxHNJRj/376dXZE3dU=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=rzbky3llPwORUtqXXYLach/dPQQkKdXP0OvNRQYlhltS7SluJXL8qfBHZWyGsGdBNfoUx/0gh+Eh95P3Ntd7emsO5jyG2qV9ECDumpKEkvULGvZfbwgwzTk4JsbOFVlcirPhHPwB32WiEPzu2Fdt3hem1opgyJXgs2PhjIKVxqc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=UStL0upx; 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="UStL0upx" Received: by smtp.kernel.org (Postfix) with ESMTPS id 3DBE1C32786; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=9PZV2oaUAHNabR3o2Wqz9RCVTRxHNJRj/376dXZE3dU=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=UStL0upxFDoftjI1gP8WRJx0mDnS3fFJDdyoqsUaSWiZwTLm1/i8VYILAbMwvxZYA +j/OExZzvg3sGyKqR3cgu59C86/BBAw9asAn448FB7yApQXYxVkA9Hvu/kDp6GBBED Kk57J+Hp2HTGRrbRvbcmIKzBGAzJOQQgOrGdVVhC/dtBs9158oyLBmVPPOSjjL0++l ZApcifDbG8cNnAsPqAp43OU6dB+TzDVSFCwM/JHJ/c6gbsDp+a8OwQay7wjiVQAYYa KUaGj8dFCiZNowBRW76cI9B5J/3VJ6L1n4JC3OuGBWUHl3ItcAFQBq6gk9qY3n8BaL YBgDNMwHmvxjw== 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 2964CC43458; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:50 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-10-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=37069; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=I8BqfvrAQpD+AF5vXhESODkvMMc34oJWaHZZ4Z+VVCQ=; b=O3XFMr8vNYwaLIO9XrnOKQOEfhSTCaxDSeXC3V5im3Ubcr7YiiXhCy8ERvIu3HfPB71IeFIFn Yv5zQ2TprsJAPrMyWtx1ewvvlMGzJ+COuGjBLgTvEQxkfW5d4WSS9Y/ 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 2bafb6c32b3c..f741b799d383 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 Sat Jul 25 19:29:10 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 3689E388876; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=O7lMg/rj9DAi5ohKPs16ad2nMgg6wWWU15AJQZRzd1Yb/lcJFY41lZu5LzfNs8mLuBr1Ez8oQFs78m82htIwucioRxjpR2MwXaXYerYFP+XPi3NhkVSB9MvFDtV9gNNlmq7zpWK3n8Yds6EWKzhhOfLuNlTMJvJqprsocxSOoXo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=wjGU47dMiUmh3ewETIM8jiZt5gDJ6tkeOfiYVNEuSWQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=YqNeQr5bpq6qsEtORcFb/il+vsCe2Fmx6WdAIpJ352kcQyFIhlMry8LKND2X3xBzvkWO1hT+Ci92HkaCSxUgPwtFpCxRsqTjTu3HW7AgrMoe0ITNiID3ZlnEkp5G29X53ofxqDBn3fZylSfmE8/oSCP34rNTwLd1EzBTnTngDm8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Zv33E7d8; 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="Zv33E7d8" Received: by smtp.kernel.org (Postfix) with ESMTPS id 4AFF2C4AF09; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=wjGU47dMiUmh3ewETIM8jiZt5gDJ6tkeOfiYVNEuSWQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=Zv33E7d8ed9/BvFKwkEi3mHLVnU6dSCZ+UhX/nSbjJ8bCY+Rj6rzSEL1O0f2q2Yob 0sjkofeyQcY9sLktiI9+RZLfhqEJTGRFZ+blfCE/MEh9d27ltVVxJBQ1g2CjKfwhH9 SsrgSaiIK/0oP/Tq9Z1VI5w6hWv/x6o1BY1qWi8GT0Ul6FrMEdJA4smbLl3he0mzng 9Ipqqbm4h4cOx9gEpfQb8L5nC0JZ6ferBbcF8uH56ZBaVqkpPMLWMCKd3nAYH/ylaM ZnTFN60VRljubGThQeEtkTTLtqBo+SicgnYGX2O72t4Hodw6UTxba5EbmomiFAXkae zBRTMTR7ATZhQ== 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 3915AC44508; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:51 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-11-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=9246; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=fxc3LNHFMCvmFNXVI1Q9NxrOcuqJsc2ClYxY6gEQXnU=; b=zL1nss05LnpHhluxWURzyPLKba1sO30gxIo5AmVDHR1mCLtBmD7u0drU0SRKi6jpjZKni6D4A HJIg4qDrPBMAYxZJHfYM1/uEbmuzOSChr4QNgmypQimY6HUffAlEHsY 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 Sat Jul 25 19:29:10 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 4E40A38B7A6; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=ifKWJxv7IP58Xuqry9WoqU53b1PzwnuoeBIWECUCu7Kd+Gie4VDD48+WWItop+cs2S2mhXBVEIfeyI/296aRMKjhdb5DC5kls7z6kXQbxHX6wm86/ruLKpgcVKgoZm75EErb+dwVCrN2mJtbq+PiQfpqsW18p+KVuodgccGrC+Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=EBQdqatAeYwThD1Kg75N1fl4xdSgZxFed5ax1mVnjSI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=SkVH20+3fgPPV59SCE7rllbKBD4CyxTuMyfRF/xa9uC6x2uhrGe5gNGQ/RgRaGjy3DCnoROc0c+l8GGu4TsC4gg68bD5PLC4swT4E57t+IQoMSe8rZQfZzMbT4T2T5Hxbs77sLqYPmn6WyXJGyyXVI0Zypkh151pg1y5F/cZLOE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fSARlrEl; 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="fSARlrEl" Received: by smtp.kernel.org (Postfix) with ESMTPS id 5C471C32781; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=EBQdqatAeYwThD1Kg75N1fl4xdSgZxFed5ax1mVnjSI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=fSARlrEl8qXjasg3p+VmEZ9e9Ba6AqeyyXD9es0zI7Gt6hsnyqccXx2TAmwwhLWHl Dw/h8lMC7ANOKgsgjh8gWyMw/HxipuH5WUEOSk1LvngpbaEDxmI5CAR1wU6qVkEMMC 7Nbo8lFt6ye1KkYYA33YIv80p7vT3bqzCw1ntlMK+PqHSNhBX4a0MB2rg9fGSiH90+ HMw38SzuM9fDqAmqmeTtCiWzc+MfW9pLO4h3u17g22Dr0jCQI9LqrCjZ2UmGHFp1C8 N3VydwFBx82/QsMf2V+oegMQsl8KC4HRUxAV64R0TMvPe4GbRnkhx6ZC1NSpApi9dz jYhKTT8cb74ag== 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 47E83C44507; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:52 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-12-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=18823; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=0DkNlFjtphp2n9PU/luo6bJ9oh2P2mCD1ZGZxRc7MoI=; b=cGQmYN9QPcVNZ1lSdQKgVgC+QjX10iR8swLYE12RtAKgkKKHWvFaVh753c3n+5JllDG3HO6A6 6ap3XCA5VKpACXFxNY7FLB88XMQ/x5wTqTCJvpc65u/fY+X+jBMVy9Q 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 Sat Jul 25 19:29:10 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 4490838A706; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=Fs1n74ElGoCHDZpOmtnQKCWa63kNgxPJD4t5nv48Xx5mmYplzqLzxEeCZXkw0lMms5O06lP/TAL93u4btLJ4Aa/rowcUrAz3iyUU5TzpIFJEX+JBzgfBWPxIWTcdeBz7KHzlcis3HTRZHN5BZ5xCXEh/vXxCILHUVq5JgFV+MPg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=cdWGCg9lveikbiQs/oqw9YNa23Rby9UQAzabTNGO/Dk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=CovWw4LEyQW3qXwzjazb/+FlfQIzZQa/u12OAa7tpnv2s1iPDYuvtLMIbh/XslSf05HS3vOsOH9oXWYCPNf//u+F0CChBJtPUMasysI4VoHo9vRnADteIT1ojxWblSxGjL6Mdr+8s94iFeZwqHRdife6cskTKPaPh2c48+mTiKk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QjamoWtN; 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="QjamoWtN" Received: by smtp.kernel.org (Postfix) with ESMTPS id 6DD04C2BCFA; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=cdWGCg9lveikbiQs/oqw9YNa23Rby9UQAzabTNGO/Dk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=QjamoWtNHNF0Abg+/o0LkP1kyd3jU33zcHLBhs/l/YHbmcENlYrDPCYMMy/nJAqGK jHLSTAzT7cy6PBXo0WPG1h6dCrBSZmX/OLuF72Go8UgJqc4dNGqFIhjLEcqrwOSZ2B FdutMa9qygCw+pmIRXJQ062Prgt4I2/qcE4oH41HKGxFyf3y/SJ8LjzSDZW4OOp3Xd blBt81il8aotiKlXlfVqMMRLGYZC3x+3hatYxzyrK3/IxW23wpEepT2GF1fh2QTGOZ JxFIAAA/bTiij5OCVuy8cqO6VwnBmSfRYdeGY6WKdhh9CzYDcq4TTkMKIenSsANJZW x0vDCF6DpczvQ== 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 585BCC44501; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:53 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-13-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=19605; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=474u69FlHVfNx/IDQzFlMEJzhXZxTQzHfl2XrDS005o=; b=R+n8hNmPYA1B/PRCG+2D1X596/8aBwzPn7oKQUrDJdteNPO7vDcojEaqkCwtZJj+dz0+fjXbZ ks6CisjykJ4BcFbYkkKuGz8LAXjb6w0AsdR0Uy9VTPiHfG8x3UJXwfR 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 | 331 +++++++++++++++++++++++++++++++++++++= +++- 2 files changed, 327 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..fef0e3b360a7 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,132 @@ 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) { + 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) { + st->ram_fwu_cancel =3D false; + 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; + + memcpy(&st->tx_buf[1], fw_data->words, wcount * AD9910_RAM_WORD_SIZE); + + /* write ram data and restore profile register */ + ret =3D ad9910_spi_write(st, AD9910_REG_RAM, + wcount * AD9910_RAM_WORD_SIZE, false); + 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_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, +}; + static const struct iio_info ad9910_info =3D { .read_raw =3D ad9910_read_raw, .write_raw =3D ad9910_write_raw, @@ -1570,6 +1867,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 +1967,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 Sat Jul 25 19:29:10 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 3B93737D128; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=DapqULYiKAP2GOoqb7XmntXsA4mwp69XIlMRFP1JkNQvxM1RKA/ezjyyP9qSnRnzkLDv6wKjikQ2WVdE/vWsRQo+emZoveyFXL5+B+ccktihVegPuc6UKorsRvM2yOWu5mEqzywiCkoG80q+Jr/F6mcOZmh9gLeQy8K0NtzaGvc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=NoLwX+07FoBt47zWLtitA0dnXwzqvaD8Io/4xTsV/7I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bzDK7glKBVfSX9LU7QJCQof399q//ronQu4qqlWuCknNCeSarJZWHIvcxf3py5S9hZGjT59mPMGFhJKRg7l9bryM5tG0vrvlsM6xl8EOCV5p73/PiX/EBqZ4FvF5vqvJKtulyLEjNiATa6o+MVRsKsBlw35VSRiihHWagHJQtw4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=azoHWWKS; 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="azoHWWKS" Received: by smtp.kernel.org (Postfix) with ESMTPS id 7D344C2BCF7; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=NoLwX+07FoBt47zWLtitA0dnXwzqvaD8Io/4xTsV/7I=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=azoHWWKSkuzNNnPIinoRQneaxOny4jVmYYjT7qPCKVBxdZkokqoChTJLtb712eU1V 4VyR8NQOxAyunmcdYP41g6+T8lFvH1iSsH0Idz6VH4dE1RgWF5aMTjJO9NEtfD41Mz i+j2eJpH67OwPh+erjR0p8kV9PigViSgAuNBGxebHy9JWxoq49S/4YKJrMpK2McfTg c53K5KEylPUfxSO3O34qITmTI7u9ITb3p8AVh1ppcFYMNDIfLpDje9UJApyEUjxE+Q iZ+8A2lE9e5uRXhWTZXkWnjBavgtEYFtNSgEtdlEzHVs8/hOiH0bjYZP7fTFdqTbXA 6GT6Z3wxEZG4w== 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 6917AC43458; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:54 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-14-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=10549; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=0cahCIiRPjcytxmPzv/LMTE5IfqfI+3pk8xx7/661Ww=; b=U+NjAACBhdKTftp/S8/+rqClPpv/zl2N+3rGSgTDWuBsbUm+Y+xGnzdgZkoZJwoA0HHnyI98k ZSV98GsoImNBuzlsjyMgOkdUmLnnT3dPg3BdAoytblznmizYJywviDL 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 fef0e3b360a7..407a7d19f71f 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, @@ -1860,6 +2045,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 Sat Jul 25 19:29:10 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 71BFA38C423; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=pCd9W5NGdNZll3s6Ym9+CotX3h6QZAPfcmui70tCP9Fq7J/0G+91VA9Lei1tYNEYzwjlB0bkum/T6NOQbErtizyvJCfbTWX49QZsdjCblkIYahtcbt8YKfU8CXmZXCdsVVk1nH8mrECfTj/2DoSQN4b663QRuhG+JYJDB+KYsh4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=F2vRvABXwS14uRb3GaR/uGI/cNeqlugKbTm/TE07trI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=oF0P41B8V2Tg7WQ8N0A3DFn4t5XW9VVpVxqWiKM/o5eoPWw5cmz3ll2vL12NPRjb8PgNRwxOYU0ZN8PDhBSW4nRaA5b0tiBUAU2FjR6zSY/NeGbJI1DExbuLQDwe4kOYDAWuhxubDZgLJrZ8slmLLEk+h/YgqnNPbFgr8UGHkYs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CUo8NqrE; 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="CUo8NqrE" Received: by smtp.kernel.org (Postfix) with ESMTPS id 8A2F0C2BCFC; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=F2vRvABXwS14uRb3GaR/uGI/cNeqlugKbTm/TE07trI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=CUo8NqrEWYes6HVG9ZspmiBrWlEEtR6wGeevp/t/BsNH6B/C758OtS1sn/B5WmGp2 wrRWihb/v8SlOnkQfAXNlsHenm6Dz4WNViDW5oaELqbos5KRnGpkgDYpzObvWS0SqH 4/cIwZzzW3fNqHlIKH5RAUTC67EGryFN4o+/eu63HiDSWw/AwA2WZVY6FBf+PuguAG u0cFQ+ahZfq2LlzFEPpwA4rZv1wIokonh8EhoMGEGjTeY06Z8LEyLKFQeWRP3EaPw5 27/VDp7kWLHF2LWLoNwIfy5p3GwUbJPLuT45oxfh1fIBaD1ZlDYQ9Xt+YUPXPze7aa AAW8S2iR4wVyQ== 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 7899EC44508; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:55 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-15-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=8242; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=Dbwgol+6Hq/KvfYff2aNasGtVZeFFUZk8me0PgZie5A=; b=4dYE1LQ05Xoe1nafZG8ClfoGjGAhNFGjEkSjjOCNVOmQwnHcLVFb5+JG4gdvrGdJ+khtkydoR yKN7oOAqpxSAurtJfFQd2a9R/esO5RiHKs8BWbYME7QB1vO2Kb6Zj5W 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 407a7d19f71f..17b53aad237a 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, @@ -2068,6 +2082,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 { @@ -2171,7 +2350,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 Sat Jul 25 19:29:10 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 427F1389114; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=bA6crbO4CTj0r6U+hu+dEJGz4HyvT85rPCTEqDq3GLGBesL7xHjU9XzI+NvkzM51dZ0mP6mj83Mvr1tX+1E9hwfayL9KiV1kIHkBUZajVvSFVDlrbnhS+6kCX+t+wQGFiIL0gXSi7RaJVrtIRT2hCvQ9OSmwH0gAib8ChBZtgKQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=218M8FrPiWDgACs8EXS/X/X/8uBrgu04ceut2d4D8rw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=bznZKh3/wYIwF40t2o5T0zqVaz93Eg7vM/6tVbTEq2Yw17a9VvymP3tBeJUknWx+kIT9HLpjX6B0pj/BI20UmC0Su+lmxmEq3jLMdHrC3Ne5bX5wuFdTDvaiq/ApNzPjVkqXhbBl0t3u+7FLZSXkKhq1bvwu08+eCHAMoZNw1ZI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=G6c3/mLV; 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="G6c3/mLV" Received: by smtp.kernel.org (Postfix) with ESMTPS id 9A639C2BCFD; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=218M8FrPiWDgACs8EXS/X/X/8uBrgu04ceut2d4D8rw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=G6c3/mLVvZhKQ+CoS31C7rvrgYAiRC9PAkT31tr+od2RdTlAkj6WawMn2pRNPYowW PaYU+gTyOYLk/LUcomAfJD/wRJz5OMeROWvAgsawCoLhAwdzWs9U+gv0J01jl04UCU oXyLeTg4z+C7OD6yRbu5Q5mIkKQqvfeV6tkx7gLBbYeGvqMspEw0Yc6wiXrOMCsHWt 1wRzrA4y6zv80VRBSVXzmX5tmY1UyjTbqawdjEThyxQQrOf5BGvpeMSnNJQ4eASxph 71pDc4bwookgxS1j7oJpcukHJPE6AkRND8cEsOP/r04z27B5DQqlXyIl+Sqn8CjHZn FYl6eK2JGWZ8g== 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 88324C4450A; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:56 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-16-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=4220; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=GuhwolTQpd8DFn9kJcnMvrj2DfAjx99cqypPcX1I4W8=; b=eowpbGlqGpJvQZAyjCD4Yn1yeicDaxc6UKd7Q6RHUnmwFgOKoV20qEeAjp7nQG8p5e/xzyT8u +3rbN84CDWnCKpi8ODUKfvxHHc97MWjoy59zGEdkq//SUbd9mXo+htc 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 f741b799d383..b923e5b25ddc 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 Sat Jul 25 19:29:10 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 51B4B38B7B6; Tue, 14 Jul 2026 11:02:54 +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=1784026974; cv=none; b=eUJAkgkTzbZwXvNTL8q0UZz+Tkjj/wkUswarWtPzhYYWxAIpSUYGmbF8YMvvmqpcxV78Bonwa7VlQPd620KjGHyl2q75e4ulV7ZL0x8EUTEyMcxIIYtzc8ayUonM6g3LGM7KXiwOpsUa1E9fQjewww6gVMULygZ+6m3vEh8qW7k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784026974; c=relaxed/simple; bh=uGzvob5z+AahqChXTGTlCtWn/cblNuKh/of2iTTvGKI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fOHPM1JTowy6Smz8c28P41DgkuOLe9B1q0nD/ctTv6hwUH8W8/u6YRTMvENQ6VHvKZd7+jsazprpsbXRx8D1U9vufNjhffmz98K7YT8g3eroy5z2Ns60SYwBNQsklk6K0xdK0r5Msi6gLZ/bepimiZQv7A8yjFG9f3g9vLO+E+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=llTpXz00; 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="llTpXz00" Received: by smtp.kernel.org (Postfix) with ESMTPS id A8C61C2BD00; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1784026973; bh=uGzvob5z+AahqChXTGTlCtWn/cblNuKh/of2iTTvGKI=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=llTpXz00xXHTjIi+MBQoYTAqykYxH4HVNSNktJ9EoWYSFEsZ/Z10zyPmqMXSa1slD Fk+BAgDycJyTZUY3XTnGrEmsuyK8Yn5/UyNeDMfc73eni/m1D5W7FVe9O4r1ISaLEM RdCjNJZqHlGaLndpzS0lSWuWfVLwgjofrA+QCQc+Hk1yKtDdB5cK68/kPJas0Dy78F muAurZkAwHhTNO6YJLxRbzTOvFoqN+VzZEHb+PZmgLUerJWQEu6InO//xQcpkf4gRY xyTQR0KXKhg9X7WzPlmASPBIHQnMipYYftNQcvIboCaf1Uq7ldUmSRpScIdpXvkUE/ JlVLCBUjBQ66w== 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 97BF4C44501; Tue, 14 Jul 2026 11:02:53 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 14 Jul 2026 12:02:57 +0100 Subject: [PATCH v8 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: <20260714-ad9910-iio-driver-v8-17-36939c3c07d2@analog.com> References: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@analog.com> In-Reply-To: <20260714-ad9910-iio-driver-v8-0-36939c3c07d2@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=1784026971; l=26543; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=Z1UcgxwvHdxY9gW2kSgsReU2ouE5NybcL1VfK8R8Pd0=; b=RCCCCJUJ932YVtINSXtwfcI4ITSOmsPYeCNJ+VDHcwb2Otfrru7SMqC6B6UFOLAfGqRUKX8zg UX+5go9T5rcDfSlu5kMsm5NgqDcxqwP6BB5k/dw+kYFI+bQcNncT0mx 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 b923e5b25ddc..bc1fc30b9650 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