From nobody Thu Apr 9 16:35:12 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 571431E230E; Tue, 3 Mar 2026 13:27:25 +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=1772544445; cv=none; b=m2TSVnOuAVn55PPGCSU/7EsiJe/QqIA6jT/lXyT2fL1EOgg9s2mUNEPOX7d9oAeNFAqdxqwo3d8lT72c3iTl6EyE8E+/02dtCmcRWG4xBVlJfUK56UGJ1Vj+LtShrCTdo1cSfKkljaLA8JEcT7dWYj0lNxtqJO4TThl2wAhUXX8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772544445; c=relaxed/simple; bh=taqMyQrzu3ECY/aOMZFTVunNUR+yiqX4OGw5hYUMhF4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HXe7hPfyFFEFUJjEBz2l9Ku0YtpJeEt1Z0AKy8oyCfxVgxzHMXDakdzA6nDDwDwDPztFLa+icLuL5pEklYJBGfxDaurl931CSPXlvrNsMipyucwjwKpGJF1/zKp33FukwAmPutEKeII7GERVEIzbYiHnWBhebVu5lyIbVtoDuoE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=SOX/0mXz; 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="SOX/0mXz" Received: by smtp.kernel.org (Postfix) with ESMTPS id 11CADC19422; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772544445; bh=taqMyQrzu3ECY/aOMZFTVunNUR+yiqX4OGw5hYUMhF4=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=SOX/0mXzRUCA6ILNimp0KmM5eK01kDJ+AzF868t6OEayVjbr3fFx/INUvCNsB5RQ7 GPdKrN57Ne5/L/UQNWTb1d1ubVRaMhYA0cmOnyw/XgVI2ogw6EobY/YTpoOouQDtJ5 SUzPBn891N7YL5tucKZ7m91wL7yCZ3ZGSjEJk68s5LDGG4iwXO1pEwVXM5GHFk7eDZ qgjn36KJT8ZH2YLvsF0sSNdCvRMt3/rlrHaehkYl1Uid8f1BdBPiLHk5W7f/5XZgwt ww89++wE6PCFmTRlm5aSL8VJNWFxPrDCGk8GNzC9abBZlZJC9KhE5RrdrfxloX2My6 3wI95V2K8Tudg== 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 F2253EC1438; Tue, 3 Mar 2026 13:27:24 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 03 Mar 2026 13:27:06 +0000 Subject: [PATCH v8 01/10] dt-bindings: iio: frequency: add adf41513 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: <20260303-adf41513-iio-driver-v8-1-8dd2417cc465@analog.com> References: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> In-Reply-To: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-doc@vger.kernel.org Cc: Jonathan Cameron , David Lechner , Andy Shevchenko , Lars-Peter Clausen , Michael Hennerich , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Andrew Morton , Rodrigo Alencar , Krzysztof Kozlowski X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772544443; l=9279; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=rEirALo9csLZROaj+sBPvPeAe3rrOkDWGK2ha+EsbgU=; b=WdULBmQ261wJehyLBmb+s8A24JLciG2+F/muJBUB4CsUJL1Hj3V2jJ3LaOC6xJZm6BjwK9yg7 Cb/Q/Rp3AO/AGLAgawY2nlm2ELpT2QnVw8QvrYv+1gKI8XfGW8g4Uo0 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 ADF41513, an ultralow noise PLL frequency synthesizer that can be used to implement local oscillators (LOs) as high as 26.5 GHz. Some properties are based upon an existing PLL device properties (e.g. ADF4350). Reviewed-by: Krzysztof Kozlowski Signed-off-by: Rodrigo Alencar --- .../bindings/iio/frequency/adi,adf41513.yaml | 215 +++++++++++++++++= ++++ MAINTAINERS | 7 + 2 files changed, 222 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.y= aml b/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml new file mode 100644 index 000000000000..59592fbcedde --- /dev/null +++ b/Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml @@ -0,0 +1,215 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/frequency/adi,adf41513.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADF41513 PLL Frequency Synthesizer + +maintainers: + - Rodrigo Alencar + +description: + The ADF41513 is an ultralow noise frequency synthesizer that can be used= to + implement local oscillators (LOs) as high as 26.5 GHz in the upconversio= n and + downconversion sections of wireless receivers and transmitters. The ADF4= 1510 + supports frequencies up to 10 GHz. + + https://www.analog.com/en/products/adf41510.html + https://www.analog.com/en/products/adf41513.html + +$ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + enum: + - adi,adf41510 + - adi,adf41513 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 25000000 + + clocks: + maxItems: 1 + description: Clock that provides the reference input frequency. + + avdd1-supply: + description: PFD and Up and Down Digital Driver Power Supply (3.3 V) + + avdd2-supply: + description: RF Buffer and Prescaler Power Supply (3.3 V) + + avdd3-supply: + description: N Divider Power Supply (3.3 V) + + avdd4-supply: + description: R Divider and Lock Detector Power Supply (3.3 V) + + avdd5-supply: + description: Sigma-Delta Modulator and SPI Power Supply (3.3 V) + + vp-supply: + description: Charge Pump Power Supply (3.3 V) + + enable-gpios: + description: + GPIO that controls the chip enable pin. A logic low on this pin + powers down the device and puts the charge pump output into + three-state mode. + maxItems: 1 + + lock-detect-gpios: + description: + GPIO for lock detect functionality. When configured for digital lock + detect, this pin will output a logic high when the PLL is locked. + maxItems: 1 + + adi,power-up-frequency-mhz: + minimum: 1000 + maximum: 26500 + default: 10000 + description: + The PLL tunes to this frequency during the initialization sequence. + This property should be set to a frequency supported by the loop fil= ter + and VCO used in the design. Range is 1 GHz to 26.5 GHz for ADF41513, + and 1 GHz to 10 GHz for ADF41510. + + adi,reference-div-factor: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 1 + maximum: 32 + default: 1 + description: + Value for the reference division factor (R Counter). The driver will + increment R Counter as needed to achieve a PFD frequency within the + allowed range. High R counter values will reduce the PFD frequency, = which + lowers the frequency resolution, and affects phase noise performance. + As it affects the PFD frequency, this value depends on the loop filt= er + design. + + adi,reference-doubler-enable: + description: + Enables the reference doubler when deriving the PFD frequency. + The maximum reference frequency when the doubler is enabled is 225 M= Hz. + As it affects the PFD frequency, this value depends on the loop filt= er + design. + type: boolean + + adi,reference-div2-enable: + description: + Enables the reference divide-by-2 function when deriving the PFD + frequency. As it affects the PFD frequency, this value depends on the + loop filter design. + type: boolean + + adi,charge-pump-resistor-ohms: + minimum: 1800 + maximum: 10000 + default: 2700 + description: + External charge pump resistor (R_SET) value in ohms. This sets the m= aximum + charge pump current along with the charge pump current setting. + + adi,charge-pump-current-microamp: + description: + Charge pump current (I_CP) in microamps. The value will be rounded t= o the + nearest supported value. Range of acceptable values depends on the + charge pump resistor value, such that 810 mV <=3D I_CP * R_SET <=3D = 12960 mV. + This value depends on the loop filter and the VCO design. + + adi,logic-level-1v8-enable: + description: + Set MUXOUT and DLD logic levels to 1.8V. Default is 3.3V. + type: boolean + + adi,phase-detector-polarity-positive-enable: + description: + Set phase detector polarity to positive. Default is negative. + Use positive polarity with non-inverting loop filter and VCO with + positive tuning slope, or with inverting loop filter and VCO with + negative tuning slope. + type: boolean + + adi,lock-detector-count: + $ref: /schemas/types.yaml#/definitions/uint32 + default: 64 + description: + Sets the value for Lock Detector count of the PLL, which determines = the + number of consecutive phase detector cycles that must be within the = lock + detector window before lock is declared. Lower values increase the l= ock + detection sensitivity, while higher values provides a more stable lo= ck + detection. Applications that consume the lock detect signal may requ= ire + different settings based on system requirements. + enum: [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192] + + adi,phase-resync-period-ns: + default: 0 + description: + When this value is non-zero, enable phase resync functionality, which + produces a consistent output phase offset with respect to the input + reference. The value specifies the resync period in nanoseconds, used + to configure clock dividers with respect to the PFD frequency. This = value + should be set to a value that is at least as long as the worst case = lock + time, i.e., it depends mostly on the loop filter design. + + adi,le-sync-enable: + description: + Synchronizes Load Enable (LE) transitions with the reference signal = to + avoid asynchronous glitches in the output. This is recommended when = using + the PLL as a frequency synthesizer, where the reference signal will = always + be present while the device is being configured. When using the PLL = as a + frequency tracker, where the reference signal may be absent, LE sync + should be left disabled. + type: boolean + +dependencies: + adi,charge-pump-resistor-ohms: ["adi,charge-pump-current-microamp"] + +required: + - compatible + - reg + - clocks + - avdd1-supply + - avdd2-supply + - avdd3-supply + - avdd4-supply + - avdd5-supply + - vp-supply + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells =3D <1>; + #size-cells =3D <0>; + + pll@0 { + compatible =3D "adi,adf41513"; + reg =3D <0>; + spi-max-frequency =3D <25000000>; + clocks =3D <&ref_clk>; + avdd1-supply =3D <&avdd1_3v3>; + avdd2-supply =3D <&avdd2_3v3>; + avdd3-supply =3D <&avdd3_3v3>; + avdd4-supply =3D <&avdd4_3v3>; + avdd5-supply =3D <&avdd5_3v3>; + vp-supply =3D <&vp_3v3>; + enable-gpios =3D <&gpio0 10 GPIO_ACTIVE_HIGH>; + lock-detect-gpios =3D <&gpio0 11 GPIO_ACTIVE_HIGH>; + + adi,power-up-frequency-mhz =3D <15500>; + adi,charge-pump-current-microamp =3D <3600>; + adi,charge-pump-resistor-ohms =3D <2700>; + adi,reference-doubler-enable; + adi,lock-detector-count =3D <64>; + adi,phase-resync-period-ns =3D <0>; + adi,phase-detector-polarity-positive-enable; + adi,le-sync-enable; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 1251965d70bd..1bf39f6d964e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1634,6 +1634,13 @@ W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml F: drivers/iio/adc/ade9000.c =20 +ANALOG DEVICES INC ADF41513 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,adf41513.yaml + ANALOG DEVICES INC ADF4377 DRIVER M: Antoniu Miclaus L: linux-iio@vger.kernel.org --=20 2.43.0 From nobody Thu Apr 9 16:35:12 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 571DF320A00; Tue, 3 Mar 2026 13:27:25 +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=1772544445; cv=none; b=bPCJbLL/ZRdtiSmRXBuCFb0V9crXuwWr1RKV5B41/2uyHQ3FCVD+PaLV9D6bm1jWFiZILvvrwOwdYgNazQQAZVM2Fqu/BiREA97KOUMUJWQywUj+Od+iZWGUhtA1PZlKEshpMsb/br/mI+D+PF9EdNHNtsm/9y6pH5BI4WawDjY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772544445; c=relaxed/simple; bh=N1VfDg8H/7194eoYlOpUeo1wfbx91H88u4KEWIcBo/I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=T9gy9cPYDTd7MOMZJeTgKZU42gI9KQEsZDx7pRDerW7oU4YGkTY2nhGzcHPi35MgyBSbq+7VcRRDJ1cpRRo2p+w18+SBgQ+bREuN0yYpBDT1CH7Q8jhfs+XQRXDeNAP6s/QytElGRcYDixM3sJeHsc/jFtLITDTNZYsliOQiHmY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Q7C8x/Po; 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="Q7C8x/Po" Received: by smtp.kernel.org (Postfix) with ESMTPS id 22224C2BCB1; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772544445; bh=N1VfDg8H/7194eoYlOpUeo1wfbx91H88u4KEWIcBo/I=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=Q7C8x/PoLrq5kh/pCh3/2IA8YgA9Syu7k3EUuzGmSexJOvIIpu2i2rBE3SPKWe6R7 gIsFYMgE9FC/X2Fy/+AL8IlAZxEW0tycqgh6Eg0j4oOY7HRdwIGuwEp/09qWeDQU8a cWZtgH+Pxa2BncxZqpuNka2q775m+T9SuuU2ZctMvi6uuZrz4USrfRW+iP4BRW93Q5 d8o9V4fJcOhWymqoHBE4GlFcd+4Gi8fGug58zUlQSU4eitGQmMjOSjrEB5fIqe/q9V sxkwwzolq95I+8IDtzGKjZno0se8VHZl8lb9EKv0QkT97hRAuwBL6399VFSbAvYrIv WapCsG0PbFuqQ== 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 11FFBEC1441; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 03 Mar 2026 13:27:07 +0000 Subject: [PATCH v8 02/10] lib: kstrtox: add kstrntoull() helper 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: <20260303-adf41513-iio-driver-v8-2-8dd2417cc465@analog.com> References: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> In-Reply-To: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-doc@vger.kernel.org Cc: Jonathan Cameron , David Lechner , Andy Shevchenko , Lars-Peter Clausen , Michael Hennerich , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Andrew Morton , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772544443; l=3621; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=QOA2ebXlCGOu4rEF7LtCPczO4bNdBpAyoo2OhH/870A=; b=wV3bcB78rfX1cS1C53uRVYeEZWLj9IlYYYqbKdCGwFXsZ9gp+BQgGEIz+SeKEFkki+7v3FBtC yQX8C5gQY2JD/Sxq364rygWsMEnB7CNS5Gh/P9MyRdSWPl4nE2Gmg3A 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 kstrntoull() function, which converts a string to an ULL with a max character limit. The function is an alternative integer parsing function that does not require a null-terminated string. It becomes a better option over simple_strtoull() or kstrtoull() when parsing integers from a buffer with custom delimiters without having to create temporary copies. The function is consumed inside the implementation _kstrtoull(), promoting reuse. Signed-off-by: Rodrigo Alencar --- include/linux/kstrtox.h | 3 +++ lib/kstrtox.c | 47 +++++++++++++++++++++++++++++++++++++++++++--= -- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/include/linux/kstrtox.h b/include/linux/kstrtox.h index 6ea897222af1..49a6102b8e15 100644 --- a/include/linux/kstrtox.h +++ b/include/linux/kstrtox.h @@ -9,6 +9,9 @@ int __must_check _kstrtoul(const char *s, unsigned int base, unsigned long= *res); int __must_check _kstrtol(const char *s, unsigned int base, long *res); =20 +ssize_t __must_check kstrntoull(const char *s, unsigned int base, + unsigned long long *res, size_t max_chars); + int __must_check kstrtoull(const char *s, unsigned int base, unsigned long= long *res); int __must_check kstrtoll(const char *s, unsigned int base, long long *res= ); =20 diff --git a/lib/kstrtox.c b/lib/kstrtox.c index bdde40cd69d7..202ecc058284 100644 --- a/lib/kstrtox.c +++ b/lib/kstrtox.c @@ -93,17 +93,56 @@ unsigned int _parse_integer(const char *s, unsigned int= base, unsigned long long return _parse_integer_limit(s, base, p, INT_MAX); } =20 -static int _kstrtoull(const char *s, unsigned int base, unsigned long long= *res) +/** + * kstrntoull - convert a string to an unsigned long long with a maximum + * character limit + * @s: The start of the string. The string does not need to be null-termin= ated. + * The first character cannot be a sign. + * @base: The number base to use. The maximum supported base is 16. If bas= e is + * given as 0, then the base of the string is automatically detected with= the + * conventional semantics - If it begins with 0x the number will be parse= d as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will= be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + * @res: Where to write the result of the conversion. + * @max_chars: The maximum number of characters to convert. + * + * Conversion stops when the maximum number of characters is reached or a + * non-digit character is encountered. + * + * Returns the number of characters consumed on success, -ERANGE on overfl= ow and + * -EINVAL on invalid input. Return code must be checked. + */ +noinline +ssize_t kstrntoull(const char *s, unsigned int base, unsigned long long *r= es, + size_t max_chars) { - unsigned long long _res; + const char *cp; + size_t prefix_cnt; unsigned int rv; =20 - s =3D _parse_integer_fixup_radix(s, &base); - rv =3D _parse_integer(s, base, &_res); + cp =3D _parse_integer_fixup_radix(s, &base); + prefix_cnt =3D cp - s; + if (prefix_cnt >=3D max_chars) + return -EINVAL; + + rv =3D _parse_integer_limit(cp, base, res, max_chars - prefix_cnt); if (rv & KSTRTOX_OVERFLOW) return -ERANGE; if (rv =3D=3D 0) return -EINVAL; + + return prefix_cnt + rv; +} +EXPORT_SYMBOL(kstrntoull); + +static int _kstrtoull(const char *s, unsigned int base, unsigned long long= *res) +{ + unsigned long long _res; + ssize_t rv; + + rv =3D kstrntoull(s, base, &_res, INT_MAX); + if (rv < 0) + return rv; s +=3D rv; if (*s =3D=3D '\n') s++; --=20 2.43.0 From nobody Thu Apr 9 16:35:12 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 8B75F426EC9; Tue, 3 Mar 2026 13:27:25 +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=1772544445; cv=none; b=lAqLvfGiKFlLtR32l6uO/rbye1dsJkMWhn4zL2ezVtpfZJ4l8b3sNtEKjRexoMEMZAdAHsltmuWk/5Fy64tyVfO8DfLM1nbr0wBZz6ExbIpEzVxrN6almCSRhBlz3kTF7Is4wLgdGFQTLe/WoPTAt6mLSnMb/hVULn2Of/niLlY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772544445; c=relaxed/simple; bh=xOGftZZO3xR/YkCPAbbzPAMCQRytfrvcdY0SCUp90pE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Kxc3g5/OaRAn/7ug8uEjchgx+4AfsGQbyT3KU+C3f4wkR57TARHk8tKqEmua99JtfwcNDX/9CcJzPkL0VqTRURKb15I097JQom48a49kkCBwTLvF/9lzuZFKaj7iceKzWAOkCcxa3CKmPRgsxTlSMBu3SKYa22Wo6pwuVliVJIU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QFwMtiHQ; 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="QFwMtiHQ" Received: by smtp.kernel.org (Postfix) with ESMTPS id 69478C2BCAF; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772544445; bh=xOGftZZO3xR/YkCPAbbzPAMCQRytfrvcdY0SCUp90pE=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=QFwMtiHQxhAD6BBLhlJ8Hyn3ZqTPfbjZ9qGTMN25nP15HTXZy3mAM1S3kebVMsrgi xSDDGgFSqWodXEmlMx6DtwdLKXzEI4ofxXwXLFW5WjkhGAnoYAf8fyNXtAKhDMCa27 MFlAV2uH0iqNb7Z6EN7+vNNM6NJnzeBywZ4z8YT+8JWuzi+f51z5iZcibR4U4hUpvI UMRUuMjU5VSGEhkXmJXxk7tRHt4/vFXP17G8ejz4DQYFBIyNr8m5kPIwNzh+EGe4sR ipl7EAlQrdvusunDqnkQUPFmigUXOBFQtxAClYGmNbvNV/NEfkCa5SVT4emfslPAHe 9Lz7o/I2iBmcA== 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 5E922EC1441; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 03 Mar 2026 13:27:08 +0000 Subject: [PATCH v8 03/10] lib: test-kstrtox: add tests for kstrntoull() 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: <20260303-adf41513-iio-driver-v8-3-8dd2417cc465@analog.com> References: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> In-Reply-To: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-doc@vger.kernel.org Cc: Jonathan Cameron , David Lechner , Andy Shevchenko , Lars-Peter Clausen , Michael Hennerich , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Andrew Morton , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772544443; l=4874; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=on3FpvpRalj0PIdH7WCk6vmRu4hDbH1Z0/+fJowkZVQ=; b=IAqWCFIDmLHXOgRb5CvzPQvGqnHhRHpKfOaZW2lL/RGQ/C7gRe4aBF8eQDyfQF0fjl3sJe3+9 0PBIjUMveHnBKypsaz0Ham7YEca4p9sowenwjvj85kd75Vtl1zFOSU8 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 test cases for the new kstrntoull() function which converts a string to unsigned long long with a maximum character limit. The OK tests cover basic decimal conversion, max_chars truncation, octal and hex with both autodetection and explicit base, stop-at-non-digit behavior, and chained conversions where the return value is used to advance through a compound string (e.g. "192.168.1.0", "123::456"). The fail tests cover overflow for decimal, hex, and octal, prefix consumed entirely by max_chars, zero max_chars, empty string, and bare hex prefix. Signed-off-by: Rodrigo Alencar --- lib/test-kstrtox.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 121 insertions(+) diff --git a/lib/test-kstrtox.c b/lib/test-kstrtox.c index ee87fef66cb5..c3fe2a3bc142 100644 --- a/lib/test-kstrtox.c +++ b/lib/test-kstrtox.c @@ -703,6 +703,124 @@ static void __init test_kstrtos8_fail(void) TEST_FAIL(kstrtos8, s8, "%hhd", test_s8_fail); } =20 +static void __init test_kstrntoull_ok(void) +{ + struct test_ntoull { + const char *str; + size_t step; + unsigned int base; + size_t max_chars; + ssize_t expected_rv; + unsigned long long expected_res; + }; + static const struct test_ntoull test[] __initconst =3D { + /* basic decimal */ + {"0", 0, 10, 1, 1, 0ULL}, + {"1", 0, 10, 1, 1, 1ULL}, + {"123", 0, 10, 3, 3, 123ULL}, + {"123", 0, 10, 10, 3, 123ULL}, + /* max_chars truncation */ + {"1234567", 0, 10, 1, 1, 1ULL}, + {"1234567", 0, 10, 3, 3, 123ULL}, + {"1234567", 0, 10, 5, 5, 12345ULL}, + /* octal with base autodetection */ + {"0177", 0, 0, 4, 4, 0177ULL}, + {"0177", 0, 0, 3, 3, 017ULL}, + {"0177", 0, 0, 2, 2, 01ULL}, + /* octal with explicit base */ + {"01777", 0, 8, 4, 4, 0177ULL}, + {"01777", 0, 8, 3, 3, 017ULL}, + /* hex with base autodetection */ + {"0xff", 0, 0, 4, 4, 0xffULL}, + {"0xffff", 0, 0, 4, 4, 0xffULL}, + {"0xffff", 0, 0, 6, 6, 0xffffULL}, + {"0xFF", 0, 0, 4, 4, 0xffULL}, + /* hex with explicit base */ + {"0xff", 0, 16, 4, 4, 0xffULL}, + {"ff", 0, 16, 2, 2, 0xffULL}, + /* stop at non-digit */ + {"12abc", 0, 10, 5, 2, 12ULL}, + /* large values */ + {"18446744073709551615", 0, 10, 20, 20, 18446744073709551615ULL}, + {"ffffffffffffffff", 0, 16, 16, 16, 0xffffffffffffffffULL}, + /* chained tests */ + {"123::456", 0, 10, 10, 3, 123ULL}, + {NULL, 2, 10, 10, 3, 456ULL}, + {"192.168.1.0", 0, 10, 10, 3, 192ULL}, + {NULL, 1, 10, 10, 3, 168ULL}, + {NULL, 1, 10, 10, 1, 1ULL}, + {NULL, 1, 10, 10, 1, 0ULL}, + {"123-beef", 0, 10, 10, 3, 123ULL}, + {NULL, 1, 16, 10, 4, 0xBEEFULL}, + {"12345678", 0, 10, 5, 5, 12345ULL}, + {NULL, 0, 10, 5, 3, 678ULL}, + }; + unsigned int i; + const char *s =3D NULL; + + for_each_test(i, test) { + const struct test_ntoull *t =3D &test[i]; + unsigned long long res =3D ~0ULL; + ssize_t rv; + + if (t->str) + s =3D t->str; + s +=3D t->step; + + rv =3D kstrntoull(s, t->base, &res, t->max_chars); + if (rv !=3D t->expected_rv) { + WARN(1, "str '%s', base %u, max_chars %zu, expected rv %zd, got %zd\n", + s, t->base, t->max_chars, t->expected_rv, rv); + continue; + } + if (rv >=3D 0 && res !=3D t->expected_res) { + WARN(1, "str '%s', base %u, max_chars %zu, expected %llu, got %llu\n", + s, t->base, t->max_chars, t->expected_res, res); + continue; + } + s +=3D rv; + } +} + +static void __init test_kstrntoull_fail(void) +{ + struct test_ntoull_fail { + const char *str; + unsigned int base; + size_t max_chars; + ssize_t expected_rv; + }; + static const struct test_ntoull_fail test[] __initconst =3D { + /* overflow */ + {"18446744073709551616", 10, 20, -ERANGE}, + {"10000000000000000", 16, 17, -ERANGE}, + {"2000000000000000000000", 8, 22, -ERANGE}, + /* max_chars is too small */ + {"123", 10, 0, -EINVAL}, + {"0xff", 0, 1, -EINVAL}, + {"0xff", 0, 2, -EINVAL}, + {"0xff", 16, 1, -EINVAL}, + {"0xff", 16, 2, -EINVAL}, + /* empty string */ + {"", 10, 10, -EINVAL}, + {"0x", 16, 10, -EINVAL}, + }; + unsigned int i; + + for_each_test(i, test) { + const struct test_ntoull_fail *t =3D &test[i]; + unsigned long long res =3D 0; + ssize_t rv; + + rv =3D kstrntoull(t->str, t->base, &res, t->max_chars); + if (rv !=3D t->expected_rv) { + WARN(1, "str '%s', base %u, max_chars %zu, expected %zd, got %zd\n", + t->str, t->base, t->max_chars, t->expected_rv, rv); + continue; + } + } +} + static int __init test_kstrtox_init(void) { test_kstrtoull_ok(); @@ -729,6 +847,9 @@ static int __init test_kstrtox_init(void) test_kstrtou8_fail(); test_kstrtos8_ok(); test_kstrtos8_fail(); + + test_kstrntoull_ok(); + test_kstrntoull_fail(); return -EINVAL; } module_init(test_kstrtox_init); --=20 2.43.0 From nobody Thu Apr 9 16:35:12 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 A07CA426ED5; Tue, 3 Mar 2026 13:27:25 +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=1772544445; cv=none; b=qyl5l8JiSEK5H2ZV+KXfzjNiA2c/vE3Imj4USzviczvv8YY0WK22k4J92wJVTW9451NUDWuiJpHQt50jl3dlTlHxPD9j+4MbzrKFqtxOOEMTeNH5KO9wZYTpFYcv3BFxY/zQeHsjcjouJjgmMSh98XQA373WRLW8zB9+cohpwT4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772544445; c=relaxed/simple; bh=DadI7Pm+LcMob1lJ1yq3GRCvXcJb0mTs7GhDj1FkOig=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=Uwq8eL9wX0/mT2THWiaCU1+ZQmiz5GUTsfp7zZAkoR+cleaSLiDS4yq/JAFgkeVnZ4X2hT3NtvLWrTUKTxF+W+eWlSRscsF+6N6OTnUB+Hfz8dLYTUfHaPau0MmJxMLRoxg75+lNRWSBd6aq0LOUpNgDZOCe3mAnT35MdhDSBlQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=g8kHq2TS; 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="g8kHq2TS" Received: by smtp.kernel.org (Postfix) with ESMTPS id 8139AC2BC9E; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772544445; bh=DadI7Pm+LcMob1lJ1yq3GRCvXcJb0mTs7GhDj1FkOig=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=g8kHq2TS1FqOi1o+Q22UJ9PZh6r4gjB7ebx98NFmSPeCvrOWksMCaYnO/FmFVOc2Y DVx0rnPASbTVM2yTbC5bfxsrroVqS4RxA8c3Mr0ffqnbOz8Lk910e82vLQQEPO6uzD pkn/fxPhBgqP2a7yUQVlFnzE3emJp8TCA/NQVbGbZaMyPTsqU91MYtvByau6bLCI9p IPbhMFVqFLPcPBa6vuaEio36xrzip0KdexYqkvpugGuCVOlBBI100+BXquJVEzFara S5T91/lTRWI8jRmQxcNZNkRK7Ojdhc748rTm2jky7nnBdCkpmU/4NIwWIpvbfVkzks skS14D4sr86zQ== 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 78890EC143E; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 03 Mar 2026 13:27:09 +0000 Subject: [PATCH v8 04/10] iio: core: add fixed point parsing with 64-bit parts 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: <20260303-adf41513-iio-driver-v8-4-8dd2417cc465@analog.com> References: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> In-Reply-To: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-doc@vger.kernel.org Cc: Jonathan Cameron , David Lechner , Andy Shevchenko , Lars-Peter Clausen , Michael Hennerich , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Andrew Morton , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772544443; l=5981; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=VNv64hEq1DZF87gKYc9yb7EPjvgLxM+SfdYJSvzRCYg=; b=oobZ+5X4lz+i5hZs1l5gaUZmz+tXISbmFISbn50MOvuot3udJX0rHNV+zlvzErdb/E6zt+KHY Xu9iR8GozovD4ceqXkOAX/8Wj//bc8Or4LEHdNqWTWIybtRfNj700hI 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_str_to_fixpoint64() function that leverages simple_strtoull() to parse numbers from a string. A helper function __iio_str_to_fixpoint64() replaces __iio_str_to_fixpoint() implementation, extending its usage for 64-bit fixed-point parsing. Signed-off-by: Rodrigo Alencar --- drivers/iio/industrialio-core.c | 167 ++++++++++++++++++++++++++++--------= ---- include/linux/iio/iio.h | 2 + 2 files changed, 119 insertions(+), 50 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-cor= e.c index 3115d59c1372..3d7f0427c811 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -881,6 +881,93 @@ static ssize_t iio_read_channel_info_avail(struct devi= ce *dev, } } =20 +/** + * __iio_str_to_fixpoint64() - Parse a fixed-point number from a string + * @str: The string to parse + * @fract_mult: Multiplier for the first decimal place, should be a power = of 10 + * @integer: The integer part of the number + * @fract: The fractional part of the number + * @scale_db: True if this should parse as dB + * + * This variant uses 64-bit integers for both integer and fractional parts. + * Parsed positive values greater than S64_MAX are returned as-is. Parsed + * negative values less than S64_MIN are treated as range error, so -ERANG= E is + * returned. + * + * Returns: + * 0 on success, or a negative error code if the string could not be parse= d. + */ +static int __iio_str_to_fixpoint64(const char *str, u64 fract_mult, + s64 *integer, s64 *fract, bool scale_db) +{ + u64 i =3D 0, f =3D 0; + int precision =3D ffs(fract_mult); + bool negative =3D false; + ssize_t len; + + if (precision > 20) /* ceil(log10(U64_MAX)) =3D 20 */ + return -EINVAL; + + if (str[0] =3D=3D '-') { + negative =3D true; + str++; + } else if (str[0] =3D=3D '+') { + str++; + } + + if (*str !=3D '.') { + len =3D kstrntoull(str, 10, &i, SIZE_MAX); + if (len < 0) + return len; + str +=3D len; + } + + if (precision && *str =3D=3D '.') { + str++; /* skip decimal point */ + len =3D kstrntoull(str, 10, &f, precision); + if (len < 0) + return len; + str +=3D len; + + if (len < precision) /* scale up */ + f *=3D int_pow(10, precision - len); + + while (isdigit(*str)) /* truncate: ignore further digits */ + str++; + } + + if (scale_db) { + /* Ignore the dB suffix */ + if (!strncmp(str, " dB", sizeof(" dB") - 1)) + str +=3D sizeof(" dB") - 1; + else if (!strncmp(str, "dB", sizeof("dB") - 1)) + str +=3D sizeof("dB") - 1; + } + + if (*str =3D=3D '\n') + str++; + + if (*str !=3D '\0') + return -EINVAL; + + if (negative) { + if (i) { + if ((s64)-i > 0) + return -ERANGE; + i =3D -i; + } else { + if ((s64)-f > 0) + return -ERANGE; + f =3D -f; + } + } + + *integer =3D i; + *fract =3D f; + + return 0; +} + /** * __iio_str_to_fixpoint() - Parse a fixed-point number from a string * @str: The string to parse @@ -895,63 +982,43 @@ static ssize_t iio_read_channel_info_avail(struct dev= ice *dev, static int __iio_str_to_fixpoint(const char *str, int fract_mult, int *integer, int *fract, bool scale_db) { - int i =3D 0, f =3D 0; - bool integer_part =3D true, negative =3D false; + s64 integer64, fract64; + int ret; =20 - if (fract_mult =3D=3D 0) { - *fract =3D 0; + ret =3D __iio_str_to_fixpoint64(str, fract_mult, &integer64, &fract64, + scale_db); + if (ret) + return ret; =20 - return kstrtoint(str, 0, integer); - } + if (integer64 < INT_MIN || integer64 > UINT_MAX || + fract64 < INT_MIN || fract64 > UINT_MAX) + return -ERANGE; =20 - if (str[0] =3D=3D '-') { - negative =3D true; - str++; - } else if (str[0] =3D=3D '+') { - str++; - } - - while (*str) { - if ('0' <=3D *str && *str <=3D '9') { - if (integer_part) { - i =3D i * 10 + *str - '0'; - } else { - f +=3D fract_mult * (*str - '0'); - fract_mult /=3D 10; - } - } else if (*str =3D=3D '\n') { - if (*(str + 1) =3D=3D '\0') - break; - return -EINVAL; - } else if (!strncmp(str, " dB", sizeof(" dB") - 1) && scale_db) { - /* Ignore the dB suffix */ - str +=3D sizeof(" dB") - 1; - continue; - } else if (!strncmp(str, "dB", sizeof("dB") - 1) && scale_db) { - /* Ignore the dB suffix */ - str +=3D sizeof("dB") - 1; - continue; - } else if (*str =3D=3D '.' && integer_part) { - integer_part =3D false; - } else { - return -EINVAL; - } - str++; - } - - if (negative) { - if (i) - i =3D -i; - else - f =3D -f; - } - - *integer =3D i; - *fract =3D f; + *integer =3D integer64; + *fract =3D fract64; =20 return 0; } =20 +/** + * iio_str_to_fixpoint64() - Parse a fixed-point number from a string + * @str: The string to parse + * @fract_mult: Multiplier for the first decimal place, should be a power = of 10 + * @integer: The integer part of the number + * @fract: The fractional part of the number + * + * This variant uses 64-bit integers for both integer and fractional parts. + * + * Returns: + * 0 on success, or a negative error code if the string could not be parse= d. + */ +int iio_str_to_fixpoint64(const char *str, u64 fract_mult, s64 *integer, + s64 *fract) +{ + return __iio_str_to_fixpoint64(str, fract_mult, integer, fract, false); +} +EXPORT_SYMBOL_GPL(iio_str_to_fixpoint64); + /** * iio_str_to_fixpoint() - Parse a fixed-point number from a string * @str: The string to parse diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index a9ecff191bd9..cb30d153465a 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -1055,6 +1055,8 @@ int iio_active_scan_mask_index(struct iio_dev *indio_= dev); =20 ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals= ); =20 +int iio_str_to_fixpoint64(const char *str, u64 fract_mult, s64 *integer, + s64 *fract); int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer, int *fract); =20 --=20 2.43.0 From nobody Thu Apr 9 16:35:12 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 C0E83428834; Tue, 3 Mar 2026 13:27:25 +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=1772544445; cv=none; b=oQJ0WCy2PX6FSV/f3d6pTTxH8JGvvmTH4gDz0tSfMqSmDOW5KUnL/YUXNBPHbInz1ckPKrOyLeCsWZSuhT740KFJpeOOOO24VxRBqTn7LxlxzKPUUPz/duK4IMhh/1NJlddRz3+F5iJj7YEWjJ8gQ53NBSSENKoj3geV1jAXrS8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772544445; c=relaxed/simple; bh=LoT/MwyW6irFFGk3XbxSmhNlYy09mgw4qgZOmNoZT34=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=HUcIM0EX24aQ8voEpaXYkbusPPIzaADdd9a8SmzEKYDRFGMIYRlVLc4prfw71b822IF1IdeZXkmrwosRaQzKDx1y30YL07TxPFmPqNmllps22vavBqK1Gfcw9JvfcQRJ2PzE+bMyAK6WBS4tc4JBlvyIGnXs3oWmgekOgVhiFZk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FxLyEIWH; 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="FxLyEIWH" Received: by smtp.kernel.org (Postfix) with ESMTPS id 9A054C116C6; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772544445; bh=LoT/MwyW6irFFGk3XbxSmhNlYy09mgw4qgZOmNoZT34=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=FxLyEIWHMfO0gWe00FuOnbdimyYhOUDlTXYxF1joiI1nOSoy5ctvowmlwpyt9QAza WI+ENDQjj+3XPrysLb+lD3ByfE97n2bg5icLj7BOUFqv/oinmfMWouHPXkjfjEFqIi Xck1vVPkzg0X9itn1XkCBdlqxfe47Ygy81nhEJMCbfctZ2UyYWZ3SfzeTotd0fGLyh Z+F29SH2QRsbqpXwwiwytrWzyFaVDc+U7keaBoUyAAvQ6uTs9o4LocAToRJshwfsqf wybXEJDLSY+ZUs+EmPtNW2sC1Iyzwfz+kYQfxYagUQvIQci1G1fKOxKi4QKp3tZwgO I3OCHsAhovksg== 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 8F6CBEC1438; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 03 Mar 2026 13:27:10 +0000 Subject: [PATCH v8 05/10] iio: test: add kunit test for fixed-point parsing 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: <20260303-adf41513-iio-driver-v8-5-8dd2417cc465@analog.com> References: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> In-Reply-To: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-doc@vger.kernel.org Cc: Jonathan Cameron , David Lechner , Andy Shevchenko , Lars-Peter Clausen , Michael Hennerich , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Andrew Morton , Rodrigo Alencar , Andy Shevchenko X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772544443; l=18700; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=+yfvv3VeJBnet8WMz5yVGXzQUj8CngD0kPKe93doM7g=; b=H6Vd3795YJPqVHOV/mfr8ohfKdYskf+fnLm8lgPwutTPye0RMThosmqui2fWYSxH15KFfyTap zFGHLrohiwPBuVHHFKUxD5IJtD6bWVLzHoSzD8X+7KpK59lR8NtXCTT 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 kunit test cases that aims to verify expected behavior for iio_str_to_fixpoint() and iio_str_to_fixpoint64(). To run the test, create a .kunitconfig file with: CONFIG_KUNIT=3Dy CONFIG_IIO=3Dy CONFIG_IIO_FIXPOINT_PARSE_KUNIT_TEST=3Dy and run the command: ./tools/testing/kunit/kunit.py run --kunitconfig=3D.kunitconfig Reviewed-by: Andy Shevchenko Signed-off-by: Rodrigo Alencar --- MAINTAINERS | 6 + drivers/iio/test/Kconfig | 12 + drivers/iio/test/Makefile | 1 + drivers/iio/test/iio-test-fixpoint-parse.c | 470 +++++++++++++++++++++++++= ++++ 4 files changed, 489 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1bf39f6d964e..463c888b428f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12325,6 +12325,12 @@ F: include/dt-bindings/iio/ F: include/linux/iio/ F: tools/iio/ =20 +IIO FIXED POINT PARSE TEST +M: Rodrigo Alencar +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/test/iio-test-fixpoint-parse.c + IIO UNIT CONVERTER M: Peter Rosin L: linux-iio@vger.kernel.org diff --git a/drivers/iio/test/Kconfig b/drivers/iio/test/Kconfig index 4fc17dd0dcd7..c60dff504bc2 100644 --- a/drivers/iio/test/Kconfig +++ b/drivers/iio/test/Kconfig @@ -29,6 +29,18 @@ config IIO_RESCALE_KUNIT_TEST =20 If unsure, say N. =20 +config IIO_FIXPOINT_PARSE_KUNIT_TEST + tristate "Test IIO fixpoint parsing functions" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + build unit tests for the IIO fixpoint parsing functions. + + 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_FORMAT_KUNIT_TEST tristate "Test IIO formatting functions" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/drivers/iio/test/Makefile b/drivers/iio/test/Makefile index 0c846bc21acd..0c31aaeed755 100644 --- a/drivers/iio/test/Makefile +++ b/drivers/iio/test/Makefile @@ -5,6 +5,7 @@ =20 # Keep in alphabetical order obj-$(CONFIG_IIO_RESCALE_KUNIT_TEST) +=3D iio-test-rescale.o +obj-$(CONFIG_IIO_FIXPOINT_PARSE_KUNIT_TEST) +=3D iio-test-fixpoint-parse.o obj-$(CONFIG_IIO_FORMAT_KUNIT_TEST) +=3D iio-test-format.o obj-$(CONFIG_IIO_GTS_KUNIT_TEST) +=3D iio-test-gts.o obj-$(CONFIG_IIO_MULTIPLY_KUNIT_TEST) +=3D iio-test-multiply.o diff --git a/drivers/iio/test/iio-test-fixpoint-parse.c b/drivers/iio/test/= iio-test-fixpoint-parse.c new file mode 100644 index 000000000000..69715d22d7ec --- /dev/null +++ b/drivers/iio/test/iio-test-fixpoint-parse.c @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Unit tests for IIO fixpoint parsing functions + * + * Copyright 2026 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include + +#define PRECISION(x) (int_pow(10, (x) - 1)) + +/* Test iio_str_to_fixpoint64() with valid positive integers */ +static void iio_test_str_to_fixpoint64_positive_integers(struct kunit *tes= t) +{ + s64 integer, fract; + int ret; + + /* Simple positive integer */ + ret =3D iio_str_to_fixpoint64("42", 0, &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 42); + KUNIT_EXPECT_EQ(test, fract, 0); + + /* Positive integer with leading + */ + ret =3D iio_str_to_fixpoint64("+10", 0, &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 10); + KUNIT_EXPECT_EQ(test, fract, 0); + + /* Large positive integer */ + ret =3D iio_str_to_fixpoint64("123456789", 0, &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 123456789); + KUNIT_EXPECT_EQ(test, fract, 0); +} + +/* Test iio_str_to_fixpoint64() with valid negative integers */ +static void iio_test_str_to_fixpoint64_negative_integers(struct kunit *tes= t) +{ + s64 integer, fract; + int ret; + + /* Simple negative integer */ + ret =3D iio_str_to_fixpoint64("-23", 0, &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, -23); + KUNIT_EXPECT_EQ(test, fract, 0); + + /* Large negative integer */ + ret =3D iio_str_to_fixpoint64("-987654321", 0, &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, -987654321); + KUNIT_EXPECT_EQ(test, fract, 0); +} + +/* Test iio_str_to_fixpoint64() with zero */ +static void iio_test_str_to_fixpoint64_zero(struct kunit *test) +{ + s64 integer, fract; + int ret; + + /* Zero */ + ret =3D iio_str_to_fixpoint64("0", 0, &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 0); + KUNIT_EXPECT_EQ(test, fract, 0); + + /* Zero with decimal */ + ret =3D iio_str_to_fixpoint64("0.0", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 0); + KUNIT_EXPECT_EQ(test, fract, 0); + + /* leading zeros */ + ret =3D iio_str_to_fixpoint64("00000000000000000000042", 0, &integer, + &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 42); + KUNIT_EXPECT_EQ(test, fract, 0); +} + +/* Test iio_str_to_fixpoint64() with valid decimal numbers */ +static void iio_test_str_to_fixpoint64_positive_decimals(struct kunit *tes= t) +{ + s64 integer, fract; + int ret; + + /* Positive decimal */ + ret =3D iio_str_to_fixpoint64("3.14", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 3); + KUNIT_EXPECT_EQ(test, fract, 140000); + + /* Decimal less than 1 */ + ret =3D iio_str_to_fixpoint64("0.5", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 0); + KUNIT_EXPECT_EQ(test, fract, 500000); + + /* Decimal with trailing zeros */ + ret =3D iio_str_to_fixpoint64("+123.000", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 123); + KUNIT_EXPECT_EQ(test, fract, 0); + + /* High precision decimal */ + ret =3D iio_str_to_fixpoint64("1.123456789", PRECISION(9), &integer, + &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 1); + KUNIT_EXPECT_EQ(test, fract, 123456789); + + /* Small decimal */ + ret =3D iio_str_to_fixpoint64("0.000000001", PRECISION(9), &integer, + &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 0); + KUNIT_EXPECT_EQ(test, fract, 1); +} + +/* Test iio_str_to_fixpoint64() with negative decimals */ +static void iio_test_str_to_fixpoint64_negative_decimals(struct kunit *tes= t) +{ + s64 integer, fract; + int ret; + + /* Negative decimal */ + ret =3D iio_str_to_fixpoint64("-2.71", PRECISION(5), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, -2); + KUNIT_EXPECT_EQ(test, fract, 71000); + + /* Negative decimal less than -1 */ + ret =3D iio_str_to_fixpoint64("-0.5", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 0); + KUNIT_EXPECT_EQ(test, fract, -500000); + + /* Negative with high precision */ + ret =3D iio_str_to_fixpoint64("-0.000000001", PRECISION(9), &integer, + &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 0); + KUNIT_EXPECT_EQ(test, fract, -1); +} + +/* Test iio_str_to_fixpoint64() with precision edge cases */ +static void iio_test_str_to_fixpoint64_precision_edge_cases(struct kunit *= test) +{ + s64 integer, fract; + int ret; + + /* More digits than precision - should truncate */ + ret =3D iio_str_to_fixpoint64("1.23456", PRECISION(3), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 1); + KUNIT_EXPECT_EQ(test, fract, 234); + + /* Fewer digits than precision - should pad with zeros */ + ret =3D iio_str_to_fixpoint64("1.23", PRECISION(7), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 1); + KUNIT_EXPECT_EQ(test, fract, 2300000); + + /* Single digit fractional with high precision */ + ret =3D iio_str_to_fixpoint64("5.1", PRECISION(9), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 5); + KUNIT_EXPECT_EQ(test, fract, 100000000); +} + +/* Test iio_str_to_fixpoint64() with newline characters */ +static void iio_test_str_to_fixpoint64_with_newline(struct kunit *test) +{ + s64 integer, fract; + int ret; + + /* Integer with newline */ + ret =3D iio_str_to_fixpoint64("-42\n", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, -42); + KUNIT_EXPECT_EQ(test, fract, 0); + + /* Decimal with newline */ + ret =3D iio_str_to_fixpoint64("3.141\n", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 3); + KUNIT_EXPECT_EQ(test, fract, 141000); +} + +/* Test iio_str_to_fixpoint64() with edge cases */ +static void iio_test_str_to_fixpoint64_edge_cases(struct kunit *test) +{ + s64 integer, fract; + int ret; + + /* Leading decimal point */ + ret =3D iio_str_to_fixpoint64(".5", PRECISION(4), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 0); + KUNIT_EXPECT_EQ(test, fract, 5000); + + /* Leading decimal with sign */ + ret =3D iio_str_to_fixpoint64("-.5", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 0); + KUNIT_EXPECT_EQ(test, fract, -500000); + + ret =3D iio_str_to_fixpoint64("+.5", PRECISION(3), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 0); + KUNIT_EXPECT_EQ(test, fract, 500); +} + +/* Test iio_str_to_fixpoint64() with invalid inputs */ +static void iio_test_str_to_fixpoint64_invalid(struct kunit *test) +{ + s64 integer, fract; + int ret; + + /* Empty string */ + ret =3D iio_str_to_fixpoint64("", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + /* Just a sign */ + ret =3D iio_str_to_fixpoint64("-", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + ret =3D iio_str_to_fixpoint64("+", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + /* Just a decimal point */ + ret =3D iio_str_to_fixpoint64(".", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + /* Non-numeric characters */ + ret =3D iio_str_to_fixpoint64("abc", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + ret =3D iio_str_to_fixpoint64("12a", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + ret =3D iio_str_to_fixpoint64("3.4x", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + ret =3D iio_str_to_fixpoint64("0xff", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + /* Multiple decimal points */ + ret =3D iio_str_to_fixpoint64("12.34.56", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + /* Trailing decimal without digits */ + ret =3D iio_str_to_fixpoint64("42.", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + /* Trailing spaces */ + ret =3D iio_str_to_fixpoint64("42 ", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + /* Too many digits in fractional part */ + ret =3D iio_str_to_fixpoint64("1.123456789012345678901", PRECISION(21), + &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); /* fails when checking precision */ +} + +/* Test iio_str_to_fixpoint() with valid inputs */ +static void iio_test_str_to_fixpoint_valid(struct kunit *test) +{ + int integer, fract; + int ret; + + /* Test with 6 decimal places */ + ret =3D iio_str_to_fixpoint("10.001234", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 10); + KUNIT_EXPECT_EQ(test, fract, 1234); + + ret =3D iio_str_to_fixpoint("-0.5", PRECISION(3), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 0); + KUNIT_EXPECT_EQ(test, fract, -500); + + /* Test with 9 decimal places */ + ret =3D iio_str_to_fixpoint("5.123456789", PRECISION(9), &integer, &fract= ); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 5); + KUNIT_EXPECT_EQ(test, fract, 123456789); + + ret =3D iio_str_to_fixpoint("1.0", PRECISION(9), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 1); + KUNIT_EXPECT_EQ(test, fract, 0); + + /* Test with 3 decimal places */ + ret =3D iio_str_to_fixpoint("-7.8", PRECISION(3), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, -7); + KUNIT_EXPECT_EQ(test, fract, 800); + + /* Truncation with 2 decimal places */ + ret =3D iio_str_to_fixpoint("3.1415", PRECISION(2), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 3); + KUNIT_EXPECT_EQ(test, fract, 14); + + /* Integer with 6 decimal places */ + ret =3D iio_str_to_fixpoint("42", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer, 42); + KUNIT_EXPECT_EQ(test, fract, 0); +} + +/* Test both functions with overflow cases */ +static void iio_test_str_to_fixpoint_overflow(struct kunit *test) +{ + s64 integer64, fract64; + int integer, fract; + int ret; + + /* integer overflow - value exceeds U64_MAX */ + ret =3D iio_str_to_fixpoint64("18446744073709551616", PRECISION(6), + &integer64, &fract64); + KUNIT_EXPECT_EQ(test, ret, -ERANGE); + + /* integer underflow - value less than S64_MIN */ + ret =3D iio_str_to_fixpoint64("-9223372036854775809", PRECISION(6), + &integer64, &fract64); + KUNIT_EXPECT_EQ(test, ret, -ERANGE); + + /* fractional underflow */ + ret =3D iio_str_to_fixpoint64("-0.9223372036854775810", PRECISION(19), + &integer64, &fract64); + KUNIT_EXPECT_EQ(test, ret, -ERANGE); + + /* Integer overflow - value exceeds U32_MAX */ + ret =3D iio_str_to_fixpoint("4294967296", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -ERANGE); + + /* Integer underflow - value less than INT_MIN */ + ret =3D iio_str_to_fixpoint("-2147483649", PRECISION(6), &integer, + &fract); + KUNIT_EXPECT_EQ(test, ret, -ERANGE); + + /* fractional overflow */ + ret =3D iio_str_to_fixpoint("0.4294967296", PRECISION(10), &integer, + &fract); + KUNIT_EXPECT_EQ(test, ret, -ERANGE); + + /* fractional underflow */ + ret =3D iio_str_to_fixpoint("-0.2147483649", PRECISION(10), &integer, + &fract); + KUNIT_EXPECT_EQ(test, ret, -ERANGE); +} + +/* Test iio_str_to_fixpoint() with invalid inputs */ +static void iio_test_str_to_fixpoint_invalid(struct kunit *test) +{ + int integer, fract; + int ret; + + /* Empty string */ + ret =3D iio_str_to_fixpoint("", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_NE(test, ret, 0); + + /* Non-numeric */ + ret =3D iio_str_to_fixpoint("abc", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_NE(test, ret, 0); + + /* Invalid characters */ + ret =3D iio_str_to_fixpoint("12.34x", PRECISION(6), &integer, &fract); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +/* Test both functions with boundary values */ +static void iio_test_fixpoint_boundary_values(struct kunit *test) +{ + s64 integer64, fract64; + int integer32, fract32; + int ret; + + /* S32_MAX */ + ret =3D iio_str_to_fixpoint("2147483647", PRECISION(6), &integer32, + &fract32); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer32, S32_MAX); + KUNIT_EXPECT_EQ(test, fract32, 0); + + /* U32_MAX */ + ret =3D iio_str_to_fixpoint("4294967295", PRECISION(6), &integer32, + &fract32); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (u32)integer32, U32_MAX); + KUNIT_EXPECT_EQ(test, fract32, 0); + + /* S32_MIN */ + ret =3D iio_str_to_fixpoint("-2147483648", PRECISION(6), &integer32, + &fract32); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer32, S32_MIN); + KUNIT_EXPECT_EQ(test, fract32, 0); + + /* S32_MIN with fractional part */ + ret =3D iio_str_to_fixpoint("-2147483648.2147483647", PRECISION(10), + &integer32, &fract32); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer32, S32_MIN); + KUNIT_EXPECT_EQ(test, fract32, S32_MAX); + + /* S64_MAX */ + ret =3D iio_str_to_fixpoint64("9223372036854775807", PRECISION(6), + &integer64, &fract64); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer64, S64_MAX); + KUNIT_EXPECT_EQ(test, fract64, 0); + + /* U64_MAX */ + ret =3D iio_str_to_fixpoint64("18446744073709551615", PRECISION(6), + &integer64, &fract64); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (u64)integer64, U64_MAX); + KUNIT_EXPECT_EQ(test, fract64, 0); + + /* S64_MIN */ + ret =3D iio_str_to_fixpoint64("-9223372036854775808", PRECISION(6), + &integer64, &fract64); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer64, S64_MIN); + KUNIT_EXPECT_EQ(test, fract64, 0); + + /* S64_MIN with fractional part */ + ret =3D iio_str_to_fixpoint64("-9223372036854775808.9223372036854775807", + PRECISION(19), &integer64, &fract64); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, integer64, S64_MIN); + KUNIT_EXPECT_EQ(test, fract64, S64_MAX); +} + +static struct kunit_case iio_fixpoint_parse_test_cases[] =3D { + KUNIT_CASE(iio_test_str_to_fixpoint64_positive_integers), + KUNIT_CASE(iio_test_str_to_fixpoint64_negative_integers), + KUNIT_CASE(iio_test_str_to_fixpoint64_zero), + KUNIT_CASE(iio_test_str_to_fixpoint64_positive_decimals), + KUNIT_CASE(iio_test_str_to_fixpoint64_negative_decimals), + KUNIT_CASE(iio_test_str_to_fixpoint64_precision_edge_cases), + KUNIT_CASE(iio_test_str_to_fixpoint64_with_newline), + KUNIT_CASE(iio_test_str_to_fixpoint64_edge_cases), + KUNIT_CASE(iio_test_str_to_fixpoint64_invalid), + KUNIT_CASE(iio_test_str_to_fixpoint_valid), + KUNIT_CASE(iio_test_str_to_fixpoint_overflow), + KUNIT_CASE(iio_test_str_to_fixpoint_invalid), + KUNIT_CASE(iio_test_fixpoint_boundary_values), + { } +}; + +static struct kunit_suite iio_fixpoint_parse_test_suite =3D { + .name =3D "iio-fixpoint-parse", + .test_cases =3D iio_fixpoint_parse_test_cases, +}; + +kunit_test_suite(iio_fixpoint_parse_test_suite); + +MODULE_AUTHOR("Rodrigo Alencar "); +MODULE_AUTHOR("IIO Kunit Test"); +MODULE_DESCRIPTION("Test IIO fixpoint parsing functions"); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Thu Apr 9 16:35:12 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 D74ED426EAD; Tue, 3 Mar 2026 13:27:25 +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=1772544445; cv=none; b=ktM1Jw3LSst7LHhQfyCxk/3FjopVaX+TNGrms4nfTZf0mwM/bjhBbj132p0okIpslkJsCxCUa4YVHfYAAt8KCWLDHlboS4xhnCbJASbqrEzE5KBhtS58euDAk+s22gDbSfnuHoAdj0iXVzdHtohcjApQEmFFhZmG2iBAkH9vp5U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772544445; c=relaxed/simple; bh=FHT0mj6gktH1KiRXQ0IRUW2NviOdc1QfrtOGxyoqnXo=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ZQ3jCsOE9O5k/tbDQq0Keaf8+VGJ76xKKfJsKXmLjBSRhgqq34UGGRIiXd+s5Iy9Ep3U7JBU5+YzwfuCWEzIkeZ8dmL3Ylk+w2KnGZ4cItrMUAaaaP/wisbuFbBi38d3lL6hF/rlRzixenF6Wfjlkx/trwQztJg8uYhSlqYhN3o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=HWn6UBJI; 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="HWn6UBJI" Received: by smtp.kernel.org (Postfix) with ESMTPS id B4465C2BCB3; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772544445; bh=FHT0mj6gktH1KiRXQ0IRUW2NviOdc1QfrtOGxyoqnXo=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=HWn6UBJIqUgibBMd1qpGZ1YBr1birLy9qt2ygJGcrnpTJIJecIqW0TVdsLJhS69SD nz+4Haq2XTt6D2HYUGVzWdrwT2YqGaGtbSrRHmPYtMvJq0hdeW1nWPvFsyBhVe8M0q TzQWlR71q8TfavNLXnatNLxyMW3YMXWnP11m84XRS4Ka6NcW6g9ff35LVLeiv3nGvf sL+g2b2fAHmjPmSPg91kNyoePIfwlEVk9X9UxUK6MdSjH5+8S673hK44lLhEiOEDLs zaFvA/KSA4M0sdcCsa5/4mZlDwqnvg75y11ltjx/0SGZjzl1OpgfrFgRTqPaWHaAWU HxqgD1xvCgsww== 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 A8903EC1444; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 03 Mar 2026 13:27:11 +0000 Subject: [PATCH v8 06/10] iio: frequency: adf41513: 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: <20260303-adf41513-iio-driver-v8-6-8dd2417cc465@analog.com> References: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> In-Reply-To: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-doc@vger.kernel.org Cc: Jonathan Cameron , David Lechner , Andy Shevchenko , Lars-Peter Clausen , Michael Hennerich , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Andrew Morton , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772544443; l=39126; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=I7GNVBGp6kEg9ffCAYv2yjcYsbzqIKQJkLbR7uZKrUg=; b=2E6Uiu2xFjBbeX8izj4HoDJE3dQ0cftHoV2Rg1VCh2SXczLMwh5TWilsdlMKNt+qXUrROSNow cJDZyEshi0/DatOuEJjRPQpSCXbh8NMhLqi1FehpfgHV0GuXWZ7JXVv 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 The driver is based on existing PLL drivers in the IIO subsystem and implements the following key features: - Integer-N and fractional-N (fixed/variable modulus) synthesis modes - High-resolution frequency calculations using microhertz (=C2=B5Hz) precis= ion to handle sub-Hz resolution across multi-GHz frequency ranges - IIO debugfs interface for direct register access - FW property parsing from devicetree including charge pump settings, reference path configuration and muxout options - Power management support with suspend/resume callbacks - Lock detect GPIO monitoring The driver uses 64-bit microhertz values throughout PLL calculations to maintain precision when working with frequencies that exceed 32-bit Hz representation while requiring fractional Hz resolution. When merging, ADF41513_HZ_PER_GHZ must be dropped in favor of HZ_PER_GHZ defined in linux/units.h. Signed-off-by: Rodrigo Alencar --- MAINTAINERS | 1 + drivers/iio/frequency/Kconfig | 10 + drivers/iio/frequency/Makefile | 1 + drivers/iio/frequency/adf41513.c | 1111 ++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 1123 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 463c888b428f..051bf31feea2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1640,6 +1640,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml +F: drivers/iio/frequency/adf41513.c =20 ANALOG DEVICES INC ADF4377 DRIVER M: Antoniu Miclaus diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 583cbdf4e8cd..90c6304c4bcd 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -29,6 +29,16 @@ endmenu =20 menu "Phase-Locked Loop (PLL) frequency synthesizers" =20 +config ADF41513 + tristate "Analog Devices ADF41513 PLL Frequency Synthesizer" + depends on SPI + help + Say yes here to build support for Analog Devices ADF41513 + 26.5 GHz Integer-N/Fractional-N PLL Frequency Synthesizer. + + To compile this driver as a module, choose M here: the + module will be called adf41513. + config ADF4350 tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers" depends on SPI diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 70d0e0b70e80..53b4d01414d8 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_ADF41513) +=3D adf41513.o obj-$(CONFIG_ADF4350) +=3D adf4350.o obj-$(CONFIG_ADF4371) +=3D adf4371.o obj-$(CONFIG_ADF4377) +=3D adf4377.o diff --git a/drivers/iio/frequency/adf41513.c b/drivers/iio/frequency/adf41= 513.c new file mode 100644 index 000000000000..5a0682667d1f --- /dev/null +++ b/drivers/iio/frequency/adf41513.c @@ -0,0 +1,1111 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADF41513 SPI PLL Frequency Synthesizer driver + * + * Copyright 2026 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Registers */ +#define ADF41513_REG0 0 +#define ADF41513_REG1 1 +#define ADF41513_REG2 2 +#define ADF41513_REG3 3 +#define ADF41513_REG4 4 +#define ADF41513_REG5 5 +#define ADF41513_REG6 6 +#define ADF41513_REG7 7 +#define ADF41513_REG8 8 +#define ADF41513_REG9 9 +#define ADF41513_REG10 10 +#define ADF41513_REG11 11 +#define ADF41513_REG12 12 +#define ADF41513_REG13 13 +#define ADF41513_REG_NUM 14 + +#define ADF41513_SYNC_REG0 BIT(ADF41513_REG0) +#define ADF41513_SYNC_REG1 BIT(ADF41513_REG1) +#define ADF41513_SYNC_REG2 BIT(ADF41513_REG2) +#define ADF41513_SYNC_REG3 BIT(ADF41513_REG3) +#define ADF41513_SYNC_REG4 BIT(ADF41513_REG4) +#define ADF41513_SYNC_REG5 BIT(ADF41513_REG5) +#define ADF41513_SYNC_REG6 BIT(ADF41513_REG6) +#define ADF41513_SYNC_REG7 BIT(ADF41513_REG7) +#define ADF41513_SYNC_REG9 BIT(ADF41513_REG9) +#define ADF41513_SYNC_REG11 BIT(ADF41513_REG11) +#define ADF41513_SYNC_REG12 BIT(ADF41513_REG12) +#define ADF41513_SYNC_REG13 BIT(ADF41513_REG13) +#define ADF41513_SYNC_DIFF 0 +#define ADF41513_SYNC_ALL GENMASK(ADF41513_REG13, ADF41513_REG0) + +/* REG0 Bit Definitions */ +#define ADF41513_REG0_CTRL_BITS_MSK GENMASK(3, 0) +#define ADF41513_REG0_INT_MSK GENMASK(19, 4) +#define ADF41513_REG0_VAR_MOD_MSK BIT(28) + +/* REG1 Bit Definitions */ +#define ADF41513_REG1_FRAC1_MSK GENMASK(28, 4) +#define ADF41513_REG1_DITHER2_MSK BIT(31) + +/* REG2 Bit Definitions */ +#define ADF41513_REG2_PHASE_VAL_MSK GENMASK(15, 4) +#define ADF41513_REG2_PHASE_ADJ_MSK BIT(31) + +/* REG3 Bit Definitions */ +#define ADF41513_REG3_FRAC2_MSK GENMASK(27, 4) + +/* REG4 Bit Definitions */ +#define ADF41513_REG4_MOD2_MSK GENMASK(27, 4) + +/* REG5 Bit Definitions */ +#define ADF41513_REG5_CLK1_DIV_MSK GENMASK(15, 4) +#define ADF41513_REG5_R_CNT_MSK GENMASK(20, 16) +#define ADF41513_REG5_REF_DOUBLER_MSK BIT(21) +#define ADF41513_REG5_RDIV2_MSK BIT(22) +#define ADF41513_REG5_PRESCALER_MSK BIT(23) +#define ADF41513_REG5_LSB_P1_MSK BIT(24) +#define ADF41513_REG5_CP_CURRENT_MSK GENMASK(28, 25) +#define ADF41513_REG5_DLD_MODES_MSK GENMASK(31, 30) + +/* REG6 Bit Definitions */ +#define ADF41513_REG6_COUNTER_RESET_MSK BIT(4) +#define ADF41513_REG6_CP_TRISTATE_MSK BIT(5) +#define ADF41513_REG6_POWER_DOWN_MSK BIT(6) +#define ADF41513_REG6_PD_POLARITY_MSK BIT(7) +#define ADF41513_REG6_LDP_MSK GENMASK(9, 8) +#define ADF41513_REG6_CP_TRISTATE_PD_ON_MSK BIT(16) +#define ADF41513_REG6_SD_RESET_MSK BIT(17) +#define ADF41513_REG6_LOL_ENABLE_MSK BIT(18) +#define ADF41513_REG6_ABP_MSK BIT(19) +#define ADF41513_REG6_INT_MODE_MSK BIT(20) +#define ADF41513_REG6_BLEED_ENABLE_MSK BIT(22) +#define ADF41513_REG6_BLEED_POLARITY_MSK BIT(23) +#define ADF41513_REG6_BLEED_CURRENT_MSK GENMASK(31, 24) + +/* REG7 Bit Definitions */ +#define ADF41513_REG7_CLK2_DIV_MSK GENMASK(17, 6) +#define ADF41513_REG7_CLK_DIV_MODE_MSK GENMASK(19, 18) +#define ADF41513_REG7_PS_BIAS_MSK GENMASK(21, 20) +#define ADF41513_REG7_N_DELAY_MSK GENMASK(23, 22) +#define ADF41513_REG7_LD_CLK_SEL_MSK BIT(26) +#define ADF41513_REG7_LD_COUNT_MSK GENMASK(29, 27) + +/* REG9 Bit Definitions */ +#define ADF41513_REG9_LD_BIAS_MSK GENMASK(31, 30) + +/* REG11 Bit Definitions */ +#define ADF41513_REG11_POWER_DOWN_SEL_MSK BIT(31) + +/* REG12 Bit Definitions */ +#define ADF41513_REG12_READBACK_SEL_MSK GENMASK(19, 14) +#define ADF41513_REG12_LE_SELECT_MSK BIT(20) +#define ADF41513_REG12_MASTER_RESET_MSK BIT(22) +#define ADF41513_REG12_LOGIC_LEVEL_MSK BIT(27) +#define ADF41513_REG12_MUXOUT_MSK GENMASK(31, 28) + +/* MUXOUT Selection */ +#define ADF41513_MUXOUT_TRISTATE 0x0 +#define ADF41513_MUXOUT_DVDD 0x1 +#define ADF41513_MUXOUT_DGND 0x2 +#define ADF41513_MUXOUT_R_DIV 0x3 +#define ADF41513_MUXOUT_N_DIV 0x4 +#define ADF41513_MUXOUT_DIG_LD 0x6 +#define ADF41513_MUXOUT_SDO 0x7 +#define ADF41513_MUXOUT_READBACK 0x8 +#define ADF41513_MUXOUT_CLK1_DIV 0xA +#define ADF41513_MUXOUT_R_DIV2 0xD +#define ADF41513_MUXOUT_N_DIV2 0xE + +/* DLD Mode Selection */ +#define ADF41513_DLD_TRISTATE 0x0 +#define ADF41513_DLD_DIG_LD 0x1 +#define ADF41513_DLD_LOW 0x2 +#define ADF41513_DLD_HIGH 0x3 + +/* Prescaler Selection */ +#define ADF41513_PRESCALER_4_5 0 +#define ADF41513_PRESCALER_8_9 1 +#define ADF41513_PRESCALER_AUTO 2 + +/* Specifications */ +#define ADF41513_HZ_PER_GHZ 1000000000UL +#define ADF41510_MAX_RF_FREQ_HZ (10ULL * ADF41513_HZ_PER_GHZ) +#define ADF41513_MIN_RF_FREQ_HZ (1ULL * ADF41513_HZ_PER_GHZ) +#define ADF41513_MAX_RF_FREQ_HZ (26500ULL * HZ_PER_MHZ) + +#define ADF41513_MIN_REF_FREQ_HZ (10 * HZ_PER_MHZ) +#define ADF41513_MAX_REF_FREQ_HZ (800 * HZ_PER_MHZ) +#define ADF41513_MAX_REF_FREQ_DOUBLER_HZ (225 * HZ_PER_MHZ) + +#define ADF41513_MAX_PFD_FREQ_INT_N_UHZ (250ULL * MEGA * MICROHZ_PER_HZ) +#define ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ (125ULL * MEGA * MICROHZ_PER_HZ) +#define ADF41513_MAX_FREQ_RESOLUTION_UHZ (100ULL * KILO * MICROHZ_PER_HZ) + +#define ADF41513_MIN_INT_4_5 20 +#define ADF41513_MAX_INT_4_5 511 +#define ADF41513_MIN_INT_8_9 64 +#define ADF41513_MAX_INT_8_9 1023 + +#define ADF41513_MIN_INT_FRAC_4_5 23 +#define ADF41513_MIN_INT_FRAC_8_9 75 + +#define ADF41513_MIN_R_CNT 1 +#define ADF41513_MAX_R_CNT 32 + +#define ADF41513_MIN_R_SET 1800 +#define ADF41513_DEFAULT_R_SET 2700 +#define ADF41513_MAX_R_SET 10000 + +#define ADF41513_MIN_CP_VOLTAGE_mV 810 +#define ADF41513_DEFAULT_CP_VOLTAGE_mV 6480 +#define ADF41513_MAX_CP_VOLTAGE_mV 12960 + +#define ADF41513_LD_COUNT_FAST_MIN 2 +#define ADF41513_LD_COUNT_FAST_LIMIT 64 +#define ADF41513_LD_COUNT_MIN 64 +#define ADF41513_LD_COUNT_MAX 8192 + +#define ADF41513_FIXED_MODULUS BIT(25) +#define ADF41513_MAX_MOD2 (BIT(24) - 1) +#define ADF41513_MAX_PHASE_VAL (BIT(12) - 1) +#define ADF41513_MAX_CLK_DIVIDER (BIT(12) - 1) + +#define ADF41513_HZ_DECIMAL_PRECISION 6 +#define ADF41513_PS_BIAS_INIT 0x2 +#define ADF41513_MAX_PHASE_MICRORAD ((2 * 314159265UL) / 100) + +enum { + ADF41513_FREQ, + ADF41513_POWER_DOWN, + ADF41513_FREQ_RESOLUTION, +}; + +enum adf41513_pll_mode { + ADF41513_MODE_INVALID, + ADF41513_MODE_INTEGER_N, + ADF41513_MODE_FIXED_MODULUS, + ADF41513_MODE_VARIABLE_MODULUS, +}; + +struct adf41513_chip_info { + const char *name; + u64 max_rf_freq_hz; + bool has_prescaler_8_9; +}; + +struct adf41513_data { + u64 power_up_frequency_hz; + u64 freq_resolution_uhz; + u32 charge_pump_voltage_mv; + u32 lock_detect_count; + + u8 ref_div_factor; + bool ref_doubler_en; + bool ref_div2_en; + bool phase_detector_polarity; + + bool logic_lvl_1v8_en; +}; + +struct adf41513_pll_settings { + enum adf41513_pll_mode mode; + + /* reference path parameters */ + u8 r_counter; + u8 ref_doubler; + u8 ref_div2; + u8 prescaler; + + /* frequency parameters */ + u64 target_frequency_uhz; + u64 actual_frequency_uhz; + u64 pfd_frequency_uhz; + + /* pll parameters */ + u32 frac1; + u32 frac2; + u32 mod2; + u16 int_value; +}; + +struct adf41513_state { + const struct adf41513_chip_info *chip_info; + struct spi_device *spi; + struct gpio_desc *lock_detect; + struct gpio_desc *chip_enable; + struct clk *ref_clk; + u32 ref_freq_hz; + + /* + * Lock for accessing device registers. Some operations require + * multiple consecutive R/W operations, during which the device + * shouldn't be interrupted. The buffers are also shared across + * all operations so need to be protected on stand alone reads and + * writes. + */ + struct mutex lock; + + /* Cached register values */ + u32 regs[ADF41513_REG_NUM]; + u32 regs_hw[ADF41513_REG_NUM]; + + struct adf41513_data data; + struct adf41513_pll_settings settings; +}; + +static const char * const adf41513_power_supplies[] =3D { + "avdd1", "avdd2", "avdd3", "avdd4", "avdd5", "vp", +}; + +static int adf41513_sync_config(struct adf41513_state *st, u16 sync_mask) +{ + __be32 d32; + int ret, i; + + /* write registers in reverse order (R13 to R0)*/ + for (i =3D ADF41513_REG13; i >=3D ADF41513_REG0; i--) { + if (st->regs_hw[i] =3D=3D st->regs[i] && !(sync_mask & BIT(i))) + continue; + + d32 =3D cpu_to_be32(st->regs[i] | i); + ret =3D spi_write_then_read(st->spi, &d32, sizeof(d32), NULL, 0); + if (ret < 0) + return ret; + st->regs_hw[i] =3D st->regs[i]; + dev_dbg(&st->spi->dev, "REG%d <=3D 0x%08X\n", i, st->regs[i] | i); + } + + return 0; +} + +static u64 adf41513_pll_get_rate(struct adf41513_state *st) +{ + struct adf41513_pll_settings *cfg =3D &st->settings; + + if (cfg->mode !=3D ADF41513_MODE_INVALID) + return cfg->actual_frequency_uhz; + + /* get pll settings from regs_hw */ + cfg->int_value =3D FIELD_GET(ADF41513_REG0_INT_MSK, st->regs_hw[ADF41513_= REG0]); + cfg->frac1 =3D FIELD_GET(ADF41513_REG1_FRAC1_MSK, st->regs_hw[ADF41513_RE= G1]); + cfg->frac2 =3D FIELD_GET(ADF41513_REG3_FRAC2_MSK, st->regs_hw[ADF41513_RE= G3]); + cfg->mod2 =3D FIELD_GET(ADF41513_REG4_MOD2_MSK, st->regs_hw[ADF41513_REG4= ]); + cfg->r_counter =3D FIELD_GET(ADF41513_REG5_R_CNT_MSK, st->regs_hw[ADF4151= 3_REG5]); + cfg->ref_doubler =3D FIELD_GET(ADF41513_REG5_REF_DOUBLER_MSK, st->regs_hw= [ADF41513_REG5]); + cfg->ref_div2 =3D FIELD_GET(ADF41513_REG5_RDIV2_MSK, st->regs_hw[ADF41513= _REG5]); + cfg->prescaler =3D FIELD_GET(ADF41513_REG5_PRESCALER_MSK, st->regs_hw[ADF= 41513_REG5]); + + /* calculate pfd frequency */ + cfg->pfd_frequency_uhz =3D (u64)st->ref_freq_hz * MICRO; + if (cfg->ref_doubler) + cfg->pfd_frequency_uhz <<=3D 1; + if (cfg->ref_div2) + cfg->pfd_frequency_uhz >>=3D 1; + cfg->pfd_frequency_uhz =3D div_u64(cfg->pfd_frequency_uhz, cfg->r_counter= ); + cfg->actual_frequency_uhz =3D (u64)cfg->int_value * cfg->pfd_frequency_uh= z; + + /* check if int mode is selected */ + if (FIELD_GET(ADF41513_REG6_INT_MODE_MSK, st->regs_hw[ADF41513_REG6])) { + cfg->mode =3D ADF41513_MODE_INTEGER_N; + } else { + cfg->actual_frequency_uhz +=3D mul_u64_u32_div(cfg->pfd_frequency_uhz, + cfg->frac1, + ADF41513_FIXED_MODULUS); + + /* check if variable modulus is selected */ + if (FIELD_GET(ADF41513_REG0_VAR_MOD_MSK, st->regs_hw[ADF41513_REG0])) { + cfg->actual_frequency_uhz +=3D + mul_u64_u64_div_u64(cfg->frac2, + cfg->pfd_frequency_uhz, + (u64)cfg->mod2 * ADF41513_FIXED_MODULUS); + + cfg->mode =3D ADF41513_MODE_VARIABLE_MODULUS; + } else { + /* LSB_P1 offset */ + if (!FIELD_GET(ADF41513_REG5_LSB_P1_MSK, st->regs_hw[ADF41513_REG5])) + cfg->actual_frequency_uhz +=3D + div_u64(cfg->pfd_frequency_uhz, + 2 * ADF41513_FIXED_MODULUS); + cfg->mode =3D ADF41513_MODE_FIXED_MODULUS; + } + } + + cfg->target_frequency_uhz =3D cfg->actual_frequency_uhz; + + return cfg->actual_frequency_uhz; +} + +static int adf41513_calc_pfd_frequency(struct adf41513_state *st, + struct adf41513_pll_settings *result, + u64 fpfd_limit_uhz) +{ + result->ref_div2 =3D st->data.ref_div2_en; + result->ref_doubler =3D st->data.ref_doubler_en; + + if (st->data.ref_doubler_en && + st->ref_freq_hz > ADF41513_MAX_REF_FREQ_DOUBLER_HZ) { + result->ref_doubler =3D 0; + dev_warn(&st->spi->dev, "Disabling ref doubler due to high reference fre= quency\n"); + } + + result->r_counter =3D st->data.ref_div_factor - 1; + do { + result->r_counter++; + /* f_PFD =3D REF_IN =C3=97 ((1 + D)/(R =C3=97 (1 + T))) */ + result->pfd_frequency_uhz =3D (u64)st->ref_freq_hz * MICRO; + if (result->ref_doubler) + result->pfd_frequency_uhz <<=3D 1; + if (result->ref_div2) + result->pfd_frequency_uhz >>=3D 1; + result->pfd_frequency_uhz =3D div_u64(result->pfd_frequency_uhz, + result->r_counter); + } while (result->pfd_frequency_uhz > fpfd_limit_uhz); + + if (result->r_counter > ADF41513_MAX_R_CNT) { + dev_err(&st->spi->dev, "Cannot optimize PFD frequency\n"); + return -ERANGE; + } + + return 0; +} + +static int adf41513_calc_integer_n(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u16 max_int =3D st->chip_info->has_prescaler_8_9 ? + ADF41513_MAX_INT_8_9 : ADF41513_MAX_INT_4_5; + u64 freq_error_uhz; + u16 int_value =3D div64_u64_rem(result->target_frequency_uhz, result->pfd= _frequency_uhz, + &freq_error_uhz); + + /* check if freq error is within a tolerance of 1/2 resolution */ + if (freq_error_uhz > (result->pfd_frequency_uhz >> 1) && int_value < max_= int) { + int_value++; + freq_error_uhz =3D result->pfd_frequency_uhz - freq_error_uhz; + } + + if (freq_error_uhz > st->data.freq_resolution_uhz) + return -ERANGE; + + /* set prescaler */ + if (st->chip_info->has_prescaler_8_9 && int_value >=3D ADF41513_MIN_INT_8= _9 && + int_value <=3D ADF41513_MAX_INT_8_9) + result->prescaler =3D 1; + else if (int_value >=3D ADF41513_MIN_INT_4_5 && int_value <=3D ADF41513_M= AX_INT_4_5) + result->prescaler =3D 0; + else + return -ERANGE; + + result->actual_frequency_uhz =3D (u64)int_value * result->pfd_frequency_u= hz; + result->mode =3D ADF41513_MODE_INTEGER_N; + result->int_value =3D int_value; + result->frac1 =3D 0; + result->frac2 =3D 0; + result->mod2 =3D 0; + + return 0; +} + +static int adf41513_calc_fixed_mod(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u64 freq_error_uhz; + u64 resolution_uhz =3D div_u64(result->pfd_frequency_uhz, ADF41513_FIXED_= MODULUS); + u64 target_frequency_uhz =3D result->target_frequency_uhz; + u32 frac1; + u16 int_value; + bool lsb_p1_offset =3D !FIELD_GET(ADF41513_REG5_LSB_P1_MSK, st->regs_hw[A= DF41513_REG5]); + + /* LSB_P1 adds a frequency offset of f_pfd/2^26 */ + if (lsb_p1_offset) + target_frequency_uhz -=3D resolution_uhz >> 1; + + int_value =3D div64_u64_rem(target_frequency_uhz, result->pfd_frequency_u= hz, + &freq_error_uhz); + + if (st->chip_info->has_prescaler_8_9 && int_value >=3D ADF41513_MIN_INT_F= RAC_8_9 && + int_value <=3D ADF41513_MAX_INT_8_9) + result->prescaler =3D 1; + else if (int_value >=3D ADF41513_MIN_INT_FRAC_4_5 && int_value <=3D ADF41= 513_MAX_INT_4_5) + result->prescaler =3D 0; + else + return -ERANGE; + + /* compute frac1 and fixed modulus error */ + frac1 =3D mul_u64_u64_div_u64(freq_error_uhz, ADF41513_FIXED_MODULUS, + result->pfd_frequency_uhz); + freq_error_uhz -=3D mul_u64_u32_div(result->pfd_frequency_uhz, frac1, + ADF41513_FIXED_MODULUS); + + /* check if freq error is within a tolerance of 1/2 resolution */ + if (freq_error_uhz > (resolution_uhz >> 1) && frac1 < (ADF41513_FIXED_MOD= ULUS - 1)) { + frac1++; + freq_error_uhz =3D resolution_uhz - freq_error_uhz; + } + + if (freq_error_uhz > st->data.freq_resolution_uhz) + return -ERANGE; + + /* integer part */ + result->actual_frequency_uhz =3D (u64)int_value * result->pfd_frequency_u= hz; + /* fractional part */ + if (lsb_p1_offset) + result->actual_frequency_uhz +=3D (resolution_uhz >> 1); + result->actual_frequency_uhz +=3D mul_u64_u32_div(result->pfd_frequency_u= hz, frac1, + ADF41513_FIXED_MODULUS); + result->mode =3D ADF41513_MODE_FIXED_MODULUS; + result->int_value =3D int_value; + result->frac1 =3D frac1; + result->frac2 =3D 0; + result->mod2 =3D 0; + + return 0; +} + +static int adf41513_calc_variable_mod(struct adf41513_state *st, + struct adf41513_pll_settings *result) +{ + u64 freq_error_uhz, mod2; + u32 frac1, frac2; + u16 int_value =3D div64_u64_rem(result->target_frequency_uhz, + result->pfd_frequency_uhz, + &freq_error_uhz); + + if (st->chip_info->has_prescaler_8_9 && int_value >=3D ADF41513_MIN_INT_F= RAC_8_9 && + int_value <=3D ADF41513_MAX_INT_8_9) + result->prescaler =3D 1; + else if (int_value >=3D ADF41513_MIN_INT_FRAC_4_5 && int_value <=3D ADF41= 513_MAX_INT_4_5) + result->prescaler =3D 0; + else + return -ERANGE; + + /* calculate required mod2 based on target resolution / 2 */ + mod2 =3D DIV64_U64_ROUND_CLOSEST(result->pfd_frequency_uhz << 1, + st->data.freq_resolution_uhz * ADF41513_FIXED_MODULUS); + /* ensure mod2 is at least 2 for meaningful operation */ + mod2 =3D clamp(mod2, 2, ADF41513_MAX_MOD2); + + /* calculate frac1 and frac2 */ + frac1 =3D mul_u64_u64_div_u64(freq_error_uhz, ADF41513_FIXED_MODULUS, + result->pfd_frequency_uhz); + freq_error_uhz -=3D mul_u64_u32_div(result->pfd_frequency_uhz, frac1, + ADF41513_FIXED_MODULUS); + frac2 =3D mul_u64_u64_div_u64(freq_error_uhz, mod2 * ADF41513_FIXED_MODUL= US, + result->pfd_frequency_uhz); + + /* integer part */ + result->actual_frequency_uhz =3D (u64)int_value * result->pfd_frequency_u= hz; + /* fractional part */ + result->actual_frequency_uhz +=3D mul_u64_u64_div_u64(mod2 * frac1 + frac= 2, + result->pfd_frequency_uhz, + mod2 * ADF41513_FIXED_MODULUS); + result->mode =3D ADF41513_MODE_VARIABLE_MODULUS; + result->int_value =3D int_value; + result->frac1 =3D frac1; + result->frac2 =3D frac2; + result->mod2 =3D mod2; + + return 0; +} + +static int adf41513_calc_pll_settings(struct adf41513_state *st, + struct adf41513_pll_settings *result, + u64 rf_out_uhz) +{ + u64 max_rf_freq_uhz =3D st->chip_info->max_rf_freq_hz * MICRO; + u64 min_rf_freq_uhz =3D ADF41513_MIN_RF_FREQ_HZ * MICRO; + u64 pfd_freq_limit_uhz; + int ret; + + if (rf_out_uhz < min_rf_freq_uhz || rf_out_uhz > max_rf_freq_uhz) { + dev_err(&st->spi->dev, "RF frequency %llu uHz out of range [%llu, %llu] = uHz\n", + rf_out_uhz, min_rf_freq_uhz, max_rf_freq_uhz); + return -EINVAL; + } + + result->target_frequency_uhz =3D rf_out_uhz; + + /* try integer-N first (best phase noise performance) */ + pfd_freq_limit_uhz =3D min(div_u64(rf_out_uhz, ADF41513_MIN_INT_4_5), + ADF41513_MAX_PFD_FREQ_INT_N_UHZ); + ret =3D adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz); + if (ret < 0) + return ret; + + if (adf41513_calc_integer_n(st, result) =3D=3D 0) + return 0; + + /* try fractional-N: recompute pfd frequency if necessary */ + pfd_freq_limit_uhz =3D min(div_u64(rf_out_uhz, ADF41513_MIN_INT_FRAC_4_5), + ADF41513_MAX_PFD_FREQ_FRAC_N_UHZ); + if (pfd_freq_limit_uhz < result->pfd_frequency_uhz) { + ret =3D adf41513_calc_pfd_frequency(st, result, pfd_freq_limit_uhz); + if (ret < 0) + return ret; + } + + /* fixed-modulus attempt */ + if (adf41513_calc_fixed_mod(st, result) =3D=3D 0) + return 0; + + /* variable-modulus attempt */ + ret =3D adf41513_calc_variable_mod(st, result); + if (ret < 0) { + dev_err(&st->spi->dev, + "no valid PLL configuration found for %llu uHz\n", + rf_out_uhz); + return -EINVAL; + } + + return 0; +} + +static int adf41513_set_frequency(struct adf41513_state *st, u64 freq_uhz,= u16 sync_mask) +{ + struct adf41513_pll_settings result; + int ret; + + ret =3D adf41513_calc_pll_settings(st, &result, freq_uhz); + if (ret < 0) + return ret; + + /* apply computed results to pll settings */ + st->settings =3D result; + + dev_dbg(&st->spi->dev, + "%s mode: int=3D%u, frac1=3D%u, frac2=3D%u, mod2=3D%u, fpdf=3D%llu Hz, p= rescaler=3D%s\n", + (result.mode =3D=3D ADF41513_MODE_INTEGER_N) ? "integer-n" : + (result.mode =3D=3D ADF41513_MODE_FIXED_MODULUS) ? "fixed-modulus" : "va= riable-modulus", + result.int_value, result.frac1, result.frac2, result.mod2, + div64_u64(result.pfd_frequency_uhz, MICRO), + result.prescaler ? "8/9" : "4/5"); + + st->regs[ADF41513_REG0] =3D FIELD_PREP(ADF41513_REG0_INT_MSK, + st->settings.int_value); + if (st->settings.mode =3D=3D ADF41513_MODE_VARIABLE_MODULUS) + st->regs[ADF41513_REG0] |=3D ADF41513_REG0_VAR_MOD_MSK; + + st->regs[ADF41513_REG1] =3D FIELD_PREP(ADF41513_REG1_FRAC1_MSK, + st->settings.frac1); + if (st->settings.mode !=3D ADF41513_MODE_INTEGER_N) + st->regs[ADF41513_REG1] |=3D ADF41513_REG1_DITHER2_MSK; + + st->regs[ADF41513_REG3] =3D FIELD_PREP(ADF41513_REG3_FRAC2_MSK, + st->settings.frac2); + FIELD_MODIFY(ADF41513_REG4_MOD2_MSK, &st->regs[ADF41513_REG4], + st->settings.mod2); + FIELD_MODIFY(ADF41513_REG5_R_CNT_MSK, &st->regs[ADF41513_REG5], + st->settings.r_counter); + FIELD_MODIFY(ADF41513_REG5_REF_DOUBLER_MSK, &st->regs[ADF41513_REG5], + st->settings.ref_doubler); + FIELD_MODIFY(ADF41513_REG5_RDIV2_MSK, &st->regs[ADF41513_REG5], + st->settings.ref_div2); + FIELD_MODIFY(ADF41513_REG5_PRESCALER_MSK, &st->regs[ADF41513_REG5], + st->settings.prescaler); + + if (st->settings.mode =3D=3D ADF41513_MODE_INTEGER_N) { + st->regs[ADF41513_REG6] |=3D ADF41513_REG6_INT_MODE_MSK; + st->regs[ADF41513_REG6] &=3D ~ADF41513_REG6_BLEED_ENABLE_MSK; + } else { + st->regs[ADF41513_REG6] &=3D ~ADF41513_REG6_INT_MODE_MSK; + st->regs[ADF41513_REG6] |=3D ADF41513_REG6_BLEED_ENABLE_MSK; + } + + return adf41513_sync_config(st, sync_mask | ADF41513_SYNC_REG0); +} + +static int adf41513_suspend(struct adf41513_state *st) +{ + st->regs[ADF41513_REG6] |=3D FIELD_PREP(ADF41513_REG6_POWER_DOWN_MSK, 1); + return adf41513_sync_config(st, ADF41513_SYNC_DIFF); +} + +static int adf41513_resume(struct adf41513_state *st) +{ + st->regs[ADF41513_REG6] &=3D ~ADF41513_REG6_POWER_DOWN_MSK; + return adf41513_sync_config(st, ADF41513_SYNC_DIFF); +} + +static ssize_t adf41513_read_uhz(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct adf41513_state *st =3D iio_priv(indio_dev); + u64 freq_uhz, int_hz; + u32 frac_uhz; + + guard(mutex)(&st->lock); + + switch (private) { + case ADF41513_FREQ: + freq_uhz =3D adf41513_pll_get_rate(st); + if (st->lock_detect && + !gpiod_get_value_cansleep(st->lock_detect)) { + dev_dbg(&st->spi->dev, "PLL un-locked\n"); + return -EBUSY; + } + break; + case ADF41513_FREQ_RESOLUTION: + freq_uhz =3D st->data.freq_resolution_uhz; + break; + default: + return -EINVAL; + } + + int_hz =3D div_u64_rem(freq_uhz, MICRO, &frac_uhz); + return sysfs_emit(buf, "%llu.%06u\n", int_hz, frac_uhz); +} + +static ssize_t adf41513_read_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct adf41513_state *st =3D iio_priv(indio_dev); + u32 val; + + guard(mutex)(&st->lock); + + switch (private) { + case ADF41513_POWER_DOWN: + val =3D FIELD_GET(ADF41513_REG6_POWER_DOWN_MSK, + st->regs_hw[ADF41513_REG6]); + return sysfs_emit(buf, "%u\n", val); + default: + return -EINVAL; + } +} + +static ssize_t adf41513_write_uhz(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct adf41513_state *st =3D iio_priv(indio_dev); + s64 int_hz, frac_uhz; + u64 freq_uhz; + int ret; + + ret =3D iio_str_to_fixpoint64(buf, MICRO / 10, &int_hz, &frac_uhz); + if (ret) + return ret; + + freq_uhz =3D int_hz * MICRO + frac_uhz; + guard(mutex)(&st->lock); + + switch ((u32)private) { + case ADF41513_FREQ: + ret =3D adf41513_set_frequency(st, freq_uhz, ADF41513_SYNC_DIFF); + break; + case ADF41513_FREQ_RESOLUTION: + if (freq_uhz =3D=3D 0 || freq_uhz > ADF41513_MAX_FREQ_RESOLUTION_UHZ) + return -EINVAL; + st->data.freq_resolution_uhz =3D freq_uhz; + break; + default: + return -EINVAL; + } + + return ret ?: len; +} + +static ssize_t adf41513_write_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct adf41513_state *st =3D iio_priv(indio_dev); + unsigned long readin; + int ret; + + ret =3D kstrtoul(buf, 10, &readin); + if (ret) + return ret; + + guard(mutex)(&st->lock); + + switch ((u32)private) { + case ADF41513_POWER_DOWN: + if (readin) + ret =3D adf41513_suspend(st); + else + ret =3D adf41513_resume(st); + break; + default: + return -EINVAL; + } + + return ret ?: len; +} + +#define _ADF41513_EXT_PD_INFO(_name, _ident) { \ + .name =3D _name, \ + .read =3D adf41513_read_powerdown, \ + .write =3D adf41513_write_powerdown, \ + .private =3D _ident, \ + .shared =3D IIO_SEPARATE, \ +} + +#define _ADF41513_EXT_UHZ_INFO(_name, _ident) { \ + .name =3D _name, \ + .read =3D adf41513_read_uhz, \ + .write =3D adf41513_write_uhz, \ + .private =3D _ident, \ + .shared =3D IIO_SEPARATE, \ +} + +static const struct iio_chan_spec_ext_info adf41513_ext_info[] =3D { + /* + * Ideally we would use IIO_CHAN_INFO_FREQUENCY, but the device supports + * frequency values greater 2^32 with sub-Hz resolution, i.e. 64-bit + * fixed point with 6 decimal places values are used to represent + * frequencies. + */ + _ADF41513_EXT_UHZ_INFO("frequency", ADF41513_FREQ), + _ADF41513_EXT_UHZ_INFO("frequency_resolution", ADF41513_FREQ_RESOLUTION), + _ADF41513_EXT_PD_INFO("powerdown", ADF41513_POWER_DOWN), + { } +}; + +static const struct iio_chan_spec adf41513_chan =3D { + .type =3D IIO_ALTVOLTAGE, + .indexed =3D 1, + .output =3D 1, + .channel =3D 0, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_PHASE), + .ext_info =3D adf41513_ext_info, +}; + +static int adf41513_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct adf41513_state *st =3D iio_priv(indio_dev); + u64 phase_urad; + u16 phase_val; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_PHASE: + phase_val =3D FIELD_GET(ADF41513_REG2_PHASE_VAL_MSK, + st->regs_hw[ADF41513_REG2]); + phase_urad =3D (u64)phase_val * ADF41513_MAX_PHASE_MICRORAD; + phase_urad >>=3D 12; + /* + * Before the 12-bit rshift phase_urad can be represented with + * 35 bits at most. After the shift it will fit in 32-bit value. + */ + *val =3D (u32)phase_urad / MICRO; + *val2 =3D (u32)phase_urad % MICRO; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int adf41513_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct adf41513_state *st =3D iio_priv(indio_dev); + u64 phase_urad; + u16 phase_val; + + guard(mutex)(&st->lock); + + switch (info) { + case IIO_CHAN_INFO_PHASE: + if (val < 0 || val2 < 0) + return -EINVAL; + + phase_urad =3D (u64)val * MICRO + val2; + if (phase_urad >=3D ADF41513_MAX_PHASE_MICRORAD) + return -EINVAL; + + phase_val =3D DIV_U64_ROUND_CLOSEST(phase_urad << 12, + ADF41513_MAX_PHASE_MICRORAD); + phase_val =3D min(phase_val, ADF41513_MAX_PHASE_VAL); + st->regs[ADF41513_REG2] |=3D ADF41513_REG2_PHASE_ADJ_MSK; + FIELD_MODIFY(ADF41513_REG2_PHASE_VAL_MSK, + &st->regs[ADF41513_REG2], phase_val); + return adf41513_sync_config(st, ADF41513_SYNC_REG0); + default: + return -EINVAL; + } +} + +static int adf41513_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct adf41513_state *st =3D iio_priv(indio_dev); + + if (reg > ADF41513_REG13) + return -EINVAL; + + guard(mutex)(&st->lock); + + if (!readval) { + if (reg <=3D ADF41513_REG6) + st->settings.mode =3D ADF41513_MODE_INVALID; + st->regs[reg] =3D writeval & ~0xF; /* Clear control bits */ + return adf41513_sync_config(st, BIT(reg)); + } + + *readval =3D st->regs_hw[reg]; + return 0; +} + +static const struct iio_info adf41513_info =3D { + .read_raw =3D adf41513_read_raw, + .write_raw =3D adf41513_write_raw, + .debugfs_reg_access =3D &adf41513_reg_access, +}; + +static int adf41513_parse_fw(struct adf41513_state *st) +{ + struct device *dev =3D &st->spi->dev; + u32 tmp, cp_resistance, cp_current; + int ret; + + tmp =3D ADF41510_MAX_RF_FREQ_HZ / MEGA; + device_property_read_u32(dev, "adi,power-up-frequency-mhz", &tmp); + st->data.power_up_frequency_hz =3D (u64)tmp * MEGA; + if (st->data.power_up_frequency_hz < ADF41513_MIN_RF_FREQ_HZ || + st->data.power_up_frequency_hz > ADF41513_MAX_RF_FREQ_HZ) + return dev_err_probe(dev, -ERANGE, + "power-up frequency %llu Hz out of range\n", + st->data.power_up_frequency_hz); + + tmp =3D ADF41513_MIN_R_CNT; + device_property_read_u32(dev, "adi,reference-div-factor", &tmp); + if (tmp < ADF41513_MIN_R_CNT || tmp > ADF41513_MAX_R_CNT) + return dev_err_probe(dev, -ERANGE, + "invalid reference div factor %u\n", tmp); + st->data.ref_div_factor =3D tmp; + + st->data.ref_doubler_en =3D device_property_read_bool(dev, "adi,reference= -doubler-enable"); + st->data.ref_div2_en =3D device_property_read_bool(dev, "adi,reference-di= v2-enable"); + + cp_resistance =3D ADF41513_DEFAULT_R_SET; + device_property_read_u32(dev, "adi,charge-pump-resistor-ohms", &cp_resist= ance); + if (cp_resistance < ADF41513_MIN_R_SET || cp_resistance > ADF41513_MAX_R_= SET) + return dev_err_probe(dev, -ERANGE, "R_SET %u Ohms out of range\n", cp_re= sistance); + + st->data.charge_pump_voltage_mv =3D ADF41513_DEFAULT_CP_VOLTAGE_mV; + ret =3D device_property_read_u32(dev, "adi,charge-pump-current-microamp",= &cp_current); + if (!ret) { + tmp =3D DIV_ROUND_CLOSEST(cp_current * cp_resistance, MILLI); /* convert= to mV */ + if (tmp < ADF41513_MIN_CP_VOLTAGE_mV || tmp > ADF41513_MAX_CP_VOLTAGE_mV) + return dev_err_probe(dev, -ERANGE, "I_CP %u uA (%u Ohms) out of range\n= ", + cp_current, cp_resistance); + st->data.charge_pump_voltage_mv =3D tmp; + } + + st->data.phase_detector_polarity =3D + device_property_read_bool(dev, "adi,phase-detector-polarity-positive-ena= ble"); + + st->data.logic_lvl_1v8_en =3D device_property_read_bool(dev, "adi,logic-l= evel-1v8-enable"); + + tmp =3D ADF41513_LD_COUNT_MIN; + device_property_read_u32(dev, "adi,lock-detector-count", &tmp); + if (tmp < ADF41513_LD_COUNT_FAST_MIN || tmp > ADF41513_LD_COUNT_MAX || + !is_power_of_2(tmp)) + return dev_err_probe(dev, -ERANGE, + "invalid lock detect count: %u\n", tmp); + st->data.lock_detect_count =3D tmp; + + st->data.freq_resolution_uhz =3D MICROHZ_PER_HZ; + + return 0; +} + +static int adf41513_setup(struct adf41513_state *st) +{ + u32 tmp; + + memset(st->regs_hw, 0xFF, sizeof(st->regs_hw)); + + /* assuming DLD pin is used for lock detection */ + st->regs[ADF41513_REG5] =3D FIELD_PREP(ADF41513_REG5_DLD_MODES_MSK, + ADF41513_DLD_DIG_LD); + + tmp =3D DIV_ROUND_CLOSEST(st->data.charge_pump_voltage_mv, ADF41513_MIN_C= P_VOLTAGE_mV); + st->regs[ADF41513_REG5] |=3D FIELD_PREP(ADF41513_REG5_CP_CURRENT_MSK, tmp= - 1); + + st->regs[ADF41513_REG6] =3D ADF41513_REG6_ABP_MSK | + ADF41513_REG6_LOL_ENABLE_MSK | + ADF41513_REG6_SD_RESET_MSK; + if (st->data.phase_detector_polarity) + st->regs[ADF41513_REG6] |=3D ADF41513_REG6_PD_POLARITY_MSK; + + st->regs[ADF41513_REG7] =3D FIELD_PREP(ADF41513_REG7_PS_BIAS_MSK, + ADF41513_PS_BIAS_INIT); + tmp =3D ilog2(st->data.lock_detect_count); + if (st->data.lock_detect_count < ADF41513_LD_COUNT_FAST_LIMIT) { + tmp -=3D const_ilog2(ADF41513_LD_COUNT_FAST_MIN); + st->regs[ADF41513_REG7] |=3D ADF41513_REG7_LD_CLK_SEL_MSK; + } else { + tmp -=3D const_ilog2(ADF41513_LD_COUNT_MIN); + } + st->regs[ADF41513_REG7] |=3D FIELD_PREP(ADF41513_REG7_LD_COUNT_MSK, tmp); + + st->regs[ADF41513_REG11] =3D ADF41513_REG11_POWER_DOWN_SEL_MSK; + st->regs[ADF41513_REG12] =3D FIELD_PREP(ADF41513_REG12_LOGIC_LEVEL_MSK, + st->data.logic_lvl_1v8_en ? 0 : 1); + + /* perform initialization sequence with power-up frequency */ + return adf41513_set_frequency(st, st->data.power_up_frequency_hz * MICRO, + ADF41513_SYNC_ALL); +} + +static void adf41513_power_down(void *data) +{ + struct adf41513_state *st =3D data; + + adf41513_suspend(st); + gpiod_set_value_cansleep(st->chip_enable, 0); +} + +static int adf41513_pm_suspend(struct device *dev) +{ + return adf41513_suspend(dev_get_drvdata(dev)); +} + +static int adf41513_pm_resume(struct device *dev) +{ + return adf41513_resume(dev_get_drvdata(dev)); +} + +static const struct adf41513_chip_info adf41510_chip_info =3D { + .name =3D "adf41510", + .max_rf_freq_hz =3D ADF41510_MAX_RF_FREQ_HZ, + .has_prescaler_8_9 =3D false, +}; + +static const struct adf41513_chip_info adf41513_chip_info =3D { + .name =3D "adf41513", + .max_rf_freq_hz =3D ADF41513_MAX_RF_FREQ_HZ, + .has_prescaler_8_9 =3D true, +}; + +static int adf41513_probe(struct spi_device *spi) +{ + struct device *dev =3D &spi->dev; + struct iio_dev *indio_dev; + struct adf41513_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; + st->chip_info =3D spi_get_device_match_data(spi); + if (!st->chip_info) + return -EINVAL; + + spi_set_drvdata(spi, st); + + st->ref_clk =3D devm_clk_get_enabled(dev, NULL); + if (IS_ERR(st->ref_clk)) + return PTR_ERR(st->ref_clk); + + st->ref_freq_hz =3D clk_get_rate(st->ref_clk); + if (st->ref_freq_hz < ADF41513_MIN_REF_FREQ_HZ || + st->ref_freq_hz > ADF41513_MAX_REF_FREQ_HZ) + return dev_err_probe(dev, -ERANGE, + "reference frequency %u Hz out of range\n", + st->ref_freq_hz); + + ret =3D adf41513_parse_fw(st); + if (ret) + return ret; + + ret =3D devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(adf41513_power_supplies), + adf41513_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable regulators\n"); + + st->chip_enable =3D devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH= ); + if (IS_ERR(st->chip_enable)) + return dev_err_probe(dev, PTR_ERR(st->chip_enable), + "fail to request chip enable GPIO\n"); + + st->lock_detect =3D devm_gpiod_get_optional(dev, "lock-detect", GPIOD_IN); + if (IS_ERR(st->lock_detect)) + return dev_err_probe(dev, PTR_ERR(st->lock_detect), + "fail to request lock detect GPIO\n"); + + ret =3D devm_mutex_init(dev, &st->lock); + if (ret) + return ret; + + indio_dev->name =3D st->chip_info->name; + indio_dev->info =3D &adf41513_info; + indio_dev->modes =3D INDIO_DIRECT_MODE; + indio_dev->channels =3D &adf41513_chan; + indio_dev->num_channels =3D 1; + + ret =3D adf41513_setup(st); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to setup device\n"); + + ret =3D devm_add_action_or_reset(dev, adf41513_power_down, st); + if (ret) + return dev_err_probe(dev, ret, "Failed to add power down action\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id adf41513_id[] =3D { + {"adf41510", (kernel_ulong_t)&adf41510_chip_info}, + {"adf41513", (kernel_ulong_t)&adf41513_chip_info}, + { } +}; +MODULE_DEVICE_TABLE(spi, adf41513_id); + +static const struct of_device_id adf41513_of_match[] =3D { + { .compatible =3D "adi,adf41510", .data =3D &adf41510_chip_info }, + { .compatible =3D "adi,adf41513", .data =3D &adf41513_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, adf41513_of_match); + +static DEFINE_SIMPLE_DEV_PM_OPS(adf41513_pm_ops, adf41513_pm_suspend, adf4= 1513_pm_resume); + +static struct spi_driver adf41513_driver =3D { + .driver =3D { + .name =3D "adf41513", + .pm =3D pm_ptr(&adf41513_pm_ops), + .of_match_table =3D adf41513_of_match, + }, + .probe =3D adf41513_probe, + .id_table =3D adf41513_id, +}; +module_spi_driver(adf41513_driver); + +MODULE_AUTHOR("Rodrigo Alencar "); +MODULE_DESCRIPTION("Analog Devices ADF41513 PLL Frequency Synthesizer"); +MODULE_LICENSE("GPL"); --=20 2.43.0 From nobody Thu Apr 9 16:35:12 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 0AE6042E017; Tue, 3 Mar 2026 13:27:26 +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=1772544446; cv=none; b=YvY7pfIUNWlt+PgpqY3ile665YXlEWm4Ih8NrYZmU19VDWgk8Q15A8L0gkRBQpoXE/6OcvYyQAah3/F7s7815DPJEz2NQPx8UTyKfcS2x3PvH4ypbSIZ4PH0yVitHgf9yNlR5lkaT9hnU1CjYAE2sk25SwCguyvgK5AsjWjyTUI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772544446; c=relaxed/simple; bh=UUGqq6KuJ9zIG0X2o0ubuPGPMlCBeEdzgPCnCWv6v00=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WNu9P0xzPc3JuR4wC9h7AdN7YiSCWS05iEXnvYH/n6pbQxxKDbalj9rOhCq7Tq5W12tFUOBo5ShRsJmUX8MKVJw49aJrotzVkmlsTp36H2TuPqT2M1v2j26vHxUdc/+jh9clz1s583e/njVoaBF66MRncweF9HVJvW+AU2OKNGE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=VQyCQtV8; 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="VQyCQtV8" Received: by smtp.kernel.org (Postfix) with ESMTPS id D466FC2BCB0; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772544445; bh=UUGqq6KuJ9zIG0X2o0ubuPGPMlCBeEdzgPCnCWv6v00=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=VQyCQtV8UMutcnevWwH4Mc9Ijoz4SsPoq+laa86NSpinJD1it9v5Y3RheZP6HvM3x 3W9CEdvIQM9aFmWKqf0P24UjezfgwIRoa+6oMLiHY/+skWnuTEju388cnTfDOytCTA CbWvB8mPrIQT8z1pjGpsW9xdm9eFBwwdebtH8OWW1W86Svi0AJ83rcj70nLhsJPWt3 wZQhhyCIsm7NCYHECvyOYTx2b7w41/jzbVJ0U9Zr3Jlwpam1zKpdzyIjRC9xUGpcpc wnQDrn/EW7HLZrEaZ1oDySkSeQsYidFG6an//tHuISn4MBj2i9MiCCjb7eCgyoakSr LwUe5J5rUspPw== 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 CA40BEC1446; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 03 Mar 2026 13:27:12 +0000 Subject: [PATCH v8 07/10] iio: frequency: adf41513: handle LE synchronization feature 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: <20260303-adf41513-iio-driver-v8-7-8dd2417cc465@analog.com> References: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> In-Reply-To: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-doc@vger.kernel.org Cc: Jonathan Cameron , David Lechner , Andy Shevchenko , Lars-Peter Clausen , Michael Hennerich , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Andrew Morton , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772544443; l=2998; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=La2NnbCEmWKuh8TItMnd816K8WFyfx5bTjA0xtRndnc=; b=t64EkkjlXr7ad+fhYHf+npon3wV0NG7srQARq1SSybHGSGcDbGPGcyohPRawCDZE6JHOA4Iz2 hJPtsi3o8AtCL6AjreeqGlmvszPjO4OStRGnZvGNCkx7vYV6Yagxv6M 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 When LE sync is enabled, it is must be set after powering up and must be disabled when powering down. It is recommended when using the PLL as a frequency synthesizer, where reference signal will always be present while the device is being configured. Signed-off-by: Rodrigo Alencar --- drivers/iio/frequency/adf41513.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/iio/frequency/adf41513.c b/drivers/iio/frequency/adf41= 513.c index 5a0682667d1f..caae9d53c3e2 100644 --- a/drivers/iio/frequency/adf41513.c +++ b/drivers/iio/frequency/adf41513.c @@ -222,6 +222,7 @@ struct adf41513_data { bool phase_detector_polarity; =20 bool logic_lvl_1v8_en; + bool le_sync_en; }; =20 struct adf41513_pll_settings { @@ -634,13 +635,27 @@ static int adf41513_set_frequency(struct adf41513_sta= te *st, u64 freq_uhz, u16 s static int adf41513_suspend(struct adf41513_state *st) { st->regs[ADF41513_REG6] |=3D FIELD_PREP(ADF41513_REG6_POWER_DOWN_MSK, 1); + st->regs[ADF41513_REG12] &=3D ~ADF41513_REG12_LE_SELECT_MSK; return adf41513_sync_config(st, ADF41513_SYNC_DIFF); } =20 static int adf41513_resume(struct adf41513_state *st) { + int ret; + st->regs[ADF41513_REG6] &=3D ~ADF41513_REG6_POWER_DOWN_MSK; - return adf41513_sync_config(st, ADF41513_SYNC_DIFF); + ret =3D adf41513_sync_config(st, ADF41513_SYNC_DIFF); + if (ret) + return ret; + + if (st->data.le_sync_en) { + st->regs[ADF41513_REG12] |=3D ADF41513_REG12_LE_SELECT_MSK; + ret =3D adf41513_sync_config(st, ADF41513_SYNC_DIFF); + if (ret) + return ret; + } + + return ret; } =20 static ssize_t adf41513_read_uhz(struct iio_dev *indio_dev, @@ -933,6 +948,8 @@ static int adf41513_parse_fw(struct adf41513_state *st) "invalid lock detect count: %u\n", tmp); st->data.lock_detect_count =3D tmp; =20 + /* load enable sync */ + st->data.le_sync_en =3D device_property_read_bool(dev, "adi,le-sync-enabl= e"); st->data.freq_resolution_uhz =3D MICROHZ_PER_HZ; =20 return 0; @@ -940,6 +957,7 @@ static int adf41513_parse_fw(struct adf41513_state *st) =20 static int adf41513_setup(struct adf41513_state *st) { + int ret; u32 tmp; =20 memset(st->regs_hw, 0xFF, sizeof(st->regs_hw)); @@ -973,8 +991,19 @@ static int adf41513_setup(struct adf41513_state *st) st->data.logic_lvl_1v8_en ? 0 : 1); =20 /* perform initialization sequence with power-up frequency */ - return adf41513_set_frequency(st, st->data.power_up_frequency_hz * MICRO, - ADF41513_SYNC_ALL); + ret =3D adf41513_set_frequency(st, st->data.power_up_frequency_hz * MICRO, + ADF41513_SYNC_ALL); + if (ret) + return ret; + + if (st->data.le_sync_en) { + st->regs[ADF41513_REG12] |=3D ADF41513_REG12_LE_SELECT_MSK; + ret =3D adf41513_sync_config(st, ADF41513_SYNC_DIFF); + if (ret) + return ret; + } + + return 0; } =20 static void adf41513_power_down(void *data) --=20 2.43.0 From nobody Thu Apr 9 16:35:12 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 24CE842F54A; Tue, 3 Mar 2026 13:27:26 +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=1772544446; cv=none; b=Y21Xy5dp6+lU/vd53k83Iera/BLDXJVFy7vnH1ty39xwitEW+oFBglFaKNbCGWkk7kpLU/CjjaoVyAjBuBRDRSnYZQD/FgHLIy8UHgLkhfGtA7es9DdCKMTKqnqTeeYyVms6g9+tr304d6HZHT0iJ0di+N3QT8q3HF/WkiP2KWY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772544446; c=relaxed/simple; bh=tigc4gi4sPSKdliL+ovBeKEtJZo86fHzu2ue3fguVxs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=tYAp1r+MqcRsJZQxzp8tfW0sBYsdEoNpJ/f2NdodAODvojQ5Hh04iMFbuXSDeJ9Y07EL9AdmlftyiLGmK+Gz8JFFiqoMam4PZH8dRHjviblPHEtC/mZZqX09lyREdEy7j1PwvWkBVbEem6sDEBA6jAGxic3s5zh5mqJADfP9F/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=a9rDFZ7Q; 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="a9rDFZ7Q" Received: by smtp.kernel.org (Postfix) with ESMTPS id ECEFDC19422; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772544446; bh=tigc4gi4sPSKdliL+ovBeKEtJZo86fHzu2ue3fguVxs=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=a9rDFZ7QT0PJXb7LSwAF4ra55uO6PU5NpfMoBYdlg+XJEuj9PyHLjwm/7Ddrkp2UZ T9sNiro0N2aL8SlzFw4T5jMYVK/yHulAw/XEtOrKceqo1Fvh1QHZlM81vXtR2a0Ctd F7ULV5CUfjO9p75yWOHQBf1NE1dhbMcNDTZiVsuug3CYXTx9wDZLarmqxV/lZWpSVQ L7s9gvSMpRtUoSaB92EClSUWXEJlfZ2apHDw+XgidJdviam5+VejZP7kLPejSz1p6u VHNvS+T1or/XG9yljhIpkbuCm5qzhZ9aNm55WiqFVcJC0GaZUlcGrq6QVYzlUzZdb3 s8y7AWHHXTsyA== 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 E17BDEC1442; Tue, 3 Mar 2026 13:27:25 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 03 Mar 2026 13:27:13 +0000 Subject: [PATCH v8 08/10] iio: frequency: adf41513: features on frequency change 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: <20260303-adf41513-iio-driver-v8-8-8dd2417cc465@analog.com> References: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> In-Reply-To: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-doc@vger.kernel.org Cc: Jonathan Cameron , David Lechner , Andy Shevchenko , Lars-Peter Clausen , Michael Hennerich , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Andrew Morton , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772544443; l=5625; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=mrEXi3f4W8srLUTxz0a6PhFOtrsgfGvKqjnyVji55K4=; b=ozaiX5k2acrwpv+BUEVVK7/J5WC8HQhbMGoNdM/q7uWy+j0+1/PLymuP/6d9kkxDC1veDN6Kn GVf9DuTifbNBUpxUhe9IgG0EYZvV1SEiH6GB3x0Jeu3N79I/9sFKO2H 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 Set Bleed current when PFD frequency changes (bleed enabled when in fractional mode). Set lock detector window size, handling bias and precision. Add phase resync support, setting clock dividers when PFD frequency changes. Signed-off-by: Rodrigo Alencar --- drivers/iio/frequency/adf41513.c | 100 +++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 100 insertions(+) diff --git a/drivers/iio/frequency/adf41513.c b/drivers/iio/frequency/adf41= 513.c index caae9d53c3e2..f6ed42df26d6 100644 --- a/drivers/iio/frequency/adf41513.c +++ b/drivers/iio/frequency/adf41513.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include =20 @@ -213,6 +214,7 @@ struct adf41513_chip_info { struct adf41513_data { u64 power_up_frequency_hz; u64 freq_resolution_uhz; + u32 phase_resync_period_ns; u32 charge_pump_voltage_mv; u32 lock_detect_count; =20 @@ -271,6 +273,16 @@ struct adf41513_state { struct adf41513_pll_settings settings; }; =20 +static const u16 adf41513_ld_window_x10_ns[] =3D { + 9, 12, 16, 17, 21, 28, 29, 35, /* 0 - 7 */ + 43, 47, 49, 52, 70, 79, 115, /* 8 - 14 */ +}; + +static const u8 adf41513_ldp_bias[] =3D { + 0xC, 0xD, 0xE, 0x8, 0x9, 0x4, 0xA, 0x5, /* 0 - 7 */ + 0x0, 0x6, 0xB, 0x1, 0x2, 0x7, 0x3, /* 8 - 14 */ +}; + static const char * const adf41513_power_supplies[] =3D { "avdd1", "avdd2", "avdd3", "avdd4", "avdd5", "vp", }; @@ -578,9 +590,82 @@ static int adf41513_calc_pll_settings(struct adf41513_= state *st, return 0; } =20 +static void adf41513_set_bleed_val(struct adf41513_state *st) +{ + u32 bleed_value, cp_index; + + if (st->data.phase_detector_polarity) + bleed_value =3D 90; + else + bleed_value =3D 144; + + cp_index =3D 1 + FIELD_GET(ADF41513_REG5_CP_CURRENT_MSK, + st->regs[ADF41513_REG5]); + bleed_value =3D div64_u64(st->settings.pfd_frequency_uhz * cp_index * ble= ed_value, + 1600ULL * MEGA * MICROHZ_PER_HZ); + + FIELD_MODIFY(ADF41513_REG6_BLEED_CURRENT_MSK, &st->regs[ADF41513_REG6], + bleed_value); +} + +static void adf41513_set_ld_window(struct adf41513_state *st) +{ + /* + * The ideal lock detector window size is halfway between the max + * window, set by the phase comparison period t_PFD =3D (1 / f_PFD), + * and the minimum is set by (I_BLEED/I_CP) =C3=97 t_PFD + */ + u16 ld_window_10x_ns =3D div64_u64(10ULL * NSEC_PER_SEC * MICROHZ_PER_HZ, + st->settings.pfd_frequency_uhz << 1); + u8 ld_idx, ldp, ld_bias; + + if (st->settings.mode !=3D ADF41513_MODE_INTEGER_N) { + /* account for bleed current (deduced from eq.6 and eq.7) */ + if (st->data.phase_detector_polarity) + ld_window_10x_ns +=3D 4; + else + ld_window_10x_ns +=3D 6; + } + + ld_idx =3D find_closest(ld_window_10x_ns, adf41513_ld_window_x10_ns, + ARRAY_SIZE(adf41513_ld_window_x10_ns)); + ldp =3D (adf41513_ldp_bias[ld_idx] >> 2) & 0x3; + ld_bias =3D adf41513_ldp_bias[ld_idx] & 0x3; + + FIELD_MODIFY(ADF41513_REG6_LDP_MSK, &st->regs[ADF41513_REG6], ldp); + FIELD_MODIFY(ADF41513_REG9_LD_BIAS_MSK, &st->regs[ADF41513_REG9], ld_bias= ); +} + +static void adf41513_set_phase_resync(struct adf41513_state *st) +{ + u32 total_div, clk1_div, clk2_div; + + if (!st->data.phase_resync_period_ns) + return; + + /* assuming both clock dividers hold similar values */ + total_div =3D mul_u64_u64_div_u64(st->settings.pfd_frequency_uhz, + st->data.phase_resync_period_ns, + 1ULL * MICROHZ_PER_HZ * NSEC_PER_SEC); + clk1_div =3D clamp(int_sqrt(total_div), 1, + ADF41513_MAX_CLK_DIVIDER); + clk2_div =3D clamp(DIV_ROUND_CLOSEST(total_div, clk1_div), 1, + ADF41513_MAX_CLK_DIVIDER); + + FIELD_MODIFY(ADF41513_REG5_CLK1_DIV_MSK, &st->regs[ADF41513_REG5], + clk1_div); + FIELD_MODIFY(ADF41513_REG7_CLK2_DIV_MSK, &st->regs[ADF41513_REG7], + clk2_div); + + /* enable phase resync */ + st->regs[ADF41513_REG7] |=3D ADF41513_REG7_CLK_DIV_MODE_MSK; +} + static int adf41513_set_frequency(struct adf41513_state *st, u64 freq_uhz,= u16 sync_mask) { struct adf41513_pll_settings result; + bool pfd_change =3D false; + bool mode_change =3D false; int ret; =20 ret =3D adf41513_calc_pll_settings(st, &result, freq_uhz); @@ -588,6 +673,8 @@ static int adf41513_set_frequency(struct adf41513_state= *st, u64 freq_uhz, u16 s return ret; =20 /* apply computed results to pll settings */ + pfd_change =3D st->settings.pfd_frequency_uhz !=3D result.pfd_frequency_u= hz; + mode_change =3D st->settings.mode !=3D result.mode; st->settings =3D result; =20 dev_dbg(&st->spi->dev, @@ -629,6 +716,14 @@ static int adf41513_set_frequency(struct adf41513_stat= e *st, u64 freq_uhz, u16 s st->regs[ADF41513_REG6] |=3D ADF41513_REG6_BLEED_ENABLE_MSK; } =20 + if (pfd_change) { + adf41513_set_bleed_val(st); + adf41513_set_phase_resync(st); + } + + if (pfd_change || mode_change) + adf41513_set_ld_window(st); + return adf41513_sync_config(st, sync_mask | ADF41513_SYNC_REG0); } =20 @@ -938,6 +1033,11 @@ static int adf41513_parse_fw(struct adf41513_state *s= t) st->data.phase_detector_polarity =3D device_property_read_bool(dev, "adi,phase-detector-polarity-positive-ena= ble"); =20 + st->data.phase_resync_period_ns =3D 0; + ret =3D device_property_read_u32(dev, "adi,phase-resync-period-ns", &tmp); + if (!ret) + st->data.phase_resync_period_ns =3D tmp; + st->data.logic_lvl_1v8_en =3D device_property_read_bool(dev, "adi,logic-l= evel-1v8-enable"); =20 tmp =3D ADF41513_LD_COUNT_MIN; --=20 2.43.0 From nobody Thu Apr 9 16:35:12 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 38C6542F561; Tue, 3 Mar 2026 13:27:26 +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=1772544446; cv=none; b=WfYe9haWi32ernOHHjkDXQ47Xk/JVG6xm8+9NqQFSj7y18wL8x/mHBm5KX7fG2oIahgkbH80MJYhM6w8jHZGhIQ0enK8uGWcYADiVwsi77r5eZ9/FyPBl69XQPrhKwEyh9+934nWQ/2sT3SCoOlcMPh2shXS4MvVsBjXyligYjE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772544446; c=relaxed/simple; bh=w/rxjAWYU2KOxaJpbNIp7TzvtNTgXMOsc4OEBLbpIJA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=inAWjUhO1qJCW/EqsQAAI8xPifnHayerln2GFBmQjPrFFQTzQsEicVuTHpmjzghO2GL/oeqXFts3Bwzu9zyRbi/TCKnydDKK6GcBjBp9hZR80FXOoJ+xIwdsCqcaEZoYvaCc+P8234cDyjye0tfZcpFFyag0cLVLhTsSv3cfm8g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=uzNO25gr; 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="uzNO25gr" Received: by smtp.kernel.org (Postfix) with ESMTPS id 14AF8C2BCC7; Tue, 3 Mar 2026 13:27:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772544446; bh=w/rxjAWYU2KOxaJpbNIp7TzvtNTgXMOsc4OEBLbpIJA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=uzNO25grAPbcWMgUMKbJ1JBLPB5zy/Ag8GKeKxKCnVUqlVkyjMKpyrirJvFqJ/hxX FdtXhDXMheEmaj6EKJ7Qzoq8PKT+MZRw6dICmpJqGUECEYxQO+UpOgl+BK3oAvb6xb +40BhQkFVhfSHGlFRkwKj5l5++ieXZhOSJ6oQ9LSSk9VCxtBF5aQUOnmuH5921bJnt zmsdb8DTxmsLj73QNzEQj6ds1lEcAAiaE9zExVujNSvoTDRL0Sgh3riL5jLmMtb6ao lKjPl41tYzq/gn6opYXZXuP0m5GFNt6lfZua1qy7c4+i4tkuoBrLAgAvV5SV/FspB9 4BOCAV/THJXqg== 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 05F7DEC1444; Tue, 3 Mar 2026 13:27:26 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 03 Mar 2026 13:27:14 +0000 Subject: [PATCH v8 09/10] docs: iio: add documentation for adf41513 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: <20260303-adf41513-iio-driver-v8-9-8dd2417cc465@analog.com> References: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> In-Reply-To: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-doc@vger.kernel.org Cc: Jonathan Cameron , David Lechner , Andy Shevchenko , Lars-Peter Clausen , Michael Hennerich , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Andrew Morton , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772544443; l=9001; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=LDXTaT5jfG3KRzKE9ByIC/7sTxo2HJDqQu0ZUjzPaN8=; b=GK085WiY7LiSntA414L3elxjzJ15AHq7arlw4a1tKV5Zp7qZRB1fyc6nqCOXM07QQ/t1td2u4 GliTMnwd14GCxLn8Anzzc9xNvUU8cCv+q7sCJu2hD5ccx4T7eJ64F5R 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 ADF41513 driver, which describes the device driver files and shows how userspace may consume the ABI for various tasks. Signed-off-by: Rodrigo Alencar --- Documentation/iio/adf41513.rst | 199 +++++++++++++++++++++++++++++++++++++= ++++ Documentation/iio/index.rst | 1 + MAINTAINERS | 1 + 3 files changed, 201 insertions(+) diff --git a/Documentation/iio/adf41513.rst b/Documentation/iio/adf41513.rst new file mode 100644 index 000000000000..4193c825b532 --- /dev/null +++ b/Documentation/iio/adf41513.rst @@ -0,0 +1,199 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +ADF41513 driver +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +This driver supports Analog Devices' ADF41513 and similar SPI PLL frequency +synthesizers. + +1. Supported devices +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +* `ADF41510 `_ +* `ADF41513 `_ + +The ADF41513 is an ultralow noise frequency synthesizer that can be used to +implement local oscillators (LOs) as high as 26.5 GHz in the upconversion = and +downconversion sections of wireless receivers and transmitters. The ADF415= 10 +is a similar device that supports frequencies up to 10 GHz. + +Both devices support integer-N and fractional-N operation modes, providing +excellent phase noise performance and flexible frequency generation +capabilities. + +Key Features: + +- **ADF41510**: 1 GHz to 10 GHz frequency range +- **ADF41513**: 1 GHz to 26.5 GHz frequency range +- Integer-N and fractional-N operation modes +- Ultra-low phase noise (-235 dBc/Hz integer-N, -231 dBc/Hz fractional-N) +- High maximum PFD frequency (250 MHz integer-N, 125 MHz fractional-N) +- 25-bit fixed modulus or 49-bit variable modulus fractional modes +- Programmable charge pump currents with 16x range +- Digital lock detect functionality +- Phase resync capability for consistent output phase + +2. Device attributes +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The ADF41513 driver provides the following IIO extended attributes for +frequency control and monitoring: + +Each IIO device has a device folder under ``/sys/bus/iio/devices/iio:devic= eX``, +where X is the IIO index of the device. Under these folders reside a set of +device files that provide access to the synthesizer's functionality. + +The following table shows the ADF41513 related device files: + ++----------------------+--------------------------------------------------= -----+ +| Device file | Description = | ++=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D+=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D+ +| frequency | RF output frequency control and readback (Hz) = | ++----------------------+--------------------------------------------------= -----+ +| frequency_resolution | Target frequency resolution control (Hz) = | ++----------------------+--------------------------------------------------= -----+ +| powerdown | Power management control (0=3Dactive, 1=3Dpower d= own) | ++----------------------+--------------------------------------------------= -----+ +| phase | RF output phase adjustment and readback (radians)= | ++----------------------+--------------------------------------------------= -----+ + +2.1 Frequency Control +---------------------- + +The ``frequency`` attribute controls the RF output frequency with sub-Hz +precision. The driver automatically selects between integer-N and fraction= al-N +modes to achieve the requested frequency with the best possible phase noise +performance. + +**Supported ranges:** + +- **ADF41510**: 1,000,000,000 Hz to 10,000,000,000 Hz (1 GHz to 10 GHz) +- **ADF41513**: 1,000,000,000 Hz to 26,500,000,000 Hz (1 GHz to 26.5 GHz) + +The frequency is specified in Hz, for sub-Hz precision use decimal notatio= n. +For example, 12.102 GHz would be written as "12102000000.000000". + +2.2 Frequency Resolution Control +-------------------------------- + +The ``frequency_resolution`` attribute controls the target frequency resol= ution +that the driver attempts to achieve. This affects the choice between integ= er-N +and fractional-N modes, including fixed modulus (25-bit) and variable modu= lus +(49-bit) fractional-N modes: + +- **Integer-N**: Resolution =3D f_PFD +- **Fixed modulus**: Resolution =3D f_PFD / 2^25 (~3 Hz with 100 MHz PFD) +- **Variable modulus**: Resolution =3D f_PFD / 2^49 (=C2=B5Hz resolution p= ossible) + +Default resolution is 1 Hz (1,000,000 =C2=B5Hz). + +2.3 Phase adjustment +-------------------- + +The ``phase`` attribute allows adjustment of the output phase in radians. +Setting this attribute enables phase adjustment. It can be set from 0 to 2= *pi +radians. Reading this attribute returns the current phase offset of the ou= tput +signal. To create a consistent phase relationship with the reference signa= l, +the phase resync feature needs to be enabled by setting a non-zero value t= o the +``adi,phase-resync-period-ns`` device property, which triggers a phase +resynchronization after locking is achieved. + +3. Operating modes +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +3.1 Integer-N Mode +------------------ + +When the requested frequency can be achieved as an integer multiple of the= PFD +frequency (within the specified resolution tolerance), the driver automati= cally +selects integer-N mode for optimal phase noise performance. + +In integer-N mode: + +- Phase noise: -235 dBc/Hz normalized floor +- Frequency resolution: f_PFD (same as PFD frequency) +- Maximum PFD frequency: 250 MHz +- Bleed current: Disabled + +3.2 Fractional-N Mode +--------------------- + +When sub-integer frequency steps are required, the driver automatically se= lects +fractional-N mode using either fixed or variable modulus. + +**Fixed Modulus (25-bit)**: + +- Used when variable modulus is not required +- Resolution: f_PFD / 2^25 +- Simpler implementation, faster settling + +**Variable Modulus (49-bit)**: + +- Used for maximum resolution requirements +- Resolution: f_PFD / 2^49 (theoretical) +- Exact frequency synthesis capability + +In fractional-N mode: + +- Phase noise: -231 dBc/Hz normalized floor +- Maximum PFD frequency: 125 MHz +- Bleed current: Automatically enabled and optimized +- Dithering: Enabled to reduce fractional spurs + +3.3 Automatic Mode Selection +---------------------------- + +The driver automatically selects the optimal operating mode based on: + +1. **Frequency accuracy requirements**: Determined by frequency_resolution= setting +2. **Phase noise optimization**: Integer-N preferred when possible +3. **PFD frequency constraints**: Different limits for integer vs fraction= al modes +4. **Prescaler selection**: Automatic 4/5 vs 8/9 prescaler selection based= on frequency + +4. Usage examples +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +4.1 Basic Frequency Setting +---------------------------- + +Set output frequency to 12.102 GHz: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> echo 12102000000 > out_altvolta= ge0_frequency + +Read current frequency: + +.. code-block:: bash + + root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency + 12101999999.582767 + +4.2 High Resolution Frequency Control +------------------------------------- + +Configure for sub-Hz resolution and set a precise frequency: + +.. code-block:: bash + + # Set resolution to 0.1 Hz (100,000 =C2=B5Hz) + root:/sys/bus/iio/devices/iio:device0> echo 0.1 > out_altvoltage0_freq= uency_resolution + + # Set frequency to 12.102 GHz (1 =C2=B5Hz precision) + root:/sys/bus/iio/devices/iio:device0> echo 12102000000 > out_altvolta= ge0_frequency + root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency + 12101999999.980131 + +4.3 Monitor Lock Status +----------------------- + +When lock detect GPIO is configured, check if PLL is locked: + +.. code-block:: bash + + # Read frequency - will return error if not locked + root:/sys/bus/iio/devices/iio:device0> cat out_altvoltage0_frequency + +If the PLL is not locked, the frequency read will return ``-EBUSY`` (Devic= e or +resource busy). diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst index ba3e609c6a13..605871765c78 100644 --- a/Documentation/iio/index.rst +++ b/Documentation/iio/index.rst @@ -30,6 +30,7 @@ Industrial I/O Kernel Drivers ad7625 ad7944 ade9000 + adf41513 adis16475 adis16480 adis16550 diff --git a/MAINTAINERS b/MAINTAINERS index 051bf31feea2..200f0d0fcbdd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1640,6 +1640,7 @@ L: linux-iio@vger.kernel.org S: Supported W: https://ez.analog.com/linux-software-drivers F: Documentation/devicetree/bindings/iio/frequency/adi,adf41513.yaml +F: Documentation/iio/adf41513.rst F: drivers/iio/frequency/adf41513.c =20 ANALOG DEVICES INC ADF4377 DRIVER --=20 2.43.0 From nobody Thu Apr 9 16:35:12 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 5463442F57C; Tue, 3 Mar 2026 13:27:26 +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=1772544446; cv=none; b=gLAayRSfUq95Svjiu4wdTj9GgqH7XjpvKuL1rgDAjBOeAW9MluO3Tp2812r16NQqgEqrFGK/dEog1TmIK9sCGgPXVFFlW0JQvijh69T9N8nZ0g4kLlKRkVF3Wba5jfpJQBIv8guB1QhYZITPcyxlKdYIRPeBcMhDhJJxbw65RV0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772544446; c=relaxed/simple; bh=9B+csSwynGBtD47HQVlSlvUhKYSoSrYkjk2oqbGoZOQ=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=RPfG0neUYHHEEqSOYnvXwZ7HMxsoZfSnX557ZVltDMjQUzW3dQ7tYp1qdK+HvECmuijkj1jnsvEfYILiQBGPdpN7q0fsjlpJH3MdZbqtf6Kr4gQeZtKf6Gg+CWk4hG7h0Mz4bcIzBeACEhuLxvRDT/El+5KRTQ9SL6/z9nNmuM4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=S0fv+2Wz; 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="S0fv+2Wz" Received: by smtp.kernel.org (Postfix) with ESMTPS id 2257FC116C6; Tue, 3 Mar 2026 13:27:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772544446; bh=9B+csSwynGBtD47HQVlSlvUhKYSoSrYkjk2oqbGoZOQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=S0fv+2WzqWxTyNArHPad+WOkTTPcPbu+p9lwjrVPWg5YQp5JxsMKF58roAtrAusCn tJbrWdKoPr33ZsdDvHaQh6yCZz3HD4hMfGcDN5E8FfAao52BQvSf8dAJO7IeZ63XzP JXyeIhEUkc9fY/p9JfzEAoh4LxkufMoKoZd1CK2Vv0JGqRw2cJ9eXJV2HckXj8GygN 3od00yyJjBh9wHmYWuLafy9dIWmo56sGg8q59HRf+PlUvG7iVTiRiR1t0wTTSmjfNO dYqGIXXG8CLirp0llDIpu4ObxdpcHcPmYHl3io+IJfz+CpT89vxLbWb6sHpBPc7Hzd ZI/vTk3vORIWw== 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 19E4DEC1442; Tue, 3 Mar 2026 13:27:26 +0000 (UTC) From: Rodrigo Alencar via B4 Relay Date: Tue, 03 Mar 2026 13:27:15 +0000 Subject: [PATCH v8 10/10] Documentation: ABI: testing: add common ABI file for iio/frequency 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: <20260303-adf41513-iio-driver-v8-10-8dd2417cc465@analog.com> References: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> In-Reply-To: <20260303-adf41513-iio-driver-v8-0-8dd2417cc465@analog.com> To: linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-doc@vger.kernel.org Cc: Jonathan Cameron , David Lechner , Andy Shevchenko , Lars-Peter Clausen , Michael Hennerich , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Jonathan Corbet , Andrew Morton , Rodrigo Alencar X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1772544443; l=2210; i=rodrigo.alencar@analog.com; s=default; h=from:subject:message-id; bh=OhPLAamt4hmbeCatH+/iPHehi/HIntujTnWeOacWI/A=; b=1HLrvBUl4XUjDp85NenVgpnUhF9QEWduuqFD2Ibkb5S42i6e1GK/T6kVS0Pho6X6PDuAu8UpT h4llzrCrJiIAOgExN2AZDiiy63XmKIkudONA9U2pdTRqGmyGsXz/VJh 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 ABI documentation file for PLL/DDS devices with frequency_resolution sysfs entry attribute used by both ADF4350 and ADF41513. Signed-off-by: Rodrigo Alencar --- Documentation/ABI/testing/sysfs-bus-iio-frequency | 11 +++++++++++ Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 | 10 ---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency b/Documentat= ion/ABI/testing/sysfs-bus-iio-frequency new file mode 100644 index 000000000000..1ce8ae578fd6 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency @@ -0,0 +1,11 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resoluti= on +KernelVersion: 6.20 +Contact: linux-iio@vger.kernel.org +Description: + Stores channel Y frequency resolution/channel spacing in Hz for PLL + devices. The given value directly influences the operating mode when + fractional-N synthesis is required, as it derives values for + configurable modulus parameters used in the calculation of the output + frequency. It is assumed that the algorithm that is used to compute + the various dividers, is able to generate proper values for multiples + of channel spacing. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 b/Do= cumentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 index 1254457a726e..76987a119feb 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350 @@ -1,13 +1,3 @@ -What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency_resoluti= on -KernelVersion: 3.4.0 -Contact: linux-iio@vger.kernel.org -Description: - Stores channel Y frequency resolution/channel spacing in Hz. - The value given directly influences the MODULUS used by - the fractional-N PLL. It is assumed that the algorithm - that is used to compute the various dividers, is able to - generate proper values for multiples of channel spacing. - What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_refin_frequency KernelVersion: 3.4.0 Contact: linux-iio@vger.kernel.org --=20 2.43.0