Devicetree
 help / color / mirror / Atom feed
* RE: [PATCH 1/2] dt-bindings: connector: pcie-m2-e: Add 3.3Vaux supply support
From: Sherry Sun @ 2026-06-10 10:13 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Krzysztof Kozlowski, sashiko-reviews@lists.linux.dev,
	manivannan.sadhasivam@oss.qualcomm.com, linux-pci@vger.kernel.org,
	robh@kernel.org, Frank.Li@kernel.org, conor+dt@kernel.org,
	devicetree@vger.kernel.org, imx@lists.linux.dev
In-Reply-To: <mvxoynvocxcalxcwogbiovg5yurjczxqfu2fqkji33bunmlplz@zxqjsdjts65j>

> On Wed, Jun 10, 2026 at 08:40:54AM +0000, Sherry Sun wrote:
> > > On Tue, Jun 09, 2026 at 03:44:08AM +0000, sashiko-bot@kernel.org wrote:
> > > > Thank you for your contribution! Sashiko AI review found 1
> > > > potential
> > > issue(s) to consider:
> > > > - [Medium] The `vpcie3v3aux-supply` property describes a
> > > > non-existent
> > > hardware feature on the M.2 Key E connector to work around a
> > > software policy.
> > >
> > > Feels valid. Describe which pin on M2 connector are you representing.
> > >
> >
> > Refer to PCI Express M.2 Specification r5.1 sec3.1.1 Power Sources and
> > Grounds.
> >
> > PCI Express M.2 Socket 1 utilizes a 3.3 V power source. The voltage
> > source, 3.3 V, is expected to be available during the system’s
> > stand-by/suspend state to support wake event processing on the
> > communications card.
> >
> > But the current vpcie3v3-supply may be gated off during system suspend.
> > So I  tried to add vpcie3v3aux-supply to let this 3.3 V power source
> > always on for PCIe M.2 Key E connector. That means vpcie3v3aux-supply
> > and vpcie3v3-supply actually refer to the same 3.3 V power source.
> >
> > @Mani, do you think this is reasonable? Or do you have any other
> > better solutions? Thanks!
> >
> 
> There is no Vaux defined in the M.2 spec. So you cannot define that supply in
> the binding. You can define the custom Vaux supply as a fixed regulator in DT
> and mark it always on so that it is keeps supplying 3.3v to the card.
> 

Hi Mani, thanks for the suggestion, but adding an always on regulator may
cause board power waste, current solution ensures the 3.3v regulator is
only enabled when M.2 connector needs to work .

Best Regards
Sherry

^ permalink raw reply

* Re: [PATCH 0/4] sound: qcom: audioreach: support WSA speakers only on WSA2
From: Mark Brown @ 2026-06-10 10:12 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: Srinivas Kandagatla, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
	Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, kancy2333, linux-sound, linux-arm-msm, linux-kernel,
	devicetree, Srinivas Kandagatla
In-Reply-To: <20260610-topic-sm8650-ayaneo-pocket-s2-wsa2-fix-v1-0-18bb19c5ca22@linaro.org>

[-- Attachment #1: Type: text/plain, Size: 209 bytes --]

On Wed, Jun 10, 2026 at 09:41:44AM +0200, Neil Armstrong wrote:

> ---
> base-commit: 1ed783a6906ab62a54d631ff3e8c5cba0f4f4b54

I don't seem to have this, it doesn't seem to be anything from my tree
or -next.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply

* Re: [PATCH v5 1/3] dt-bindings: dmaengine: Add support for ATCDMAC300 DMA engine
From: sashiko-bot @ 2026-06-10 10:11 UTC (permalink / raw)
  To: CL Wang; +Cc: Frank.Li, conor+dt, devicetree, dmaengine, robh, vkoul
In-Reply-To: <20260610095724.1980622-2-cl634@andestech.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The schema uses `additionalProperties: false` instead of `unevaluatedProperties: false` while referencing a common subsystem schema.
--

commit 0c4fc28dab967baedae4dfdcb32dfa0eb77d45a5
Author: CL Wang <cl634@andestech.com>

dt-bindings: dmaengine: Add support for ATCDMAC300 DMA engine

This commit documents devicetree bindings for the Andes ATCDMAC300 DMA
engine, which is embedded in AndesCore-based platforms such as AE350 and
Qilai.

> diff --git a/Documentation/devicetree/bindings/dma/andestech,ae350-dma.yaml b/Documentation/devicetree/bindings/dma/andestech,ae350-dma.yaml
> new file mode 100644
> index 0000000000000..f040a2bf7d4b9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/andestech,ae350-dma.yaml

[ ... ]

> +allOf:
> +  - $ref: dma-controller.yaml#

[ ... ]

> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - "#dma-cells"
> +
> +additionalProperties: false

[Severity: Low]
Does using additionalProperties: false here prevent the use of common DMA
properties defined in dma-controller.yaml (like dma-channels or
dma-requests)? 

Since this binding includes another schema under allOf, should it use
unevaluatedProperties: false instead, so that dt-validate correctly
evaluates and permits those inherited properties?

> +
> +examples:
> +  - |

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260610095724.1980622-1-cl634@andestech.com?part=1

^ permalink raw reply

* RE: [PATCH 2/7] ASoC: codecs: ES8389: Fix the issue about mclk_src
From: Zhang Yi @ 2026-06-10 10:06 UTC (permalink / raw)
  To: krzk
  Cc: alsa-devel, broonie, conor+dt, devicetree, krzk+dt, robh, tiwai,
	zhangyi
In-Reply-To: <20260609-visionary-truthful-viper-ceff75@quoll>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 984 bytes --]

> > Fix the issue with incorrect modifications to mclk_src
> 
> What issue? Your commit msgs are really poor - explain nothing. You just
> duplicated subject... and anything can be a fix.

I'm sorry I didn't explain that clearly.
When the system needs to be configured to use the MCLK from the SCLK pin,
the old code still sets the relevant registers to use the MCLK from the MCLK pin.
I will include a more detailed description in future versions.

> >  	struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
> >  
> > -	ret = device_property_read_u8(component->dev, "everest,mclk-src", &es8389->mclk_src);
> 
> Why are you changing implemented ABI?

In the old ABI, `mclk_src` was defined as `u8`, which meant that users could set `mclk_src` to any value in the DTS¡ªsuch as `0x02`,
but the code wouldn't recognize what that value represented.
The actual purpose of `mclk_src` is to indicate whether `sclk` should be used as `mclk`.
So I've changed it to bool.


^ permalink raw reply

* Re: [RFC PATCH v3 0/9] accel: rocket: Add RK3568 NPU support
From: Diederik de Haas @ 2026-06-10 10:05 UTC (permalink / raw)
  To: Chaoyi Chen, Midgy Balon
  Cc: tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro, will,
	robin.murphy, dri-devel, linux-rockchip, devicetree,
	linux-arm-kernel, iommu, linux-kernel, Simon Xue, Finley Xiao,
	Jonas Karlman
In-Reply-To: <b05f7154-e85f-4207-80ae-f080282ba780@rock-chips.com>

Hi,

On Wed Jun 10, 2026 at 3:14 AM CEST, Chaoyi Chen wrote:
> Hi Midgy,
>
> On 6/9/2026 7:11 PM, Midgy Balon wrote:
>> Hello Chaoyi,
>> 
>> You were right - building rocket as a module fixes it. Thanks for the pointer.
>> 
>> I rebuilt with CONFIG_DRM_ACCEL_ROCKET=m (everything else the same:
>> need_regulator on
>> the RK3568 NPU power domain via a DOMAIN_M_R variant, domain-supply =
>> <&vdd_npu>, and the
>> regulator-always-on workaround dropped). The board now boots cleanly
>> and, more importantly,
>> an NPU job submit no longer hangs: I ran the test workload five times
>> with no RCU stall and
>> no freeze.
>> 
>> So with rocket=m the need_regulator approach works on RK3568, and I'll
>> keep it for v4
>> (domain-supply + need_regulator, instead of marking vdd_npu
>> always-on). rocket=m is the
>> normal configuration anyway; my earlier hang came from building it =y
>> in a self-contained
>> image, so it probed in the initcalls (around 2 s) and the genpd ->
>> I2C-PMIC regulator
>> transition ran before the system was ready. As a module it loads from
>> udev much later
>> (~6.8 s here), after the I2C controller and regulator core are fully up.
>> 
>> On your question of when the device-link error is printed - it is at
>> power-domain
>> controller probe, not at the rocket probe:
>> 
>>   [    2.700618] vdd_npu: Bringing 500000uV into 825000-825000uV
>>   [    2.749637] rockchip-pm-domain fdd90000.power-management:power-controller:
>>                  Failed to create device link (0x180) with supplier 0-0020 for
>>                  /power-management@fdd90000/power-controller/power-domain@6
>>   [    2.945955] platform fde40000.npu: Adding to iommu group 3
>>   ...
>>   [    6.840374] rocket: loading out-of-tree module taints kernel.
>>   [    6.877647] [drm] Initialized rocket 0.0.0 for rknn on minor 0
>>   [    6.879950] rocket fde40000.npu: Rockchip NPU core 0 version: 0
>> 
>> So the device-link to the rk809 PMIC (0-0020) fails to form at ~2.75
>> s, well before rocket
>> loads at ~6.8 s. It is non-fatal here - the vdd_npu rail is brought up
>> by the regulator core
>> and all jobs run - and there is no "failed to get ack on domain npu"
>> NoC warning this boot
>> (the always-on kernel had one). The complete boot log is attached.
>> 
>> Two notes / one question:
>> - This boot used fw_devlink=permissive on the command line. Is the
>> "Failed to create device
>>   link ... supplier 0-0020" at pmdomain probe expected/benign, or is
>> there a clean way to make
>>   it order correctly (so it also works without permissive, and a =y
>> build wouldn't deadlock in
>>   the initcalls)?
>
> We encountered the same issue on the RK3588 NPU before. And it was
> resolved with the following patch at that time.
>
> https://lore.kernel.org/all/20251216055247.13150-1-rmxpzlb@gmail.com/
>
> Please compare the differences in NPU pmdomain and DTS configuration
> between the RK3568 and RK3588.

About a month ago on #linux-rockchip we were discussing PM 'stuff':
https://libera.catirclogs.org/linux-rockchip/2026-05-15#39939137;
which references this paste
https://paste.sr.ht/~diederik/89d9f84e22474e837b55286d213b67f03859ce2e
I've since removed the DCDC_REG2 for PineTab2 and the 'fix' should likely
be extended to cover all RK3566/RK3568 devices though.

It's what I made at the time hoping to fix a suspend/resume issue when
trying upstream TF-A. It didn't fix the issue at the time, but may still
be useful/needed and I think it's what Chaoyi hinted at.

Just yesterday, Jonas posted this patch which may be useful/needed too:
https://lore.kernel.org/linux-rockchip/20260609154124.445182-1-jonas@kwiboo.se/

HTH,
  Diederik

>> - (The convolution output is still uniform zero-point / the job times
>> out - that is the
>>   separate NPU compute-completion issue, unrelated to the power-domain
>> work. Finley, that is
>>   the one I flagged earlier re PVTPLL/NoC.)
>> 
>> Kind regards,
>> Midgy
>> 


^ permalink raw reply

* [PATCH v2 2/2] arm64: dts: rockchip: Add HINLINK H28K
From: Chukun Pan @ 2026-06-10 10:00 UTC (permalink / raw)
  To: Heiko Stuebner
  Cc: Rob Herring, Chukun Pan, Conor Dooley, Krzysztof Kozlowski,
	linux-arm-kernel, linux-rockchip, linux-kernel, devicetree
In-Reply-To: <20260610100006.366963-1-amadeus@jmu.edu.cn>

The HINLINK H28K (LinkStar H28K) is a SBC with the
Rockchip RK3528 SoC. It has the following features:

- 1x USB 2.0
- 8/32GB eMMC
- 1/2/4GB LPDDR4
- MicroSD card slot
- 1x 1GbE RTL8111H Ethernet
- 1x 1GbE RTL8211F Ethernet

Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
---
 arch/arm64/boot/dts/rockchip/Makefile         |   1 +
 .../boot/dts/rockchip/rk3528-hinlink-h28k.dts | 318 ++++++++++++++++++
 2 files changed, 319 insertions(+)
 create mode 100644 arch/arm64/boot/dts/rockchip/rk3528-hinlink-h28k.dts

diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
index 761d82b4f4f2..a68d07296c9f 100644
--- a/arch/arm64/boot/dts/rockchip/Makefile
+++ b/arch/arm64/boot/dts/rockchip/Makefile
@@ -92,6 +92,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire-excavator.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399pro-rock-pi-n10.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3528-armsom-sige1.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3528-hinlink-h28k.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3528-nanopi-zero2.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3528-radxa-e20c.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3528-rock-2a.dtb
diff --git a/arch/arm64/boot/dts/rockchip/rk3528-hinlink-h28k.dts b/arch/arm64/boot/dts/rockchip/rk3528-hinlink-h28k.dts
new file mode 100644
index 000000000000..0ec50e9156eb
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3528-hinlink-h28k.dts
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+/dts-v1/;
+
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/leds/common.h>
+#include <dt-bindings/pwm/pwm.h>
+#include "rk3528.dtsi"
+
+/ {
+	model = "HINLINK H28K";
+	compatible = "hinlink,h28k", "rockchip,rk3528";
+
+	aliases {
+		ethernet0 = &gmac1;
+		mmc0 = &sdhci;
+		mmc1 = &sdmmc;
+		serial0 = &uart0;
+	};
+
+	chosen {
+		stdout-path = "serial0:1500000n8";
+	};
+
+	keys {
+		compatible = "adc-keys";
+		io-channels = <&saradc 0>;
+		io-channel-names = "buttons";
+		keyup-threshold-microvolt = <1800000>;
+		poll-interval = <100>;
+
+		button-boot {
+			label = "BOOT";
+			linux,code = <KEY_SETUP>;
+			press-threshold-microvolt = <0>;
+		};
+	};
+
+	leds {
+		compatible = "gpio-leds";
+		pinctrl-names = "default";
+		pinctrl-0 = <&lan_led>, <&wan_led>, <&work_led>;
+
+		led-0 {
+			color = <LED_COLOR_ID_AMBER>;
+			function = LED_FUNCTION_LAN;
+			gpios = <&gpio4 RK_PC1 GPIO_ACTIVE_LOW>;
+			linux,default-trigger = "netdev";
+		};
+
+		led-1 {
+			color = <LED_COLOR_ID_BLUE>;
+			function = LED_FUNCTION_WAN;
+			gpios = <&gpio4 RK_PC0 GPIO_ACTIVE_LOW>;
+			linux,default-trigger = "netdev";
+		};
+
+		led-2 {
+			color = <LED_COLOR_ID_GREEN>;
+			function = LED_FUNCTION_STATUS;
+			gpios = <&gpio4 RK_PB7 GPIO_ACTIVE_LOW>;
+			linux,default-trigger = "default-on";
+		};
+	};
+
+	vdd_0v9: regulator-0v9-vdd {
+		compatible = "regulator-fixed";
+		regulator-name = "vdd_0v9";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <900000>;
+		regulator-max-microvolt = <900000>;
+		vin-supply = <&vcc5v0_sys>;
+	};
+
+	vcc_ddr: regulator-1v1-vcc-ddr {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_ddr";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <1100000>;
+		regulator-max-microvolt = <1100000>;
+		vin-supply = <&vcc5v0_sys>;
+	};
+
+	vcc_1v8: regulator-1v8-vcc {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_1v8";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		vin-supply = <&vcc_3v3>;
+	};
+
+	vcc_3v3: regulator-3v3-vcc {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_3v3";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		vin-supply = <&vcc5v0_sys>;
+	};
+
+	vcc3v3_sd: regulator-3v3-vcc-sd {
+		compatible = "regulator-fixed";
+		gpios = <&gpio4 RK_PA1 GPIO_ACTIVE_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdmmc_pwren_l>;
+		regulator-name = "vcc3v3_sd";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		vin-supply = <&vcc_3v3>;
+	};
+
+	vcc5v0_sys: regulator-5v0-vcc-sys {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc5v0_sys";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+	};
+
+	vccio_sd: regulator-vccio-sd {
+		compatible = "regulator-gpio";
+		gpios = <&gpio4 RK_PB6 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdmmc_vol_ctrl_h>;
+		regulator-name = "vccio_sd";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+		states = <1800000 0x0>, <3300000 0x1>;
+		vin-supply = <&vcc5v0_sys>;
+	};
+
+	vdd_arm: regulator-vdd-arm {
+		compatible = "pwm-regulator";
+		pwms = <&pwm1 0 5000 PWM_POLARITY_INVERTED>;
+		pwm-supply = <&vcc5v0_sys>;
+		regulator-name = "vdd_arm";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <746000>;
+		regulator-max-microvolt = <1201000>;
+		regulator-settling-time-up-us = <250>;
+	};
+
+	vdd_logic: regulator-vdd-logic {
+		compatible = "pwm-regulator";
+		pwms = <&pwm2 0 5000 PWM_POLARITY_INVERTED>;
+		pwm-supply = <&vcc5v0_sys>;
+		regulator-name = "vdd_logic";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <705000>;
+		regulator-max-microvolt = <1006000>;
+		regulator-settling-time-up-us = <250>;
+	};
+};
+
+&combphy {
+	status = "okay";
+};
+
+&cpu0 {
+	cpu-supply = <&vdd_arm>;
+};
+
+&cpu1 {
+	cpu-supply = <&vdd_arm>;
+};
+
+&cpu2 {
+	cpu-supply = <&vdd_arm>;
+};
+
+&cpu3 {
+	cpu-supply = <&vdd_arm>;
+};
+
+&gmac1 {
+	clock_in_out = "output";
+	phy-handle = <&rgmii_phy>;
+	phy-mode = "rgmii-id";
+	phy-supply = <&vcc_3v3>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&rgmii_miim>,
+		    <&rgmii_tx_bus2>,
+		    <&rgmii_rx_bus2>,
+		    <&rgmii_rgmii_clk>,
+		    <&rgmii_rgmii_bus>;
+	status = "okay";
+};
+
+&gpu {
+	mali-supply = <&vdd_logic>;
+	status = "okay";
+};
+
+&mdio1 {
+	rgmii_phy: ethernet-phy@1 {
+		compatible = "ethernet-phy-ieee802.3-c22";
+		reg = <0x1>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&gmac1_rstn_l>;
+		reset-assert-us = <20000>;
+		reset-deassert-us = <100000>;
+		reset-gpios = <&gpio4 RK_PC2 GPIO_ACTIVE_LOW>;
+	};
+};
+
+&pcie {
+	pinctrl-names = "default";
+	pinctrl-0 = <&rtl8111hs_isolateb_l>;
+	reset-gpios = <&gpio4 RK_PA3 GPIO_ACTIVE_HIGH>;
+	vpcie3v3-supply = <&vcc_3v3>;
+	status = "okay";
+};
+
+&pinctrl {
+	gmac {
+		gmac1_rstn_l: gmac1-rstn-l {
+			rockchip,pins = <4 RK_PC2 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	leds {
+		lan_led: lan-led {
+			rockchip,pins = <4 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		wan_led: wan-led {
+			rockchip,pins = <4 RK_PC0 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		work_led: work-led {
+			rockchip,pins = <4 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	pcie {
+		rtl8111hs_isolateb_l: rtl8111hs-isolateb-l {
+			rockchip,pins = <4 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+	sdmmc {
+		sdmmc_pwren_l: sdmmc-pwren-l {
+			rockchip,pins = <4 RK_PA1 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		sdmmc_vol_ctrl_h: sdmmc-vol-ctrl-h {
+			rockchip,pins = <4 RK_PB6 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&pwm1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pwm1m0_pins>;
+	status = "okay";
+};
+
+&pwm2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pwm2m0_pins>;
+	status = "okay";
+};
+
+&saradc {
+	vref-supply = <&vcc_1v8>;
+	status = "okay";
+};
+
+&sdhci {
+	bus-width = <8>;
+	cap-mmc-highspeed;
+	mmc-hs200-1_8v;
+	non-removable;
+	vmmc-supply = <&vcc_3v3>;
+	vqmmc-supply = <&vcc_1v8>;
+	status = "okay";
+};
+
+&sdmmc {
+	bus-width = <4>;
+	cap-sd-highspeed;
+	disable-wp;
+	sd-uhs-sdr104;
+	vmmc-supply = <&vcc3v3_sd>;
+	vqmmc-supply = <&vccio_sd>;
+	status = "okay";
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0m0_xfer>;
+	status = "okay";
+};
+
+&usb2phy {
+	status = "okay";
+};
+
+&usb2phy_host {
+	phy-supply = <&vcc5v0_sys>;
+	status = "okay";
+};
+
+&usb_host0_ehci {
+	status = "okay";
+};
+
+&usb_host0_ohci {
+	status = "okay";
+};
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 1/2] dt-bindings: arm: rockchip: Add HINLINK H28K
From: Chukun Pan @ 2026-06-10 10:00 UTC (permalink / raw)
  To: Heiko Stuebner
  Cc: Rob Herring, Chukun Pan, Conor Dooley, Krzysztof Kozlowski,
	linux-arm-kernel, linux-rockchip, linux-kernel, devicetree
In-Reply-To: <20260610100006.366963-1-amadeus@jmu.edu.cn>

The HINLINK H28K (also known as LinkStar H28K) is a dual-gigabit SBC
based on the RK3528 SoC. Add devicetree binding documentation for it.

Link: https://wiki.seeedstudio.com/H28K_Datasheet/
Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
---
 Documentation/devicetree/bindings/arm/rockchip.yaml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/arm/rockchip.yaml b/Documentation/devicetree/bindings/arm/rockchip.yaml
index 1a9dde18626d..3952987a2c3c 100644
--- a/Documentation/devicetree/bindings/arm/rockchip.yaml
+++ b/Documentation/devicetree/bindings/arm/rockchip.yaml
@@ -724,6 +724,11 @@ properties:
           - const: hardkernel,odroid-m2
           - const: rockchip,rk3588s
 
+      - description: HINLINK H28K
+        items:
+          - const: hinlink,h28k
+          - const: rockchip,rk3528
+
       - description: HINLINK H66K / H68K
         items:
           - enum:
-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 0/2] arm64: dts: rockchip: Add HINLINK H28K
From: Chukun Pan @ 2026-06-10 10:00 UTC (permalink / raw)
  To: Heiko Stuebner
  Cc: Rob Herring, Chukun Pan, Conor Dooley, Krzysztof Kozlowski,
	linux-arm-kernel, linux-rockchip, linux-kernel, devicetree

The HINLINK H28K is a dual-gigabit SBC based on the RK3528 SoC.

There is a version that SeeedStudio distributes called LinkStar-H28K.
It's no different from the HINLINK H28K. The schematic can be found here:

https://www.hinlink.cn/wp-content/uploads/2024/03/20240428015024130824.pdf
https://files.seeedstudio.com/wiki/H28K/Open_source/H28K-SCH.zip

The U-Boot patch will be sent after the kernel device tree is merged.

Changes in v2:
- Add USB 2.0 support
- Remove MangoPi patches
- Improve commit messages

Chukun Pan (2):
  dt-bindings: arm: rockchip: Add HINLINK H28K
  arm64: dts: rockchip: Add HINLINK H28K

 .../devicetree/bindings/arm/rockchip.yaml     |   5 +
 arch/arm64/boot/dts/rockchip/Makefile         |   1 +
 .../boot/dts/rockchip/rk3528-hinlink-h28k.dts | 318 ++++++++++++++++++
 3 files changed, 324 insertions(+)
 create mode 100644 arch/arm64/boot/dts/rockchip/rk3528-hinlink-h28k.dts

-- 
2.34.1


^ permalink raw reply

* [PATCH v5 1/3] dt-bindings: dmaengine: Add support for ATCDMAC300 DMA engine
From: CL Wang @ 2026-06-10  9:57 UTC (permalink / raw)
  To: vkoul, Frank.Li, robh, krzk+dt, conor+dt, dmaengine
  Cc: devicetree, linux-kernel, tim609, cl634, Conor Dooley
In-Reply-To: <20260610095724.1980622-1-cl634@andestech.com>

Document devicetree bindings for Andes ATCDMAC300 DMA engine

ATCDMAC300 is the IP name, which is embedded in AndesCore-based
platforms or SoCs such as AE350 and Qilai.

Signed-off-by: CL Wang <cl634@andestech.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>

---
  Changes for v5:
    - No changes from v4

  Changes for v4:
    - Use items list format with descriptions for reg property
      as suggested by Conor Dooley

  Changes for v3:
    - Rename DT binding file from andestech,qilai-dma.yaml to
      andestech,ae350-dma.yaml.
    - Deprecate IP-core-based compatible usage and align with
      SoC/platform-based strings.
    - Dropped Acked-by tag from Conor Dooley due to the above change.
---
 .../bindings/dma/andestech,ae350-dma.yaml     | 67 +++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/andestech,ae350-dma.yaml

diff --git a/Documentation/devicetree/bindings/dma/andestech,ae350-dma.yaml b/Documentation/devicetree/bindings/dma/andestech,ae350-dma.yaml
new file mode 100644
index 000000000000..f040a2bf7d4b
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/andestech,ae350-dma.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/andestech,ae350-dma.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Andes ATCDMAC300 DMA Controller
+
+maintainers:
+  - CL Wang <cl634@andestech.com>
+
+allOf:
+  - $ref: dma-controller.yaml#
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - andestech,qilai-dma
+          - const: andestech,ae350-dma
+      - const: andestech,ae350-dma
+
+  reg:
+    minItems: 1
+    items:
+      - description: DMA controller register range
+      - description: cache control in IOCP controller
+
+  reg-names:
+    minItems: 1
+    items:
+      - const: dma
+      - const: iocp
+
+  interrupts:
+    maxItems: 1
+
+  "#dma-cells":
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - "#dma-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        dma-controller@f0c00000 {
+            compatible = "andestech,ae350-dma";
+            reg = <0x0 0xf0c00000 0x0 0x1000>,
+                  <0x0 0xe8000000 0x0 0x10>;
+            reg-names = "dma", "iocp";
+            interrupts = <10 IRQ_TYPE_LEVEL_HIGH>;
+            #dma-cells = <1>;
+        };
+    };
+...
-- 
2.34.1


^ permalink raw reply related

* [PATCH v5 0/3] dmaengine: atcdmac300: Add Andes ATCDMAC300 DMA driver
From: CL Wang @ 2026-06-10  9:57 UTC (permalink / raw)
  To: vkoul, Frank.Li, robh, krzk+dt, conor+dt, dmaengine
  Cc: devicetree, linux-kernel, tim609, cl634

This patch series adds support for the Andes ATCDMAC300 DMA controller,
a memory-to-memory and peripheral DMA controller that provides
scatter-gather, cyclic, and slave transfer capabilities.

The ATCDMAC300 IP is embedded in AndesCore-based platforms or SoCs
such as AE350 and Qilai.

Changes in v5:
  - Update copyright year to 2026
  - Remove redundant headers (init.h, iopoll.h, mod_devicetable.h)
  - Move atcdmac_init_iocp() before of_dma_controller_register() in probe
  - Change builtin_platform_driver() to module_platform_driver()
  - Implement .remove callback to support safe module unloading
  - Update Kconfig entry from bool to tristate
  - Add MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE macros

Changes in v4:
  - Use items list format with descriptions for reg property in DT binding
    as suggested by Conor Dooley
  - Re-add Acked-by from Conor Dooley for DT binding patch

Changes in v3:
  - Rename DT binding file from andestech,qilai-dma.yaml to
    andestech,ae350-dma.yaml
  - Deprecate IP-core-based compatible usage and align with
    SoC/platform-based strings
  - Dropped Acked-by from Conor Dooley due to the above binding change
  - Remove "andestech,atcdmac300" from of_device_id table
  - Replace deprecated tasklet with threaded IRQ using
    devm_request_threaded_irq() and IRQF_ONESHOT
  - Update locking from spin_lock_bh() to spin_lock_irqsave()
  - Use builtin_platform_driver() instead of module_platform_driver()
  - Remove "select DMATEST" from Kconfig
  - Add separate MAINTAINERS patch (patch 3/3)

Please kindly review.

CL Wang (3):
  dt-bindings: dmaengine: Add support for ATCDMAC300 DMA engine
  dmaengine: atcdmac300: Add driver for Andes ATCDMAC300 DMA controller
  MAINTAINERS: Add entry for Andes ATCDMAC300

 .../bindings/dma/andestech,ae350-dma.yaml     |   67 +
 MAINTAINERS                                   |    6 +
 drivers/dma/Kconfig                           |   11 +
 drivers/dma/Makefile                          |    1 +
 drivers/dma/atcdmac300.c                      | 1518 +++++++++++++++++
 drivers/dma/atcdmac300.h                      |  296 ++++
 6 files changed, 1899 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/andestech,ae350-dma.yaml
 create mode 100644 drivers/dma/atcdmac300.c
 create mode 100644 drivers/dma/atcdmac300.h

-- 
2.34.1


^ permalink raw reply

* Re: [PATCH v2] arm64: dts: qcom: sm8550: add SDHC4 controller node
From: William Bright @ 2026-06-10  9:59 UTC (permalink / raw)
  To: Vladimir Zapolskiy
  Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-arm-msm, devicetree, linux-kernel,
	Ram Boukobza, Tendai Makumire
In-Reply-To: <5a5cbf8f-07ef-419a-8d30-c1b0b2786312@linaro.org>

On Wed, Jun 10, 2026 at 11:21:53AM +0300, Vladimir Zapolskiy wrote:
> FWIW due to https://www.nxp.com/docs/en/data-sheet/IW416.pdf "10.7.1 VIO_SD
> DC characteristics" SDR104 speed mode is not supported by the module, thus
> the selection of the SDR50 speed mode on the host side sounds to be correct
> in your case.
> 
> In SDR50 speed mode gcc_sdcc4_apps_clk clock frequency should be exactly
> 100MHz, and since it differs, it has an impact during the tuning phase.
> 
> Definitely clk/qcom/gcc-sm8550.c says that the maximum supported frequency
> is 75MHz, the same is found in the downstream v5.15 kernel:
> 
> static const struct freq_tbl ftbl_gcc_sdcc4_apps_clk_src[] = {
>         F(400000, P_BI_TCXO, 12, 1, 4),
>         F(25000000, P_GCC_GPLL0_OUT_EVEN, 12, 0, 0),
>         F(37500000, P_GCC_GPLL0_OUT_EVEN, 8, 0, 0),
>         F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0),
>         { }
> };
> 
> Can you dump CAPS1 register value of SM8550 SDHC4? What does it say about
> SDR50 mode support and need for SDR50 mode tuning?
> 
> -- 
> Best wishes,
> Vladimir
CAPS0 and CAPS1 are below:
  sdhci_msm 8844000.mmc: CAPS0: 0x3029c8b2 CAPS1: 0x0000a08b
For CAPS1: 
  Bit 0 (SDR50 support) = 1
  Bit 13 (Use Tuning for SDR50) = 1
It looks to report that SDR50 is supported with tuning required.

Best regards,
Will

^ permalink raw reply

* RE: [PATCH 7/7] ASoC: dt-bindings: ES8389: Add members about HPF and clock
From: Zhang Yi @ 2026-06-10  9:58 UTC (permalink / raw)
  To: krzk
  Cc: alsa-devel, broonie, conor+dt, devicetree, krzk+dt, robh, tiwai,
	zhangyi
In-Reply-To: <20260609-vociferous-thick-lyrebird-d53eda@quoll>

> Please organize the patch documenting the ABI (DT bindings)
> before the patch using that ABI.
> See also: https://elixir.bootlin.com/linux/v6.14-rc6/source/Documentation/devicetree/bindings/submitting-patches.rst#L46

Thanks for the reminder

> > +  everest,mclk-from-sclk:
> > +    $ref: /schemas/types.yaml#/definitions/flag
> > +    description:
> > +      Indicates that SCLK is used as the internal clock.
> 
> And what happens with mclk in such case? Is it still wired?

Yes, setting mclk-from-sclk does not affect the MCLK connection.

> > +
> > +  everest,hpfl:
> > +    $ref: /schemas/types.yaml#/definitions/uint8
> > +    description:
> > +      the HPF value of ADCL.
> 
> Is HPF value in dB? If so, use proper unit suffix and proper units.

No, the values here correspond to the values in the registers.
The value is not in dB

> >          vddd-supply = <&vdd3v3>;
> >          vdda-supply = <&vdd3v3>;
> > +        everest,hpfl = [0a];
> 
> <0xa>? What did you want to say here?

I just wanted to give an example to show how to set the values of everest,hpfl to 0x0a.

^ permalink raw reply

* [PATCH v5 3/3] MAINTAINERS: Add entry for Andes ATCDMAC300
From: CL Wang @ 2026-06-10  9:57 UTC (permalink / raw)
  To: vkoul, Frank.Li, robh, krzk+dt, conor+dt, dmaengine
  Cc: devicetree, linux-kernel, tim609, cl634
In-Reply-To: <20260610095724.1980622-1-cl634@andestech.com>

Add a MAINTAINERS entry for the Andes ATCDMAC300 DMA engine driver and its
associated Device Tree bindings.

Signed-off-by: CL Wang <cl634@andestech.com>

---
  Changes for v5:
    - No changes from v4

  Changes for v4:
    - No changes from v3
---
 MAINTAINERS | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 2fb1c75afd16..0d17580a6d17 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1868,6 +1868,12 @@ S:	Supported
 F:	drivers/clk/analogbits/*
 F:	include/linux/clk/analogbits*
 
+ANDES ATCDMAC300 DMA DRIVER
+M:	CL Wang <cl634@andestech.com>
+S:	Supported
+F:	Documentation/devicetree/bindings/dma/andestech,ae350-dma.yaml
+F:	drivers/dma/atcdmac300*
+
 ANDES ATCSPI200 SPI DRIVER
 M:	CL Wang <cl634@andestech.com>
 S:	Supported
-- 
2.34.1


^ permalink raw reply related

* [PATCH v5 2/3] dmaengine: atcdmac300: Add driver for Andes ATCDMAC300 DMA controller
From: CL Wang @ 2026-06-10  9:57 UTC (permalink / raw)
  To: vkoul, Frank.Li, robh, krzk+dt, conor+dt, dmaengine
  Cc: devicetree, linux-kernel, tim609, cl634
In-Reply-To: <20260610095724.1980622-1-cl634@andestech.com>

This patch adds support for the Andes ATCDMAC300 DMA controller.

The ATCDMAC300 is a memory-to-memory and peripheral DMA controller
that provides scatter-gather, cyclic, and slave transfer capabilities.

Signed-off-by: CL Wang <cl634@andestech.com>

---
  Changes for v5:
    - Update copyright year to 2026
    - Remove redundant headers (init.h, iopoll.h, mod_devicetable.h)
    - Move atcdmac_init_iocp() before of_dma_controller_register() in probe
    - Change builtin_platform_driver() to module_platform_driver()
    - Implement .remove callback to support safe module unloading
    - Update Kconfig entry from bool to tristate
    - Add MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_LICENSE macros

  Changes for v4:
    - No changes from v3

  Changes for v3:
    - Remove "andestech,atcdmac300" from of_device_id
    - Replace deprecated tasklet with threaded IRQ using
      devm_request_threaded_irq() and IRQF_ONESHOT to handle bottom-half
      processing.
    - Update locking mechanism from spin_lock_bh() to spin_lock_irqsave()
    - Minor cleanups and correctness fixes
        - Initialize descriptor pointers (first = NULL) explicitly
        - Add missing headers (err.h, iopoll.h, log2.h, sprintf.h)
        - Remove unused code paths related to tasklets
        - Use builtin_platform_driver() instead of module_platform_driver()
        - Remove "select DMATEST" from Kconfig
---
 drivers/dma/Kconfig      |   11 +
 drivers/dma/Makefile     |    1 +
 drivers/dma/atcdmac300.c | 1518 ++++++++++++++++++++++++++++++++++++++
 drivers/dma/atcdmac300.h |  296 ++++++++
 4 files changed, 1826 insertions(+)
 create mode 100644 drivers/dma/atcdmac300.c
 create mode 100644 drivers/dma/atcdmac300.h

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index ae6a682c9f76..dabd2c8cc94f 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -100,6 +100,17 @@ config ARM_DMA350
 	help
 	  Enable support for the Arm DMA-350 controller.
 
+config ATCDMAC300
+	tristate "Andes ATCDMAC300 DMA support"
+	depends on ARCH_ANDES
+	depends on OF
+	select DMA_ENGINE
+	help
+	  Enable support for the Andes ATCDMAC300 DMA controller.
+	  Select Y or M if your platform includes an ATCDMAC300 device that
+	  requires DMA engine support. This driver supports DMA_SLAVE,
+	  DMA_MEMCPY, and DMA_CYCLIC transfer modes.
+
 config AT_HDMAC
 	tristate "Atmel AHB DMA support"
 	depends on ARCH_AT91 || COMPILE_TEST
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 14aa086629d5..c8fffb31efc4 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
 obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
 obj-$(CONFIG_APPLE_ADMAC) += apple-admac.o
 obj-$(CONFIG_ARM_DMA350) += arm-dma350.o
+obj-$(CONFIG_ATCDMAC300) += atcdmac300.o
 obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
 obj-$(CONFIG_AT_XDMAC) += at_xdmac.o
 obj-$(CONFIG_AXI_DMAC) += dma-axi-dmac.o
diff --git a/drivers/dma/atcdmac300.c b/drivers/dma/atcdmac300.c
new file mode 100644
index 000000000000..be3bb477bb3b
--- /dev/null
+++ b/drivers/dma/atcdmac300.c
@@ -0,0 +1,1518 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Andes ATCDMAC300 controller driver
+ *
+ * Copyright (C) 2026 Andes Technology Corporation
+ */
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/sprintf.h>
+#include <linux/regmap.h>
+#include "dmaengine.h"
+#include "atcdmac300.h"
+
+static int atcdmac_is_chan_enabled(struct atcdmac_chan *dmac_chan)
+{
+	struct atcdmac_dmac *dmac =
+		atcdmac_dev_to_dmac(dmac_chan->dma_chan.device);
+
+	return regmap_test_bits(dmac->regmap,
+				REG_CH_EN,
+				BIT(dmac_chan->chan_id));
+}
+
+static void atcdmac_enable_chan(struct atcdmac_chan *dmac_chan, bool enable)
+{
+	regmap_update_bits(dmac_chan->regmap, REG_CH_CTL_OFF, CHEN, enable);
+}
+
+static void atcdmac_abort_chan(struct atcdmac_chan *dmac_chan)
+{
+	regmap_write_bits(dmac_chan->dma_dev->regmap,
+			  REG_CH_ABT,
+			  BIT(dmac_chan->chan_id),
+			  BIT(dmac_chan->chan_id));
+}
+
+static dma_cookie_t atcdmac_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct atcdmac_chan *dmac_chan = atcdmac_chan_to_dmac_chan(tx->chan);
+	struct atcdmac_desc *desc = atcdmac_txd_to_dma_desc(tx);
+	dma_cookie_t cookie;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dmac_chan->lock, flags);
+	cookie = dma_cookie_assign(tx);
+	list_add_tail(&desc->desc_node, &dmac_chan->queue_list);
+	spin_unlock_irqrestore(&dmac_chan->lock, flags);
+
+	return cookie;
+}
+
+static struct atcdmac_desc *
+atcdmac_get_active_head(struct atcdmac_chan *dmac_chan)
+{
+	return list_first_entry(&dmac_chan->active_list,
+				struct atcdmac_desc,
+				desc_node);
+}
+
+static struct atcdmac_desc *atcdmac_alloc_desc(struct dma_chan *chan,
+					       gfp_t gfp_flags)
+{
+	struct atcdmac_dmac *dmac = atcdmac_dev_to_dmac(chan->device);
+	struct atcdmac_desc *desc;
+	dma_addr_t phys;
+
+	desc = dma_pool_zalloc(dmac->dma_desc_pool, gfp_flags, &phys);
+	if (desc) {
+		INIT_LIST_HEAD(&desc->tx_list);
+		dma_async_tx_descriptor_init(&desc->txd, chan);
+		desc->txd.flags = DMA_CTRL_ACK;
+		desc->txd.tx_submit = atcdmac_tx_submit;
+		desc->txd.phys = phys;
+	}
+
+	return desc;
+}
+
+static struct atcdmac_desc *atcdmac_get_desc(struct atcdmac_chan *dmac_chan)
+{
+	struct atcdmac_desc *ret = NULL;
+	struct atcdmac_desc *desc_next;
+	struct atcdmac_desc *desc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dmac_chan->lock, flags);
+	list_for_each_entry_safe(desc, desc_next,
+				 &dmac_chan->free_list,
+				 desc_node) {
+		if (async_tx_test_ack(&desc->txd)) {
+			list_del_init(&desc->desc_node);
+			ret = desc;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&dmac_chan->lock, flags);
+
+	if (!ret) {
+		ret = atcdmac_alloc_desc(&dmac_chan->dma_chan, GFP_ATOMIC);
+		if (ret) {
+			spin_lock_irqsave(&dmac_chan->lock, flags);
+			dmac_chan->descs_allocated++;
+			spin_unlock_irqrestore(&dmac_chan->lock, flags);
+		} else {
+			dev_warn(atcdmac_chan_to_dev(&dmac_chan->dma_chan),
+				 "not enough descriptors available\n");
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * atcdmac_put_desc_nolock - move a descriptor to the free list
+ * @dmac_chan: DMA channel we work on
+ * @desc: Head of the descriptor chain to be added to the free list
+ *
+ * This function does not use a lock to protect any linked lists in
+ * 'struct atcdmac_chan', so please remember to add a proper lock when
+ * calling the function.
+ */
+static void atcdmac_put_desc_nolock(struct atcdmac_chan *dmac_chan,
+				    struct atcdmac_desc *desc)
+{
+	struct atcdmac_desc *child, *tmp;
+
+	if (desc) {
+		list_for_each_entry_safe(child,
+					 tmp,
+					 &desc->tx_list,
+					 desc_node) {
+			list_del_init(&child->desc_node);
+			child->at = NULL;
+			child->num_sg = 0;
+			INIT_LIST_HEAD(&child->tx_list);
+			list_add_tail(&child->desc_node,
+				      &dmac_chan->free_list);
+		}
+
+		list_del_init(&desc->desc_node);
+		desc->at = NULL;
+		desc->num_sg = 0;
+		INIT_LIST_HEAD(&desc->tx_list);
+		list_add_tail(&desc->desc_node, &dmac_chan->free_list);
+	}
+}
+
+static void atcdmac_put_desc(struct atcdmac_chan *dmac_chan,
+			     struct atcdmac_desc *desc)
+{
+	unsigned long flags;
+
+	if (!desc) {
+		dev_err(atcdmac_chan_to_dev(&dmac_chan->dma_chan),
+			"A NULL descriptor was found.\n");
+		return;
+	}
+
+	spin_lock_irqsave(&dmac_chan->lock, flags);
+	atcdmac_put_desc_nolock(dmac_chan, desc);
+	spin_unlock_irqrestore(&dmac_chan->lock, flags);
+}
+
+static void atcdmac_show_desc(struct atcdmac_chan *dmac_chan,
+			      struct atcdmac_desc *desc)
+{
+	struct device *dev = atcdmac_chan_to_dev(&dmac_chan->dma_chan);
+
+	dev_dbg(dev, "Dump desc info of chan: %u\n", dmac_chan->chan_id);
+	dev_dbg(dev, "chan ctrl: 0x%08x\n", desc->regs.ctrl);
+	dev_dbg(dev, "trans size: 0x%08x\n", desc->regs.trans_size);
+	dev_dbg(dev, "src addr: hi:0x%08x lo:0x%08x\n",
+		desc->regs.src_addr_hi,
+		desc->regs.src_addr_lo);
+	dev_dbg(dev, "dst addr: hi:0x%08x lo:0x%08x\n",
+		desc->regs.dst_addr_hi,
+		desc->regs.dst_addr_lo);
+	dev_dbg(dev, "link addr: hi:0x%08x lo:0x%08x\n",
+		desc->regs.ll_ptr_hi,
+		desc->regs.ll_ptr_lo);
+}
+
+/**
+ * atcdmac_chain_desc - Chain a DMA descriptor into a linked-list
+ * @first: Pointer to the first descriptor in the chain
+ * @prev: Pointer to the previous descriptor in the chain
+ * @desc: The descriptor to be added to the chain
+ * @cyclic: Indicates if the transfer operates in cyclic mode
+ *
+ * This function appends a DMA descriptor (desc) to a linked-list of
+ * descriptors. If this is the first descriptor being added, it initializes
+ * the list and sets *first to point to desc. Otherwise, it links the
+ * new descriptor to the end of the list managed by *first.
+ *
+ * For non-cyclic descriptors, it updates the hardware linked list pointers
+ * (ll_ptr_lo and ll_ptr_hi) of the previous descriptor (*prev) to point to
+ * the physical address of the new descriptor.
+ *
+ * Finally, it adds the new descriptor to the list and updates *prev to
+ * point to the current descriptor (desc).
+ */
+static void atcdmac_chain_desc(struct atcdmac_desc **first,
+			       struct atcdmac_desc **prev,
+			       struct atcdmac_desc *desc,
+			       bool cyclic)
+{
+	if (!(*first)) {
+		*first = desc;
+		desc->at = &desc->tx_list;
+	} else {
+		if (!cyclic) {
+			(*prev)->regs.ll_ptr_lo =
+				lower_32_bits(desc->txd.phys);
+			(*prev)->regs.ll_ptr_hi =
+				upper_32_bits(desc->txd.phys);
+		}
+		list_add_tail(&desc->desc_node, &(*first)->tx_list);
+	}
+	*prev = desc;
+
+	desc->regs.ll_ptr_hi = 0;
+	desc->regs.ll_ptr_lo = 0;
+}
+
+/**
+ * atcdmac_start_transfer - Start the DMA engine with the provided descriptor
+ * @dmac_chan: The DMA channel to be started
+ * @first_desc: The first descriptor in the list to begin the transfer
+ *
+ * This function configures the DMA engine by programming the hardware
+ * registers with the information from the provided descriptor (first_desc).
+ * It then starts the DMA transfer for the specified channel (dmac_chan).
+ *
+ * The first_desc contains the initial configuration for the transfer,
+ * including source and destination addresses, transfer size, and any linked
+ * list pointers for subsequent descriptors in the chain.
+ */
+static void atcdmac_start_transfer(struct atcdmac_chan *dmac_chan,
+				   struct atcdmac_desc *first_desc)
+{
+	struct atcdmac_dmac *dmac = dmac_chan->dma_dev;
+	struct regmap *reg = dmac_chan->regmap;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dmac->lock, flags);
+	dmac->used_chan |= BIT(dmac_chan->chan_id);
+	spin_unlock_irqrestore(&dmac->lock, flags);
+
+	regmap_write(reg, REG_CH_CTL_OFF, first_desc->regs.ctrl);
+	regmap_write(reg, REG_CH_SIZE_OFF, first_desc->regs.trans_size);
+	regmap_write(reg, REG_CH_SRC_LOW_OFF, first_desc->regs.src_addr_lo);
+	regmap_write(reg, REG_CH_DST_LOW_OFF, first_desc->regs.dst_addr_lo);
+	regmap_write(reg, REG_CH_LLP_LOW_OFF, first_desc->regs.ll_ptr_lo);
+	regmap_write(reg, REG_CH_SRC_HIGH_OFF, first_desc->regs.src_addr_hi);
+	regmap_write(reg, REG_CH_DST_HIGH_OFF, first_desc->regs.dst_addr_hi);
+	regmap_write(reg, REG_CH_LLP_HIGH_OFF, first_desc->regs.ll_ptr_hi);
+	atcdmac_enable_chan(dmac_chan, 1);
+}
+
+/**
+ * atcdmac_start_next_trans - Retrieve and initiate the next DMA transfer
+ * @dmac_chan: Pointer to the DMA channel structure
+ *
+ * In non-cyclic mode, if active_list is empty, the function moves
+ * descriptors from queue_list (if available) to active_list and starts
+ * the transfer. If both lists are empty, it marks the channel as unused.
+ * If there are already active descriptors in active_list, the function
+ * retrieves the next DMA descriptor from it and starts the transfer.
+ *
+ * In cyclic mode, the function retrieves the next DMA descriptor
+ * from the linked list of tx_list to maintain continuous transfers.
+ */
+static void atcdmac_start_next_trans(struct atcdmac_chan *dmac_chan)
+{
+	struct atcdmac_desc *next_tx = NULL;
+	struct atcdmac_desc *dma_desc;
+
+	if (dmac_chan->cyclic) {
+		/* Get the next DMA descriptor from tx_list. */
+		dma_desc = atcdmac_get_active_head(dmac_chan);
+		dma_desc->at = dma_desc->at->next;
+		if ((uintptr_t)dma_desc->at == (uintptr_t)&dma_desc->tx_list)
+			next_tx = list_entry(dma_desc->at,
+					     struct atcdmac_desc,
+					     tx_list);
+		else
+			next_tx = list_entry(dma_desc->at,
+					     struct atcdmac_desc,
+					     desc_node);
+	} else {
+		if (list_empty(&dmac_chan->active_list)) {
+			if (!list_empty(&dmac_chan->queue_list)) {
+				list_splice_init(&dmac_chan->queue_list,
+						 &dmac_chan->active_list);
+				next_tx = atcdmac_get_active_head(dmac_chan);
+			}
+		} else {
+			next_tx = atcdmac_get_active_head(dmac_chan);
+		}
+	}
+
+	if (next_tx) {
+		dmac_chan->chan_used = 1;
+		atcdmac_start_transfer(dmac_chan, next_tx);
+	} else {
+		dmac_chan->chan_used = 0;
+	}
+}
+
+static void atcdmac_run_tx_complete_actions(struct atcdmac_desc *desc,
+					    enum dmaengine_tx_result result)
+{
+	struct dma_async_tx_descriptor *txd = &desc->txd;
+	struct dmaengine_result res;
+
+	res.result = result;
+	dma_cookie_complete(txd);
+	dma_descriptor_unmap(txd);
+	dmaengine_desc_get_callback_invoke(txd, &res);
+	dma_run_dependencies(txd);
+}
+
+/**
+ * atcdmac_advance_work - Process the completed transaction and move to the
+ *                        next descriptor
+ * @dmac_chan: DMA channel where the transaction has completed
+ *
+ * This function is responsible for performing necessary operations after
+ * a DMA transaction completes successfully. It retrieves the next descriptor
+ * from the active list, initiates its transfer, and communicates the result
+ * of the completed transaction to the DMA engine framework.
+ */
+static void atcdmac_advance_work(struct atcdmac_chan *dmac_chan)
+{
+	struct atcdmac_dmac *dmac =
+		atcdmac_dev_to_dmac(dmac_chan->dma_chan.device);
+	struct atcdmac_desc *desc_next, *dma_desc, *desc;
+	struct dmaengine_result res;
+	LIST_HEAD(completed);
+	unsigned long flags;
+	unsigned short stop;
+
+	spin_lock_irqsave(&dmac_chan->lock, flags);
+	if (list_empty(&dmac_chan->active_list)) {
+		spin_unlock_irqrestore(&dmac_chan->lock, flags);
+		return;
+	}
+
+	dma_desc = atcdmac_get_active_head(dmac_chan);
+	stop = READ_ONCE(dmac->stop_mask) & BIT(dmac_chan->chan_id);
+	if (dmac_chan->cyclic) {
+		if (!stop)
+			atcdmac_start_next_trans(dmac_chan);
+
+		spin_unlock_irqrestore(&dmac_chan->lock, flags);
+		res.result = DMA_TRANS_NOERROR;
+		dmaengine_desc_get_callback_invoke(&dma_desc->txd, &res);
+	} else {
+		if (list_is_singular(&dmac_chan->active_list)) {
+			list_splice_init(&dmac_chan->active_list, &completed);
+			list_splice_init(&dmac_chan->queue_list,
+					 &dmac_chan->active_list);
+		} else {
+			list_move_tail(&dma_desc->desc_node, &completed);
+		}
+
+		if (!stop)
+			atcdmac_start_next_trans(dmac_chan);
+
+		spin_unlock_irqrestore(&dmac_chan->lock, flags);
+
+		list_for_each_entry_safe(desc,
+					 desc_next,
+					 &completed,
+					 desc_node) {
+			atcdmac_run_tx_complete_actions(desc,
+							DMA_TRANS_NOERROR);
+			atcdmac_put_desc(dmac_chan, desc);
+		}
+	}
+}
+
+/**
+ * atcdmac_handle_error - Handle errors reported by the DMA controller
+ * @dmac_chan: DMA channel where the error occurred
+ *
+ * This function is invoked when the DMA controller detects an error during a
+ * transaction. This function ensures that the DMA channel can recover from
+ * errors while reporting issues to aid in debugging. It prevents the DMA
+ * controller from operating on potentially invalid memory regions and
+ * attempts to maintain system stability.
+ */
+static void atcdmac_handle_error(struct atcdmac_chan *dmac_chan)
+{
+	struct device *dev = atcdmac_chan_to_dev(&dmac_chan->dma_chan);
+	struct atcdmac_dmac *dmac =
+		atcdmac_dev_to_dmac(dmac_chan->dma_chan.device);
+	struct atcdmac_desc *bad_desc;
+	unsigned long flags;
+	unsigned short stop;
+
+	spin_lock_irqsave(&dmac_chan->lock, flags);
+
+	/*
+	 * If the active list is empty, the descriptor has already been
+	 * handled by another function (e.g., atcdmac_terminate_all()).
+	 * Therefore, no further action is needed.
+	 */
+	if (!list_empty(&dmac_chan->active_list)) {
+		/*
+		 * Identify the problematic descriptor at the head of the
+		 * active list and remove it from the list for further
+		 * processing.
+		 */
+		bad_desc = atcdmac_get_active_head(dmac_chan);
+		list_del_init(&bad_desc->desc_node);
+
+		/*
+		 * Transfer any pending descriptors from the queue list to the
+		 * active list, allowing them to be processed in subsequent
+		 * operations.
+		 */
+		list_splice_init(&dmac_chan->queue_list,
+				 dmac_chan->active_list.prev);
+
+		stop = READ_ONCE(dmac->stop_mask) & BIT(dmac_chan->chan_id);
+		if (!list_empty(&dmac_chan->active_list) && stop == 0)
+			atcdmac_start_transfer(dmac_chan, atcdmac_get_active_head(dmac_chan));
+		else
+			dmac_chan->chan_used = 0;
+
+		spin_unlock_irqrestore(&dmac_chan->lock, flags);
+
+		/*
+		 * Show the detailed information of the bad descriptor and
+		 * return "DMA_TRANS_ABORTED" to the DMA engine framework.
+		 */
+		dev_err(dev, "DMA transaction failed, possible DMA desc error\n");
+		atcdmac_show_desc(dmac_chan, bad_desc);
+		atcdmac_run_tx_complete_actions(bad_desc, DMA_TRANS_ABORTED);
+
+		atcdmac_put_desc(dmac_chan, bad_desc);
+
+		return;
+	}
+
+	spin_unlock_irqrestore(&dmac_chan->lock, flags);
+}
+
+static irqreturn_t atcdmac_irq_thread(int irq, void *dev_id)
+{
+	struct atcdmac_dmac *dmac = dev_id;
+	struct atcdmac_chan *dmac_chan;
+	int i;
+	bool handled = false;
+
+	for (i = 0; i < dmac->num_ch; i++) {
+		dmac_chan = &dmac->chan[i];
+
+		if (test_and_clear_bit(ATCDMAC_STA_TC, &dmac_chan->status)) {
+			atcdmac_advance_work(dmac_chan);
+			handled = true;
+		}
+
+		if (test_and_clear_bit(ATCDMAC_STA_ERR, &dmac_chan->status)) {
+			atcdmac_handle_error(dmac_chan);
+			handled = true;
+		}
+
+		/*
+		 * ATCDMAC_STA_ABORT only occurs when the DMA channel is
+		 * terminated or freed, and all descriptors and callbacks
+		 * have already been processed. Therefore, no additional
+		 * handling is required.
+		 */
+		if (test_and_clear_bit(ATCDMAC_STA_ABORT, &dmac_chan->status))
+			handled = true;
+	}
+
+	return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static irqreturn_t atcdmac_interrupt(int irq, void *dev_id)
+{
+	struct atcdmac_dmac *dmac = dev_id;
+	struct atcdmac_chan *dmac_chan;
+	unsigned int status;
+	unsigned int int_ch;
+	int ret = IRQ_NONE;
+	int i;
+
+	regmap_read(dmac->regmap, REG_INT_STA, &status);
+	int_ch = READ_ONCE(dmac->used_chan) & DMA_INT_ALL(status);
+
+	while (int_ch) {
+		spin_lock(&dmac->lock);
+		dmac->used_chan = READ_ONCE(dmac->used_chan) & ~int_ch;
+		spin_unlock(&dmac->lock);
+		regmap_write(dmac->regmap, REG_INT_STA, DMA_INT_CLR(int_ch));
+
+		for (i = 0; i < dmac->num_ch; i++) {
+			if (int_ch & BIT(i)) {
+				int_ch &= ~BIT(i);
+				dmac_chan = &dmac->chan[i];
+
+				if (status & DMA_TC(i))
+					set_bit(ATCDMAC_STA_TC,
+						&dmac_chan->status);
+
+				if (status & DMA_ERR(i))
+					set_bit(ATCDMAC_STA_ERR,
+						&dmac_chan->status);
+
+				if (status & DMA_ABT(i))
+					set_bit(ATCDMAC_STA_ABORT,
+						&dmac_chan->status);
+
+				ret = IRQ_WAKE_THREAD;
+			}
+			if (!int_ch)
+				break;
+		}
+
+		regmap_read(dmac->regmap, REG_INT_STA, &status);
+		int_ch = READ_ONCE(dmac->used_chan) & DMA_INT_ALL(status);
+	}
+
+	return ret;
+}
+
+/**
+ * atcdmac_issue_pending - Trigger the execution of queued DMA transactions
+ * @chan: DMA channel on which to issue pending transactions
+ *
+ * This function checks if the DMA channel is currently idle and if there are
+ * descriptors waiting in the queue_list. If the channel is idle and the
+ * queue_list is not empty, it moves the first queued descriptor to the active
+ * list and starts the DMA transfer by calling atcdmac_start_transfer().
+ */
+static void atcdmac_issue_pending(struct dma_chan *chan)
+{
+	struct atcdmac_chan *dmac_chan = atcdmac_chan_to_dmac_chan(chan);
+	struct atcdmac_dmac *dmac =
+		atcdmac_dev_to_dmac(dmac_chan->dma_chan.device);
+	unsigned long flags;
+	unsigned short stop;
+
+	spin_lock_irqsave(&dmac_chan->lock, flags);
+	stop = READ_ONCE(dmac->stop_mask) & BIT(dmac_chan->chan_id);
+	if (dmac_chan->chan_used == 0 && stop == 0 &&
+	    !list_empty(&dmac_chan->queue_list)) {
+		dmac_chan->chan_used = 1;
+		list_move(dmac_chan->queue_list.next, &dmac_chan->active_list);
+		atcdmac_start_transfer(dmac_chan,
+				       atcdmac_get_active_head(dmac_chan));
+	}
+
+	spin_unlock_irqrestore(&dmac_chan->lock, flags);
+}
+
+static unsigned char atcdmac_map_buswidth(enum dma_slave_buswidth addr_width)
+{
+	switch (addr_width) {
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+		return 0;
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		return 1;
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		return 2;
+	case DMA_SLAVE_BUSWIDTH_8_BYTES:
+		return 3;
+	case DMA_SLAVE_BUSWIDTH_16_BYTES:
+		return 4;
+	case DMA_SLAVE_BUSWIDTH_32_BYTES:
+		return 5;
+	default:
+		return 0;
+	}
+}
+
+static unsigned int atcdmac_map_tran_width(dma_addr_t src,
+					   dma_addr_t dst,
+					   size_t len,
+					   unsigned int max_align_bytes)
+{
+	unsigned int align = src | dst | len | max_align_bytes;
+	unsigned int width;
+
+	if (!(align & 0x1F))
+		width = WIDTH_32_BYTES;
+	else if (!(align & 0xF))
+		width = WIDTH_16_BYTES;
+	else if (!(align & 0x7))
+		width = WIDTH_8_BYTES;
+	else if (!(align & 0x3))
+		width = WIDTH_4_BYTES;
+	else if (!(align & 0x1))
+		width = WIDTH_2_BYTES;
+	else
+		width = WIDTH_1_BYTE;
+
+	return width;
+}
+
+/**
+ * atcdmac_convert_burst - Convert burst size to a power of two index
+ * @burst_size: Actual burst size in bytes
+ *
+ * This function converts a burst size (e.g., 1, 2, 4, 8...) into the
+ * corresponding hardware-encoded value, which represents the index of
+ * the power of two.
+ *
+ * Return: The zero-based power-of-two index corresponding to @burst_size.
+ *
+ * Example:
+ * If burst_size is 8 (binary 1000), the most significant bit is at position
+ * 4, so the function returns 3 (i.e., 4 - 1).
+ */
+static unsigned char atcdmac_convert_burst(unsigned int burst_size)
+{
+	return fls(burst_size) - 1;
+}
+
+static struct atcdmac_desc *
+atcdmac_build_desc(struct atcdmac_chan *dmac_chan,
+		   dma_addr_t src,
+		   dma_addr_t dst,
+		   unsigned int ctrl,
+		   unsigned int trans_size,
+		   unsigned int num_sg)
+{
+	struct atcdmac_desc *desc;
+
+	desc = atcdmac_get_desc(dmac_chan);
+	if (!desc)
+		return NULL;
+
+	desc->regs.src_addr_lo = lower_32_bits(src);
+	desc->regs.src_addr_hi = upper_32_bits(src);
+	desc->regs.dst_addr_lo = lower_32_bits(dst);
+	desc->regs.dst_addr_hi = upper_32_bits(dst);
+	desc->regs.ctrl = ctrl;
+	desc->regs.trans_size = trans_size;
+	desc->num_sg = num_sg;
+
+	return desc;
+}
+
+/**
+ * atcdmac_prep_dma_memcpy - Prepare a DMA memcpy operation for the specified
+ *                           channel
+ * @chan: DMA channel to configure for the operation
+ * @dst: Physical destination address for the transfer
+ * @src: Physical source address for the transfer
+ * @len: Size of the data to transfer, in bytes
+ * @flags: Status flags for the transfer descriptor
+ *
+ * This function sets up a DMA memcpy operation to transfer data from the
+ * specified source address to the destination address. It returns a DMA
+ * descriptor that represents the configured transaction.
+ */
+static struct dma_async_tx_descriptor *
+atcdmac_prep_dma_memcpy(struct dma_chan *chan,
+			dma_addr_t dst,
+			dma_addr_t src,
+			size_t len,
+			unsigned long flags)
+{
+	struct atcdmac_chan *dmac_chan = atcdmac_chan_to_dmac_chan(chan);
+	struct atcdmac_dmac *dmac = atcdmac_dev_to_dmac(chan->device);
+	struct atcdmac_desc *desc;
+	unsigned int src_width;
+	unsigned int dst_width;
+	unsigned int ctrl;
+	unsigned char src_max_burst;
+
+	if (unlikely(!len)) {
+		dev_warn(atcdmac_chan_to_dev(chan),
+			 "Failed to prepare DMA operation: len is zero\n");
+		return NULL;
+	}
+
+	src_max_burst =
+		atcdmac_convert_burst((unsigned int)SRC_BURST_SIZE_1024);
+	src_width = atcdmac_map_tran_width(src,
+					   dst,
+					   len,
+					   1 << dmac->data_width);
+	dst_width = src_width;
+	ctrl = SRC_BURST_SIZE(src_max_burst) |
+	       SRC_ADDR_MODE_INCR |
+	       DST_ADDR_MODE_INCR |
+	       DST_WIDTH(dst_width) |
+	       SRC_WIDTH(src_width);
+
+	desc = atcdmac_build_desc(dmac_chan, src, dst, ctrl,
+				  len >> src_width, 1);
+	if (!desc)
+		goto err_desc_get;
+
+	return &desc->txd;
+
+err_desc_get:
+	dev_warn(atcdmac_chan_to_dev(chan), "Failed to allocate descriptor\n");
+	return NULL;
+}
+
+static int atcdmac_get_slave_cfg(struct dma_slave_config *sconfig,
+				 enum dma_transfer_direction dir,
+				 struct atcdmac_slave_cfg *cfg)
+{
+	if (dir == DMA_MEM_TO_DEV) {
+		cfg->reg         = sconfig->dst_addr;
+		cfg->burst_bytes = sconfig->dst_addr_width * sconfig->dst_maxburst;
+		cfg->dev_width   = sconfig->dst_addr_width;
+		cfg->width_src   = atcdmac_map_buswidth(sconfig->src_addr_width);
+		cfg->width_dst   = atcdmac_map_buswidth(sconfig->dst_addr_width);
+	} else if (dir == DMA_DEV_TO_MEM) {
+		cfg->reg         = sconfig->src_addr;
+		cfg->burst_bytes = sconfig->src_addr_width * sconfig->src_maxburst;
+		cfg->dev_width   = sconfig->src_addr_width;
+		cfg->width_src   = atcdmac_map_buswidth(sconfig->src_addr_width);
+		cfg->width_dst   = atcdmac_map_buswidth(sconfig->dst_addr_width);
+	} else {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static struct atcdmac_desc *
+atcdmac_build_slave_desc(struct atcdmac_chan *dmac_chan,
+			 const struct atcdmac_slave_cfg *cfg,
+			 dma_addr_t mem, unsigned int len,
+			 enum dma_transfer_direction dir,
+			 unsigned int data_width, unsigned int num_desc)
+{
+	unsigned int   width_cal;
+	unsigned short burst_size;
+	unsigned int   ctrl;
+	dma_addr_t     src, dst;
+
+	width_cal = atcdmac_map_tran_width(mem, cfg->reg, len,
+					   (1 << data_width) | cfg->burst_bytes);
+	if (dir == DMA_MEM_TO_DEV) {
+		if (cfg->burst_bytes < (1 << width_cal)) {
+			burst_size = cfg->burst_bytes;
+			width_cal  = WIDTH_1_BYTE;
+		} else {
+			burst_size = cfg->burst_bytes / (1 << width_cal);
+		}
+		ctrl = SRC_ADDR_MODE_INCR | DST_ADDR_MODE_FIXED |
+		       DST_HS | DST_REQ(dmac_chan->req_num) |
+		       SRC_WIDTH(width_cal) | DST_WIDTH(cfg->width_dst) |
+		       SRC_BURST_SIZE(ilog2(burst_size));
+		src = mem;
+		dst = cfg->reg;
+	} else {
+		burst_size = cfg->burst_bytes / cfg->dev_width;
+		ctrl = SRC_ADDR_MODE_FIXED | DST_ADDR_MODE_INCR |
+		       SRC_HS | SRC_REQ(dmac_chan->req_num) |
+		       SRC_WIDTH(cfg->width_src) | DST_WIDTH(width_cal) |
+		       SRC_BURST_SIZE(ilog2(burst_size));
+		src      = cfg->reg;
+		dst      = mem;
+		width_cal = cfg->width_src;
+	}
+	return atcdmac_build_desc(dmac_chan, src, dst, ctrl,
+				  len >> width_cal, num_desc);
+}
+
+/**
+ * atcdmac_prep_device_sg - Prepare descriptors for memory/device DMA
+ *                          transactions
+ * @chan: DMA channel to configure for the operation
+ * @sgl: Scatter-gather list representing the memory regions to transfer
+ * @sg_len: Number of entries in the scatter-gather list
+ * @direction: Direction of the DMA transfer
+ * @flags: Status flags for the transfer descriptor
+ * @context: transaction context (ignored)
+ *
+ * This function prepares a DMA transaction by setting up the required
+ * descriptors based on the provided scatter-gather list and parameters.
+ * It supports memory-to-device and device-to-memory DMA transfers.
+ */
+static struct dma_async_tx_descriptor *
+atcdmac_prep_device_sg(struct dma_chan *chan,
+		       struct scatterlist *sgl,
+		       unsigned int sg_len,
+		       enum dma_transfer_direction direction,
+		       unsigned long flags,
+		       void *context)
+{
+	struct atcdmac_chan *dmac_chan = atcdmac_chan_to_dmac_chan(chan);
+	struct atcdmac_dmac *dmac = atcdmac_dev_to_dmac(chan->device);
+	struct atcdmac_slave_cfg cfg;
+	struct atcdmac_desc *first = NULL;
+	struct atcdmac_desc *prev = NULL;
+	struct scatterlist *sg;
+	unsigned int i;
+
+	if (unlikely(!sg_len)) {
+		dev_warn(atcdmac_chan_to_dev(chan), "sg_len is zero\n");
+		return NULL;
+	}
+
+	if (atcdmac_get_slave_cfg(&dmac_chan->dma_sconfig, direction, &cfg)) {
+		dev_err(atcdmac_chan_to_dev(chan),
+			"Invalid transfer direction %d\n", direction);
+		return NULL;
+	}
+
+	for_each_sg(sgl, sg, sg_len, i) {
+		struct atcdmac_desc *desc;
+		dma_addr_t mem = sg_dma_address(sg);
+		unsigned int len = sg_dma_len(sg);
+
+		if (unlikely(!len)) {
+			dev_err(atcdmac_chan_to_dev(chan),
+				"sg(%u) data len is zero\n", i);
+			goto err;
+		}
+
+		desc = atcdmac_build_slave_desc(dmac_chan, &cfg, mem, len,
+						direction, dmac->data_width,
+						sg_len);
+		if (!desc)
+			goto err_desc_get;
+
+		atcdmac_chain_desc(&first, &prev, desc, false);
+	}
+
+	first->txd.cookie = -EBUSY;
+	first->txd.flags = flags;
+
+	return &first->txd;
+
+err_desc_get:
+	dev_warn(atcdmac_chan_to_dev(chan), "Failed to allocate descriptor\n");
+	if (first)
+		first->num_sg = i;
+
+err:
+	if (first)
+		atcdmac_put_desc(dmac_chan, first);
+	return NULL;
+}
+
+static struct dma_async_tx_descriptor *
+atcdmac_prep_dma_cyclic(struct dma_chan *chan,
+			dma_addr_t buf_addr,
+			size_t buf_len,
+			size_t period_len,
+			enum dma_transfer_direction direction,
+			unsigned long flags)
+{
+	struct atcdmac_chan *dmac_chan = atcdmac_chan_to_dmac_chan(chan);
+	struct atcdmac_dmac *dmac = atcdmac_dev_to_dmac(chan->device);
+	struct atcdmac_slave_cfg cfg;
+	struct atcdmac_desc *first = NULL;
+	struct atcdmac_desc *prev = NULL;
+	unsigned int num_periods;
+	unsigned int period;
+
+	if (period_len == 0 || buf_len == 0) {
+		dev_warn(atcdmac_chan_to_dev(chan),
+			 "invalid cyclic params buf_len=%zu period_len=%zu\n",
+			 buf_len, period_len);
+		return NULL;
+	}
+
+	if (atcdmac_get_slave_cfg(&dmac_chan->dma_sconfig, direction, &cfg)) {
+		dev_err(atcdmac_chan_to_dev(chan),
+			"Invalid transfer direction %d\n", direction);
+		return NULL;
+	}
+
+	num_periods = (buf_len + period_len - 1) / period_len;
+
+	for (period = 0; period < buf_len; period += period_len) {
+		struct atcdmac_desc *desc;
+		dma_addr_t mem = buf_addr + period;
+		unsigned int len = min_t(unsigned int, period_len,
+					 buf_len - period);
+
+		desc = atcdmac_build_slave_desc(dmac_chan, &cfg, mem, len,
+						direction, dmac->data_width,
+						num_periods);
+		if (!desc)
+			goto err_desc_get;
+		atcdmac_chain_desc(&first, &prev, desc, true);
+	}
+
+	first->txd.flags = flags;
+	dmac_chan->cyclic = true;
+
+	return &first->txd;
+
+err_desc_get:
+	dev_warn(atcdmac_chan_to_dev(chan), "Failed to allocate descriptor\n");
+	if (first)
+		atcdmac_put_desc(dmac_chan, first);
+
+	return NULL;
+}
+
+static int atcdmac_set_device_config(struct dma_chan *chan,
+				     struct dma_slave_config *sconfig)
+{
+	struct atcdmac_chan *dmac_chan = atcdmac_chan_to_dmac_chan(chan);
+
+	/* Check if this chan is configured for device transfers */
+	if (!dmac_chan->dev_chan)
+		return -EINVAL;
+
+	/* Must be powers of two according to ATCDMAC300 spec */
+	if (!is_power_of_2(sconfig->src_maxburst) ||
+	    !is_power_of_2(sconfig->dst_maxburst) ||
+	    !is_power_of_2(sconfig->src_addr_width) ||
+	    !is_power_of_2(sconfig->dst_addr_width))
+		return -EINVAL;
+
+	memcpy(&dmac_chan->dma_sconfig, sconfig, sizeof(*sconfig));
+
+	return 0;
+}
+
+static int atcdmac_terminate_all(struct dma_chan *chan)
+{
+	struct atcdmac_chan *dmac_chan = atcdmac_chan_to_dmac_chan(chan);
+	struct atcdmac_desc *desc_cur, *desc_next;
+	LIST_HEAD(list);
+	unsigned long flags;
+	unsigned int val;
+	int ret;
+
+	spin_lock_irqsave(&dmac_chan->lock, flags);
+	atcdmac_abort_chan(dmac_chan);
+	atcdmac_enable_chan(dmac_chan, 0);
+	ret = regmap_read_poll_timeout_atomic(dmac_chan->dma_dev->regmap,
+					      REG_CH_EN,
+					      val,
+					      !(val & BIT(dmac_chan->chan_id)),
+					      10,
+					      ATCDMAC_CHAN_TIMEOUT_US);
+	if (ret)
+		dev_err(atcdmac_chan_to_dev(chan),
+			"Timed out waiting for channel to disable\n");
+
+	list_splice_init(&dmac_chan->queue_list,  &list);
+	list_splice_init(&dmac_chan->active_list, &list);
+	dmac_chan->chan_used = 0;
+	spin_unlock_irqrestore(&dmac_chan->lock, flags);
+
+	list_for_each_entry_safe(desc_cur, desc_next, &list, desc_node) {
+		atcdmac_run_tx_complete_actions(desc_cur, DMA_TRANS_ABORTED);
+		atcdmac_put_desc(dmac_chan, desc_cur);
+	}
+
+	return ret;
+}
+
+static enum dma_status atcdmac_get_tx_status(struct dma_chan *chan,
+					     dma_cookie_t cookie,
+					     struct dma_tx_state *state)
+{
+	return dma_cookie_status(chan, cookie, state);
+}
+
+static int atcdmac_wait_chan_idle(struct atcdmac_dmac *dmac,
+				  unsigned short chan_mask,
+				  unsigned int timeout_us)
+{
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read_poll_timeout(dmac->regmap,
+				       REG_CH_EN,
+				       val,
+				       !(val & chan_mask),
+				       50,
+				       timeout_us);
+	if (ret)
+		dev_err(dmac->dma_device.dev,
+			"Timeout waiting for device ready %d\n", ret);
+
+	return ret;
+}
+
+/**
+ * atcdmac_alloc_chan_resources - Allocate resources for a DMA channel
+ * @chan: The DMA channel for which resources are being allocated
+ *
+ * This function sets up and allocates the necessary resources for the
+ * specified DMA channel (chan). It ensures the channel is prepared to
+ * handle DMA requests from clients by allocating descriptors and any other
+ * required resources.
+ *
+ * Return: The number of descriptors successfully allocated, or a negative
+ *         error code on failure.
+ */
+static int atcdmac_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct atcdmac_chan *dmac_chan = atcdmac_chan_to_dmac_chan(chan);
+	struct atcdmac_dmac *dmac = atcdmac_dev_to_dmac(chan->device);
+	struct atcdmac_desc *desc;
+	int i;
+
+	if (atcdmac_is_chan_enabled(dmac_chan)) {
+		dev_err(atcdmac_chan_to_dev(chan),
+			"DMA channel is not in an idle state\n");
+		return -EBUSY;
+	}
+
+	if (!list_empty(&dmac_chan->free_list))
+		return dmac_chan->descs_allocated;
+
+	/*
+	 * Spin-lock protection is not necessary during DMA channel
+	 * initialization, as the channel is not yet in use at this stage,
+	 * and the shared resources are not accessed by other threads.
+	 */
+	for (i = 0; i < ATCDMAC_DESC_PER_CHAN; i++) {
+		desc = atcdmac_alloc_desc(chan, GFP_KERNEL);
+		if (!desc) {
+			dev_warn(dmac->dma_device.dev,
+				 "Insufficient descriptors: only %d descriptors available\n",
+				 i);
+			break;
+		}
+		list_add_tail(&desc->desc_node, &dmac_chan->free_list);
+	}
+	dmac_chan->descs_allocated = i;
+	dmac_chan->cyclic = false;
+	dma_cookie_init(chan);
+	spin_lock_irq(&dmac->lock);
+	dmac->stop_mask &= ~BIT(dmac_chan->chan_id);
+	spin_unlock_irq(&dmac->lock);
+
+	return dmac_chan->descs_allocated;
+}
+
+/**
+ * atcdmac_free_chan_resources - Release a DMA channel's resources
+ * @chan: The DMA channel to release
+ *
+ * This function ensures that any remaining DMA descriptors in the
+ * active_list and queue_list are properly reclaimed. It releases all
+ * resources associated with the specified DMA channel and resets the
+ * channel's management structures to their initial states.
+ */
+static void atcdmac_free_chan_resources(struct dma_chan *chan)
+{
+	struct atcdmac_chan *dmac_chan = atcdmac_chan_to_dmac_chan(chan);
+	struct atcdmac_dmac *dmac = atcdmac_dev_to_dmac(chan->device);
+	struct atcdmac_desc *desc_next, *desc;
+	unsigned long flags;
+
+	WARN_ON_ONCE(atcdmac_is_chan_enabled(dmac_chan));
+
+	spin_lock_irq(&dmac->lock);
+	dmac->stop_mask |= BIT(dmac_chan->chan_id);
+	spin_unlock_irq(&dmac->lock);
+
+	atcdmac_terminate_all(chan);
+
+	spin_lock_irqsave(&dmac_chan->lock, flags);
+	list_for_each_entry_safe(desc,
+				 desc_next,
+				 &dmac_chan->free_list,
+				 desc_node) {
+		list_del(&desc->desc_node);
+		dma_pool_free(dmac->dma_desc_pool, desc, desc->txd.phys);
+	}
+
+	INIT_LIST_HEAD(&dmac_chan->free_list);
+	dmac_chan->descs_allocated = 0;
+	dmac_chan->status = 0;
+	dmac_chan->chan_used = 0;
+	dmac_chan->dev_chan = 0;
+	spin_unlock_irqrestore(&dmac_chan->lock, flags);
+}
+
+static bool atcdmac_filter_chan(struct dma_chan *chan, void *dma_dev)
+{
+	if (dma_dev == chan->device->dev)
+		return true;
+
+	return false;
+}
+
+static struct dma_chan *atcdmac_dma_xlate_handler(struct of_phandle_args *dmac,
+						  struct of_dma *of_dma)
+{
+	struct platform_device *dmac_pdev;
+	struct atcdmac_chan *dmac_chan;
+	struct dma_chan *chan;
+	dma_cap_mask_t mask;
+
+	dmac_pdev = of_find_device_by_node(dmac->np);
+	if (!dmac_pdev)
+		return NULL;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	chan = dma_request_channel(mask, atcdmac_filter_chan, &dmac_pdev->dev);
+	put_device(&dmac_pdev->dev);
+
+	if (!chan)
+		return NULL;
+
+	dmac_chan = atcdmac_chan_to_dmac_chan(chan);
+	dmac_chan->dev_chan = true;
+	dmac_chan->req_num = dmac->args[0] & 0xff;
+
+	return chan;
+}
+
+/**
+ * atcdmac_reset_and_wait_chan_idle - Reset the DMA controller and wait for
+ *                                    all channels to become idle
+ * @dmac: Pointer to the DMA controller structure
+ *
+ * This function performs a reset of the DMA controller and ensures that all
+ * DMA channels are disabled.
+ */
+static int atcdmac_reset_and_wait_chan_idle(struct atcdmac_dmac *dmac)
+{
+	regmap_update_bits(dmac->regmap, REG_CTL, DMAC_RESET, 1);
+	msleep(20);
+	regmap_update_bits(dmac->regmap, REG_CTL, DMAC_RESET, 0);
+
+	return atcdmac_wait_chan_idle(dmac,
+				      BIT(dmac->num_ch) - 1,
+				      ATCDMAC_CHAN_TIMEOUT_US);
+}
+
+static int atcdmac_restore_iocp(struct atcdmac_dmac *dmac)
+{
+	if (!dmac->regmap_iocp)
+		return 0;
+
+	return regmap_write(dmac->regmap_iocp,
+			    0,
+			    IOCP_CACHE_DMAC0_AW |
+			    IOCP_CACHE_DMAC0_AR |
+			    IOCP_CACHE_DMAC1_AW |
+			    IOCP_CACHE_DMAC1_AR);
+}
+
+static int atcdmac_init_iocp(struct platform_device *pdev,
+			     struct atcdmac_dmac *dmac)
+{
+	const struct regmap_config iocp_regmap_config = {
+		.name = "iocp",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.pad_bits = 0,
+		.max_register = 0,
+		.cache_type = REGCACHE_NONE,
+	};
+	struct resource *res;
+	void __iomem *regs;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iocp");
+	if (!res) {
+		dmac->regmap_iocp = NULL;
+		return 0;
+	}
+
+	regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(regs)) {
+		dmac->regmap_iocp = NULL;
+		return dev_err_probe(&pdev->dev, PTR_ERR(regs),
+				     "Failed to create ioremap for IOCP\n");
+	}
+
+	dmac->regmap_iocp = devm_regmap_init_mmio(&pdev->dev,
+						  regs,
+						  &iocp_regmap_config);
+	if (IS_ERR(dmac->regmap_iocp)) {
+		int ret = PTR_ERR(dmac->regmap_iocp);
+
+		dmac->regmap_iocp = NULL;
+		return dev_err_probe(&pdev->dev, ret,
+				     "Failed to create regmap for IOCP\n");
+	}
+
+	return atcdmac_restore_iocp(dmac);
+}
+
+static void atcdmac_init_dma_device(struct platform_device *pdev,
+				    struct atcdmac_dmac *dmac)
+{
+	struct dma_device *device = &dmac->dma_device;
+
+	device->device_alloc_chan_resources = atcdmac_alloc_chan_resources;
+	device->device_free_chan_resources = atcdmac_free_chan_resources;
+	device->device_tx_status = atcdmac_get_tx_status;
+	device->device_issue_pending = atcdmac_issue_pending;
+	device->device_prep_dma_memcpy = atcdmac_prep_dma_memcpy;
+	device->device_prep_slave_sg = atcdmac_prep_device_sg;
+	device->device_config = atcdmac_set_device_config;
+	device->device_terminate_all = atcdmac_terminate_all;
+	device->device_prep_dma_cyclic = atcdmac_prep_dma_cyclic;
+
+	device->dev = &pdev->dev;
+	device->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+				  BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+				  BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+	device->dst_addr_widths = device->src_addr_widths;
+	device->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+	device->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
+
+	dma_cap_set(DMA_SLAVE, device->cap_mask);
+	dma_cap_set(DMA_MEMCPY, device->cap_mask);
+	dma_cap_set(DMA_CYCLIC, device->cap_mask);
+}
+
+static int atcdmac_init_channels(struct platform_device *pdev,
+				 struct atcdmac_dmac *dmac)
+{
+	struct regmap_config chan_regmap_config = {
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.pad_bits = 0,
+		.cache_type = REGCACHE_NONE,
+		.max_register = REG_CH_LLP_HIGH_OFF,
+	};
+	struct atcdmac_chan *dmac_chan;
+	int ret;
+	int i;
+
+	INIT_LIST_HEAD(&dmac->dma_device.channels);
+
+	for (i = 0; i < dmac->num_ch; i++) {
+		char *regmap_name = kasprintf(GFP_KERNEL, "chan%d", i);
+
+		if (!regmap_name)
+			return -ENOMEM;
+
+		dmac_chan = &dmac->chan[i];
+		chan_regmap_config.name = regmap_name;
+		dmac_chan->regmap =
+			devm_regmap_init_mmio(&pdev->dev,
+					      dmac->regs + REG_CH_OFF(i),
+					      &chan_regmap_config);
+		kfree(regmap_name);
+
+		if (IS_ERR(dmac_chan->regmap)) {
+			ret = PTR_ERR(dmac_chan->regmap);
+			dev_err_probe(&pdev->dev, ret,
+				      "Failed to create regmap for DMA chan%d\n",
+				      i);
+			return ret;
+		}
+
+		spin_lock_init(&dmac_chan->lock);
+		dmac_chan->dma_chan.device = &dmac->dma_device;
+		dmac_chan->dma_dev = dmac;
+		dmac_chan->chan_id = i;
+		dmac_chan->chan_used = 0;
+
+		INIT_LIST_HEAD(&dmac_chan->active_list);
+		INIT_LIST_HEAD(&dmac_chan->queue_list);
+		INIT_LIST_HEAD(&dmac_chan->free_list);
+
+		list_add_tail(&dmac_chan->dma_chan.device_node,
+			      &dmac->dma_device.channels);
+		dma_cookie_init(&dmac_chan->dma_chan);
+	}
+
+	return 0;
+}
+
+static int atcdmac_init_desc_pool(struct platform_device *pdev,
+				  struct atcdmac_dmac *dmac)
+{
+	dmac->dma_desc_pool = dmam_pool_create(dev_name(&pdev->dev),
+					       &pdev->dev,
+					       sizeof(struct atcdmac_desc),
+					       64,
+					       4096);
+	if (!dmac->dma_desc_pool)
+		return dev_err_probe(&pdev->dev, -ENOMEM,
+				     "Failed to create memory pool for DMA descriptors\n");
+	return 0;
+}
+
+static int atcdmac_init_irq(struct platform_device *pdev,
+			    struct atcdmac_dmac *dmac)
+{
+	int irq = platform_get_irq(pdev, 0);
+
+	if (irq < 0)
+		return irq;
+
+	return devm_request_threaded_irq(&pdev->dev,
+					 irq,
+					 atcdmac_interrupt,
+					 atcdmac_irq_thread,
+					 IRQF_SHARED | IRQF_ONESHOT,
+					 dev_name(&pdev->dev),
+					 dmac);
+}
+
+static int atcdmac_init_ioremap_and_regmap(struct platform_device *pdev,
+					   struct atcdmac_dmac **out_dmac)
+{
+	const struct regmap_config dmac_regmap_config = {
+		.name = dev_name(&pdev->dev),
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.pad_bits = 0,
+		.cache_type = REGCACHE_NONE,
+		.max_register = REG_CH_EN,
+	};
+	struct atcdmac_dmac *dmac;
+	struct regmap *regmap;
+	void __iomem *regs;
+	size_t size;
+	unsigned int val;
+	int ret = 0;
+	unsigned char num_ch;
+
+	regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(regs))
+		return dev_err_probe(&pdev->dev, PTR_ERR(regs),
+				     "Failed to ioremap I/O resource\n");
+
+	regmap = devm_regmap_init_mmio(&pdev->dev, regs, &dmac_regmap_config);
+	if (IS_ERR(regmap))
+		return dev_err_probe(&pdev->dev, PTR_ERR(regmap),
+				     "Failed to create regmap for I/O\n");
+
+	regmap_read(regmap, REG_CFG, &val);
+	num_ch = val & CH_NUM;
+	size = sizeof(*dmac) + num_ch * sizeof(struct atcdmac_chan);
+	dmac = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+	if (!dmac)
+		return -ENOMEM;
+
+	dmac->regmap = regmap;
+	dmac->num_ch = num_ch;
+	dmac->regs = regs;
+
+	/*
+	 * Adjust the AXI bus data width (from the DMAC Configuration
+	 * Register) to align with the transfer width encoding (in the
+	 * Channel n Control Register). For example, an AXI width of 0
+	 * (32-bit) corresponds to a transfer width of 2 (word transfer).
+	 */
+	dmac->data_width = FIELD_GET(DATA_WIDTH, val) + 2;
+	spin_lock_init(&dmac->lock);
+
+	platform_set_drvdata(pdev, dmac);
+	if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)))
+		ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret,
+				     "Failed to set DMA mask\n");
+
+	*out_dmac = dmac;
+
+	return ret;
+}
+
+static int atcdmac_probe(struct platform_device *pdev)
+{
+	struct atcdmac_dmac *dmac;
+	int ret;
+
+	ret = atcdmac_init_ioremap_and_regmap(pdev, &dmac);
+	if (ret)
+		return ret;
+
+	ret = atcdmac_reset_and_wait_chan_idle(dmac);
+	if (ret)
+		return ret;
+
+	ret = atcdmac_init_desc_pool(pdev, dmac);
+	if (ret)
+		return ret;
+
+	ret = atcdmac_init_channels(pdev, dmac);
+	if (ret)
+		return ret;
+
+	atcdmac_init_dma_device(pdev, dmac);
+
+	ret = dma_async_device_register(&dmac->dma_device);
+	if (ret)
+		return ret;
+
+	ret = atcdmac_init_irq(pdev, dmac);
+	if (ret)
+		goto err_dma_async_register;
+
+	ret = atcdmac_init_iocp(pdev, dmac);
+	if (ret)
+		goto err_dma_async_register;
+
+	ret = of_dma_controller_register(pdev->dev.of_node,
+					 atcdmac_dma_xlate_handler,
+					 dmac);
+	if (ret)
+		goto err_dma_async_register;
+
+	return 0;
+
+err_dma_async_register:
+	dma_async_device_unregister(&dmac->dma_device);
+
+	return ret;
+}
+
+static int atcdmac_resume(struct device *dev)
+{
+	struct dma_chan *chan;
+	struct dma_chan *chan_next;
+	struct atcdmac_dmac *dmac = dev_get_drvdata(dev);
+	struct atcdmac_chan *dmac_chan;
+	unsigned long flags;
+	int ret;
+
+	ret = atcdmac_reset_and_wait_chan_idle(dmac);
+	if (ret)
+		return ret;
+
+	ret = atcdmac_restore_iocp(dmac);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&dmac->lock, flags);
+	dmac->stop_mask = 0;
+	spin_unlock_irqrestore(&dmac->lock, flags);
+	list_for_each_entry_safe(chan,
+				 chan_next,
+				 &dmac->dma_device.channels,
+				 device_node) {
+		dmac_chan = atcdmac_chan_to_dmac_chan(chan);
+		spin_lock_irqsave(&dmac_chan->lock, flags);
+		atcdmac_start_next_trans(dmac_chan);
+		spin_unlock_irqrestore(&dmac_chan->lock, flags);
+	}
+
+	return 0;
+}
+
+static int atcdmac_suspend(struct device *dev)
+{
+	struct atcdmac_dmac *dmac = dev_get_drvdata(dev);
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dmac->lock, flags);
+	dmac->stop_mask = BIT(dmac->num_ch) - 1;
+	spin_unlock_irqrestore(&dmac->lock, flags);
+	ret = atcdmac_wait_chan_idle(dmac,
+				     dmac->stop_mask,
+				     ATCDMAC_CHAN_TIMEOUT_US * dmac->num_ch);
+
+	return ret;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(atcdmac_pm_ops,
+				atcdmac_suspend,
+				atcdmac_resume);
+
+static void atcdmac_remove(struct platform_device *pdev)
+{
+	struct atcdmac_dmac *dmac = platform_get_drvdata(pdev);
+
+	/*
+	 * Quiesce the controller first so the devm-managed IRQ, which is
+	 * released only after remove() returns, has nothing left to service.
+	 */
+	atcdmac_reset_and_wait_chan_idle(dmac);
+	of_dma_controller_free(pdev->dev.of_node);
+	dma_async_device_unregister(&dmac->dma_device);
+}
+
+static const struct of_device_id atcdmac_dt_ids[] = {
+	{ .compatible = "andestech,ae350-dma", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, atcdmac_dt_ids);
+
+static struct platform_driver atcdmac_driver = {
+	.probe = atcdmac_probe,
+	.remove = atcdmac_remove,
+	.driver = {
+		.name = "atcdmac300",
+		.of_match_table = atcdmac_dt_ids,
+		.pm = pm_sleep_ptr(&atcdmac_pm_ops),
+	},
+};
+module_platform_driver(atcdmac_driver);
+
+MODULE_AUTHOR("CL Wang <cl634@andestech.com>");
+MODULE_DESCRIPTION("Andes ATCDMAC300 controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/atcdmac300.h b/drivers/dma/atcdmac300.h
new file mode 100644
index 000000000000..3aee00e02130
--- /dev/null
+++ b/drivers/dma/atcdmac300.h
@@ -0,0 +1,296 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Header file for Andes ATCDMAC300 DMA controller driver
+ *
+ * Copyright (C) 2025 Andes Technology Corporation
+ */
+#ifndef ATCDMAC300_H
+#define ATCDMAC300_H
+
+#include <linux/bitfield.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+/*
+ * Register Map Definitions for ATCDMAC300 DMA Controller
+ *
+ * These macros define the offsets and bit masks for various registers
+ * within the ATCDMAC300 DMA controller.
+ */
+
+/* Global DMAC Registers */
+#define REG_CFG			0x10
+#define CH_NUM			GENMASK(3, 0)
+#define DATA_WIDTH		GENMASK(25, 24)
+
+#define REG_CTL			0x20
+#define DMAC_RESET		BIT(0)
+
+#define REG_CH_ABT		0x24
+
+#define REG_INT_STA		0x30
+#define TC_OFFSET		16
+#define ABT_OFFSET		8
+#define ERR_OFFSET		0
+#define DMA_TC(val)		BIT(TC_OFFSET + (val))
+#define DMA_ABT(val)		BIT(ABT_OFFSET + (val))
+#define DMA_ERR(val)		BIT(ERR_OFFSET + (val))
+#define DMA_TC_FIELD		GENMASK(TC_OFFSET + 7, TC_OFFSET)
+#define DMA_ABT_FIELD		GENMASK(ABT_OFFSET + 7, ABT_OFFSET)
+#define DMA_ERR_FIELD		GENMASK(ERR_OFFSET + 7, ERR_OFFSET)
+#define DMA_INT_ALL(val)	(FIELD_GET(DMA_TC_FIELD, (val)) |	\
+				 FIELD_GET(DMA_ABT_FIELD, (val)) |	\
+				 FIELD_GET(DMA_ERR_FIELD, (val)))
+#define DMA_INT_CLR(val)	(FIELD_PREP(DMA_TC_FIELD, (val)) |	\
+				 FIELD_PREP(DMA_ABT_FIELD, (val)) |	\
+				 FIELD_PREP(DMA_ERR_FIELD, (val)))
+
+#define REG_CH_EN		0x34
+
+#define REG_CH_OFF(ch)		((ch) * 0x20 + 0x40)
+#define REG_CH_CTL_OFF		0x0
+#define REG_CH_SIZE_OFF		0x4
+#define REG_CH_SRC_LOW_OFF	0x8
+#define REG_CH_SRC_HIGH_OFF	0xC
+#define REG_CH_DST_LOW_OFF	0x10
+#define REG_CH_DST_HIGH_OFF	0x14
+#define REG_CH_LLP_LOW_OFF	0x18
+#define REG_CH_LLP_HIGH_OFF	0x1C
+
+#define SRC_BURST_SIZE_1	BIT(0)
+#define SRC_BURST_SIZE_2	BIT(1)
+#define SRC_BURST_SIZE_4	BIT(2)
+#define SRC_BURST_SIZE_8	BIT(3)
+#define SRC_BURST_SIZE_16	BIT(4)
+#define SRC_BURST_SIZE_32	BIT(5)
+#define SRC_BURST_SIZE_64	BIT(6)
+#define SRC_BURST_SIZE_128	BIT(7)
+#define SRC_BURST_SIZE_256	BIT(8)
+#define SRC_BURST_SIZE_512	BIT(9)
+#define SRC_BURST_SIZE_1024	BIT(10)
+#define SRC_BURST_SIZE_MASK	GENMASK(27, 24)
+#define SRC_BURST_SIZE(size)	FIELD_PREP(SRC_BURST_SIZE_MASK, size)
+
+#define WIDTH_1_BYTE		0x0
+#define WIDTH_2_BYTES		0x1
+#define WIDTH_4_BYTES		0x2
+#define WIDTH_8_BYTES		0x3
+#define WIDTH_16_BYTES		0x4
+#define WIDTH_32_BYTES		0x5
+#define SRC_WIDTH_MASK		GENMASK(23, 21)
+#define SRC_WIDTH(width)	FIELD_PREP(SRC_WIDTH_MASK, width)
+#define SRC_WIDTH_GET(val)	FIELD_GET(SRC_WIDTH_MASK, val)
+#define DST_WIDTH_MASK		GENMASK(20, 18)
+#define DST_WIDTH(width)	FIELD_PREP(DST_WIDTH_MASK, width)
+#define DST_WIDTH_GET(val)	FIELD_GET(DST_WIDTH_MASK, val)
+
+/* DMA handshake mode */
+#define SRC_HS			BIT(17)
+#define DST_HS			BIT(16)
+
+/* Address control */
+#define SRC_ADDR_CTRL_MASK	GENMASK(15, 14)
+#define SRC_ADDR_MODE_INCR	FIELD_PREP(SRC_ADDR_CTRL_MASK, 0x0)
+#define SRC_ADDR_MODE_DECR	FIELD_PREP(SRC_ADDR_CTRL_MASK, 0x1)
+#define SRC_ADDR_MODE_FIXED	FIELD_PREP(SRC_ADDR_CTRL_MASK, 0x2)
+#define DST_ADDR_CTRL_MASK	GENMASK(13, 12)
+#define DST_ADDR_MODE_INCR	FIELD_PREP(DST_ADDR_CTRL_MASK, 0x0)
+#define DST_ADDR_MODE_DECR	FIELD_PREP(DST_ADDR_CTRL_MASK, 0x1)
+#define DST_ADDR_MODE_FIXED	FIELD_PREP(DST_ADDR_CTRL_MASK, 0x2)
+
+/* DMA request select */
+#define SRC_REQ_SEL_MASK	GENMASK(11, 8)
+#define SRC_REQ(req_num)	FIELD_PREP(SRC_REQ_SEL_MASK, (req_num))
+#define DST_REQ_SEL_MASK	GENMASK(7, 4)
+#define DST_REQ(req_num)	FIELD_PREP(DST_REQ_SEL_MASK, (req_num))
+
+/* Channel abort interrupt mask */
+#define INT_ABT_MASK		BIT(3)
+/* Channel error interrupt mask */
+#define INT_ERR_MASK		BIT(2)
+/* Channel terminal count interrupt mask */
+#define INT_TC_MASK		BIT(1)
+
+/* Channel Enable */
+#define CHEN			BIT(0)
+
+#define IOCP_CACHE_DMAC0_AW	GENMASK(3, 0)
+#define IOCP_CACHE_DMAC0_AR	GENMASK(7, 4)
+#define IOCP_CACHE_DMAC1_AW	GENMASK(11, 8)
+#define IOCP_CACHE_DMAC1_AR	GENMASK(15, 12)
+
+#define ATCDMAC_DESC_PER_CHAN	64
+#define ATCDMAC_CHAN_TIMEOUT_US	100000
+/*
+ * Status for bottom-half processing.
+ */
+enum dma_sta {
+	ATCDMAC_STA_TC = 0,
+	ATCDMAC_STA_ERR,
+	ATCDMAC_STA_ABORT
+};
+
+/**
+ * struct atcdmac_regs - Hardware DMA descriptor registers.
+ *
+ * @ctrl: Channel Control Register.
+ * @trans_size: Transfer Size Register.
+ * @src_addr_lo: Source Address Register (low 32-bit).
+ * @src_addr_hi: Source Address Register (high 32-bit).
+ * @dst_addr_lo: Destination Address Register (low 32-bit).
+ * @dst_addr_hi: Destination Address Register (high 32-bit).
+ * @ll_ptr_lo: Linked List Pointer Register (low 32-bit).
+ * @ll_ptr_hi: Linked List Pointer Register (high 32-bit).
+ */
+struct atcdmac_regs {
+	unsigned int ctrl;
+	unsigned int trans_size;
+	unsigned int src_addr_lo;
+	unsigned int src_addr_hi;
+	unsigned int dst_addr_lo;
+	unsigned int dst_addr_hi;
+	unsigned int ll_ptr_lo;
+	unsigned int ll_ptr_hi;
+};
+
+/**
+ * struct atcdmac_desc - Internal representation of a DMA descriptor.
+ *
+ * @regs: Hardware registers for this descriptor.
+ * @txd: DMA transaction descriptor for dmaengine framework.
+ * @desc_node: Node for linking descriptors in software lists.
+ * @tx_list: List head for chaining multiple descriptors in a single transfer.
+ * @at: Next descriptor in a cyclic transfer (for internal use).
+ * @num_sg: Number of scatterlist entries this descriptor handles.
+ */
+struct atcdmac_desc {
+	struct atcdmac_regs		regs;
+	struct dma_async_tx_descriptor	txd;
+	struct list_head		desc_node;
+	struct list_head		tx_list;
+	struct list_head		*at;
+	unsigned short			num_sg;
+};
+
+/**
+ * struct atcdmac_chan - Private data for each DMA channel.
+ *
+ * @dma_chan: Common DMA engine channel object members
+ * @dma_dev: Pointer to the struct atcdmac_dmac
+ * @dma_sconfig: DMA transfer config for device-to-memory or memory-to-device
+ * @regmap: Regmap for the DMA channel register
+ * @active_list: List of descriptors being processed by the DMA engine
+ * @queue_list: List of descriptors ready to be submitted to the DMA engine
+ * @free_list: List of descriptors available for reuse by the channel
+ * @lock: Protects data access in atomic operations
+ * @status: Transmit status info, shared between top-half and threaded IRQ
+ * @descs_allocated: Number of descriptors currently allocated in the pool
+ * @chan_id: Channel ID number
+ * @req_num: Request number assigned to the channel
+ * @chan_used: Indicates whether the DMA channel is currently in use
+ * @cyclic: Indicates if the transfer operates in cyclic mode
+ * @dev_chan: Indicates if the DMA channel transfers data between device
+ *            and memory
+ */
+struct atcdmac_chan {
+	struct dma_chan		dma_chan;
+	struct atcdmac_dmac	*dma_dev;
+	struct dma_slave_config	dma_sconfig;
+	struct regmap		*regmap;
+
+	struct list_head	active_list;
+	struct list_head	queue_list;
+	struct list_head	free_list;
+
+	spinlock_t		lock;		/* protects active_list, queue_list, status */
+	unsigned long		status;
+	unsigned short		descs_allocated;
+	unsigned char		chan_id;
+	unsigned char		req_num;
+	bool			chan_used;
+	bool			cyclic;
+	bool			dev_chan;
+};
+
+/**
+ * struct atcdmac_dmac - Representation of the ATCDMAC300 DMA controller
+ * @dma_device: DMA device object for integration with DMA engine framework
+ * @regmap: Regmap for main DMA controller registers
+ * @regmap_iocp: Regmap for IOCP registers
+ * @dma_desc_pool: DMA descriptor pool for allocating and managing descriptors
+ * @regs: Memory-mapped base address of the main DMA controller registers
+ * @lock: Protects data access in atomic operations
+ * @used_chan: Bitmask of DMA channels actively issuing DMA descriptors
+ * @stop_mask: Stops the DMA channel after the current transaction completes
+ * @data_width: Max data bus width supported by the DMA controller
+ * @num_ch: Total number of DMA channels available in the controller
+ * @chan: Array of DMA channel structures, sized by the controller's channel
+ *        count
+ */
+struct atcdmac_dmac {
+	struct dma_device	dma_device;
+	struct regmap		*regmap;
+	struct regmap		*regmap_iocp;
+	struct dma_pool		*dma_desc_pool;
+	void __iomem		*regs;
+	spinlock_t		lock;		/* protects used_chan, stop_mask */
+	unsigned short		used_chan;
+	unsigned short		stop_mask;
+	unsigned char		data_width;
+	unsigned char		num_ch;
+	struct atcdmac_chan	chan[] __counted_by(num_ch);
+};
+
+/**
+ * struct atcdmac_slave_cfg - Pre-computed per-transfer slave DMA configuration
+ *
+ * @reg: Device-side register address (source for DEV_TO_MEM, destination
+ *       for MEM_TO_DEV).
+ * @burst_bytes: Burst size in bytes (addr_width * maxburst from
+ *               dma_slave_config). Used to derive the hardware burst-count
+ *               field and to select the optimal transfer width.
+ * @dev_width: Device-side bus width in bytes (src_addr_width for DEV_TO_MEM,
+ *             dst_addr_width for MEM_TO_DEV). Used to convert @burst_bytes
+ *             into a hardware burst-count for the device side.
+ * @width_src: Hardware-encoded source transfer width (output of
+ *             atcdmac_map_buswidth() on the source bus width).
+ * @width_dst: Hardware-encoded destination transfer width (output of
+ *             atcdmac_map_buswidth() on the destination bus width).
+ */
+struct atcdmac_slave_cfg {
+	dma_addr_t	reg;
+	unsigned int	burst_bytes;
+	unsigned int	dev_width;
+	unsigned int	width_src;
+	unsigned int	width_dst;
+};
+
+/*
+ * Helper functions to convert between dmaengine and internal structures.
+ */
+static inline struct atcdmac_desc *
+atcdmac_txd_to_dma_desc(struct dma_async_tx_descriptor *txd)
+{
+	return container_of(txd, struct atcdmac_desc, txd);
+}
+
+static inline struct atcdmac_chan *
+atcdmac_chan_to_dmac_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct atcdmac_chan, dma_chan);
+}
+
+static inline struct atcdmac_dmac *atcdmac_dev_to_dmac(struct dma_device *dev)
+{
+	return container_of(dev, struct atcdmac_dmac, dma_device);
+}
+
+static inline struct device *atcdmac_chan_to_dev(struct dma_chan *chan)
+{
+	return &chan->dev->device;
+}
+
+#endif
-- 
2.34.1


^ permalink raw reply related

* [net-next v1 4/6] net: stmmac: Add NCSI VLAN setting
From: Minda Chen @ 2026-06-10  7:24 UTC (permalink / raw)
  To: Andrew Lunn, David S . Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Jose Abreu, Maxime Coquelin, Russell King,
	Giuseppe Cavallaro, Alexandre Torgue, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, netdev
  Cc: linux-kernel, linux-stm32, devicetree, Minda Chen
In-Reply-To: <20260610072420.64699-1-minda.chen@starfivetech.com>

Add NCSI vlan setting while in NCSI cases,
The code process is like NCSI mode in faraday
ftgmac100.c.

Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
---
 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index b6af53783883..b8ce04d71d08 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -6866,6 +6866,9 @@ static int stmmac_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid
 	bool is_double = false;
 	int ret;
 
+	if (priv->plat->use_ncsi)
+		return ncsi_vlan_rx_add_vid(ndev, proto, vid);
+
 	ret = pm_runtime_resume_and_get(priv->device);
 	if (ret < 0)
 		return ret;
@@ -6908,6 +6911,9 @@ static int stmmac_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vi
 	bool is_double = false;
 	int ret;
 
+	if (priv->plat->use_ncsi)
+		return ncsi_vlan_rx_kill_vid(ndev, proto, vid);
+
 	ret = pm_runtime_resume_and_get(priv->device);
 	if (ret < 0)
 		return ret;
@@ -7943,6 +7949,9 @@ static int __stmmac_dvr_probe(struct device *device,
 	ndev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
 			     NETDEV_XDP_ACT_XSK_ZEROCOPY;
 
+	if (priv->plat->use_ncsi)
+		ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
 	ret = stmmac_tc_init(priv, priv);
 	if (!ret) {
 		ndev->hw_features |= NETIF_F_HW_TC;
-- 
2.17.1


^ permalink raw reply related

* Re: [PATCH 2/2] regulator: qcom-refgen: add support for the IPQ9650 SoC
From: Kathiravan Thirumoorthy @ 2026-06-10  9:49 UTC (permalink / raw)
  To: Konrad Dybcio, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Konrad Dybcio
  Cc: linux-arm-msm, linux-kernel, devicetree
In-Reply-To: <b7c5441c-de69-4a1b-9121-3d15bc24612e@oss.qualcomm.com>


On 6/9/2026 7:18 PM, Konrad Dybcio wrote:
> On 6/2/26 11:22 AM, Kathiravan Thirumoorthy wrote:
>> IPQ9650 SoC has 2 REFGEN blocks providing the reference current to the
>> PCIe and USB, UNIPHY PHYs. For the other SoCs, clocks for this block is
>> enabled on power up but that's not the case for IPQ9650 and we have to
>> enable those clocks explicitly to bring up the PHYs properly.
>>
>> As per the design team, REFGEN block provides the reference current.
>> Hence marked the regulator type as REGULATOR_CURRENT.
>>
>> Signed-off-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
>> ---
> I'm slightly confused. Is there no register programming needed?

Yeah, no programming is needed. Similar to Kodiak, PHYs in IPQ9650 does 
the HW votes. No SW vote is needed.

> Can we at least retrieve the running state in .is_enabled() by reading
> some value?

Based on the discussion with IP team, I can use the REFGEN_STATUS 
register. Let me update the is_enabled() to query this information.

>
> Konrad

^ permalink raw reply

* [PATCH v2] dt-bindings: phy: sc8280xp-qmp-pcie: Disallow bifurcation register on Purwa
From: Konrad Dybcio @ 2026-06-10  9:45 UTC (permalink / raw)
  To: Vinod Koul, Neil Armstrong, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley
  Cc: linux-arm-msm, linux-phy, devicetree, linux-kernel,
	Krzysztof Kozlowski, YijieYang, Konrad Dybcio

From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Neither of the two Gen4x4 PHYs found on Purwa supports bifurcation.
The PHY is however physically laid out as if it were to, since there
are two separate ports (A/B).

Split out a new if-then block to un-require the bifurcation register
handle to squash this warning:

purwa-iot-evk.dtb: phy@1bd4000 (qcom,x1p42100-qmp-gen4x4-pcie-phy): 'qcom,4ln-config-sel' is a required property

Fixes: 2e1ffd4c1805 ("dt-bindings: phy: qcom,qmp-pcie: Add X1P42100 PCIe Gen4x4 PHY")
Reported-by: Rob Herring <robh@kernel.org>
Closes: https://lore.kernel.org/linux-arm-msm/176857775469.1631885.16133311938753588148.robh@kernel.org/
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
Changes in v2:
- Rebase
- Don't drop the mention of the warning, as it now actually exists in
  the tree..
- Link to v1: https://lore.kernel.org/r/20260119-topic-purwa_phy_shutup_warning-v1-1-997a692b31c6@oss.qualcomm.com

To: Vinod Koul <vkoul@kernel.org>
To: Neil Armstrong <neil.armstrong@linaro.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Cc: Krzysztof Kozlowski <krzk@kernel.org>
Cc: linux-arm-msm@vger.kernel.org
Cc: linux-phy@lists.infradead.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
 .../devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml
index 3a35120a77ec..431e8cb5df84 100644
--- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-pcie-phy.yaml
@@ -136,13 +136,22 @@ allOf:
           items:
             - description: port a
             - description: port b
-      required:
-        - qcom,4ln-config-sel
     else:
       properties:
         reg:
           maxItems: 1
 
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,sc8280xp-qmp-gen3x4-pcie-phy
+              - qcom,x1e80100-qmp-gen4x4-pcie-phy
+    then:
+      required:
+        - qcom,4ln-config-sel
+
   - if:
       properties:
         compatible:

---
base-commit: 49e02880ec0a8c378e811bc9d85da188d7c6204c
change-id: 20260119-topic-purwa_phy_shutup_warning-891e8aab29f2

Best regards,
--  
Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>


^ permalink raw reply related

* Re: [PATCH RFC v4 1/6] dt-bindings: iio: add Open Sensor Fusion device
From: Kim Jinseob @ 2026-06-10  9:33 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Jonathan Cameron, linux-iio, David Lechner, Nuno Sá,
	Andy Shevchenko, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jonathan Corbet, Shuah Khan, devicetree, linux-kernel, linux-doc
In-Reply-To: <20260609-glacial-colossal-38b4937ec620@spud>

> Do you think it makes sense to permit a regulator here, so that the
> "host" OS can power on/off the board running the osf stack?

From the OSF hardware side, yes, that makes sense.

The current prototype used for testing is powered independently, but an OSF
device may also be integrated as a host-powered UART peripheral. In that case
allowing the host to control the board supply through an optional regulator
would be useful.

Unless the IIO side prefers otherwise, I will add an optional supply property
to the binding and matching optional regulator handling in the driver in the
next revision.

Jinseob


2026년 6월 10일 (수) 오전 1:19, Conor Dooley <conor@kernel.org>님이 작성:
>
> Jonathan/IIO folks,
>
> On Mon, Jun 08, 2026 at 08:43:38AM +0900, Jinseob Kim wrote:
>
> > diff --git a/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml b/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> > new file mode 100644
> > index 000000000..a4049715a
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/opensensorfusion,osf.yaml
> > @@ -0,0 +1,43 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/iio/opensensorfusion,osf.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Open Sensor Fusion Sensor Aggregation Hub
> > +
> > +maintainers:
> > +  - Jinseob Kim <kimjinseob88@gmail.com>
> > +
> > +description: |
> > +  Open Sensor Fusion is a sensor aggregation hub. The hub exposes an OSF
> > +  protocol data stream over its host interface and may report capabilities and
> > +  samples for multiple sensor classes. The Linux driver discovers the actual
> > +  sensor channels from OSF capability reports instead of describing those
> > +  sensors in Device Tree.
> > +
> > +  Open Sensor Fusion is not a generic industry standard. Public project
> > +  documentation is available at:
> > +
> > +    https://github.com/opensensorfusion
> > +
> > +allOf:
> > +  - $ref: /schemas/serial/serial-peripheral-props.yaml#
> > +
> > +properties:
> > +  compatible:
> > +    const: opensensorfusion,osf
> > +
> > +required:
> > +  - compatible
>
> Do you think it makes sense to permit a regulator here, so that the
> "host" OS can power on/off the board running the osf stack?
>
> > +
> > +unevaluatedProperties: false
> > +
> > +examples:
> > +  - |
> > +    serial {
> > +        sensor {
> > +            compatible = "opensensorfusion,osf";
> > +        };
> > +    };
> > +...

^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: connector: pcie-m2-e: Add 3.3Vaux supply support
From: Manivannan Sadhasivam @ 2026-06-10  9:28 UTC (permalink / raw)
  To: Sherry Sun
  Cc: Krzysztof Kozlowski, sashiko-reviews@lists.linux.dev,
	manivannan.sadhasivam@oss.qualcomm.com, linux-pci@vger.kernel.org,
	robh@kernel.org, Frank.Li@kernel.org, conor+dt@kernel.org,
	devicetree@vger.kernel.org, imx@lists.linux.dev
In-Reply-To: <VI0PR04MB121140EFB8E614326D799503A921A2@VI0PR04MB12114.eurprd04.prod.outlook.com>

On Wed, Jun 10, 2026 at 08:40:54AM +0000, Sherry Sun wrote:
> > On Tue, Jun 09, 2026 at 03:44:08AM +0000, sashiko-bot@kernel.org wrote:
> > > Thank you for your contribution! Sashiko AI review found 1 potential
> > issue(s) to consider:
> > > - [Medium] The `vpcie3v3aux-supply` property describes a non-existent
> > hardware feature on the M.2 Key E connector to work around a software
> > policy.
> > 
> > Feels valid. Describe which pin on M2 connector are you representing.
> > 
> 
> Refer to PCI Express M.2 Specification r5.1 sec3.1.1 Power Sources and
> Grounds.
> 
> PCI Express M.2 Socket 1 utilizes a 3.3 V power source. The voltage
> source, 3.3 V, is expected to be available during the system’s
> stand-by/suspend state to support wake event processing on the
> communications card.
> 
> But the current vpcie3v3-supply may be gated off during system suspend.
> So I  tried to add vpcie3v3aux-supply to let this 3.3 V power source
> always on for PCIe M.2 Key E connector. That means vpcie3v3aux-supply
> and vpcie3v3-supply actually refer to the same 3.3 V power source.
> 
> @Mani, do you think this is reasonable? Or do you have any other better
> solutions? Thanks!
> 

There is no Vaux defined in the M.2 spec. So you cannot define that supply in
the binding. You can define the custom Vaux supply as a fixed regulator in DT
and mark it always on so that it is keeps supplying 3.3v to the card.

- Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply

* Re: [PATCH 2/3] hwmon: ina2xx: support ina232
From: Loic Poulain @ 2026-06-10  9:26 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: linux-hwmon, robh, conor+dt, devicetree
In-Reply-To: <20260610084357.C62271F00893@smtp.kernel.org>

On Wed, Jun 10, 2026 at 10:44 AM <sashiko-bot@kernel.org> wrote:
>
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
>
> New issues:
> - [Medium] The INA232 configuration implicitly omits `.has_update_interval = true`, silently disabling sysfs update interval configuration.
>
> Pre-existing issues:
> - [High] Multiple alert limit calculations in `ina226_alert_to_reg` suffer from integer overflows and signedness mismatches on 32-bit systems, causing hardware limits to be incorrectly programmed.
> - [High] Integer overflow in power calculations inside `ina2xx_get_value()` incorrectly returns negative power measurements at higher loads.
> --
>
> commit 553d30269962c93d687fffb0c6e1dcc15cbdf020
> Author: Martino Facchin <m.facchin@arduino.cc>
> Subject: hwmon: ina2xx: support ina232
>
> This commit adds support for the INA232 current and power monitor. It defines
> the INA232 configuration values and adds the device to the corresponding i2c
> and device tree identification tables.
>
> > diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
> > index 613ffb622b7c4..122e7aa4fdfff 100644
> > --- a/drivers/hwmon/ina2xx.c
> > +++ b/drivers/hwmon/ina2xx.c
>
> [ ... ]
>
> > @@ -196,6 +197,17 @@ static const struct ina2xx_config ina2xx_config[] = {
> >               .current_shift = 4,
> >               .has_update_interval = true,
> >       },
> > +     [ina232] = {
> > +             .config_default = INA226_CONFIG_DEFAULT,
> > +             .calibration_value = 2048,
> > +             .shunt_div = 400,
> > +             .bus_voltage_shift = 0,
> > +             .bus_voltage_lsb = 1600,
> > +             .power_lsb_factor = 32,
>
> [Severity: High]
> This is a pre-existing issue, but can the power calculation overflow when
> using larger power_lsb_factor values like the one added here for INA232?
>
> In ina2xx_get_value(), the INA2XX_POWER calculation is:
>
>     val = regval * data->power_lsb_uW;
>
> The result is returned as a signed 32-bit int. For the INA232 with a common
> 2mOhm shunt, power_lsb_uW becomes 40,000. When the 16-bit regval exceeds
> 53,687, the product exceeds INT_MAX. This overflows the 32-bit signed math,
> wrapping to a negative integer and reporting erroneous negative power
> readings to userspace.
>
> [Severity: High]
> This is also a pre-existing issue, but do the alert limit calculations in
> ina226_alert_to_reg() cause hardware limits to be incorrectly programmed on
> 32-bit systems?
>
> For INA2XX_POWER, the upper bound is clamped:
>
>     val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW);
>
> clamp_val() implicitly casts the upper bound to a 32-bit signed long on
> 32-bit platforms. This results in a negative number, meaning any valid
> power limit is clamped to a negative value and ultimately programmed as 0.
>
> Similarly, for INA2XX_SHUNT_VOLTAGE, the initial clamp uses SHRT_MAX *
> shunt_div instead of division. The subsequent multiplication:
>
>     val *= data->config->shunt_div;
>
> overflows LONG_MAX on 32-bit platforms for high inputs, wrapping to a
> negative value and also programming the hardware limit to 0.
>
> Additionally, for INA2XX_BUS_VOLTAGE on parts with bus_voltage_shift > 0,
> the calculation (val * 1000) << shift can exceed LONG_MAX for limits over
> ~134V, wrapping to negative and setting the limit to 0.
>
> > +             .has_alerts = true,
> > +             .has_ishunt = false,
> > +             .has_power_average = false,
> > +     },
>
> [Severity: Medium]
> Does this configuration intentionally omit the update interval?
>
> Because has_update_interval is not explicitly set, it defaults to false.
> This hides the hwmon_chip_update_interval sysfs attribute, preventing
> adjustments to the averaging time despite the INA232 sharing the INA226
> register layout.

These are pre-existing issues related to out-of-scope voltage and
power values. They will not be addressed in this series.

^ permalink raw reply

* Re: Re: [PATCH v7 1/2] dt-bindings: pwm: dwc: Add eswin compatible and resets property
From: Xuyang Dong @ 2026-06-10  9:25 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  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: <c4fa85f1-365e-49ab-9549-d6d46529a68b@kernel.org>

> 
> On 09/06/2026 11:31, Xuyang Dong wrote:
> >>>
> >>> EIC7700 use DesignWare IP for PWM controllers. Add ESWIN EIC7700 support
> >>> in snps,dw-apb-timers-pwm2.yaml.
> >>>
> >>> 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.
> >>>
> >>> The generic snps,dw-apb-timers-pwm2 binding allows one or two optional
> >>
> >> I don't know what is the generic binding, but it does not allow. Open
> >> the file: there are no resets at all, so it does not allow them. Or you
> >> mixed tenses here and you wanted to describe the change?
> >>
> > 
> > Hi Krzysztof,
> > 
> > Thanks for your comments and time.
> > 
> > Regarding snps,dw-apb-timers-pwm2, we previously intended to add the 
> > resets property in the same patch. 
> > However, as you suggested, we will split it into a separate patch.
> 
> I find commit msg still confusing. Please always clearly explain WHY you
> are doing changes.
> 

Hi Krzysztof,

Thanks for your comments.

According to the DesignWare IP for PWM controllers manual, it seems to me 
that Ben's previous submission may have missed the optional resets property.

Therefore, I would like to confirm: should I submit a separate fix patch for
the resets property, or include it as a feature patch in this series?

Best regards,
Xuyang Dong

^ permalink raw reply

* [PATCH v3 3/3] arm64: dts: imx93-11x11-evk: Add DY1212W-4856 LVDS panel
From: Liu Ying @ 2026-06-10  9:26 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Peng Fan
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Marco Felsch,
	Liu Ying
In-Reply-To: <20260610-imx93-ldb-v3-0-c9b65d742753@nxp.com>

DY1212W-4856 [1] is a 12.1" (WXGA) TFT LCD panel with LVDS interface.
The panel's 40-pin connector allows it to be directly connected to
i.MX93 11x11 EVK board.

Link: https://www.nxp.com/design/design-center/development-boards-and-designs/dy1212w-4856-tft-lcd-panel-with-lvds-interface:DY1212W-4856 [1]
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
 arch/arm64/boot/dts/freescale/Makefile             |  4 ++
 .../freescale/imx93-11x11-evk-dy1212w-4856.dtso    | 81 ++++++++++++++++++++++
 2 files changed, 85 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile
index 8ddaab127ab9..dbe27d757c86 100644
--- a/arch/arm64/boot/dts/freescale/Makefile
+++ b/arch/arm64/boot/dts/freescale/Makefile
@@ -588,6 +588,10 @@ dtb-$(CONFIG_ARCH_MXC) += imx93-9x9-qsb-ontat-kd50g21-40nt-a1.dtb
 dtb-$(CONFIG_ARCH_MXC) += imx93-9x9-qsb-tianma-tm050rdh03.dtb
 
 dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-evk.dtb
+
+imx93-11x11-evk-dy1212w-4856-dtbs += imx93-11x11-evk.dtb imx93-11x11-evk-dy1212w-4856.dtbo
+dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-evk-dy1212w-4856.dtb
+
 dtb-$(CONFIG_ARCH_MXC) += imx93-11x11-frdm.dtb
 
 imx93-11x11-frdm-pixpaper-dtbs += imx93-11x11-frdm.dtb imx93-11x11-frdm-pixpaper.dtbo
diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dy1212w-4856.dtso b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dy1212w-4856.dtso
new file mode 100644
index 000000000000..35f7c5699e3a
--- /dev/null
+++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk-dy1212w-4856.dtso
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright 2026 NXP
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/imx93-clock.h>
+
+&{/} {
+	panel-lvds {
+		compatible = "boe,ev121wxm-n10-1850";
+		backlight = <&backlight_lvds>;
+		power-supply = <&buck4>;
+
+		panel-timing {
+			/*
+			 * Set clock frequency to 71142858Hz to accommodate
+			 * IMX93_CLK_VIDEO_PLL rate at 498000000Hz in a rate
+			 * table.
+			 */
+			clock-frequency = <71142858>;
+			hactive = <1280>;
+			vactive = <800>;
+			hfront-porch = <48>;
+			hback-porch = <80>;
+			hsync-len = <32>;
+			vfront-porch = <3>;
+			vback-porch = <14>;
+			vsync-len = <6>;
+		};
+
+		port {
+			panel_lvds_in: endpoint {
+				remote-endpoint = <&ldb_lvds_ch0>;
+			};
+		};
+	};
+};
+
+&backlight_lvds {
+	status = "okay";
+};
+
+&lcdif {
+	status = "okay";
+};
+
+&lvds_bridge {
+	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@1 {
+			reg = <1>;
+
+			ldb_lvds_ch0: endpoint {
+				remote-endpoint = <&panel_lvds_in>;
+			};
+		};
+	};
+};
+
+&media_blk_ctrl {
+	assigned-clocks = <&clk IMX93_CLK_MEDIA_AXI>,
+			  <&clk IMX93_CLK_MEDIA_APB>,
+			  <&clk IMX93_CLK_MEDIA_DISP_PIX>,
+			  <&clk IMX93_CLK_VIDEO_PLL>;
+	assigned-clock-parents = <&clk IMX93_CLK_SYS_PLL_PFD1>,
+				 <&clk IMX93_CLK_SYS_PLL_PFD1_DIV2>,
+				 <&clk IMX93_CLK_VIDEO_PLL>;
+	/*
+	 * Set IMX93_CLK_MEDIA_DISP_PIX rate to 71142858Hz to accommodate
+	 * IMX93_CLK_VIDEO_PLL rate at 498000000Hz in a rate table.
+	 */
+	assigned-clock-rates = <400000000>, <133333333>, <71142858>, <498000000>;
+	status = "okay";
+};

-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 2/3] arm64: dts: imx93: Add LVDS Display Bridge support
From: Liu Ying @ 2026-06-10  9:26 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Peng Fan
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Marco Felsch,
	Liu Ying
In-Reply-To: <20260610-imx93-ldb-v3-0-c9b65d742753@nxp.com>

Add LVDS Display Bridge(LDB) child node to mediamix blk-ctrl node
so that video could be output through a LVDS interface.

Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
 arch/arm64/boot/dts/freescale/imx93.dtsi | 37 ++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/imx93.dtsi b/arch/arm64/boot/dts/freescale/imx93.dtsi
index b9abe143cb56..79fb4a15b733 100644
--- a/arch/arm64/boot/dts/freescale/imx93.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx93.dtsi
@@ -178,6 +178,7 @@ &lcdif {
 	port {
 		lcdif_to_ldb: endpoint@1 {
 			reg = <1>;
+			remote-endpoint = <&ldb_from_lcdif>;
 		};
 
 		lcdif_to_dsi: endpoint@2 {
@@ -186,6 +187,42 @@ lcdif_to_dsi: endpoint@2 {
 	};
 };
 
+&media_blk_ctrl {
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	lvds_bridge: bridge@20 {
+		compatible = "fsl,imx93-ldb";
+		reg = <0x20 0x4>, <0x24 0x4>;
+		reg-names = "ldb", "lvds";
+		clocks = <&clk IMX93_CLK_LVDS_GATE>;
+		clock-names = "ldb";
+		assigned-clocks = <&clk IMX93_CLK_MEDIA_LDB>;
+		assigned-clock-parents = <&clk IMX93_CLK_VIDEO_PLL>;
+		status = "disabled";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+
+				ldb_from_lcdif: endpoint {
+					remote-endpoint = <&lcdif_to_ldb>;
+				};
+			};
+
+			port@1 {
+				reg = <1>;
+
+				ldb_lvds_ch0: endpoint {
+				};
+			};
+		};
+	};
+};
+
 &src {
 	mlmix: power-domain@44461800 {
 		compatible = "fsl,imx93-src-slice";

-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 1/3] dt-bindings: soc: imx: fsl,imx93-media-blk-ctrl: Allow LVDS Display Bridge child node
From: Liu Ying @ 2026-06-10  9:26 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Peng Fan
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Marco Felsch,
	Liu Ying
In-Reply-To: <20260610-imx93-ldb-v3-0-c9b65d742753@nxp.com>

i.MX93 SoC mediamix blk-ctrl contains one LDB_CTRL register and one LVDS
register which control video output through a LVDS interface.  Allow the
LVDS Display Bridge(LDB) child node and add the child node to example.

i.MX93 LDB child node(bridge@20) is an addressable node, while i.MX93
Parallel Display Format Configuration(PDFC) child node(dpi-bridge) is a
non-addressable node.  Mixing the addressable and non-addressable child
nodes is allowed according to discussion [1].

Link: https://lore.kernel.org/all/n6akxiayi3g6gxcqhreb4iaohmeokoalnqup6h5r2fwdt4zijt@u2wyps55ayqm/ [1]
Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
 .../bindings/soc/imx/fsl,imx93-media-blk-ctrl.yaml | 39 ++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/Documentation/devicetree/bindings/soc/imx/fsl,imx93-media-blk-ctrl.yaml b/Documentation/devicetree/bindings/soc/imx/fsl,imx93-media-blk-ctrl.yaml
index d828c2e82965..124f5c206ee3 100644
--- a/Documentation/devicetree/bindings/soc/imx/fsl,imx93-media-blk-ctrl.yaml
+++ b/Documentation/devicetree/bindings/soc/imx/fsl,imx93-media-blk-ctrl.yaml
@@ -26,6 +26,12 @@ properties:
   reg:
     maxItems: 1
 
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 1
+
   '#power-domain-cells':
     const: 1
 
@@ -92,6 +98,11 @@ properties:
       - compatible
       - ports
 
+  bridge@20:
+    type: object
+    $ref: /schemas/display/bridge/fsl,ldb.yaml#
+    unevaluatedProperties: false
+
 allOf:
   - if:
       properties:
@@ -112,6 +123,7 @@ allOf:
             - const: lcdif
             - const: isi
             - const: csi
+        bridge@20: false
   - if:
       properties:
         compatible:
@@ -163,6 +175,8 @@ examples:
                <&clk IMX93_CLK_MIPI_DSI_GATE>;
                clock-names = "apb", "axi", "nic", "disp", "cam",
                              "pxp", "lcdif", "isi", "csi", "dsi";
+      #address-cells = <1>;
+      #size-cells = <1>;
       #power-domain-cells = <1>;
 
       dpi-bridge {
@@ -190,4 +204,29 @@ examples:
           };
         };
       };
+
+      bridge@20 {
+        compatible = "fsl,imx93-ldb";
+        reg = <0x20 0x4>, <0x24 0x4>;
+        reg-names = "ldb", "lvds";
+        clocks = <&clk IMX93_CLK_LVDS_GATE>;
+        clock-names = "ldb";
+
+        ports {
+          #address-cells = <1>;
+          #size-cells = <0>;
+
+          port@0 {
+            reg = <0>;
+
+            endpoint {
+              remote-endpoint = <&lcdif_to_ldb>;
+            };
+          };
+
+          port@1 {
+            reg = <1>;
+          };
+        };
+      };
     };

-- 
2.43.0


^ permalink raw reply related

* [PATCH v3 0/3] arm64: dts: imx93-11x11-evk: Add DY1212W-4856 LVDS panel
From: Liu Ying @ 2026-06-10  9:26 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Peng Fan
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Marco Felsch,
	Liu Ying

Hi,

This patch series aims to add DY1212W-4856 [1] LVDS panel to i.MX93 11x11
EVK board.

Patch 1 allows LVDS Display Bridge (LDB) child node in i.MX93 mediamix
blk-ctrl DT binding.
Patch 2 adds LDB child node to mediamix blk-ctrl node in imx93.dtsi.
Patch 3 adds a DT overlay to support the DY1212W-4856 LVDS panel on
i.MX93 11x11 EVK board.

[1] https://www.nxp.com/design/design-center/development-boards-and-designs/dy1212w-4856-tft-lcd-panel-with-lvds-interface:DY1212W-4856

Signed-off-by: Liu Ying <victor.liu@nxp.com>
---
Changes in v3:
- Explain in patch 1's commit message that mixing addressable and
  non-addressable DT child nodes is allowed.  (Krzysztof)
- Link to v2: https://patch.msgid.link/20260608-imx93-ldb-v2-0-1b1fe621bfda@nxp.com

Changes in v2:
- Rebase on next-20260605.
- Disallow bridge@20 for i.MX91 in patch 1.  (Sashiko bot)
- Cc Marco.
- Link to v1: https://patch.msgid.link/20260513-imx93-ldb-v1-0-d11c5c3cc197@nxp.com

---
Liu Ying (3):
      dt-bindings: soc: imx: fsl,imx93-media-blk-ctrl: Allow LVDS Display Bridge child node
      arm64: dts: imx93: Add LVDS Display Bridge support
      arm64: dts: imx93-11x11-evk: Add DY1212W-4856 LVDS panel

 .../bindings/soc/imx/fsl,imx93-media-blk-ctrl.yaml | 39 +++++++++++
 arch/arm64/boot/dts/freescale/Makefile             |  4 ++
 .../freescale/imx93-11x11-evk-dy1212w-4856.dtso    | 81 ++++++++++++++++++++++
 arch/arm64/boot/dts/freescale/imx93.dtsi           | 37 ++++++++++
 4 files changed, 161 insertions(+)
---
base-commit: 6e845bcb78c95af935094040bd4edc3c2b6dd784
change-id: 20260513-imx93-ldb-c5a4194e41ce

Best regards,
--  
Regards,
Liu Ying


^ permalink raw reply


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