Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 1/2] dt-bindings: rtc: ti,bq32k: Add delay on rtc reads
From: Adriana Stancu @ 2026-04-16  9:57 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: linux-rtc, devicetree, linux-kernel, robh, krzk+dt, conor+dt,
	Adriana Stancu
In-Reply-To: <20260416095706.3212158-1-adriana@arista.com>

Add a configurable "ti,read-settle-us" property to resolve a limitation
where aggressive I2C polling prevents the BQ32000's internal register to
update. This ensures the hardware has sufficient idle time to update its
buffer, preventing stale data reads on systems where the "interrupts" are
not configured.

Signed-off-by: Adriana Stancu <adriana@arista.com>
---
 Documentation/devicetree/bindings/rtc/ti,bq32000.yaml | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Documentation/devicetree/bindings/rtc/ti,bq32000.yaml b/Documentation/devicetree/bindings/rtc/ti,bq32000.yaml
index bf9c1c4ddb7e..46403f0c85a5 100644
--- a/Documentation/devicetree/bindings/rtc/ti,bq32000.yaml
+++ b/Documentation/devicetree/bindings/rtc/ti,bq32000.yaml
@@ -29,6 +29,15 @@ properties:
 
   trickle-diode-disable: true
 
+  ti,read-settle-us:
+    default: 0
+    description:
+      Delay in microseconds to wait before reading RTC registers.
+      Aggressive I2C polling on systems without an interrupt line
+      can prevent the BQ32000's internal refresh cycle, leading to
+      stale data. This delay ensures the hardware has sufficient
+      idle time to update its registers.
+
 required:
   - compatible
   - reg
-- 
2.51.0


^ permalink raw reply related

* [PATCH v2 0/2] rtc: bq32000: Add settle delay for aggressive polling
From: Adriana Stancu @ 2026-04-16  9:57 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: linux-rtc, devicetree, linux-kernel, robh, krzk+dt, conor+dt,
	Adriana Stancu
In-Reply-To: <20260416092414.3210383-1-adriana@arista.com>

This series addresses a limitation in the TI BQ32000 RTC where aggressive
I2C polling (done by userspace tools like hwclock on systems where the
interrupt line is not connected to the CPU) can prevent the refresh of
RTC registers.

This results in stale data reads or select() timeouts in userspace. The
series introduces a configurable "settle delay" via device tree to ensure
that the hardware has sufficient idle time between read attempts.

Patch 1: Adds the "ti,read-settle-us" property to the YAML bindings.
Patch 2: Implements the delay in the driver using usleep_range.

Changes in v2:
- Expanded dt-binding property description to explain use case.
- Updated commit messages on dt change to describe the scenario when the
dt property would be necessary.
- Reword the commit messages to respect wrapping at 75 columns.

Adriana Stancu (2):
  dt-bindings: rtc: ti,bq32k: Add delay on rtc reads
  rtc: bq32000: add configurable delay between RTC reads

 .../devicetree/bindings/rtc/ti,bq32000.yaml   |  9 +++++
 drivers/rtc/rtc-bq32k.c                       | 34 +++++++++++++++----
 2 files changed, 37 insertions(+), 6 deletions(-)

-- 
2.51.0


^ permalink raw reply

* Re: [PATCH 4/5] arm64: dts: qcom: add IPQ9650 SoC and rdp488 board support
From: Kathiravan Thirumoorthy @ 2026-04-16  9:54 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Konrad Dybcio,
	linux-arm-msm, linux-clk, devicetree, linux-kernel
In-Reply-To: <20260416-khaki-goose-of-will-ec76bf@quoll>


On 4/16/2026 3:05 PM, Krzysztof Kozlowski wrote:
> On Wed, Apr 15, 2026 at 07:03:19PM +0530, Kathiravan Thirumoorthy wrote:
>> +		xo_board: xo-board-clk {
>> +			compatible = "fixed-clock";
>> +			#clock-cells = <0>;
>> +		};
>> +	};
>> +
>> +	cpus {
>> +		#address-cells = <1>;
>> +		#size-cells = <0>;
>> +
>> +		CPU0: cpu@0 {
> Labels should be lowercase.

Ack.

>
> Best regards,
> Krzysztof
>

^ permalink raw reply

* RE: [PATCH] arm64: dts: renesas: rzt2h-n2h-evk: Configure eMMC/SDHI pins
From: Fabrizio Castro @ 2026-04-16  9:48 UTC (permalink / raw)
  To: Fabrizio Castro, Geert Uytterhoeven, magnus.damm, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-renesas-soc@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, Biju Das, Prabhakar Mahadev Lad
In-Reply-To: <20260331145221.7974-1-fabrizio.castro.jz@renesas.com>

Hi Geert,

Are you happy with this patch?

Without this patch fast SD cards/eMMCs may fail on the RZ/T2H and RZ/N2H.

If it helps, we may also add a couple of fixes tags to the patch, to fast
track it:
Fixes: 4d7624fc85a2 ("arm64: dts: renesas: rzt2h-rzn2h-evk: Enable eMMC")
Fixes: dba8ee27c5de ("arm64: dts: renesas: rzt2h-rzn2h-evk: Enable MicroSD card slot")

I haven't added them to the patch because I wanted to avoid being selected
for stable, as it depends on the below series:
https://lore.kernel.org/linux-renesas-soc/20260319141515.2053556-1-prabhakar.mahadev-lad.rj@bp.renesas.com/

Cheers,
Fab

> From: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
> Sent: 31 March 2026 15:52
> To: Geert Uytterhoeven <geert+renesas@glider.be>; magnus.damm <magnus.damm@gmail.com>; Rob Herring
> <robh@kernel.org>; Krzysztof Kozlowski <krzk+dt@kernel.org>; Conor Dooley <conor+dt@kernel.org>
> Cc: Fabrizio Castro <fabrizio.castro.jz@renesas.com>; linux-renesas-soc@vger.kernel.org;
> devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Biju Das <biju.das.jz@bp.renesas.com>;
> Prabhakar Mahadev Lad <prabhakar.mahadev-lad.rj@bp.renesas.com>
> Subject: [PATCH] arm64: dts: renesas: rzt2h-n2h-evk: Configure eMMC/SDHI pins
> 
> The HW user manual for the Renesas RZ/T2H and the RZ/N2H state
> that for SDR104, SDR50, and HS200 to work properly the eMMC/SDHI
> interface pins have to be configured as specified below:
> * SDn_CLK pin - drive strength: Ultra High, slew rate: fast
> * Other SDn_* pins: drive strength: High, slew rate: fast,
>   Schmitt trigger: disabled (not applicable to SDn_RST pins).
> 
> Adjust the pin definitions accordingly.
> 
> Signed-off-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
> ---
>  .../dts/renesas/rzt2h-n2h-evk-common.dtsi     | 54 ++++++++++++++++---
>  1 file changed, 46 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/arm64/boot/dts/renesas/rzt2h-n2h-evk-common.dtsi b/arch/arm64/boot/dts/renesas/rzt2h-
> n2h-evk-common.dtsi
> index f87c2492f414..3fae950db603 100644
> --- a/arch/arm64/boot/dts/renesas/rzt2h-n2h-evk-common.dtsi
> +++ b/arch/arm64/boot/dts/renesas/rzt2h-n2h-evk-common.dtsi
> @@ -275,12 +275,28 @@ data-pins {
>  				 <RZT2H_PORT_PINMUX(12, 7, 0x29)>, /* SD0_DATA5 */
>  				 <RZT2H_PORT_PINMUX(13, 0, 0x29)>, /* SD0_DATA6 */
>  				 <RZT2H_PORT_PINMUX(13, 1, 0x29)>; /* SD0_DATA7 */
> +			drive-strength-microamp = <9000>;
> +			slew-rate = <1>;
> +			input-schmitt-disable;
>  		};
> 
> -		ctrl-pins {
> -			pinmux = <RZT2H_PORT_PINMUX(12, 0, 0x29)>, /* SD0_CLK */
> -				 <RZT2H_PORT_PINMUX(12, 1, 0x29)>, /* SD0_CMD */
> -				 <RZT2H_PORT_PINMUX(13, 2, 0x29)>; /* SD0_RST# */
> +		clk-pins {
> +			pinmux = <RZT2H_PORT_PINMUX(12, 0, 0x29)>; /* SD0_CLK */
> +			drive-strength-microamp = <11800>;
> +			slew-rate = <1>;
> +		};
> +
> +		cmd-pins {
> +			pinmux = <RZT2H_PORT_PINMUX(12, 1, 0x29)>; /* SD0_CMD */
> +			drive-strength-microamp = <9000>;
> +			slew-rate = <1>;
> +			input-schmitt-disable;
> +		};
> +
> +		rst-pins {
> +			pinmux = <RZT2H_PORT_PINMUX(13, 2, 0x29)>; /* SD0_RST# */
> +			drive-strength-microamp = <9000>;
> +			slew-rate = <1>;
>  		};
>  	};
> 
> @@ -299,12 +315,23 @@ data-pins {
>  				 <RZT2H_PORT_PINMUX(12, 3, 0x29)>, /* SD0_DATA1 */
>  				 <RZT2H_PORT_PINMUX(12, 4, 0x29)>, /* SD0_DATA2 */
>  				 <RZT2H_PORT_PINMUX(12, 5, 0x29)>; /* SD0_DATA3 */
> +			drive-strength-microamp = <9000>;
> +			slew-rate = <1>;
> +			input-schmitt-disable;
> +		};
> +
> +		clk-pins {
> +			pinmux = <RZT2H_PORT_PINMUX(12, 0, 0x29)>; /* SD0_CLK */
> +			drive-strength-microamp = <11800>;
> +			slew-rate = <1>;
>  		};
> 
>  		ctrl-pins {
> -			pinmux = <RZT2H_PORT_PINMUX(12, 0, 0x29)>, /* SD0_CLK */
> -				 <RZT2H_PORT_PINMUX(12, 1, 0x29)>, /* SD0_CMD */
> +			pinmux = <RZT2H_PORT_PINMUX(12, 1, 0x29)>, /* SD0_CMD */
>  				 <RZT2H_PORT_PINMUX(22, 5, 0x29)>; /* SD0_CD */
> +			drive-strength-microamp = <9000>;
> +			slew-rate = <1>;
> +			input-schmitt-disable;
>  		};
>  	};
> 
> @@ -323,12 +350,23 @@ data-pins {
>  				 <RZT2H_PORT_PINMUX(17, 0, 0x29)>, /* SD1_DATA1 */
>  				 <RZT2H_PORT_PINMUX(17, 1, 0x29)>, /* SD1_DATA2 */
>  				 <RZT2H_PORT_PINMUX(17, 2, 0x29)>; /* SD1_DATA3 */
> +			drive-strength-microamp = <9000>;
> +			slew-rate = <1>;
> +			input-schmitt-disable;
> +		};
> +
> +		clk-pins {
> +			pinmux = <RZT2H_PORT_PINMUX(16, 5, 0x29)>; /* SD1_CLK */
> +			drive-strength-microamp = <11800>;
> +			slew-rate = <1>;
>  		};
> 
>  		ctrl-pins {
> -			pinmux = <RZT2H_PORT_PINMUX(16, 5, 0x29)>, /* SD1_CLK */
> -				 <RZT2H_PORT_PINMUX(16, 6, 0x29)>, /* SD1_CMD */
> +			pinmux = <RZT2H_PORT_PINMUX(16, 6, 0x29)>, /* SD1_CMD */
>  				 <RZT2H_PORT_PINMUX(17, 4, 0x29)>; /* SD1_CD */
> +			drive-strength-microamp = <9000>;
> +			slew-rate = <1>;
> +			input-schmitt-disable;
>  		};
>  	};
>  };
> --
> 2.34.1


^ permalink raw reply

* Re: [PATCH RFT] arm64: dts: qcom: sm8650: Fix IPA IMEM slice
From: Konrad Dybcio @ 2026-04-16  9:41 UTC (permalink / raw)
  To: Alexander Koskovich, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <20260415-fix-8650-ipa-modem-tables-v1-1-95f8f425e416@pm.me>

On 4/16/26 1:45 AM, Alexander Koskovich wrote:
> Downstream the IPA IMEM slice for SM8650 is described as:
> qcom,additional-mapping = <0x14683000 0x14683000 0x2000>;
> 
> Update upstream ipa_modem_tables to reflect downstream.
> 
> Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
> ---

Thanks for catching this!

Fixes: 581fc5d5ade6 ("arm64: dts: qcom: sm8650: Explicitly describe the IPA IMEM slice")
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* [PATCH v2 3/3] arm64: dts: qcom: eliza: Add IMEM node
From: Alexander Koskovich @ 2026-04-16  9:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio
  Cc: devicetree, linux-kernel, linux-arm-msm, Alexander Koskovich
In-Reply-To: <20260416-eliza-imem-v2-0-fb7a71123451@pm.me>

Add a node for the IMEM found on Eliza, which contains pil-reloc-info
and the modem tables for IPA, among others.

Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
 arch/arm64/boot/dts/qcom/eliza.dtsi | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/eliza.dtsi b/arch/arm64/boot/dts/qcom/eliza.dtsi
index 6fa5679c1a62..551df07e44c6 100644
--- a/arch/arm64/boot/dts/qcom/eliza.dtsi
+++ b/arch/arm64/boot/dts/qcom/eliza.dtsi
@@ -1029,6 +1029,26 @@ qup_uart14_default: qup-uart14-default-state {
 			};
 		};
 
+		sram@14680000 {
+			compatible = "qcom,eliza-imem", "mmio-sram";
+			reg = <0x0 0x14680000 0x0 0x2c000>;
+			ranges = <0x0 0x0 0x14680000 0x2c000>;
+
+			no-memory-wc;
+
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			pilreloc-sram@94c {
+				compatible = "qcom,pil-reloc-info";
+				reg = <0x94c 0xc8>;
+			};
+
+			ipa_modem_tables: modem-tables-sram@3000 {
+				reg = <0x3000 0x2000>;
+			};
+		};
+
 		apps_smmu: iommu@15000000 {
 			compatible = "qcom,eliza-smmu-500", "qcom,smmu-500", "arm,mmu-500";
 			reg = <0x0 0x15000000 0x0 0x100000>;

-- 
2.53.0



^ permalink raw reply related

* [PATCH v2 2/3] dt-bindings: sram: Document qcom,eliza-imem
From: Alexander Koskovich @ 2026-04-16  9:40 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio
  Cc: devicetree, linux-kernel, linux-arm-msm, Alexander Koskovich
In-Reply-To: <20260416-eliza-imem-v2-0-fb7a71123451@pm.me>

Add compatible for Eliza SoC IMEM.

Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
 Documentation/devicetree/bindings/sram/sram.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/sram/sram.yaml b/Documentation/devicetree/bindings/sram/sram.yaml
index 8985f89170be..27e5e274c3cb 100644
--- a/Documentation/devicetree/bindings/sram/sram.yaml
+++ b/Documentation/devicetree/bindings/sram/sram.yaml
@@ -34,6 +34,7 @@ properties:
         - nvidia,tegra186-sysram
         - nvidia,tegra194-sysram
         - nvidia,tegra234-sysram
+        - qcom,eliza-imem
         - qcom,hawi-imem
         - qcom,kaanapali-imem
         - qcom,milos-imem

-- 
2.53.0



^ permalink raw reply related

* [PATCH v2 1/3] arm64: dts: qcom: eliza: Sort nodes by unit address
From: Alexander Koskovich @ 2026-04-16  9:39 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio
  Cc: devicetree, linux-kernel, linux-arm-msm, Alexander Koskovich
In-Reply-To: <20260416-eliza-imem-v2-0-fb7a71123451@pm.me>

Qualcomm DTS uses sorting of MMIO nodes by the unit address, so move
few nodes in Eliza DTSI to fix that.

Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
 arch/arm64/boot/dts/qcom/eliza.dtsi | 74 ++++++++++++++++++-------------------
 1 file changed, 37 insertions(+), 37 deletions(-)

diff --git a/arch/arm64/boot/dts/qcom/eliza.dtsi b/arch/arm64/boot/dts/qcom/eliza.dtsi
index 4a7a0ac40ce6..6fa5679c1a62 100644
--- a/arch/arm64/boot/dts/qcom/eliza.dtsi
+++ b/arch/arm64/boot/dts/qcom/eliza.dtsi
@@ -662,16 +662,16 @@ &clk_virt SLAVE_QUP_CORE_2 QCOM_ICC_TAG_ALWAYS>,
 			};
 		};
 
-		config_noc: interconnect@1600000 {
-			compatible = "qcom,eliza-cnoc-cfg";
-			reg = <0x0 0x01600000 0x0 0x5200>;
+		cnoc_main: interconnect@1500000 {
+			compatible = "qcom,eliza-cnoc-main";
+			reg = <0x0 0x01500000 0x0 0x16080>;
 			qcom,bcm-voters = <&apps_bcm_voter>;
 			#interconnect-cells = <2>;
 		};
 
-		cnoc_main: interconnect@1500000 {
-			compatible = "qcom,eliza-cnoc-main";
-			reg = <0x0 0x01500000 0x0 0x16080>;
+		config_noc: interconnect@1600000 {
+			compatible = "qcom,eliza-cnoc-cfg";
+			reg = <0x0 0x01600000 0x0 0x5200>;
 			qcom,bcm-voters = <&apps_bcm_voter>;
 			#interconnect-cells = <2>;
 		};
@@ -862,13 +862,6 @@ tcsr: clock-controller@1fbf000 {
 			#reset-cells = <1>;
 		};
 
-		lpass_ag_noc: interconnect@7e40000 {
-			compatible = "qcom,eliza-lpass-ag-noc";
-			reg = <0x0 0x07e40000 0x0 0xe080>;
-			qcom,bcm-voters = <&apps_bcm_voter>;
-			#interconnect-cells = <2>;
-		};
-
 		lpass_lpiaon_noc: interconnect@7400000 {
 			compatible = "qcom,eliza-lpass-lpiaon-noc";
 			reg = <0x0 0x07400000 0x0 0x19080>;
@@ -883,6 +876,13 @@ lpass_lpicx_noc: interconnect@7420000 {
 			#interconnect-cells = <2>;
 		};
 
+		lpass_ag_noc: interconnect@7e40000 {
+			compatible = "qcom,eliza-lpass-ag-noc";
+			reg = <0x0 0x07e40000 0x0 0xe080>;
+			qcom,bcm-voters = <&apps_bcm_voter>;
+			#interconnect-cells = <2>;
+		};
+
 		pdc: interrupt-controller@b220000 {
 			compatible = "qcom,eliza-pdc", "qcom,pdc";
 			reg = <0x0 0x0b220000 0x0 0x40000>,
@@ -1005,6 +1005,30 @@ spmi_bus1: spmi@c432000 {
 			};
 		};
 
+		tlmm: pinctrl@f100000 {
+			compatible = "qcom,eliza-tlmm";
+			reg = <0x0 0x0f100000 0x0 0xf00000>;
+
+			interrupts = <GIC_SPI 208 IRQ_TYPE_LEVEL_HIGH>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			gpio-ranges = <&tlmm 0 0 184>;
+			wakeup-parent = <&pdc>;
+
+			qup_uart14_default: qup-uart14-default-state {
+				/* TX, RX */
+				pins = "gpio18", "gpio19";
+				function = "qup2_se5";
+				drive-strength = <2>;
+				bias-pull-up;
+			};
+		};
+
 		apps_smmu: iommu@15000000 {
 			compatible = "qcom,eliza-smmu-500", "qcom,smmu-500", "arm,mmu-500";
 			reg = <0x0 0x15000000 0x0 0x100000>;
@@ -1319,30 +1343,6 @@ cpufreq_hw: cpufreq@17d91000 {
 			#clock-cells = <1>;
 		};
 
-		tlmm: pinctrl@f100000 {
-			compatible = "qcom,eliza-tlmm";
-			reg = <0x0 0x0f100000 0x0 0xf00000>;
-
-			interrupts = <GIC_SPI 208 IRQ_TYPE_LEVEL_HIGH>;
-
-			gpio-controller;
-			#gpio-cells = <2>;
-
-			interrupt-controller;
-			#interrupt-cells = <2>;
-
-			gpio-ranges = <&tlmm 0 0 184>;
-			wakeup-parent = <&pdc>;
-
-			qup_uart14_default: qup-uart14-default-state {
-				/* TX, RX */
-				pins = "gpio18", "gpio19";
-				function = "qup2_se5";
-				drive-strength = <2>;
-				bias-pull-up;
-			};
-		};
-
 		gem_noc: interconnect@24100000 {
 			compatible = "qcom,eliza-gem-noc";
 			reg = <0x0 0x24100000 0x0 0x163080>;

-- 
2.53.0



^ permalink raw reply related

* [PATCH v2 0/3] Describe IMEM on Eliza
From: Alexander Koskovich @ 2026-04-16  9:39 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio
  Cc: devicetree, linux-kernel, linux-arm-msm, Alexander Koskovich

Add a compatible and describe the IMEM for the Eliza SoC.

Also sort nodes by unit address.

Signed-off-by: Alexander Koskovich <akoskovich@pm.me>
---
Changes in v2:
- Fix sorting of nodes in eliza.dtsi
- Link to v1: https://lore.kernel.org/r/20260415-eliza-imem-v1-0-4a90e8683799@pm.me

---
Alexander Koskovich (3):
      arm64: dts: qcom: eliza: Sort nodes by unit address
      dt-bindings: sram: Document qcom,eliza-imem
      arm64: dts: qcom: eliza: Add IMEM node

 Documentation/devicetree/bindings/sram/sram.yaml |  1 +
 arch/arm64/boot/dts/qcom/eliza.dtsi              | 94 ++++++++++++++----------
 2 files changed, 58 insertions(+), 37 deletions(-)
---
base-commit: 936c21068d7ade00325e40d82bfd2f3f29d9f659
change-id: 20260415-eliza-imem-e791f44abf1b

Best regards,
-- 
Alexander Koskovich <akoskovich@pm.me>



^ permalink raw reply

* Re: Re: [PATCH v4 1/2] dt-bindings: pwm: dwc: add reset optional
From: Xuyang Dong @ 2026-04-16  9:38 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Conor Dooley
  Cc: ukleinek, robh, krzk+dt, conor+dt, ben-linux, ben.dooks, p.zabel,
	linux-pwm, devicetree, linux-kernel, ningyu, linmin, xuxiang,
	wangguosheng, pinkesh.vaghela
In-Reply-To: <20260416-wandering-solemn-uakari-083ae7@quoll>

> > > 
> > > The DesignWare PWM includes separate reset signals dedicated to each clock
> > > domain:
> > > The presetn signal resets logic in pclk domain.
> > > The timer_N_resetn signal resets logic in the timer_N_clk domain.
> > > The resets are active-low.
> > > 
> > > Signed-off-by: Xuyang Dong <dongxuyang@eswincomputing.com>
> > 
> > This commit implies that your hardware differs from existing devices,
> > I think you should add a device-specific compatible.
> > 

Hi Conor and Krzysztof,

The DesignWare PWM Databook for 2.13a says: "The DW_apb_timers includes 
separate reset signals dedicated to each clock domain". They are:
The presetn signal resets logic in pclk domain (i.e., the bus clock in DT).
The timer_N_resetn signal resets logic in the timer_N_clk domain (i.e.,
the timer clock in DT).

These reset signals are optional; it is up to the designer's 
implementation.

According to [1], the applied YAML is also based on 2.13a, so our 
hardware is the same as the existing devices. It's just that these two 
reset signals were missing from the original YAML binding.

[1] https://lore.kernel.org/linux-pwm/8bb5103d-803e-90d2-fd93-132bb2aac2d6@sifive.com/

> > > ---
> > >  .../devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml       | 3 +++
> > >  1 file changed, 3 insertions(+)
> > > 
> > > diff --git a/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml b/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml
> > > index 7523a89a1773..a8bbad0360f8 100644
> > > --- a/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml
> > > +++ b/Documentation/devicetree/bindings/pwm/snps,dw-apb-timers-pwm2.yaml
> > > @@ -43,6 +43,9 @@ properties:
> > >        - const: bus
> > >        - const: timer
> > >  
> > > +  resets:
> > > +    maxItems: 2
> 
> And this should really be listed with description, because order is
> fixed.
> 

The description of resets will be listed in next version.

Best regards,
Xuyang Dong

^ permalink raw reply

* Re: [PATCH 1/9] dt-bindings: sound: mt2701-afe-pcm: add HDMI audio path clocks
From: Krzysztof Kozlowski @ 2026-04-16  9:38 UTC (permalink / raw)
  To: Daniel Golle
  Cc: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
	Jaroslav Kysela, Takashi Iwai, Cyril Chao, Arnd Bergmann,
	Kuninori Morimoto, Nícolas F. R. A. Prado, Eugen Hristev,
	linux-sound, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek
In-Reply-To: <50afd83a314cd20c715fb9b0d3bc85fb00f9a6eb.1776265610.git.daniel@makrotopia.org>

On Wed, Apr 15, 2026 at 04:23:27PM +0100, Daniel Golle wrote:
> Document four additional optional clocks feeding the HDMI audio
> output path on MT2701 and MT7623N: the HADDS2 PLL (root of the

There is no MT7623N compatible in this file, so that's confusing. Does
mt7622 have it? If not, then it should be restricted per variant. If
yet, the model name is confusing.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH 4/5] arm64: dts: qcom: add IPQ9650 SoC and rdp488 board support
From: Krzysztof Kozlowski @ 2026-04-16  9:35 UTC (permalink / raw)
  To: Kathiravan Thirumoorthy
  Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Konrad Dybcio,
	linux-arm-msm, linux-clk, devicetree, linux-kernel
In-Reply-To: <20260415-ipq9650_boot_to_shell-v1-4-b37eb4c3a1d1@oss.qualcomm.com>

On Wed, Apr 15, 2026 at 07:03:19PM +0530, Kathiravan Thirumoorthy wrote:
> +		xo_board: xo-board-clk {
> +			compatible = "fixed-clock";
> +			#clock-cells = <0>;
> +		};
> +	};
> +
> +	cpus {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		CPU0: cpu@0 {

Labels should be lowercase.

Best regards,
Krzysztof


^ permalink raw reply

* [PATCH v2 3/5] irqchip/starfive: Use devm_ interfaces to simplify resource release
From: Changhuang Liang @ 2026-04-16  6:47 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Gleixner,
	Philipp Zabel
  Cc: linux-kernel, devicetree, linux-riscv, Ley Foon Tan,
	Changhuang Liang
In-Reply-To: <20260416064751.632138-1-changhuang.liang@starfivetech.com>

Use devm_ interfaces to simplify resource release. Make clock and reset
get optional as they are not used on the JHB100 SoC. Replace pr_ logging
with dev_* logging. Use __free(kfree) cleanup attribute to auto-free irqc
on error paths

Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/irqchip/irq-starfive-jhb100-intc.c | 77 ++++++----------------
 1 file changed, 20 insertions(+), 57 deletions(-)

diff --git a/drivers/irqchip/irq-starfive-jhb100-intc.c b/drivers/irqchip/irq-starfive-jhb100-intc.c
index 2c9cdad7f377..c33229b39a40 100644
--- a/drivers/irqchip/irq-starfive-jhb100-intc.c
+++ b/drivers/irqchip/irq-starfive-jhb100-intc.c
@@ -7,16 +7,15 @@
  * Author: Changhuang Liang <changhuang.liang@starfivetech.com>
  */
 
-#define pr_fmt(fmt) "irq-starfive-jhb100: " fmt
-
 #include <linux/bitops.h>
+#include <linux/cleanup.h>
 #include <linux/clk.h>
 #include <linux/irq.h>
 #include <linux/irqchip.h>
 #include <linux/irqchip/chained_irq.h>
 #include <linux/irqdomain.h>
-#include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/platform_device.h>
 #include <linux/reset.h>
 #include <linux/spinlock.h>
 
@@ -117,85 +116,49 @@ static void starfive_intc_irq_handler(struct irq_desc *desc)
 static int starfive_intc_probe(struct platform_device *pdev, struct device_node *parent)
 {
 	struct device_node *intc = pdev->dev.of_node;
-	struct starfive_irq_chip *irqc;
 	struct reset_control *rst;
 	struct clk *clk;
 	int parent_irq;
-	int ret;
 
-	irqc = kzalloc_obj(*irqc);
+	struct starfive_irq_chip *irqc __free(kfree) = kzalloc_obj(*irqc);
 	if (!irqc)
 		return -ENOMEM;
 
-	irqc->base = of_iomap(intc, 0);
-	if (!irqc->base) {
-		pr_err("Unable to map registers\n");
-		ret = -ENXIO;
-		goto err_free;
-	}
+	irqc->base = devm_platform_ioremap_resource(pdev, 0);
+	if (!irqc->base)
+		return dev_err_probe(&pdev->dev, -ENXIO, "unable to map registers\n");
 
-	rst = of_reset_control_get_exclusive(intc, NULL);
-	if (IS_ERR(rst)) {
-		pr_err("Unable to get reset control %pe\n", rst);
-		ret = PTR_ERR(rst);
-		goto err_unmap;
-	}
+	rst = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL);
+	if (IS_ERR(rst))
+		return dev_err_probe(&pdev->dev, PTR_ERR(rst),
+				     "Unable to get and deassert reset control\n");
 
-	clk = of_clk_get(intc, 0);
-	if (IS_ERR(clk)) {
-		pr_err("Unable to get clock %pe\n", clk);
-		ret = PTR_ERR(clk);
-		goto err_reset_put;
-	}
+	clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
+	if (IS_ERR(clk))
+		return dev_err_probe(&pdev->dev, PTR_ERR(clk), "Unable to get and enable clock\n");
 
-	ret = reset_control_deassert(rst);
-	if (ret)
-		goto err_clk_put;
-
-	ret = clk_prepare_enable(clk);
-	if (ret)
-		goto err_reset_assert;
 
 	raw_spin_lock_init(&irqc->lock);
 
 	irqc->domain = irq_domain_create_linear(of_fwnode_handle(intc), STARFIVE_INTC_SRC_IRQ_NUM,
 						&starfive_intc_domain_ops, irqc);
-	if (!irqc->domain) {
-		pr_err("Unable to create IRQ domain\n");
-		ret = -EINVAL;
-		goto err_clk_disable;
-	}
+	if (!irqc->domain)
+		return dev_err_probe(&pdev->dev, -EINVAL, "Unable to create IRQ domain\n");
 
 	parent_irq = of_irq_get(intc, 0);
 	if (parent_irq < 0) {
-		pr_err("Failed to get main IRQ: %d\n", parent_irq);
-		ret = parent_irq;
-		goto err_remove_domain;
+		irq_domain_remove(irqc->domain);
+		return dev_err_probe(&pdev->dev, parent_irq, "Failed to get main IRQ\n");
 	}
 
 	irq_set_chained_handler_and_data(parent_irq, starfive_intc_irq_handler,
 					 irqc);
 
-	pr_info("Interrupt controller register, nr_irqs %d\n",
-		STARFIVE_INTC_SRC_IRQ_NUM);
+	dev_info(&pdev->dev, "Interrupt controller register, nr_irqs %d\n",
+		 STARFIVE_INTC_SRC_IRQ_NUM);
 
+	retain_and_null_ptr(irqc);
 	return 0;
-
-err_remove_domain:
-	irq_domain_remove(irqc->domain);
-err_clk_disable:
-	clk_disable_unprepare(clk);
-err_reset_assert:
-	reset_control_assert(rst);
-err_clk_put:
-	clk_put(clk);
-err_reset_put:
-	reset_control_put(rst);
-err_unmap:
-	iounmap(irqc->base);
-err_free:
-	kfree(irqc);
-	return ret;
 }
 
 IRQCHIP_PLATFORM_DRIVER_BEGIN(starfive_intc)
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH 3/5] dt-bindings: qcom: add IPQ9650 boards
From: Krzysztof Kozlowski @ 2026-04-16  9:34 UTC (permalink / raw)
  To: Kathiravan Thirumoorthy
  Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Konrad Dybcio,
	linux-arm-msm, linux-clk, devicetree, linux-kernel
In-Reply-To: <20260415-ipq9650_boot_to_shell-v1-3-b37eb4c3a1d1@oss.qualcomm.com>

On Wed, Apr 15, 2026 at 07:03:18PM +0530, Kathiravan Thirumoorthy wrote:
> Document the new IPQ9650 SoC/board device tree bindings.
> 
> Signed-off-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
> ---
>  Documentation/devicetree/bindings/arm/qcom.yaml | 5 +++++
>  1 file changed, 5 insertions(+)

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Best regards,
Krzysztof


^ permalink raw reply

* [PATCH v2 1/5] dt-bindings: interrupt-controller: repurpose binding for unreleased jh8100 for jhb100
From: Changhuang Liang @ 2026-04-16  6:47 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thomas Gleixner,
	Philipp Zabel
  Cc: linux-kernel, devicetree, linux-riscv, Ley Foon Tan,
	Changhuang Liang
In-Reply-To: <20260416064751.632138-1-changhuang.liang@starfivetech.com>

The StarFive JH8100 SoC was discontinued before production. The
newly taped-out JHB100 SoC uses the same interrupt controller IP.

Rename the binding file, compatible string, and MAINTAINERS entry
from "jh8100" to "jhb100". In JHB100 SoC, The clocks and resets are
not operated by users, but they exist in the hardware. Mark them as
optional.

Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
 ...00-intc.yaml => starfive,jhb100-intc.yaml} | 20 ++++---------------
 MAINTAINERS                                   |  2 +-
 2 files changed, 5 insertions(+), 17 deletions(-)
 rename Documentation/devicetree/bindings/interrupt-controller/{starfive,jh8100-intc.yaml => starfive,jhb100-intc.yaml} (68%)

diff --git a/Documentation/devicetree/bindings/interrupt-controller/starfive,jh8100-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/starfive,jhb100-intc.yaml
similarity index 68%
rename from Documentation/devicetree/bindings/interrupt-controller/starfive,jh8100-intc.yaml
rename to Documentation/devicetree/bindings/interrupt-controller/starfive,jhb100-intc.yaml
index ada5788602d6..d8a0a3862ae2 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/starfive,jh8100-intc.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/starfive,jhb100-intc.yaml
@@ -1,13 +1,13 @@
 # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
 %YAML 1.2
 ---
-$id: http://devicetree.org/schemas/interrupt-controller/starfive,jh8100-intc.yaml#
+$id: http://devicetree.org/schemas/interrupt-controller/starfive,jhb100-intc.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
 title: StarFive External Interrupt Controller
 
 description:
-  StarFive SoC JH8100 contain a external interrupt controller. It can be used
+  StarFive SoC JHB100 contain a external interrupt controller. It can be used
   to handle high-level input interrupt signals. It also send the output
   interrupt signal to RISC-V PLIC.
 
@@ -16,19 +16,11 @@ maintainers:
 
 properties:
   compatible:
-    const: starfive,jh8100-intc
+    const: starfive,jhb100-intc
 
   reg:
     maxItems: 1
 
-  clocks:
-    description: APB clock for the interrupt controller
-    maxItems: 1
-
-  resets:
-    description: APB reset for the interrupt controller
-    maxItems: 1
-
   interrupts:
     maxItems: 1
 
@@ -40,8 +32,6 @@ properties:
 required:
   - compatible
   - reg
-  - clocks
-  - resets
   - interrupts
   - interrupt-controller
   - "#interrupt-cells"
@@ -51,10 +41,8 @@ additionalProperties: false
 examples:
   - |
     interrupt-controller@12260000 {
-      compatible = "starfive,jh8100-intc";
+      compatible = "starfive,jhb100-intc";
       reg = <0x12260000 0x10000>;
-      clocks = <&syscrg_ne 76>;
-      resets = <&syscrg_ne 13>;
       interrupts = <45>;
       interrupt-controller;
       #interrupt-cells = <1>;
diff --git a/MAINTAINERS b/MAINTAINERS
index d238590a31f2..a2961727e3d1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25312,7 +25312,7 @@ F:	drivers/phy/starfive/phy-jh7110-usb.c
 STARFIVE JH8100 EXTERNAL INTERRUPT CONTROLLER DRIVER
 M:	Changhuang Liang <changhuang.liang@starfivetech.com>
 S:	Supported
-F:	Documentation/devicetree/bindings/interrupt-controller/starfive,jh8100-intc.yaml
+F:	Documentation/devicetree/bindings/interrupt-controller/starfive,jhb100-intc.yaml
 F:	drivers/irqchip/irq-starfive-jh8100-intc.c
 
 STATIC BRANCH/CALL
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH 1/5] dt-bindings: clock: add Qualcomm IPQ9650 GCC
From: Krzysztof Kozlowski @ 2026-04-16  9:33 UTC (permalink / raw)
  To: Kathiravan Thirumoorthy
  Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Philipp Zabel, Konrad Dybcio,
	linux-arm-msm, linux-clk, devicetree, linux-kernel
In-Reply-To: <20260415-ipq9650_boot_to_shell-v1-1-b37eb4c3a1d1@oss.qualcomm.com>

On Wed, Apr 15, 2026 at 07:03:16PM +0530, Kathiravan Thirumoorthy wrote:
> Add binding for the Qualcomm IPQ9650 Global Clock Controller.
> 
> Signed-off-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
> ---
>  .../bindings/clock/qcom,ipq9650-gcc.yaml           |  68 ++++++
>  include/dt-bindings/clock/qcom,ipq9650-gcc.h       | 190 +++++++++++++++++
>  include/dt-bindings/reset/qcom,ipq9650-gcc.h       | 228 +++++++++++++++++++++
>  3 files changed, 486 insertions(+)

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: pinctrl: qcom: add IPQ9650 pinctrl
From: Krzysztof Kozlowski @ 2026-04-16  9:30 UTC (permalink / raw)
  To: Kathiravan Thirumoorthy
  Cc: Bjorn Andersson, Linus Walleij, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-arm-msm, linux-gpio, devicetree, linux-kernel
In-Reply-To: <20260415-ipq9650_tlmm-v1-1-bd16ccb06332@oss.qualcomm.com>

On Wed, Apr 15, 2026 at 04:59:24PM +0530, Kathiravan Thirumoorthy wrote:
> Add device tree bindings for IPQ9650 TLMM block.
> 
> Signed-off-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
> ---
>  .../bindings/pinctrl/qcom,ipq9650-tlmm.yaml        | 118 +++++++++++++++++++++
>  1 file changed, 118 insertions(+)

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v1 1/2] dt-bindings: rtc: ti,bq32k: Add delay on rtc reads
From: Krzysztof Kozlowski @ 2026-04-16  9:27 UTC (permalink / raw)
  To: Adriana Stancu, alexandre.belloni
  Cc: linux-rtc, devicetree, linux-kernel, robh, krzk+dt, conor+dt
In-Reply-To: <20260416092414.3210383-2-adriana@arista.com>

On 16/04/2026 11:24, Adriana Stancu wrote:
> Add a configurable device tree property to specify
> if a microseconds delay should be added before reading
> the RTC registers.

But, why do we need it? Why are you adding it? You just described the
diff, but that we can read.

Please wrap commit message according to Linux coding style / submission
process (neither too early nor over the limit):
https://elixir.bootlin.com/linux/v6.4-rc1/source/Documentation/process/submitting-patches.rst#L597

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v1 2/2] rtc: bq32000: add configurable delay between RTC reads
From: Adriana Stancu @ 2026-04-16  9:24 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: linux-rtc, devicetree, linux-kernel, robh, krzk+dt, conor+dt,
	Adriana Stancu
In-Reply-To: <20260416092414.3210383-1-adriana@arista.com>

When the RTC is used on systems without a interrupt line, userspace
tools like `hwclock` fall back to a frequent polling loop to synchronize
with the edge of the next second.

On the BQ32000, this aggressive polling can temporarly lock the register
refresh cycle, because the continuous transfers prevent the hardware from
updating the buffer. This results in stale data reads or select() timeouts
in userspace.

This patch introduces a configurable settle delay via `ti,read-settle-us`
property. If this property is specified, the driver uses a delay before
reading the RTC registers. This provides a sufficient idle time for the
hardware to sync with the register buffer.

Signed-off-by: Adriana Stancu <adriana@arista.com>
---
 drivers/rtc/rtc-bq32k.c | 34 ++++++++++++++++++++++++++++------
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c
index 7ad34539be4d..0cbfa0909732 100644
--- a/drivers/rtc/rtc-bq32k.c
+++ b/drivers/rtc/rtc-bq32k.c
@@ -16,6 +16,7 @@
 #include <linux/kstrtox.h>
 #include <linux/errno.h>
 #include <linux/bcd.h>
+#include <linux/delay.h>
 
 #define BQ32K_SECONDS		0x00	/* Seconds register address */
 #define BQ32K_SECONDS_MASK	0x7F	/* Mask over seconds value */
@@ -48,6 +49,11 @@ struct bq32k_regs {
 	uint8_t		years;
 };
 
+struct bq32k_data {
+	struct rtc_device *rtc;
+	u32 read_delay_us;
+};
+
 static struct i2c_driver bq32k_driver;
 
 static int bq32k_read(struct device *dev, void *data, uint8_t off, uint8_t len)
@@ -89,9 +95,17 @@ static int bq32k_write(struct device *dev, void *data, uint8_t off, uint8_t len)
 
 static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
+	struct bq32k_data *bq32k = dev_get_drvdata(dev);
 	struct bq32k_regs regs;
 	int error;
 
+	/*
+	 * When the device doesn't have the interrupt connected, prevent
+	 * userpace from polling the RTC registers to frequently.
+	 */
+	if (bq32k && bq32k->read_delay_us)
+		usleep_range(bq32k->read_delay_us, bq32k->read_delay_us + 50);
+
 	error = bq32k_read(dev, &regs, 0, sizeof(regs));
 	if (error)
 		return error;
@@ -253,13 +267,18 @@ static void bq32k_sysfs_unregister(struct device *dev)
 static int bq32k_probe(struct i2c_client *client)
 {
 	struct device *dev = &client->dev;
-	struct rtc_device *rtc;
+	struct bq32k_data *bq32k;
 	uint8_t reg;
 	int error;
+	uint32_t settle_us = 0;
 
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 		return -ENODEV;
 
+	bq32k = devm_kzalloc(dev, sizeof(*bq32k), GFP_KERNEL);
+	if (!bq32k)
+		return -ENOMEM;
+
 	/* Check Oscillator Stop flag */
 	error = bq32k_read(dev, &reg, BQ32K_SECONDS, 1);
 	if (!error && (reg & BQ32K_STOP)) {
@@ -280,10 +299,13 @@ static int bq32k_probe(struct i2c_client *client)
 	if (client->dev.of_node)
 		trickle_charger_of_init(dev, client->dev.of_node);
 
-	rtc = devm_rtc_device_register(&client->dev, bq32k_driver.driver.name,
-						&bq32k_rtc_ops, THIS_MODULE);
-	if (IS_ERR(rtc))
-		return PTR_ERR(rtc);
+	bq32k->rtc = devm_rtc_device_register(&client->dev, bq32k_driver.driver.name,
+					      &bq32k_rtc_ops, THIS_MODULE);
+	if (IS_ERR(bq32k->rtc))
+		return PTR_ERR(bq32k->rtc);
+
+	device_property_read_u32(dev, "ti,read-settle-us", &settle_us);
+	bq32k->read_delay_us = settle_us;
 
 	error = bq32k_sysfs_register(&client->dev);
 	if (error) {
@@ -293,7 +315,7 @@ static int bq32k_probe(struct i2c_client *client)
 	}
 
 
-	i2c_set_clientdata(client, rtc);
+	i2c_set_clientdata(client, bq32k);
 
 	return 0;
 }
-- 
2.51.0


^ permalink raw reply related

* [PATCH v1 1/2] dt-bindings: rtc: ti,bq32k: Add delay on rtc reads
From: Adriana Stancu @ 2026-04-16  9:24 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: linux-rtc, devicetree, linux-kernel, robh, krzk+dt, conor+dt,
	Adriana Stancu
In-Reply-To: <20260416092414.3210383-1-adriana@arista.com>

Add a configurable device tree property to specify
if a microseconds delay should be added before reading
the RTC registers.

Signed-off-by: Adriana Stancu <adriana@arista.com>
---
 Documentation/devicetree/bindings/rtc/ti,bq32000.yaml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/rtc/ti,bq32000.yaml b/Documentation/devicetree/bindings/rtc/ti,bq32000.yaml
index bf9c1c4ddb7e..c7c2720a336b 100644
--- a/Documentation/devicetree/bindings/rtc/ti,bq32000.yaml
+++ b/Documentation/devicetree/bindings/rtc/ti,bq32000.yaml
@@ -29,6 +29,11 @@ properties:
 
   trickle-diode-disable: true
 
+  ti,read-settle-us:
+    default: 0
+    description:
+      Delay in microseconds to wait before reading RTC registers.
+
 required:
   - compatible
   - reg
-- 
2.51.0


^ permalink raw reply related

* [PATCH v1 0/2] rtc: bq32000: Add settle delay for aggressive polling
From: Adriana Stancu @ 2026-04-16  9:24 UTC (permalink / raw)
  To: alexandre.belloni
  Cc: linux-rtc, devicetree, linux-kernel, robh, krzk+dt, conor+dt,
	Adriana Stancu

This series addresses a limitation in the TI BQ32000 RTC where aggressive
I2C polling (done by userspace tools like hwclock on systems where the
interrupt line is not connected to the CPU) can prevent the refresh of
RTC registers.

This results in stale data reads or select() timeouts in userspace.
The series introduces a configurable "settle delay" via device tree
to ensure the hardware has sufficient idle time between read attempts.

Patch 1: Adds the 'ti,read-settle-us' property to the YAML bindings.
Patch 2: Implements the delay in the driver using usleep_range.

Adriana Stancu (2):
  dt-bindings: rtc: ti,bq32k: Add delay on rtc reads
  rtc: bq32000: add configurable delay between RTC reads

 .../devicetree/bindings/rtc/ti,bq32000.yaml   |  5 +++
 drivers/rtc/rtc-bq32k.c                       | 34 +++++++++++++++----
 2 files changed, 33 insertions(+), 6 deletions(-)

-- 
2.51.0


^ permalink raw reply

* Re: [PATCH v1 00/11] media: Add iMX95 neoisp driver
From: Krzysztof Kozlowski @ 2026-04-16  9:20 UTC (permalink / raw)
  To: Antoine Bouyer
  Cc: julien.vuillaumier, alexi.birlinger, daniel.baluta, peng.fan,
	frank.li, jacopo.mondi, laurent.pinchart, mchehab, robh, krzk+dt,
	conor+dt, michael.riesch, anthony.mcgivern, linux-media,
	linux-kernel, devicetree, imx, ai.luthra, paul.elder
In-Reply-To: <20260413160331.2611829-1-antoine.bouyer@nxp.com>

On Mon, Apr 13, 2026 at 06:03:20PM +0200, Antoine Bouyer wrote:
> Hello
> 
> This patch series introduces the NXP Neo Image Signal Processor (ISP)
> driver, used in the NXP i.MX95 SoC and future devices in the i.MX9 family.
> The series also includes updates to the generic v4l2-isp interface to
> support extended statistics required by the Neo ISP.
> 
> The Neo ISP processes one or more camera streams, converting RAW formats
> into YUV or RGB outputs. Its architecture is largely influenced by the
> PISP driver. To limit the number of v4l2 devices, the driver supports only
> one context, with three sink pads (main input, second input for HDR, and
> parameter buffers) and three source pads (RGB output, IR output, and
> statistics metadata).
> 
> The driver supports the generic extensible v4l2-isp framework for
> parameters, similar to rkisp1 and mali-c55, and applies the same approach
> to statistics buffers. The generic v4l2-isp framework is modified to
> factorize structure definitions and versioning, so that both parameters
> and statistics buffers share the same mechanisms.

That's a v2, not v1.

Does this work:
b4 diff 20260413160331.2611829-1-antoine.bouyer@nxp.com

? No.

Should it work? Yes.

Also, implement previous feedback - I see no changelog, so I assume you
just ignored everything. Judging by build process errors, it's even
worse.

Best regards,
Krzysztof


^ permalink raw reply

* [PATCH v8 5/6] iio: adc: ad4691: add oversampling support
From: Radu Sabau via B4 Relay @ 2026-04-16  9:18 UTC (permalink / raw)
  To: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
	Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
	Philipp Zabel, Jonathan Corbet, Shuah Khan
  Cc: linux-iio, devicetree, linux-kernel, linux-pwm, linux-gpio,
	linux-doc, Radu Sabau
In-Reply-To: <20260416-ad4692-multichannel-sar-adc-driver-v8-0-c415bd048fa3@analog.com>

From: Radu Sabau <radu.sabau@analog.com>

Add per-channel oversampling ratio (OSR) support for CNV burst mode.
The accumulator depth register (ACC_DEPTH_IN) is programmed with the
selected OSR at buffer enable time and before each single-shot read.

Supported OSR values: 1, 2, 4, 8, 16, 32.

Introduce AD4691_MANUAL_CHANNEL() for manual mode channels, which do
not expose the oversampling ratio attribute since OSR is not applicable
in that mode. A separate manual_channels array is added to
struct ad4691_channel_info and selected at probe time; offload paths
reuse the same arrays with num_channels capping access before the soft
timestamp entry.

in_voltageN_sampling_frequency represents the effective output rate for
channel N, defined as osc_freq / osr[N]. The chip has one internal
oscillator shared by all channels; each channel independently
accumulates osr[N] oscillator cycles before producing a result.

Writing sampling_frequency computes needed_osc = freq * osr[N] and
snaps down to the largest oscillator table entry that satisfies both
osc <= needed_osc and osc % osr[N] == 0, guaranteeing an exact integer
read-back. The result is stored in target_osc_freq_Hz and written to
OSC_FREQ_REG at buffer enable and single-shot time, so sampling_frequency
and oversampling_ratio can be set in any order.

in_voltageN_sampling_frequency_available is computed dynamically from
the channel's current OSR, listing only oscillator table entries that
divide evenly by osr[N], expressed as effective rates. The list becomes
sparser as OSR increases, capping at max_rate / osr[N].

Writing oversampling_ratio stores the new OSR for that channel;
target_osc_freq_Hz is left unchanged. The effective rate read back via
in_voltageN_sampling_frequency becomes target_osc_freq_Hz / new_osr
automatically. The two attributes are orthogonal: sampling_frequency
controls the oscillator, oversampling_ratio controls the averaging depth.

OSR defaults to 1 (no accumulation) for all channels.

Signed-off-by: Radu Sabau <radu.sabau@analog.com>
---
 drivers/iio/adc/ad4691.c | 251 +++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 221 insertions(+), 30 deletions(-)

diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
index fbd44f595cbe..f000e4cde339 100644
--- a/drivers/iio/adc/ad4691.c
+++ b/drivers/iio/adc/ad4691.c
@@ -115,6 +115,7 @@ enum ad4691_ref_ctrl {
 
 struct ad4691_channel_info {
 	const struct iio_chan_spec *channels;
+	const struct iio_chan_spec *manual_channels;
 	unsigned int num_channels;
 };
 
@@ -125,7 +126,34 @@ struct ad4691_chip_info {
 	const struct ad4691_channel_info *offload_info;
 };
 
+/* CNV burst mode channel — exposes oversampling ratio. */
 #define AD4691_CHANNEL(ch)						\
+	{								\
+		.type = IIO_VOLTAGE,					\
+		.indexed = 1,						\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\
+				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \
+				      BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+		.info_mask_separate_available =				\
+				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | \
+				      BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE),	\
+		.channel = ch,						\
+		.scan_index = ch,					\
+		.scan_type = {						\
+			.sign = 'u',					\
+			.realbits = 16,					\
+			.storagebits = 16,				\
+			.endianness = IIO_BE,				\
+		},							\
+	}
+
+/*
+ * Manual mode channel — no oversampling ratio attribute. OSR is not
+ * supported in manual mode; ACC_DEPTH_IN is not configured during manual
+ * buffer enable.
+ */
+#define AD4691_MANUAL_CHANNEL(ch)					\
 	{								\
 		.type = IIO_VOLTAGE,					\
 		.indexed = 1,						\
@@ -176,25 +204,65 @@ static const struct iio_chan_spec ad4693_channels[] = {
 	IIO_CHAN_SOFT_TIMESTAMP(8),
 };
 
+static const struct iio_chan_spec ad4691_manual_channels[] = {
+	AD4691_MANUAL_CHANNEL(0),
+	AD4691_MANUAL_CHANNEL(1),
+	AD4691_MANUAL_CHANNEL(2),
+	AD4691_MANUAL_CHANNEL(3),
+	AD4691_MANUAL_CHANNEL(4),
+	AD4691_MANUAL_CHANNEL(5),
+	AD4691_MANUAL_CHANNEL(6),
+	AD4691_MANUAL_CHANNEL(7),
+	AD4691_MANUAL_CHANNEL(8),
+	AD4691_MANUAL_CHANNEL(9),
+	AD4691_MANUAL_CHANNEL(10),
+	AD4691_MANUAL_CHANNEL(11),
+	AD4691_MANUAL_CHANNEL(12),
+	AD4691_MANUAL_CHANNEL(13),
+	AD4691_MANUAL_CHANNEL(14),
+	AD4691_MANUAL_CHANNEL(15),
+	IIO_CHAN_SOFT_TIMESTAMP(16),
+};
+
+static const struct iio_chan_spec ad4693_manual_channels[] = {
+	AD4691_MANUAL_CHANNEL(0),
+	AD4691_MANUAL_CHANNEL(1),
+	AD4691_MANUAL_CHANNEL(2),
+	AD4691_MANUAL_CHANNEL(3),
+	AD4691_MANUAL_CHANNEL(4),
+	AD4691_MANUAL_CHANNEL(5),
+	AD4691_MANUAL_CHANNEL(6),
+	AD4691_MANUAL_CHANNEL(7),
+	IIO_CHAN_SOFT_TIMESTAMP(8),
+};
+
+static const int ad4691_oversampling_ratios[] = { 1, 2, 4, 8, 16, 32 };
+
 static const struct ad4691_channel_info ad4691_sw_info = {
 	.channels = ad4691_channels,
+	.manual_channels = ad4691_manual_channels,
 	.num_channels = ARRAY_SIZE(ad4691_channels),
 };
 
 static const struct ad4691_channel_info ad4693_sw_info = {
 	.channels = ad4693_channels,
+	.manual_channels = ad4693_manual_channels,
 	.num_channels = ARRAY_SIZE(ad4693_channels),
 };
 
 static const struct ad4691_channel_info ad4691_offload_info = {
 	.channels = ad4691_channels,
-	/* Exclude the soft timestamp entry; num_channels caps access. */
+	/*
+	 * Offload paths share the SW channel arrays. num_channels caps access
+	 * before the soft timestamp entry, so no separate array is needed.
+	 */
+	.manual_channels = ad4691_manual_channels,
 	.num_channels = ARRAY_SIZE(ad4691_channels) - 1,
 };
 
 static const struct ad4691_channel_info ad4693_offload_info = {
 	.channels = ad4693_channels,
-	/* Exclude the soft timestamp entry; num_channels caps access. */
+	.manual_channels = ad4693_manual_channels,
 	.num_channels = ARRAY_SIZE(ad4693_channels) - 1,
 };
 
@@ -269,6 +337,19 @@ struct ad4691_state {
 	int irq;
 	int vref_uV;
 	u32 cnv_period_ns;
+	/*
+	 * Snapped oscillator frequency (Hz) shared by all channels. Set when
+	 * sampling_frequency or oversampling_ratio is written; written to
+	 * OSC_FREQ_REG at buffer enable and single-shot time so both attributes
+	 * can be set in any order. Reading in_voltageN_sampling_frequency
+	 * returns target_osc_freq_Hz / osr[N] — the effective rate for that
+	 * channel given its oversampling ratio.
+	 */
+	u32 target_osc_freq_Hz;
+	/* Per-channel oversampling ratio; always 1 in manual mode. */
+	u8 osr[16];
+	/* Scratch buffer for read_avail SAMP_FREQ; content is OSR-dependent. */
+	int samp_freq_avail[ARRAY_SIZE(ad4691_osc_freqs_Hz)];
 
 	bool manual_mode;
 	bool refbuf_en;
@@ -489,6 +570,18 @@ static const struct regmap_config ad4691_regmap_config = {
 	.cache_type = REGCACHE_MAPLE,
 };
 
+/* Write target_osc_freq_Hz to OSC_FREQ_REG. Called at use time. */
+static int ad4691_write_osc_freq(struct ad4691_state *st)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) {
+		if (ad4691_osc_freqs_Hz[i] == st->target_osc_freq_Hz)
+			return regmap_write(st->regmap, AD4691_OSC_FREQ_REG, i);
+	}
+	return -EINVAL;
+}
+
 /*
  * Index 0 in ad4691_osc_freqs_Hz is 1 MHz — valid only for AD4692/AD4694
  * (max_rate == 1 MHz). AD4691/AD4693 cap at 500 kHz so their valid range
@@ -499,36 +592,58 @@ static unsigned int ad4691_samp_freq_start(const struct ad4691_chip_info *info)
 	return (info->max_rate == 1 * HZ_PER_MHZ) ? 0 : 1;
 }
 
-static int ad4691_get_sampling_freq(struct ad4691_state *st, int *val)
+/*
+ * Find the largest oscillator table entry that is both <= needed_osc and
+ * evenly divisible by osr (guaranteeing an integer effective rate on
+ * read-back). Returns 0 if no such entry exists in the chip's valid range.
+ */
+static unsigned int ad4691_find_osc_freq(struct ad4691_state *st,
+					 unsigned int needed_osc,
+					 unsigned int osr)
 {
-	unsigned int reg_val;
-	int ret;
+	unsigned int start = ad4691_samp_freq_start(st->info);
 
-	ret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, &reg_val);
-	if (ret)
-		return ret;
+	for (unsigned int i = start; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) {
+		if ((unsigned int)ad4691_osc_freqs_Hz[i] > needed_osc)
+			continue;
+		if (ad4691_osc_freqs_Hz[i] % osr != 0)
+			continue;
+		return ad4691_osc_freqs_Hz[i];
+	}
+	return 0;
+}
 
-	*val = ad4691_osc_freqs_Hz[FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val)];
+static int ad4691_get_sampling_freq(struct ad4691_state *st, u8 osr, int *val)
+{
+	*val = st->target_osc_freq_Hz / osr;
 	return IIO_VAL_INT;
 }
 
-static int ad4691_set_sampling_freq(struct iio_dev *indio_dev, int freq)
+static int ad4691_set_sampling_freq(struct iio_dev *indio_dev,
+				    struct iio_chan_spec const *chan, int freq)
 {
 	struct ad4691_state *st = iio_priv(indio_dev);
-	unsigned int start = ad4691_samp_freq_start(st->info);
+	unsigned int osr = st->osr[chan->channel];
+	unsigned int found;
 
 	IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
 	if (IIO_DEV_ACQUIRE_FAILED(claim))
 		return -EBUSY;
 
-	for (unsigned int i = start; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) {
-		if (ad4691_osc_freqs_Hz[i] != freq)
-			continue;
-		return regmap_update_bits(st->regmap, AD4691_OSC_FREQ_REG,
-					  AD4691_OSC_FREQ_MASK, i);
-	}
+	if (freq <= 0 || (unsigned int)freq > st->info->max_rate / osr)
+		return -EINVAL;
 
-	return -EINVAL;
+	found = ad4691_find_osc_freq(st, (unsigned int)freq * osr, osr);
+	if (!found)
+		return -EINVAL;
+
+	/*
+	 * Store the snapped oscillator frequency; OSC_FREQ_REG is written at
+	 * buffer enable and single-shot time so that sampling_frequency and
+	 * oversampling_ratio can be set in any order.
+	 */
+	st->target_osc_freq_Hz = found;
+	return 0;
 }
 
 static int ad4691_read_avail(struct iio_dev *indio_dev,
@@ -540,10 +655,30 @@ static int ad4691_read_avail(struct iio_dev *indio_dev,
 	unsigned int start = ad4691_samp_freq_start(st->info);
 
 	switch (mask) {
-	case IIO_CHAN_INFO_SAMP_FREQ:
-		*vals = &ad4691_osc_freqs_Hz[start];
+	case IIO_CHAN_INFO_SAMP_FREQ: {
+		unsigned int osr = st->osr[chan->channel];
+		int n = 0;
+
+		/*
+		 * Only oscillator frequencies evenly divisible by the channel's
+		 * OSR yield an integer effective rate; expose those as effective
+		 * rates (osc / osr) so the user works entirely in output-sample
+		 * space.
+		 */
+		for (unsigned int i = start; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) {
+			if (ad4691_osc_freqs_Hz[i] % osr != 0)
+				continue;
+			st->samp_freq_avail[n++] = ad4691_osc_freqs_Hz[i] / osr;
+		}
+		*vals = st->samp_freq_avail;
 		*type = IIO_VAL_INT;
-		*length = ARRAY_SIZE(ad4691_osc_freqs_Hz) - start;
+		*length = n;
+		return IIO_AVAIL_LIST;
+	}
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		*vals = ad4691_oversampling_ratios;
+		*type = IIO_VAL_INT;
+		*length = ARRAY_SIZE(ad4691_oversampling_ratios);
 		return IIO_AVAIL_LIST;
 	default:
 		return -EINVAL;
@@ -554,7 +689,7 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
 				   struct iio_chan_spec const *chan, int *val)
 {
 	struct ad4691_state *st = iio_priv(indio_dev);
-	unsigned int reg_val, osc_idx, period_us;
+	unsigned int reg_val, period_us;
 	int ret;
 
 	guard(mutex)(&st->lock);
@@ -575,7 +710,12 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
 	if (ret)
 		return ret;
 
-	ret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, &reg_val);
+	ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(chan->channel),
+			   st->osr[chan->channel]);
+	if (ret)
+		return ret;
+
+	ret = ad4691_write_osc_freq(st);
 	if (ret)
 		return ret;
 
@@ -583,9 +723,12 @@ static int ad4691_single_shot_read(struct iio_dev *indio_dev,
 	if (ret)
 		return ret;
 
-	osc_idx = FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val);
-	/* Wait 2 oscillator periods for the conversion to complete. */
-	period_us = DIV_ROUND_UP(2UL * USEC_PER_SEC, ad4691_osc_freqs_Hz[osc_idx]);
+	/*
+	 * Wait osr + 1 oscillator periods: osr for accumulation, +1 for the
+	 * pipeline margin (one extra period ensures the final result is ready).
+	 */
+	period_us = DIV_ROUND_UP((unsigned long)(st->osr[chan->channel] + 1) * USEC_PER_SEC,
+				 st->target_osc_freq_Hz);
 	fsleep(period_us);
 
 	ret = regmap_write(st->regmap, AD4691_OSC_EN_REG, 0);
@@ -620,7 +763,10 @@ static int ad4691_read_raw(struct iio_dev *indio_dev,
 		return ad4691_single_shot_read(indio_dev, chan, val);
 	}
 	case IIO_CHAN_INFO_SAMP_FREQ:
-		return ad4691_get_sampling_freq(st, val);
+		return ad4691_get_sampling_freq(st, st->osr[chan->channel], val);
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		*val = st->osr[chan->channel];
+		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
 		*val = st->vref_uV / (MICRO / MILLI);
 		*val2 = chan->scan_type.realbits;
@@ -634,9 +780,29 @@ static int ad4691_write_raw(struct iio_dev *indio_dev,
 			    struct iio_chan_spec const *chan,
 			    int val, int val2, long mask)
 {
+	struct ad4691_state *st = iio_priv(indio_dev);
+
 	switch (mask) {
 	case IIO_CHAN_INFO_SAMP_FREQ:
-		return ad4691_set_sampling_freq(indio_dev, val);
+		return ad4691_set_sampling_freq(indio_dev, chan, val);
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+		IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+		if (IIO_DEV_ACQUIRE_FAILED(claim))
+			return -EBUSY;
+
+		for (unsigned int i = 0; i < ARRAY_SIZE(ad4691_oversampling_ratios); i++) {
+			if (ad4691_oversampling_ratios[i] != val)
+				continue;
+			/*
+			 * Store the new OSR; target_osc_freq_Hz is unchanged.
+			 * The effective rate read back via in_voltageN_sampling_frequency
+			 * becomes target_osc_freq_Hz / new_osr automatically.
+			 */
+			st->osr[chan->channel] = val;
+			return 0;
+		}
+		return -EINVAL;
+	}
 	default:
 		return -EINVAL;
 	}
@@ -691,6 +857,10 @@ static int ad4691_enter_conversion_mode(struct ad4691_state *st)
 		return regmap_update_bits(st->regmap, AD4691_DEVICE_SETUP,
 					  AD4691_MANUAL_MODE, AD4691_MANUAL_MODE);
 
+	ret = ad4691_write_osc_freq(st);
+	if (ret)
+		return ret;
+
 	ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP,
 				 AD4691_ADC_MODE_MASK, AD4691_CNV_BURST_MODE);
 	if (ret)
@@ -844,6 +1014,12 @@ static int ad4691_cnv_burst_buffer_preenable(struct iio_dev *indio_dev)
 	if (ret)
 		goto err_unoptimize;
 
+	iio_for_each_active_channel(indio_dev, i) {
+		ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(i), st->osr[i]);
+		if (ret)
+			goto err_unoptimize;
+	}
+
 	ret = ad4691_enter_conversion_mode(st);
 	if (ret)
 		goto err_unoptimize;
@@ -995,6 +1171,12 @@ static int ad4691_cnv_burst_offload_buffer_postenable(struct iio_dev *indio_dev)
 	if (ret)
 		return ret;
 
+	iio_for_each_active_channel(indio_dev, bit) {
+		ret = regmap_write(st->regmap, AD4691_ACC_DEPTH_IN(bit), st->osr[bit]);
+		if (ret)
+			return ret;
+	}
+
 	ret = ad4691_enter_conversion_mode(st);
 	if (ret)
 		return ret;
@@ -1361,6 +1543,8 @@ static int ad4691_config(struct ad4691_state *st)
 	if (ret)
 		return dev_err_probe(dev, ret, "Failed to write OSC_FREQ\n");
 
+	st->target_osc_freq_Hz = ad4691_osc_freqs_Hz[ad4691_samp_freq_start(st->info)];
+
 	ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP,
 				 AD4691_ADC_MODE_MASK, AD4691_AUTONOMOUS_MODE);
 	if (ret)
@@ -1518,6 +1702,7 @@ static int ad4691_probe(struct spi_device *spi)
 	st = iio_priv(indio_dev);
 	st->spi = spi;
 	st->info = spi_get_device_match_data(spi);
+	memset(st->osr, 1, sizeof(st->osr));
 
 	ret = devm_mutex_init(dev, &st->lock);
 	if (ret)
@@ -1552,11 +1737,17 @@ static int ad4691_probe(struct spi_device *spi)
 	indio_dev->modes = INDIO_DIRECT_MODE;
 
 	if (spi_offload) {
-		indio_dev->channels = st->info->offload_info->channels;
+		if (st->manual_mode)
+			indio_dev->channels = st->info->offload_info->manual_channels;
+		else
+			indio_dev->channels = st->info->offload_info->channels;
 		indio_dev->num_channels = st->info->offload_info->num_channels;
 		ret = ad4691_setup_offload(indio_dev, st, spi_offload);
 	} else {
-		indio_dev->channels = st->info->sw_info->channels;
+		if (st->manual_mode)
+			indio_dev->channels = st->info->sw_info->manual_channels;
+		else
+			indio_dev->channels = st->info->sw_info->channels;
 		indio_dev->num_channels = st->info->sw_info->num_channels;
 		ret = ad4691_setup_triggered_buffer(indio_dev, st);
 	}

-- 
2.43.0



^ permalink raw reply related

* [PATCH v8 6/6] docs: iio: adc: ad4691: add driver documentation
From: Radu Sabau via B4 Relay @ 2026-04-16  9:18 UTC (permalink / raw)
  To: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
	Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
	Philipp Zabel, Jonathan Corbet, Shuah Khan
  Cc: linux-iio, devicetree, linux-kernel, linux-pwm, linux-gpio,
	linux-doc, Radu Sabau
In-Reply-To: <20260416-ad4692-multichannel-sar-adc-driver-v8-0-c415bd048fa3@analog.com>

From: Radu Sabau <radu.sabau@analog.com>

Add RST documentation for the AD4691 family ADC driver covering
supported devices, IIO channels, operating modes, oversampling,
reference voltage, LDO supply, reset, GP pins, SPI offload support,
and buffer data format.

Signed-off-by: Radu Sabau <radu.sabau@analog.com>
---
 Documentation/iio/ad4691.rst | 205 +++++++++++++++++++++++++++++++++++++++++++
 Documentation/iio/index.rst  |   1 +
 MAINTAINERS                  |   1 +
 3 files changed, 207 insertions(+)

diff --git a/Documentation/iio/ad4691.rst b/Documentation/iio/ad4691.rst
new file mode 100644
index 000000000000..38e2ad28a713
--- /dev/null
+++ b/Documentation/iio/ad4691.rst
@@ -0,0 +1,205 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+=============
+AD4691 driver
+=============
+
+ADC driver for Analog Devices Inc. AD4691 family of multichannel SAR ADCs.
+The module name is ``ad4691``.
+
+
+Supported devices
+=================
+
+The following chips are supported by this driver:
+
+* `AD4691 <https://www.analog.com/en/products/ad4691.html>`_ — 16-channel, 500 kSPS
+* `AD4692 <https://www.analog.com/en/products/ad4692.html>`_ — 16-channel, 1 MSPS
+* `AD4693 <https://www.analog.com/en/products/ad4693.html>`_ — 8-channel, 500 kSPS
+* `AD4694 <https://www.analog.com/en/products/ad4694.html>`_ — 8-channel, 1 MSPS
+
+
+IIO channels
+============
+
+Each physical ADC input maps to one IIO voltage channel. The AD4691 and AD4692
+expose 16 channels (``voltage0`` through ``voltage15``); the AD4693 and AD4694
+expose 8 channels (``voltage0`` through ``voltage7``).
+
+All channels share a common scale (``in_voltage_scale``), derived from the
+reference voltage. Each channel independently exposes:
+
+* ``in_voltageN_raw`` — single-shot ADC result
+* ``in_voltageN_sampling_frequency`` — per-channel effective output rate,
+  defined as the internal oscillator frequency divided by the channel's
+  oversampling ratio. Writing this attribute selects the nearest achievable
+  rate for the current OSR; the value read back reflects the actual rate after
+  snapping to the closest valid oscillator entry.
+* ``in_voltageN_sampling_frequency_available`` — list of achievable effective
+  rates for the channel's current oversampling ratio. The list updates
+  dynamically when the oversampling ratio changes.
+
+The following attributes are only available in CNV Burst Mode:
+
+* ``in_voltageN_oversampling_ratio`` — per-channel hardware oversampling depth;
+  see `Oversampling`_ below.
+* ``in_voltageN_oversampling_ratio_available`` — valid ratios: 1, 2, 4, 8, 16,
+  32.
+
+
+Operating modes
+===============
+
+The driver supports two operating modes, selected automatically from the
+device tree at probe time.
+
+Manual Mode
+-----------
+
+Selected when no ``pwms`` property is present in the device tree. The CNV pin
+is tied to the SPI chip-select: every CS assertion triggers a conversion and
+returns the previous result. A user-defined IIO trigger (e.g. hrtimer trigger)
+drives the buffer.
+
+Oversampling is not supported in Manual Mode.
+
+CNV Burst Mode
+--------------
+
+Selected when a ``pwms`` property is present in the device tree. A PWM drives
+the CNV pin at the configured conversion rate. A GP pin wired to the SoC and
+declared in the device tree signals DATA_READY at the end of each burst,
+triggering a readout of all active channel results into the IIO buffer.
+
+The buffer output rate is controlled by the ``sampling_frequency`` attribute
+on the IIO buffer. In practice the PWM rate should be set low enough to allow
+the SPI readout to complete before the next conversion burst begins.
+
+Autonomous Mode (idle / single-shot)
+-------------------------------------
+
+When the IIO buffer is disabled, ``in_voltageN_raw`` reads perform a single
+conversion on the requested channel using the internal oscillator. The
+oscillator is started and stopped around each read to save power.
+
+
+Oversampling
+============
+
+In CNV Burst Mode each channel has an independent hardware accumulator that
+averages a configurable number of successive conversions. The result is always
+returned as a 16-bit mean, so ``realbits`` and ``storagebits`` are unaffected
+by the oversampling ratio. Valid ratios are 1, 2, 4, 8, 16 and 32; the default
+is 1 (no averaging). Oversampling is not supported in Manual Mode.
+
+.. code-block:: bash
+
+    # Set oversampling ratio to 16 on channel 0
+    echo 16 > /sys/bus/iio/devices/iio:device0/in_voltage0_oversampling_ratio
+
+    # Read the resulting effective sampling frequency
+    cat /sys/bus/iio/devices/iio:device0/in_voltage0_sampling_frequency
+
+Writing ``oversampling_ratio`` stores the new depth for that channel;
+the internal oscillator is unaffected. The effective rate read back via
+``in_voltageN_sampling_frequency`` becomes ``osc_freq / new_osr``
+automatically. ``oversampling_ratio`` and ``sampling_frequency`` are
+orthogonal: one controls averaging depth, the other controls the oscillator.
+
+All channels share one internal oscillator. Writing ``sampling_frequency`` for
+any channel updates the oscillator and therefore affects the effective rate
+read back from all other channels.
+
+
+Reference voltage
+=================
+
+The driver supports two reference configurations, mutually exclusive:
+
+* **External reference** (``ref-supply``): a voltage between 2.4 V and 5.25 V
+  supplied externally.
+* **Buffered internal reference** (``refin-supply``): an internal reference
+  buffer is enabled by the driver.
+
+Exactly one of ``ref-supply`` or ``refin-supply`` must be present in the
+device tree. The reference voltage determines the full-scale range reported
+via ``in_voltage_scale``.
+
+
+LDO supply
+==========
+
+The chip contains an internal LDO that powers part of the analog front-end.
+The supply configuration is mutually exclusive:
+
+* **External VDD** (``vdd-supply``): an external 1.8 V supply is used directly;
+  the internal LDO is disabled.
+* **Internal LDO** (``ldo-in-supply``): the internal LDO is enabled and fed
+  from the ``ldo-in`` regulator. Use this when no external 1.8 V VDD is present.
+
+Exactly one of ``vdd-supply`` or ``ldo-in-supply`` must be provided.
+
+
+Reset
+=====
+
+The driver supports two reset mechanisms:
+
+* **Hardware reset** (``reset-gpios`` in device tree): asserted at probe by
+  the reset controller framework.
+* **Software reset** (fallback when ``reset-gpios`` is absent): written
+  automatically at probe.
+
+
+GP pins and interrupts
+======================
+
+The chip exposes up to four general-purpose (GP) pins. In CNV Burst Mode
+(non-offload), one GP pin must be wired to an interrupt-capable SoC input and
+declared in the device tree using the ``interrupts`` and ``interrupt-names``
+properties. The ``interrupt-names`` value identifies which GP pin is used
+(``"gp0"`` through ``"gp3"``).
+
+Example device tree fragment::
+
+    adc@0 {
+        compatible = "adi,ad4692";
+        ...
+        interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
+        interrupt-parent = <&gpio0>;
+        interrupt-names = "gp0";
+    };
+
+
+SPI offload support
+===================
+
+When a SPI offload engine (e.g. the AXI SPI Engine) is present, the driver
+uses DMA-backed transfers for CPU-independent, high-throughput data capture.
+SPI offload is detected automatically at probe; if no offload hardware is
+available the driver falls back to the software triggered-buffer path.
+
+Two SPI offload sub-modes exist:
+
+CNV Burst offload
+-----------------
+
+Used when a ``pwms`` property is present and SPI offload is available. The PWM
+drives CNV at the configured rate; on DATA_READY the offload engine reads all
+active channel results and streams them directly to the IIO DMA buffer with no
+CPU involvement. The GP pin used as DATA_READY trigger is supplied by the
+trigger-source consumer at buffer enable time; no ``interrupt-names`` entry is
+required.
+
+Manual offload
+--------------
+
+Used when no ``pwms`` property is present and SPI offload is available. A
+periodic SPI offload trigger controls the conversion rate and the offload engine
+streams results directly to the IIO DMA buffer.
+
+The ``sampling_frequency`` attribute on the IIO buffer controls the trigger
+rate (in Hz). The initial rate is 100 kHz.
+
+Oversampling is not supported in Manual Mode.
+
diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst
index ba3e609c6a13..007e0a1fcc5a 100644
--- a/Documentation/iio/index.rst
+++ b/Documentation/iio/index.rst
@@ -23,6 +23,7 @@ Industrial I/O Kernel Drivers
    ad4000
    ad4030
    ad4062
+   ad4691
    ad4695
    ad7191
    ad7380
diff --git a/MAINTAINERS b/MAINTAINERS
index 24e4502b8292..819d8b6eb6bb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1491,6 +1491,7 @@ S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/adc/adi,ad4691.yaml
 F:	drivers/iio/adc/ad4691.c
+F:	drivers/iio/adc/ad4691.rst
 
 ANALOG DEVICES INC AD4695 DRIVER
 M:	Michael Hennerich <michael.hennerich@analog.com>

-- 
2.43.0



^ permalink raw reply related

* [PATCH v8 2/6] iio: adc: ad4691: add initial driver for AD4691 family
From: Radu Sabau via B4 Relay @ 2026-04-16  9:18 UTC (permalink / raw)
  To: Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	David Lechner, Nuno Sá, Andy Shevchenko, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Uwe Kleine-König,
	Liam Girdwood, Mark Brown, Linus Walleij, Bartosz Golaszewski,
	Philipp Zabel, Jonathan Corbet, Shuah Khan
  Cc: linux-iio, devicetree, linux-kernel, linux-pwm, linux-gpio,
	linux-doc, Radu Sabau
In-Reply-To: <20260416-ad4692-multichannel-sar-adc-driver-v8-0-c415bd048fa3@analog.com>

From: Radu Sabau <radu.sabau@analog.com>

Add support for the Analog Devices AD4691 family of high-speed,
low-power multichannel SAR ADCs: AD4691 (16-ch, 500 kSPS),
AD4692 (16-ch, 1 MSPS), AD4693 (8-ch, 500 kSPS) and
AD4694 (8-ch, 1 MSPS).

The driver implements a custom regmap layer over raw SPI to handle the
device's mixed 1/2/3/4-byte register widths and uses the standard IIO
read_raw/write_raw interface for single-channel reads.

The chip idles in Autonomous Mode so that single-shot read_raw can use
the internal oscillator without disturbing the hardware configuration.

Three voltage supply domains are managed: avdd (required), vio, and a
reference supply on either the REF pin (ref-supply, external buffer)
or the REFIN pin (refin-supply, uses the on-chip reference buffer;
REFBUF_EN is set accordingly). Hardware reset is performed via
the reset controller framework; a software reset through SPI_CONFIG_A
is used as fallback when no hardware reset is available.

Accumulator channel masking for single-shot reads uses ACC_MASK_REG via
an ADDR_DESCENDING SPI write, which covers both mask bytes in a single
16-bit transfer.

Reviewed-by: David Lechner <dlechner@baylibre.com>
Signed-off-by: Radu Sabau <radu.sabau@analog.com>
---
 MAINTAINERS              |   1 +
 drivers/iio/adc/Kconfig  |  11 +
 drivers/iio/adc/Makefile |   1 +
 drivers/iio/adc/ad4691.c | 714 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 727 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 438ca850fa1c..24e4502b8292 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1490,6 +1490,7 @@ L:	linux-iio@vger.kernel.org
 S:	Supported
 W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/iio/adc/adi,ad4691.yaml
+F:	drivers/iio/adc/ad4691.c
 
 ANALOG DEVICES INC AD4695 DRIVER
 M:	Michael Hennerich <michael.hennerich@analog.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 60038ae8dfc4..3685a03aa8dc 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -139,6 +139,17 @@ config AD4170_4
 	  To compile this driver as a module, choose M here: the module will be
 	  called ad4170-4.
 
+config AD4691
+	tristate "Analog Devices AD4691 Family ADC Driver"
+	depends on SPI
+	select REGMAP
+	help
+	  Say yes here to build support for Analog Devices AD4691 Family MuxSAR
+	  SPI analog to digital converters (ADC).
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ad4691.
+
 config AD4695
 	tristate "Analog Device AD4695 ADC Driver"
 	depends on SPI
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index c76550415ff1..4ac1ea09d773 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_AD4080) += ad4080.o
 obj-$(CONFIG_AD4130) += ad4130.o
 obj-$(CONFIG_AD4134) += ad4134.o
 obj-$(CONFIG_AD4170_4) += ad4170-4.o
+obj-$(CONFIG_AD4691) += ad4691.o
 obj-$(CONFIG_AD4695) += ad4695.o
 obj-$(CONFIG_AD4851) += ad4851.o
 obj-$(CONFIG_AD7091R) += ad7091r-base.o
diff --git a/drivers/iio/adc/ad4691.c b/drivers/iio/adc/ad4691.c
new file mode 100644
index 000000000000..61b5f74205af
--- /dev/null
+++ b/drivers/iio/adc/ad4691.c
@@ -0,0 +1,714 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024-2026 Analog Devices, Inc.
+ * Author: Radu Sabau <radu.sabau@analog.com>
+ */
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/limits.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/units.h>
+#include <linux/unaligned.h>
+
+#include <linux/iio/iio.h>
+
+#define AD4691_VREF_uV_MIN			2400000
+#define AD4691_VREF_uV_MAX			5250000
+#define AD4691_VREF_2P5_uV_MAX			2750000
+#define AD4691_VREF_3P0_uV_MAX			3250000
+#define AD4691_VREF_3P3_uV_MAX			3750000
+#define AD4691_VREF_4P096_uV_MAX		4500000
+
+#define AD4691_SPI_CONFIG_A_REG			0x000
+#define AD4691_SW_RESET				(BIT(7) | BIT(0))
+
+#define AD4691_STATUS_REG			0x014
+#define AD4691_CLAMP_STATUS1_REG		0x01A
+#define AD4691_CLAMP_STATUS2_REG		0x01B
+#define AD4691_DEVICE_SETUP			0x020
+#define AD4691_LDO_EN				BIT(4)
+#define AD4691_REF_CTRL				0x021
+#define AD4691_REF_CTRL_MASK			GENMASK(4, 2)
+#define AD4691_REFBUF_EN			BIT(0)
+#define AD4691_OSC_FREQ_REG			0x023
+#define AD4691_OSC_FREQ_MASK			GENMASK(3, 0)
+#define AD4691_STD_SEQ_CONFIG			0x025
+#define AD4691_SPARE_CONTROL			0x02A
+
+#define AD4691_OSC_EN_REG			0x180
+#define AD4691_STATE_RESET_REG			0x181
+#define AD4691_STATE_RESET_ALL			0x01
+#define AD4691_ADC_SETUP			0x182
+#define AD4691_ADC_MODE_MASK			GENMASK(1, 0)
+#define AD4691_AUTONOMOUS_MODE			0x02
+/*
+ * ACC_MASK_REG covers both mask bytes via ADDR_DESCENDING SPI: writing a
+ * 16-bit BE value to 0x185 auto-decrements to 0x184 for the second byte.
+ */
+#define AD4691_ACC_MASK_REG			0x185
+#define AD4691_ACC_DEPTH_IN(n)			(0x186 + (n))
+#define AD4691_GPIO_MODE1_REG			0x196
+#define AD4691_GPIO_MODE2_REG			0x197
+#define AD4691_GPIO_READ			0x1A0
+#define AD4691_ACC_STATUS_FULL1_REG		0x1B0
+#define AD4691_ACC_STATUS_FULL2_REG		0x1B1
+#define AD4691_ACC_STATUS_OVERRUN1_REG		0x1B2
+#define AD4691_ACC_STATUS_OVERRUN2_REG		0x1B3
+#define AD4691_ACC_STATUS_SAT1_REG		0x1B4
+#define AD4691_ACC_STATUS_SAT2_REG		0x1BE
+#define AD4691_ACC_SAT_OVR_REG(n)		(0x1C0 + (n))
+#define AD4691_AVG_IN(n)			(0x201 + (2 * (n)))
+#define AD4691_AVG_STS_IN(n)			(0x222 + (3 * (n)))
+#define AD4691_ACC_IN(n)			(0x252 + (3 * (n)))
+#define AD4691_ACC_STS_DATA(n)			(0x283 + (4 * (n)))
+
+static const char * const ad4691_supplies[] = { "avdd", "vio" };
+
+enum ad4691_ref_ctrl {
+	AD4691_VREF_2P5   = 0,
+	AD4691_VREF_3P0   = 1,
+	AD4691_VREF_3P3   = 2,
+	AD4691_VREF_4P096 = 3,
+	AD4691_VREF_5P0   = 4,
+};
+
+struct ad4691_chip_info {
+	const struct iio_chan_spec *channels;
+	const char *name;
+	unsigned int num_channels;
+	unsigned int max_rate;
+};
+
+#define AD4691_CHANNEL(ch)						\
+	{								\
+		.type = IIO_VOLTAGE,					\
+		.indexed = 1,						\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)		\
+				    | BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+		.info_mask_separate_available =				\
+				      BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE),	\
+		.channel = ch,						\
+		.scan_index = ch,					\
+		.scan_type = {						\
+			.sign = 'u',					\
+			.realbits = 16,					\
+			.storagebits = 16,				\
+		},							\
+	}
+
+static const struct iio_chan_spec ad4691_channels[] = {
+	AD4691_CHANNEL(0),
+	AD4691_CHANNEL(1),
+	AD4691_CHANNEL(2),
+	AD4691_CHANNEL(3),
+	AD4691_CHANNEL(4),
+	AD4691_CHANNEL(5),
+	AD4691_CHANNEL(6),
+	AD4691_CHANNEL(7),
+	AD4691_CHANNEL(8),
+	AD4691_CHANNEL(9),
+	AD4691_CHANNEL(10),
+	AD4691_CHANNEL(11),
+	AD4691_CHANNEL(12),
+	AD4691_CHANNEL(13),
+	AD4691_CHANNEL(14),
+	AD4691_CHANNEL(15),
+};
+
+static const struct iio_chan_spec ad4693_channels[] = {
+	AD4691_CHANNEL(0),
+	AD4691_CHANNEL(1),
+	AD4691_CHANNEL(2),
+	AD4691_CHANNEL(3),
+	AD4691_CHANNEL(4),
+	AD4691_CHANNEL(5),
+	AD4691_CHANNEL(6),
+	AD4691_CHANNEL(7),
+};
+
+/*
+ * Internal oscillator frequency table. Index is the OSC_FREQ_REG[3:0] value.
+ * Index 0 (1 MHz) is only valid for AD4692/AD4694; AD4691/AD4693 support
+ * up to 500 kHz and use index 1 as their highest valid rate.
+ */
+static const int ad4691_osc_freqs_Hz[] = {
+	[0x0] = 1000000,
+	[0x1] = 500000,
+	[0x2] = 400000,
+	[0x3] = 250000,
+	[0x4] = 200000,
+	[0x5] = 167000,
+	[0x6] = 133000,
+	[0x7] = 125000,
+	[0x8] = 100000,
+	[0x9] = 50000,
+	[0xA] = 25000,
+	[0xB] = 12500,
+	[0xC] = 10000,
+	[0xD] = 5000,
+	[0xE] = 2500,
+	[0xF] = 1250,
+};
+
+static const struct ad4691_chip_info ad4691_chip_info = {
+	.channels = ad4691_channels,
+	.name = "ad4691",
+	.num_channels = ARRAY_SIZE(ad4691_channels),
+	.max_rate = 500 * HZ_PER_KHZ,
+};
+
+static const struct ad4691_chip_info ad4692_chip_info = {
+	.channels = ad4691_channels,
+	.name = "ad4692",
+	.num_channels = ARRAY_SIZE(ad4691_channels),
+	.max_rate = 1 * HZ_PER_MHZ,
+};
+
+static const struct ad4691_chip_info ad4693_chip_info = {
+	.channels = ad4693_channels,
+	.name = "ad4693",
+	.num_channels = ARRAY_SIZE(ad4693_channels),
+	.max_rate = 500 * HZ_PER_KHZ,
+};
+
+static const struct ad4691_chip_info ad4694_chip_info = {
+	.channels = ad4693_channels,
+	.name = "ad4694",
+	.num_channels = ARRAY_SIZE(ad4693_channels),
+	.max_rate = 1 * HZ_PER_MHZ,
+};
+
+struct ad4691_state {
+	const struct ad4691_chip_info *info;
+	struct regmap *regmap;
+	int vref_uV;
+	bool refbuf_en;
+	bool ldo_en;
+	/*
+	 * Synchronize access to members of the driver state, and ensure
+	 * atomicity of consecutive SPI operations.
+	 */
+	struct mutex lock;
+};
+
+static int ad4691_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+	struct spi_device *spi = context;
+	u8 tx[2], rx[4];
+	int ret;
+
+	/* Set bit 15 to mark the operation as READ. */
+	put_unaligned_be16(0x8000 | reg, tx);
+
+	switch (reg) {
+	case 0 ... AD4691_OSC_FREQ_REG:
+	case AD4691_SPARE_CONTROL ... AD4691_ACC_SAT_OVR_REG(15):
+		ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 1);
+		if (ret)
+			return ret;
+		*val = rx[0];
+		return 0;
+	case AD4691_STD_SEQ_CONFIG:
+	case AD4691_AVG_IN(0) ... AD4691_AVG_IN(15):
+		ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 2);
+		if (ret)
+			return ret;
+		*val = get_unaligned_be16(rx);
+		return 0;
+	case AD4691_AVG_STS_IN(0) ... AD4691_AVG_STS_IN(15):
+	case AD4691_ACC_IN(0) ... AD4691_ACC_IN(15):
+		ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 3);
+		if (ret)
+			return ret;
+		*val = get_unaligned_be24(rx);
+		return 0;
+	case AD4691_ACC_STS_DATA(0) ... AD4691_ACC_STS_DATA(15):
+		ret = spi_write_then_read(spi, tx, sizeof(tx), rx, 4);
+		if (ret)
+			return ret;
+		*val = get_unaligned_be32(rx);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ad4691_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+	struct spi_device *spi = context;
+	u8 tx[4];
+
+	put_unaligned_be16(reg, tx);
+
+	switch (reg) {
+	case 0 ... AD4691_OSC_FREQ_REG:
+	case AD4691_SPARE_CONTROL ... AD4691_ACC_MASK_REG - 1:
+	case AD4691_ACC_MASK_REG + 1 ... AD4691_GPIO_MODE2_REG:
+		if (val > U8_MAX)
+			return -EINVAL;
+		tx[2] = val;
+		return spi_write_then_read(spi, tx, 3, NULL, 0);
+	case AD4691_ACC_MASK_REG:
+	case AD4691_STD_SEQ_CONFIG:
+		if (val > U16_MAX)
+			return -EINVAL;
+		put_unaligned_be16(val, &tx[2]);
+		return spi_write_then_read(spi, tx, 4, NULL, 0);
+	default:
+		return -EINVAL;
+	}
+}
+
+static bool ad4691_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case AD4691_STATUS_REG:
+	case AD4691_CLAMP_STATUS1_REG:
+	case AD4691_CLAMP_STATUS2_REG:
+	case AD4691_GPIO_READ:
+	case AD4691_ACC_STATUS_FULL1_REG ... AD4691_ACC_STATUS_SAT2_REG:
+	case AD4691_ACC_SAT_OVR_REG(0) ... AD4691_ACC_SAT_OVR_REG(15):
+	case AD4691_AVG_IN(0) ... AD4691_AVG_IN(15):
+	case AD4691_AVG_STS_IN(0) ... AD4691_AVG_STS_IN(15):
+	case AD4691_ACC_IN(0) ... AD4691_ACC_IN(15):
+	case AD4691_ACC_STS_DATA(0) ... AD4691_ACC_STS_DATA(15):
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool ad4691_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0 ... AD4691_OSC_FREQ_REG:
+	case AD4691_SPARE_CONTROL ... AD4691_ACC_SAT_OVR_REG(15):
+	case AD4691_STD_SEQ_CONFIG:
+	case AD4691_AVG_IN(0) ... AD4691_AVG_IN(15):
+	case AD4691_AVG_STS_IN(0) ... AD4691_AVG_STS_IN(15):
+	case AD4691_ACC_IN(0) ... AD4691_ACC_IN(15):
+	case AD4691_ACC_STS_DATA(0) ... AD4691_ACC_STS_DATA(15):
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool ad4691_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0 ... AD4691_OSC_FREQ_REG:
+	case AD4691_STD_SEQ_CONFIG:
+	case AD4691_SPARE_CONTROL ... AD4691_GPIO_MODE2_REG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config ad4691_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 32,
+	.reg_read = ad4691_reg_read,
+	.reg_write = ad4691_reg_write,
+	.volatile_reg = ad4691_volatile_reg,
+	.readable_reg = ad4691_readable_reg,
+	.writeable_reg = ad4691_writeable_reg,
+	.max_register = AD4691_ACC_STS_DATA(15),
+	.cache_type = REGCACHE_MAPLE,
+};
+
+/*
+ * Index 0 in ad4691_osc_freqs_Hz is 1 MHz — valid only for AD4692/AD4694
+ * (max_rate == 1 MHz). AD4691/AD4693 cap at 500 kHz so their valid range
+ * starts at index 1.
+ */
+static unsigned int ad4691_samp_freq_start(const struct ad4691_chip_info *info)
+{
+	return (info->max_rate == 1 * HZ_PER_MHZ) ? 0 : 1;
+}
+
+static int ad4691_get_sampling_freq(struct ad4691_state *st, int *val)
+{
+	unsigned int reg_val;
+	int ret;
+
+	ret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, &reg_val);
+	if (ret)
+		return ret;
+
+	*val = ad4691_osc_freqs_Hz[FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val)];
+	return IIO_VAL_INT;
+}
+
+static int ad4691_set_sampling_freq(struct iio_dev *indio_dev, int freq)
+{
+	struct ad4691_state *st = iio_priv(indio_dev);
+	unsigned int start = ad4691_samp_freq_start(st->info);
+
+	IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+	if (IIO_DEV_ACQUIRE_FAILED(claim))
+		return -EBUSY;
+
+	for (unsigned int i = start; i < ARRAY_SIZE(ad4691_osc_freqs_Hz); i++) {
+		if (ad4691_osc_freqs_Hz[i] != freq)
+			continue;
+		return regmap_update_bits(st->regmap, AD4691_OSC_FREQ_REG,
+					  AD4691_OSC_FREQ_MASK, i);
+	}
+
+	return -EINVAL;
+}
+
+static int ad4691_read_avail(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     const int **vals, int *type,
+			     int *length, long mask)
+{
+	struct ad4691_state *st = iio_priv(indio_dev);
+	unsigned int start = ad4691_samp_freq_start(st->info);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*vals = &ad4691_osc_freqs_Hz[start];
+		*type = IIO_VAL_INT;
+		*length = ARRAY_SIZE(ad4691_osc_freqs_Hz) - start;
+		return IIO_AVAIL_LIST;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ad4691_single_shot_read(struct iio_dev *indio_dev,
+				   struct iio_chan_spec const *chan, int *val)
+{
+	struct ad4691_state *st = iio_priv(indio_dev);
+	unsigned int reg_val, osc_idx, period_us;
+	int ret;
+
+	guard(mutex)(&st->lock);
+
+	/* Use AUTONOMOUS mode for single-shot reads. */
+	ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG,
+			   AD4691_STATE_RESET_ALL);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, AD4691_STD_SEQ_CONFIG,
+			   BIT(chan->channel));
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, AD4691_ACC_MASK_REG,
+			   ~BIT(chan->channel) & GENMASK(15, 0));
+	if (ret)
+		return ret;
+
+	ret = regmap_read(st->regmap, AD4691_OSC_FREQ_REG, &reg_val);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(st->regmap, AD4691_OSC_EN_REG, 1);
+	if (ret)
+		return ret;
+
+	osc_idx = FIELD_GET(AD4691_OSC_FREQ_MASK, reg_val);
+	/* Wait 2 oscillator periods for the conversion to complete. */
+	period_us = DIV_ROUND_UP(2UL * USEC_PER_SEC, ad4691_osc_freqs_Hz[osc_idx]);
+	fsleep(period_us);
+
+	ret = regmap_write(st->regmap, AD4691_OSC_EN_REG, 0);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(st->regmap, AD4691_AVG_IN(chan->channel), &reg_val);
+	if (ret)
+		return ret;
+
+	*val = reg_val;
+
+	ret = regmap_write(st->regmap, AD4691_STATE_RESET_REG, AD4691_STATE_RESET_ALL);
+	if (ret)
+		return ret;
+
+	return IIO_VAL_INT;
+}
+
+static int ad4691_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *chan, int *val,
+			   int *val2, long info)
+{
+	struct ad4691_state *st = iio_priv(indio_dev);
+
+	switch (info) {
+	case IIO_CHAN_INFO_RAW: {
+		IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
+		if (IIO_DEV_ACQUIRE_FAILED(claim))
+			return -EBUSY;
+
+		return ad4691_single_shot_read(indio_dev, chan, val);
+	}
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return ad4691_get_sampling_freq(st, val);
+	case IIO_CHAN_INFO_SCALE:
+		*val = st->vref_uV / (MICRO / MILLI);
+		*val2 = chan->scan_type.realbits;
+		return IIO_VAL_FRACTIONAL_LOG2;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ad4691_write_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val, int val2, long mask)
+{
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return ad4691_set_sampling_freq(indio_dev, val);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ad4691_reg_access(struct iio_dev *indio_dev, unsigned int reg,
+			     unsigned int writeval, unsigned int *readval)
+{
+	struct ad4691_state *st = iio_priv(indio_dev);
+
+	guard(mutex)(&st->lock);
+
+	if (readval)
+		return regmap_read(st->regmap, reg, readval);
+
+	return regmap_write(st->regmap, reg, writeval);
+}
+
+static const struct iio_info ad4691_info = {
+	.read_raw = &ad4691_read_raw,
+	.write_raw = &ad4691_write_raw,
+	.read_avail = &ad4691_read_avail,
+	.debugfs_reg_access = &ad4691_reg_access,
+};
+
+static int ad4691_regulator_setup(struct ad4691_state *st)
+{
+	struct device *dev = regmap_get_device(st->regmap);
+	int ret;
+
+	ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4691_supplies),
+					     ad4691_supplies);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
+
+	/*
+	 * vdd-supply and ldo-in-supply are mutually exclusive:
+	 *   vdd-supply present  → external 1.8V VDD; disable internal LDO.
+	 *   vdd-supply absent   → enable internal LDO fed from ldo-in-supply.
+	 * Having both simultaneously is strongly inadvisable per the datasheet.
+	 */
+	ret = devm_regulator_get_enable(dev, "vdd");
+	if (ret == -ENODEV) {
+		ret = devm_regulator_get_enable(dev, "ldo-in");
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "Failed to get and enable LDO-IN\n");
+		st->ldo_en = true;
+	} else if (ret) {
+		return dev_err_probe(dev, ret, "Failed to get and enable VDD\n");
+	}
+
+	st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "ref");
+	if (st->vref_uV == -ENODEV) {
+		st->vref_uV = devm_regulator_get_enable_read_voltage(dev, "refin");
+		st->refbuf_en = true;
+	}
+	if (st->vref_uV < 0)
+		return dev_err_probe(dev, st->vref_uV,
+				     "Failed to get reference supply\n");
+
+	if (st->vref_uV < AD4691_VREF_uV_MIN || st->vref_uV > AD4691_VREF_uV_MAX)
+		return dev_err_probe(dev, -EINVAL,
+				     "vref(%d) must be in the range [%u...%u]\n",
+				     st->vref_uV, AD4691_VREF_uV_MIN,
+				     AD4691_VREF_uV_MAX);
+
+	return 0;
+}
+
+static int ad4691_reset(struct ad4691_state *st)
+{
+	struct device *dev = regmap_get_device(st->regmap);
+	struct reset_control *rst;
+
+	rst = devm_reset_control_get_optional_exclusive(dev, NULL);
+	if (IS_ERR(rst))
+		return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset\n");
+
+	if (rst) {
+		/*
+		 * reset_gpio_probe() already drives the pin asserted, so the
+		 * device is held in reset before we get here.
+		 * devm_reset_control_get_optional_exclusive_deasserted() cannot
+		 * be used because it deasserts immediately without delay; the
+		 * datasheet (Table 5) requires a ≥300 µs reset pulse width
+		 * before deassertion.
+		 */
+		fsleep(300);
+		return reset_control_deassert(rst);
+	}
+
+	/* No hardware reset available, fall back to software reset. */
+	return regmap_write(st->regmap, AD4691_SPI_CONFIG_A_REG,
+			    AD4691_SW_RESET);
+}
+
+static int ad4691_config(struct ad4691_state *st)
+{
+	struct device *dev = regmap_get_device(st->regmap);
+	enum ad4691_ref_ctrl ref_val;
+	unsigned int val;
+	int ret;
+
+	switch (st->vref_uV) {
+	case AD4691_VREF_uV_MIN ... AD4691_VREF_2P5_uV_MAX:
+		ref_val = AD4691_VREF_2P5;
+		break;
+	case AD4691_VREF_2P5_uV_MAX + 1 ... AD4691_VREF_3P0_uV_MAX:
+		ref_val = AD4691_VREF_3P0;
+		break;
+	case AD4691_VREF_3P0_uV_MAX + 1 ... AD4691_VREF_3P3_uV_MAX:
+		ref_val = AD4691_VREF_3P3;
+		break;
+	case AD4691_VREF_3P3_uV_MAX + 1 ... AD4691_VREF_4P096_uV_MAX:
+		ref_val = AD4691_VREF_4P096;
+		break;
+	case AD4691_VREF_4P096_uV_MAX + 1 ... AD4691_VREF_uV_MAX:
+		ref_val = AD4691_VREF_5P0;
+		break;
+	default:
+		return dev_err_probe(dev, -EINVAL,
+				     "Unsupported vref voltage: %d uV\n",
+				     st->vref_uV);
+	}
+
+	val = FIELD_PREP(AD4691_REF_CTRL_MASK, ref_val);
+	if (st->refbuf_en)
+		val |= AD4691_REFBUF_EN;
+
+	ret = regmap_write(st->regmap, AD4691_REF_CTRL, val);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to write REF_CTRL\n");
+
+	ret = regmap_assign_bits(st->regmap, AD4691_DEVICE_SETUP,
+				 AD4691_LDO_EN, st->ldo_en);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to write DEVICE_SETUP\n");
+
+	/*
+	 * Set the internal oscillator to the highest rate this chip supports.
+	 * Index 0 (1 MHz) exceeds the 500 kHz max of AD4691/AD4693, so those
+	 * chips start at index 1 (500 kHz).
+	 */
+	ret = regmap_write(st->regmap, AD4691_OSC_FREQ_REG,
+			   ad4691_samp_freq_start(st->info));
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to write OSC_FREQ\n");
+
+	ret = regmap_update_bits(st->regmap, AD4691_ADC_SETUP,
+				 AD4691_ADC_MODE_MASK, AD4691_AUTONOMOUS_MODE);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to write ADC_SETUP\n");
+
+	return 0;
+}
+
+static int ad4691_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	struct iio_dev *indio_dev;
+	struct ad4691_state *st;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	st = iio_priv(indio_dev);
+	st->info = spi_get_device_match_data(spi);
+
+	ret = devm_mutex_init(dev, &st->lock);
+	if (ret)
+		return ret;
+
+	st->regmap = devm_regmap_init(dev, NULL, spi, &ad4691_regmap_config);
+	if (IS_ERR(st->regmap))
+		return dev_err_probe(dev, PTR_ERR(st->regmap),
+				     "Failed to initialize regmap\n");
+
+	ret = ad4691_regulator_setup(st);
+	if (ret)
+		return ret;
+
+	ret = ad4691_reset(st);
+	if (ret)
+		return ret;
+
+	ret = ad4691_config(st);
+	if (ret)
+		return ret;
+
+	indio_dev->name = st->info->name;
+	indio_dev->info = &ad4691_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	indio_dev->channels = st->info->channels;
+	indio_dev->num_channels = st->info->num_channels;
+
+	return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id ad4691_of_match[] = {
+	{ .compatible = "adi,ad4691", .data = &ad4691_chip_info },
+	{ .compatible = "adi,ad4692", .data = &ad4692_chip_info },
+	{ .compatible = "adi,ad4693", .data = &ad4693_chip_info },
+	{ .compatible = "adi,ad4694", .data = &ad4694_chip_info },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ad4691_of_match);
+
+static const struct spi_device_id ad4691_id[] = {
+	{ "ad4691", (kernel_ulong_t)&ad4691_chip_info },
+	{ "ad4692", (kernel_ulong_t)&ad4692_chip_info },
+	{ "ad4693", (kernel_ulong_t)&ad4693_chip_info },
+	{ "ad4694", (kernel_ulong_t)&ad4694_chip_info },
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, ad4691_id);
+
+static struct spi_driver ad4691_driver = {
+	.driver = {
+		.name = "ad4691",
+		.of_match_table = ad4691_of_match,
+	},
+	.probe = ad4691_probe,
+	.id_table = ad4691_id,
+};
+module_spi_driver(ad4691_driver);
+
+MODULE_AUTHOR("Radu Sabau <radu.sabau@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD4691 Family ADC Driver");
+MODULE_LICENSE("GPL");

-- 
2.43.0



^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox