Devicetree
 help / color / mirror / Atom feed
* [PATCH v5 5/6] arm64: dts: rockchip: add PWM nodes to RK3576 SoC dtsi
From: Nicolas Frattaroli @ 2026-04-20 13:52 UTC (permalink / raw)
  To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Heiko Stuebner, Lee Jones, William Breathitt Gray,
	Damon Ding
  Cc: Nicolas Frattaroli, kernel, Jonas Karlman, Alexey Charkov,
	linux-rockchip, linux-pwm, devicetree, linux-arm-kernel,
	linux-kernel, linux-iio
In-Reply-To: <20260420-rk3576-pwm-v5-0-ae7cfbbe5427@collabora.com>

The RK3576 SoC features three distinct PWM controllers, with variable
numbers of channels. Add each channel as a separate node to the SoC's
device tree, as they don't really overlap in register ranges.

Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
 arch/arm64/boot/dts/rockchip/rk3576.dtsi | 208 +++++++++++++++++++++++++++++++
 1 file changed, 208 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
index e12a2a0cfb89..55d6b103c329 100644
--- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
@@ -1032,6 +1032,32 @@ uart1: serial@27310000 {
 			status = "disabled";
 		};
 
+		pwm0_2ch_0: pwm@27330000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x27330000 0x0 0x1000>;
+			clocks = <&cru CLK_PMU1PWM>, <&cru PCLK_PMU1PWM>,
+				 <&cru CLK_PMU1PWM_OSC>, <&cru CLK_PMU1PWM_RC>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm0m0_ch0>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm0_2ch_1: pwm@27331000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x27331000 0x0 0x1000>;
+			clocks = <&cru CLK_PMU1PWM>, <&cru PCLK_PMU1PWM>,
+				 <&cru CLK_PMU1PWM_OSC>, <&cru CLK_PMU1PWM_RC>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm0m0_ch1>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
 		pmu: power-management@27380000 {
 			compatible = "rockchip,rk3576-pmu", "syscon", "simple-mfd";
 			reg = <0x0 0x27380000 0x0 0x800>;
@@ -2630,6 +2656,188 @@ uart9: serial@2adc0000 {
 			status = "disabled";
 		};
 
+		pwm1_6ch_0: pwm@2add0000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2add0000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
+				 <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm1m0_ch0>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm1_6ch_1: pwm@2add1000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2add1000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
+				 <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm1m0_ch1>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm1_6ch_2: pwm@2add2000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2add2000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
+				 <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm1m0_ch2>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm1_6ch_3: pwm@2add3000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2add3000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
+				 <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm1m0_ch3>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm1_6ch_4: pwm@2add4000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2add4000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
+				 <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm1m0_ch4>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm1_6ch_5: pwm@2add5000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2add5000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>,
+				 <&cru CLK_OSC_PWM1>, <&cru CLK_RC_PWM1>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm1m0_ch5>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm2_8ch_0: pwm@2ade0000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2ade0000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
+				 <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm2m0_ch0>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm2_8ch_1: pwm@2ade1000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2ade1000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
+				 <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm2m0_ch1>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm2_8ch_2: pwm@2ade2000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2ade2000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
+				 <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm2m0_ch2>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm2_8ch_3: pwm@2ade3000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2ade3000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
+				 <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm2m0_ch3>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm2_8ch_4: pwm@2ade4000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2ade4000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
+				 <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm2m0_ch4>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm2_8ch_5: pwm@2ade5000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2ade5000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
+				 <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm2m0_ch5>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm2_8ch_6: pwm@2ade6000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2ade6000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
+				 <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm2m0_ch6>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
+		pwm2_8ch_7: pwm@2ade7000 {
+			compatible = "rockchip,rk3576-pwm";
+			reg = <0x0 0x2ade7000 0x0 0x1000>;
+			clocks = <&cru CLK_PWM2>, <&cru PCLK_PWM2>,
+				 <&cru CLK_OSC_PWM2>, <&cru CLK_RC_PWM2>;
+			clock-names = "pwm", "pclk", "osc", "rc";
+			interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwm2m0_ch7>;
+			#pwm-cells = <3>;
+			status = "disabled";
+		};
+
 		saradc: adc@2ae00000 {
 			compatible = "rockchip,rk3576-saradc", "rockchip,rk3588-saradc";
 			reg = <0x0 0x2ae00000 0x0 0x10000>;

-- 
2.53.0


^ permalink raw reply related

* [PATCH v5 4/6] counter: Add rockchip-pwm-capture driver
From: Nicolas Frattaroli @ 2026-04-20 13:52 UTC (permalink / raw)
  To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Heiko Stuebner, Lee Jones, William Breathitt Gray,
	Damon Ding
  Cc: Nicolas Frattaroli, kernel, Jonas Karlman, Alexey Charkov,
	linux-rockchip, linux-pwm, devicetree, linux-arm-kernel,
	linux-kernel, linux-iio
In-Reply-To: <20260420-rk3576-pwm-v5-0-ae7cfbbe5427@collabora.com>

Among many other things, Rockchip's new PWMv4 IP in the RK3576 supports
PWM capture functionality.

Add a basic driver for this that works to expose HPC/LPC counts and
state change events to userspace through the counter framework. It's
quite basic, but works well enough to demonstrate the device function
exclusion stuff that mfpwm does, in order to eventually support all the
functions of this device in drivers within their appropriate subsystems,
without them interfering with each other.

Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
 MAINTAINERS                            |   1 +
 drivers/counter/Kconfig                |  11 ++
 drivers/counter/Makefile               |   1 +
 drivers/counter/rockchip-pwm-capture.c | 307 +++++++++++++++++++++++++++++++++
 4 files changed, 320 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 68bb9ee07a47..3f72784dd5bc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23178,6 +23178,7 @@ L:	linux-rockchip@lists.infradead.org
 L:	linux-pwm@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/pwm/rockchip,rk3576-pwm.yaml
+F:	drivers/counter/rockchip-pwm-capture.c
 F:	drivers/mfd/rockchip-mfpwm.c
 F:	drivers/pwm/pwm-rockchip-v4.c
 F:	include/linux/mfd/rockchip-mfpwm.h
diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig
index d30d22dfe577..85adeb41aeed 100644
--- a/drivers/counter/Kconfig
+++ b/drivers/counter/Kconfig
@@ -90,6 +90,17 @@ config MICROCHIP_TCB_CAPTURE
 	  To compile this driver as a module, choose M here: the
 	  module will be called microchip-tcb-capture.
 
+config ROCKCHIP_PWM_CAPTURE
+	tristate "Rockchip PWM Counter Capture driver"
+	depends on MFD_ROCKCHIP_MFPWM
+	help
+	  Generic counter framework driver for the multi-function PWM on
+	  Rockchip SoCs such as the RK3576.
+
+	  Uses the Rockchip Multi-function PWM controller driver infrastructure
+	  to guarantee exclusive operation with other functions of the same
+	  device implemented by drivers in other subsystems.
+
 config RZ_MTU3_CNT
 	tristate "Renesas RZ/G2L MTU3a counter driver"
 	depends on RZ_MTU3
diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile
index fa3c1d08f706..2bfcfc2c584b 100644
--- a/drivers/counter/Makefile
+++ b/drivers/counter/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_FTM_QUADDEC)	+= ftm-quaddec.o
 obj-$(CONFIG_MICROCHIP_TCB_CAPTURE)	+= microchip-tcb-capture.o
 obj-$(CONFIG_INTEL_QEP)		+= intel-qep.o
 obj-$(CONFIG_TI_ECAP_CAPTURE)	+= ti-ecap-capture.o
+obj-$(CONFIG_ROCKCHIP_PWM_CAPTURE)	+= rockchip-pwm-capture.o
diff --git a/drivers/counter/rockchip-pwm-capture.c b/drivers/counter/rockchip-pwm-capture.c
new file mode 100644
index 000000000000..09a92f2bc409
--- /dev/null
+++ b/drivers/counter/rockchip-pwm-capture.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Collabora Ltd.
+ *
+ * A counter driver for the Pulse-Width-Modulation (PWM) hardware found on
+ * Rockchip SoCs such as the RK3576, internally referred to as "PWM v4". It
+ * allows for measuring the high cycles and low cycles of a PWM signal through
+ * the generic counter framework, while guaranteeing exclusive use over the
+ * MFPWM device while the counter is enabled.
+ *
+ * Authors:
+ *     Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
+ */
+
+#include <linux/cleanup.h>
+#include <linux/counter.h>
+#include <linux/devm-helpers.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/rockchip-mfpwm.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#define RKPWMC_INT_MASK			(PWMV4_INT_LPC | PWMV4_INT_HPC)
+
+struct rockchip_pwm_capture {
+	struct rockchip_mfpwm_func *pwmf;
+	struct counter_device *counter;
+};
+
+static struct counter_signal rkpwmc_signals[] = {
+	{
+		.id = 0,
+		.name = "PWM Clock"
+	},
+};
+
+static const enum counter_synapse_action rkpwmc_hpc_lpc_actions[] = {
+	COUNTER_SYNAPSE_ACTION_BOTH_EDGES,
+	COUNTER_SYNAPSE_ACTION_NONE,
+};
+
+static struct counter_synapse rkpwmc_pwm_synapses[] = {
+	{
+		.actions_list = rkpwmc_hpc_lpc_actions,
+		.num_actions = ARRAY_SIZE(rkpwmc_hpc_lpc_actions),
+		.signal = &rkpwmc_signals[0]
+	},
+};
+
+static const enum counter_function rkpwmc_functions[] = {
+	COUNTER_FUNCTION_INCREASE,
+};
+
+static inline bool rkpwmc_is_enabled(struct rockchip_mfpwm_func *pwmf)
+{
+	return mfpwm_get_mode(pwmf) == PWMV4_MODE_CAPTURE;
+}
+
+static bool rkpwmc_acquire_if_enabled(struct rockchip_pwm_capture *pc)
+{
+	int ret;
+
+	ret = mfpwm_acquire(pc->pwmf);
+	if (ret < 0)
+		return false;
+
+	if (rkpwmc_is_enabled(pc->pwmf))
+		return true;
+
+	mfpwm_release(pc->pwmf);
+
+	return false;
+}
+
+static int rkpwmc_enable_read(struct counter_device *counter,
+			       struct counter_count *count,
+			       u8 *enable)
+{
+	struct rockchip_pwm_capture *pc = counter_priv(counter);
+
+	*enable = rkpwmc_is_enabled(pc->pwmf);
+
+	return 0;
+}
+
+static int rkpwmc_enable_write(struct counter_device *counter,
+			       struct counter_count *count,
+			       u8 enable)
+{
+	struct rockchip_pwm_capture *pc = counter_priv(counter);
+	int ret;
+
+	ret = mfpwm_acquire(pc->pwmf);
+	if (ret)
+		return ret;
+
+	if (!!enable != rkpwmc_is_enabled(pc->pwmf)) {
+		if (enable) {
+			mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,
+					 PWMV4_EN(false));
+			mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_CTRL,
+					 PWMV4_CTRL_CAP_FLAGS);
+			mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_INT_EN,
+					 PWMV4_INT_LPC_W(true) |
+					 PWMV4_INT_HPC_W(true));
+			mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,
+					 PWMV4_EN(true) | PWMV4_CLK_EN(true));
+
+			ret = clk_enable(pc->pwmf->core);
+			if (ret)
+				goto err_release;
+
+			ret = clk_rate_exclusive_get(pc->pwmf->core);
+			if (ret)
+				goto err_disable_pwm_clk;
+
+			ret = mfpwm_acquire(pc->pwmf);
+			if (ret)
+				goto err_unprotect_pwm_clk;
+		} else {
+			mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_INT_EN,
+					 PWMV4_INT_LPC_W(false) |
+					 PWMV4_INT_HPC_W(false));
+			mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_ENABLE,
+					 PWMV4_EN(false) | PWMV4_CLK_EN(false));
+			clk_rate_exclusive_put(pc->pwmf->core);
+			clk_disable(pc->pwmf->core);
+			mfpwm_release(pc->pwmf);
+		}
+	}
+
+	mfpwm_release(pc->pwmf);
+
+	return 0;
+
+err_unprotect_pwm_clk:
+	clk_rate_exclusive_put(pc->pwmf->core);
+err_disable_pwm_clk:
+	clk_disable(pc->pwmf->core);
+err_release:
+	mfpwm_release(pc->pwmf);
+
+	return ret;
+}
+
+static struct counter_comp rkpwmc_ext[] = {
+	COUNTER_COMP_ENABLE(rkpwmc_enable_read, rkpwmc_enable_write),
+};
+
+enum rkpwmc_count_id {
+	COUNT_LPC = 0,
+	COUNT_HPC = 1,
+};
+
+static struct counter_count rkpwmc_counts[] = {
+	{
+		.id = COUNT_LPC,
+		.name = "Low Polarity Capture",
+		.functions_list = rkpwmc_functions,
+		.num_functions = ARRAY_SIZE(rkpwmc_functions),
+		.synapses = rkpwmc_pwm_synapses,
+		.num_synapses = ARRAY_SIZE(rkpwmc_pwm_synapses),
+		.ext = rkpwmc_ext,
+		.num_ext = ARRAY_SIZE(rkpwmc_ext),
+	},
+	{
+		.id = COUNT_HPC,
+		.name = "High Polarity Capture",
+		.functions_list = rkpwmc_functions,
+		.num_functions = ARRAY_SIZE(rkpwmc_functions),
+		.synapses = rkpwmc_pwm_synapses,
+		.num_synapses = ARRAY_SIZE(rkpwmc_pwm_synapses),
+		.ext = rkpwmc_ext,
+		.num_ext = ARRAY_SIZE(rkpwmc_ext),
+	},
+};
+
+static int rkpwmc_count_read(struct counter_device *counter,
+			     struct counter_count *count, u64 *value)
+{
+	struct rockchip_pwm_capture *pc = counter_priv(counter);
+
+	switch (count->id) {
+	case COUNT_LPC:
+		if (rkpwmc_acquire_if_enabled(pc)) {
+			*value = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_LPC);
+			mfpwm_release(pc->pwmf);
+		} else {
+			*value = 0;
+		}
+		return 0;
+	case COUNT_HPC:
+		if (rkpwmc_acquire_if_enabled(pc)) {
+			*value = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_HPC);
+			mfpwm_release(pc->pwmf);
+		} else {
+			*value = 0;
+		}
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct counter_ops rkpwmc_ops = {
+	.count_read = rkpwmc_count_read,
+};
+
+static irqreturn_t rkpwmc_irq_handler(int irq, void *data)
+{
+	struct rockchip_pwm_capture *pc = data;
+	u32 intsts;
+	u32 clr = 0;
+
+	intsts = mfpwm_reg_read(pc->pwmf->base, PWMV4_REG_INTSTS);
+
+	if (!(intsts & RKPWMC_INT_MASK))
+		return IRQ_NONE;
+
+	if (intsts & PWMV4_INT_LPC) {
+		clr |= PWMV4_INT_LPC;
+		counter_push_event(pc->counter, COUNTER_EVENT_CHANGE_OF_STATE, 0);
+	}
+
+	if (intsts & PWMV4_INT_HPC) {
+		clr |= PWMV4_INT_HPC;
+		counter_push_event(pc->counter, COUNTER_EVENT_CHANGE_OF_STATE, 1);
+	}
+
+	if (clr)
+		mfpwm_reg_write(pc->pwmf->base, PWMV4_REG_INTSTS, clr);
+
+	/* If other interrupt status bits are set, they're not for this driver */
+	if (intsts != clr)
+		return IRQ_NONE;
+
+	return IRQ_HANDLED;
+}
+
+static int rockchip_pwm_capture_probe(struct platform_device *pdev)
+{
+	struct rockchip_mfpwm_func *pwmf = dev_get_platdata(&pdev->dev);
+	struct rockchip_pwm_capture *pc;
+	struct counter_device *counter;
+	int ret;
+
+	/* Set our (still unset) OF node to the parent MFD device's OF node */
+	pdev->dev.parent->of_node_reused = true;
+	device_set_node(&pdev->dev,
+			of_fwnode_handle(no_free_ptr(pdev->dev.parent->of_node)));
+
+	counter = devm_counter_alloc(&pdev->dev, sizeof(*pc));
+	if (IS_ERR(counter))
+		return PTR_ERR(counter);
+
+	pc = counter_priv(counter);
+	pc->pwmf = pwmf;
+
+	platform_set_drvdata(pdev, pc);
+
+	/* If the counter is on at module probe, acquire it */
+	rkpwmc_acquire_if_enabled(pc);
+
+	counter->name = pdev->name;
+	counter->signals = rkpwmc_signals;
+	counter->num_signals = ARRAY_SIZE(rkpwmc_signals);
+	counter->ops = &rkpwmc_ops;
+	counter->counts = rkpwmc_counts;
+	counter->num_counts = ARRAY_SIZE(rkpwmc_counts);
+
+	pc->counter = counter;
+
+	ret = devm_counter_add(&pdev->dev, counter);
+	if (ret < 0)
+		return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");
+
+	ret = devm_request_irq(&pdev->dev, pwmf->irq, rkpwmc_irq_handler,
+			       IRQF_SHARED, pdev->name, pc);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "Failed requesting IRQ\n");
+
+	return 0;
+}
+
+static const struct platform_device_id rockchip_pwm_capture_id_table[] = {
+	{ .name = "rockchip-pwm-capture", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, rockchip_pwm_capture_id_table);
+
+static struct platform_driver rockchip_pwm_capture_driver = {
+	.probe = rockchip_pwm_capture_probe,
+	.id_table = rockchip_pwm_capture_id_table,
+	.driver = {
+		.name = "rockchip-pwm-capture",
+	},
+};
+module_platform_driver(rockchip_pwm_capture_driver);
+
+MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>");
+MODULE_DESCRIPTION("Rockchip PWM Counter Capture Driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("ROCKCHIP_MFPWM");
+MODULE_IMPORT_NS("COUNTER");
+MODULE_ALIAS("platform:rockchip-pwm-capture");

-- 
2.53.0


^ permalink raw reply related

* [PATCH v5 6/6] arm64: dts: rockchip: Add cooling fan to ROCK 4D
From: Nicolas Frattaroli @ 2026-04-20 13:52 UTC (permalink / raw)
  To: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Heiko Stuebner, Lee Jones, William Breathitt Gray,
	Damon Ding
  Cc: Nicolas Frattaroli, kernel, Jonas Karlman, Alexey Charkov,
	linux-rockchip, linux-pwm, devicetree, linux-arm-kernel,
	linux-kernel, linux-iio
In-Reply-To: <20260420-rk3576-pwm-v5-0-ae7cfbbe5427@collabora.com>

The ROCK 4D has a header to connect a small cooling fan. This fan is
driven by one of the SoC's PWM outputs driving a transistor, that in
turn controls the fan's power.

With the introduction of PWM support, add a description of this cooling
fan, as well as the additional trips and cooling-maps for it.

Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
---
 arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts | 50 +++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
index 899a84b1fbf9..2d5ede010ad0 100644
--- a/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3576-rock-4d.dts
@@ -45,6 +45,14 @@ rfkill {
 		shutdown-gpios = <&gpio2 RK_PD1 GPIO_ACTIVE_HIGH>;
 	};
 
+	fan: pwm-fan {
+		compatible = "pwm-fan";
+		cooling-levels = <0 180 205 230 255>;
+		fan-supply = <&vcc_5v0_sys>;
+		pwms = <&pwm2_8ch_5 0 60000 0>;
+		#cooling-cells = <2>;
+	};
+
 	leds: leds {
 		compatible = "gpio-leds";
 		pinctrl-names = "default";
@@ -711,6 +719,36 @@ rgmii_phy0: ethernet-phy@1 {
 	};
 };
 
+&package_thermal {
+	polling-delay = <100>;
+
+	trips {
+		package_fan0: package-fan0 {
+			temperature = <50000>;
+			hysteresis = <2000>;
+			type = "active";
+		};
+
+		package_fan1: package-fan1 {
+			temperature = <60000>;
+			hysteresis = <2000>;
+			type = "active";
+		};
+	};
+
+	cooling-maps {
+		map1 {
+			trip = <&package_fan0>;
+			cooling-device = <&fan THERMAL_NO_LIMIT 1>;
+		};
+
+		map2 {
+			trip = <&package_fan1>;
+			cooling-device = <&fan 2 THERMAL_NO_LIMIT>;
+		};
+	};
+};
+
 &pcie0 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&pcie_reset>;
@@ -720,6 +758,13 @@ &pcie0 {
 };
 
 &pinctrl {
+	fan {
+		fan_pwm: fan-pwm {
+			rockchip,pins =
+				<4 RK_PC5 14 &pcfg_pull_down_drv_level_5>;
+		};
+	};
+
 	hym8563 {
 		hym8563_int: hym8563-int {
 			rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;
@@ -770,6 +815,11 @@ wifi_en_h: wifi-en-h {
 	};
 };
 
+&pwm2_8ch_5 {
+	pinctrl-0 = <&fan_pwm>;
+	status = "okay";
+};
+
 &sai6 {
 	status = "okay";
 };

-- 
2.53.0


^ permalink raw reply related

* [PATCH v4 1/4] arm64: dts: amlogic: t7: Add uart_c pinctrl pins group
From: Ronald Claveau @ 2026-04-20 13:54 UTC (permalink / raw)
  To: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-arm-kernel, linux-amlogic, devicetree, linux-kernel,
	Ronald Claveau
In-Reply-To: <20260420-add-bluetooth-t7-vim4-v4-0-9505df0e7016@aliel.fr>

Add the pin multiplexing configuration for UART C (TX, RX, CTS, RTS)
in the T7 SoC pinctrl node, required to route the UART C signals
through the correct pads before enabling the controller.

Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
---
 arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi b/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi
index 7fe72c94ed623..4a55d9641bc9b 100644
--- a/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi
+++ b/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi
@@ -553,6 +553,18 @@ mux {
 						bias-pull-up;
 					};
 				};
+
+				uart_c_pins: uart-c {
+					mux {
+						groups = "uart_c_tx",
+							 "uart_c_rx",
+							 "uart_c_cts",
+							 "uart_c_rts";
+						bias-pull-up;
+						output-high;
+						function = "uart_c";
+					};
+				};
 			};
 
 			gpio_intc: interrupt-controller@4080 {

-- 
2.49.0


^ permalink raw reply related

* [PATCH v4 0/4] arm64: dts: amlogic: t7: Add UART support and enable Bluetooth on VIM4
From: Ronald Claveau @ 2026-04-20 13:54 UTC (permalink / raw)
  To: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-arm-kernel, linux-amlogic, devicetree, linux-kernel,
	Ronald Claveau

This series adds all UART controllers for the Amlogic T7 SoC and enables
the Bluetooth controller on the Khadas VIM4 board.

The T7 SoC ships with six UART controllers (A through F), but only
uart_a was previously described in the device tree.

  - Patch 1 adds the pinctrl group for UART C, which is needed to route
    its four signals (TX, RX, CTS, RTS) through the correct pads.

  - Patch 2 completes the uart_a node (peripheral clock) and
    repositions it to respect the ascending reg address order required
    by the DT specification. It then adds nodes for UART B through F,
    each with their respective peripheral clock.

  - Patch 3 removes redundant clocks and clock-names for UART A on
    Khadas VIM4 DT. It then uses those defined from DTSI.

  - Patch 4 enables UART C on the Khadas VIM4 board and attaches the
    on-board BCM43438 Bluetooth controller to it, with hardware flow
    control, wakeup GPIOs, LPO clock and power supplies.

Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
---
Changes in v4:
- Add missing trailers from v2.
- Link to v3: https://lore.kernel.org/r/20260420-add-bluetooth-t7-vim4-v3-0-669cd2530ae5@aliel.fr

Changes in v3:
- Patches 3-4: split into two separate patches:
               3. remove redundant clocks from UART A
               4. add UART C node according to Neil's feedback.
- Link to v2: https://lore.kernel.org/r/20260416-add-bluetooth-t7-vim4-v2-0-9a57098fd055@aliel.fr

Changes in v2:
- PATCH 1: change underscore to dash in pin node name,
           according to Xianwei's feedback.
- PATCH 3: remove clocks and clock-names as already defined in DTSI,
           according to Xianwei's feedback.
- Link to v1: https://lore.kernel.org/r/20260415-add-bluetooth-t7-vim4-v1-0-0ba0746cc1d6@aliel.fr

---
Ronald Claveau (4):
      arm64: dts: amlogic: t7: Add uart_c pinctrl pins group
      arm64: dts: amlogic: t7: Add UART controllers nodes
      arm64: dts: amlogic: t7: khadas-vim4: Remove redundant clocks from UART A
      arm64: dts: amlogic: t7: khadas-vim4: Enable Bluetooth

 .../dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts  | 21 ++++++-
 arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi        | 73 +++++++++++++++++++---
 2 files changed, 85 insertions(+), 9 deletions(-)
---
base-commit: 6aa9edb4f8266cfb913ee74f5e55116550b5574d
change-id: 20260414-add-bluetooth-t7-vim4-f01e03c4ec2a

Best regards,
-- 
Ronald Claveau <linux-kernel-dev@aliel.fr>


^ permalink raw reply

* [PATCH v4 3/4] arm64: dts: amlogic: t7: khadas-vim4: Remove redundant clocks from UART A
From: Ronald Claveau @ 2026-04-20 13:54 UTC (permalink / raw)
  To: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-arm-kernel, linux-amlogic, devicetree, linux-kernel,
	Ronald Claveau
In-Reply-To: <20260420-add-bluetooth-t7-vim4-v4-0-9505df0e7016@aliel.fr>

Remove clocks and clock-names for UART A, as they are defined in DTSI.

Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
---
 arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts | 2 --
 1 file changed, 2 deletions(-)

diff --git a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
index 69d6118ba57e7..3227ab27de107 100644
--- a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
+++ b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
@@ -250,6 +250,4 @@ &sd_emmc_c {
 
 &uart_a {
 	status = "okay";
-	clocks = <&xtal>, <&xtal>, <&xtal>;
-	clock-names = "xtal", "pclk", "baud";
 };

-- 
2.49.0


^ permalink raw reply related

* [PATCH v4 2/4] arm64: dts: amlogic: t7: Add UART controllers nodes
From: Ronald Claveau @ 2026-04-20 13:54 UTC (permalink / raw)
  To: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-arm-kernel, linux-amlogic, devicetree, linux-kernel,
	Ronald Claveau
In-Reply-To: <20260420-add-bluetooth-t7-vim4-v4-0-9505df0e7016@aliel.fr>

Add device tree nodes for UART B through F (serial@7a000 to
serial@82000), completing the UART controller description for the T7
SoC. Each node includes the peripheral clock.

While at it, move the uart_a node to its correct position in the
bus address order (0x78000) to comply with the DT requirement that
nodes be sorted by their reg address. Complete the
uart_a node with its peripheral clock (CLKID_SYS_UART_A) and the
associated clock-names, matching the vendor default clock assignment,
consistent with the other UART nodes.

Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
---
 arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi | 61 +++++++++++++++++++++++++----
 1 file changed, 54 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi b/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi
index 4a55d9641bc9b..81c26b1e3e7a4 100644
--- a/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi
+++ b/arch/arm64/boot/dts/amlogic/amlogic-t7.dtsi
@@ -577,13 +577,6 @@ gpio_intc: interrupt-controller@4080 {
 					<10 11 12 13 14 15 16 17 18 19 20 21>;
 			};
 
-			uart_a: serial@78000 {
-				compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
-				reg = <0x0 0x78000 0x0 0x18>;
-				interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>;
-				status = "disabled";
-			};
-
 			gp0: clock-controller@8080 {
 				compatible = "amlogic,t7-gp0-pll";
 				reg = <0x0 0x8080 0x0 0x20>;
@@ -713,6 +706,60 @@ pwm_ao_cd: pwm@60000 {
 				status = "disabled";
 			};
 
+			uart_a: serial@78000 {
+				compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
+				reg = <0x0 0x78000 0x0 0x18>;
+				interrupts = <GIC_SPI 168 IRQ_TYPE_EDGE_RISING>;
+				clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_A>, <&xtal>;
+				clock-names = "xtal", "pclk", "baud";
+				status = "disabled";
+			};
+
+			uart_b: serial@7a000 {
+				compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
+				reg = <0x0 0x7a000 0x0 0x18>;
+				interrupts = <GIC_SPI 169 IRQ_TYPE_EDGE_RISING>;
+				clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_B>, <&xtal>;
+				clock-names = "xtal", "pclk", "baud";
+				status = "disabled";
+			};
+
+			uart_c: serial@7c000 {
+				compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
+				reg = <0x0 0x7c000 0x0 0x18>;
+				interrupts = <GIC_SPI 170 IRQ_TYPE_EDGE_RISING>;
+				clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_C>, <&xtal>;
+				clock-names = "xtal", "pclk", "baud";
+				status = "disabled";
+			};
+
+			uart_d: serial@7e000 {
+				compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
+				reg = <0x0 0x7e000 0x0 0x18>;
+				interrupts = <GIC_SPI 171 IRQ_TYPE_EDGE_RISING>;
+				clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_D>, <&xtal>;
+				clock-names = "xtal", "pclk", "baud";
+				status = "disabled";
+			};
+
+			uart_e: serial@80000 {
+				compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
+				reg = <0x0 0x80000 0x0 0x18>;
+				interrupts = <GIC_SPI 172 IRQ_TYPE_EDGE_RISING>;
+				clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_E>, <&xtal>;
+				clock-names = "xtal", "pclk", "baud";
+				status = "disabled";
+			};
+
+			uart_f: serial@82000 {
+				compatible = "amlogic,t7-uart", "amlogic,meson-s4-uart";
+				reg = <0x0 0x82000 0x0 0x18>;
+				interrupts = <GIC_SPI 173 IRQ_TYPE_EDGE_RISING>;
+				clocks = <&xtal>, <&clkc_periphs CLKID_SYS_UART_F>, <&xtal>;
+				clock-names = "xtal", "pclk", "baud";
+				status = "disabled";
+			};
+
 			sd_emmc_a: mmc@88000 {
 				compatible = "amlogic,t7-mmc", "amlogic,meson-axg-mmc";
 				reg = <0x0 0x88000 0x0 0x800>;

-- 
2.49.0


^ permalink raw reply related

* [PATCH v4 4/4] arm64: dts: amlogic: t7: khadas-vim4: Enable Bluetooth
From: Ronald Claveau @ 2026-04-20 13:54 UTC (permalink / raw)
  To: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-arm-kernel, linux-amlogic, devicetree, linux-kernel,
	Ronald Claveau
In-Reply-To: <20260420-add-bluetooth-t7-vim4-v4-0-9505df0e7016@aliel.fr>

Enable UART C on the Khadas VIM4 board and attach the BCM43438
 compatible Bluetooth controller to it. The node configures the RTS/CTS
hardware flow control, the associated pinmux, the power supplies (vddao_3v3
and vddao_1v8), the 32 kHz LPO clock shared with the wifi32k fixed
clock, and the GPIO lines used for host wakeup, device wakeup and
shutdown.

Signed-off-by: Ronald Claveau <linux-kernel-dev@aliel.fr>
---
 .../dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts     | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
index 3227ab27de107..8ea7ae609fbd5 100644
--- a/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
+++ b/arch/arm64/boot/dts/amlogic/amlogic-t7-a311d2-khadas-vim4.dts
@@ -251,3 +251,22 @@ &sd_emmc_c {
 &uart_a {
 	status = "okay";
 };
+
+&uart_c {
+	status = "okay";
+	pinctrl-0 = <&uart_c_pins>;
+	pinctrl-names = "default";
+	uart-has-rtscts;
+
+	bluetooth {
+		compatible = "brcm,bcm43438-bt";
+		shutdown-gpios = <&gpio GPIOX_17 GPIO_ACTIVE_HIGH>;
+		host-wakeup-gpios = <&gpio GPIOX_18 GPIO_ACTIVE_HIGH>;
+		device-wakeup-gpios = <&gpio GPIOX_19 GPIO_ACTIVE_HIGH>;
+		max-speed = <3000000>;
+		clocks = <&wifi32k>;
+		clock-names = "lpo";
+		vbat-supply = <&vddao_3v3>;
+		vddio-supply = <&vddao_1v8>;
+	};
+};

-- 
2.49.0


^ permalink raw reply related

* Re: [PATCH v3 1/2] dt-bindings: input: Add PixArt PAJ7620 gesture sensor
From: Krzysztof Kozlowski @ 2026-04-20 13:59 UTC (permalink / raw)
  To: Harpreet Saini
  Cc: Rob Herring, linux-input, devicetree, linux-kernel,
	Dmitry Torokhov, Krzysztof Kozlowski, Conor Dooley
In-Reply-To: <20260418062241.104697-2-sainiharpreet29@yahoo.com>

On Sat, Apr 18, 2026 at 02:22:32AM -0400, Harpreet Saini wrote:
> The binding include mandatory power supplies (vdd, vbus, vled)

Drop this part

> and optional GPIO controller properties to describe the hardware's
> ability to repurpose SPI pins opeating in I2C mode.

And just explain what is the purpose of GPIO controller - is this a GPIO
controller?

Plus language typo, run spell check.

> 
> Signed-off-by: Harpreet Saini <sainiharpreet29@yahoo.com>
> ---
>  .../bindings/input/pixart,paj7620.yaml        | 79 +++++++++++++++++++
>  .../devicetree/bindings/vendor-prefixes.yaml  |  2 +
>  2 files changed, 81 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/pixart,paj7620.yaml
> 
> diff --git a/Documentation/devicetree/bindings/input/pixart,paj7620.yaml b/Documentation/devicetree/bindings/input/pixart,paj7620.yaml
> new file mode 100644
> index 000000000000..ad051cf641a6
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/pixart,paj7620.yaml
> @@ -0,0 +1,79 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/input/pixart,paj7620.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: PixArt PAJ7620 Gesture Sensor
> +
> +maintainers:
> +  - Harpreet Saini <sainiharpreet29@yahoo.com>
> +
> +description: |

Do not need '|' unless you need to preserve formatting.

> +  The PixArt PAJ7620 is a gesture recognition sensor with an integrated
> +  infrared LED and CMOS array. It communicates over an I2C interface and
> +  provides gesture data via a dedicated interrupt pin.
> +
> +properties:
> +  compatible:
> +    const: pixart,paj7620
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  vdd-supply:
> +    description: Main power supply.
> +
> +  vbus-supply:
> +    description: I/O and I2C bus power supply.
> +
> +  vled-supply:
> +    description: Power for the integrated IR LED.
> +
> +  linux,keycodes:
> +    minItems: 9
> +    maxItems: 9
> +    description: |

Do not need '|' unless you need to preserve formatting.

> +      List of keycodes mapping to the 9 supported gestures.
> +
> +  gpio-controller: true
> +
> +  "#gpio-cells":
> +    const: 2
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +  - vdd-supply
> +  - vbus-supply
> +  - vled-supply
> +

Missing allOf: with $ref to input.yaml schema. You use its properties.

Commit description said SPI, so missing ref to spi-peripheral-props.

> +additionalProperties: false

And this should be then unevaluatedProperties: false.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH 02/11] media: iris: Add iris vpu bus support and register it with iommu_buses
From: Vishnu Reddy @ 2026-04-20 14:02 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Abhinav Kumar, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Joerg Roedel, Will Deacon,
	Robin Murphy, Bjorn Andersson, Konrad Dybcio, Stefan Schmidt,
	Hans Verkuil, linux-media, linux-arm-msm, devicetree,
	linux-kernel, iommu
In-Reply-To: <7bnl34zyexz4z54fmz6ymax7izfey6nyp7lenhvfp3hkfmhdia@lssovgsnas72>


On 4/17/2026 11:49 PM, Dmitry Baryshkov wrote:
> On Fri, Apr 17, 2026 at 08:29:21PM +0530, Vishnu Reddy wrote:
>> apologies for re-sending (earlier responses was rejected due to HTML format)
> Ugh.
>
>> On 4/17/2026 8:22 PM, Vishnu Reddy wrote:
>>> On 4/14/2026 8:44 PM, Dmitry Baryshkov wrote:
>>>> On Tue, Apr 14, 2026 at 10:29:58AM +0530, Vishnu Reddy wrote:
>>>>> From: Vikash Garodia<vikash.garodia@oss.qualcomm.com>
>>>>>
>>>>> Add a dedicated iris VPU bus type and register it into the iommu_buses
>>>>> list. Iris devices require their own bus so that each device can run its
>>>>> own dma_configure() logic.
>>>> This really tells nothing, unless one has full context about the Iris
>>>> needs. Start by describing the issue (that the device needs to have
>>>> multiple devices talking to describe IOMMUs / VAs for several hardware
>>>> functions), then continue by describing what is needed from the IOMMU
>>>> subsys.
>>> This series handles firmware device which do not require multiple
>>> devices part.
>>> given this device need for specific IOMMU configuration, I'll update the
>>> description
>>> accordingly.
>>>
>>>>> Signed-off-by: Vikash Garodia<vikash.garodia@oss.qualcomm.com>
>>>>> Signed-off-by: Vishnu Reddy<busanna.reddy@oss.qualcomm.com>
>>>>> ---
>>>>>   drivers/iommu/iommu.c                           |  4 ++++
>>>>>   drivers/media/platform/qcom/iris/Makefile       |  4 ++++
>>>>>   drivers/media/platform/qcom/iris/iris_vpu_bus.c | 32 +++++++++++++++++++++++++
>>>>>   include/linux/iris_vpu_bus.h                    | 13 ++++++++++
>>>> How are you supposed to merge this? Through IOMMU tree? Through venus
>>>> tree? Can we add one single bus to the IOMMU code and use it for Iris,
>>>> Venus, FastRPC, host1x and all other device drivers which require
>>>> per-device DMA configuration?
>>> Separating out the bus definition and the Iris driver handling would
>>> provide a
>>> cleaner merge path.
> Then why wasn't it done from the ground up?
>
>>>> Your colleagues from the FastRPC team posted a very similar code few
>>>> weeks ago and got exactly the same feedback. Is there a reason why your
>>>> teams don't sync on the IOMMU parts at all?
>>> I would admit that I missed to review that, thank you for bringing that
>>> discussion.
>>> FastRPC patches generalizes the handling for host1x, FastRPC and the
>>> same can be
>>> extended for Iris. I have left few comments there.
>>>
>>>>>   4 files changed, 53 insertions(+)
>>>>>
>>>>> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
>>>>> index 61c12ba78206..d8ed6ef70ecd 100644
>>>>> --- a/drivers/iommu/iommu.c
>>>>> +++ b/drivers/iommu/iommu.c
>>>>> @@ -13,6 +13,7 @@
>>>>>   #include <linux/bug.h>
>>>>>   #include <linux/types.h>
>>>>>   #include <linux/init.h>
>>>>> +#include <linux/iris_vpu_bus.h>
>>>>>   #include <linux/export.h>
>>>>>   #include <linux/slab.h>
>>>>>   #include <linux/errno.h>
>>>>> @@ -179,6 +180,9 @@ static const struct bus_type * const iommu_buses[] = {
>>>>>   #ifdef CONFIG_CDX_BUS
>>>>>   	&cdx_bus_type,
>>>>>   #endif
>>>>> +#if IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS)
>>>>> +	&iris_vpu_bus_type,
>>>>> +#endif
>>>>>   };
>>>>>   /*
>>>>> diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile
>>>>> index 2abbd3aeb4af..6f4052b98491 100644
>>>>> --- a/drivers/media/platform/qcom/iris/Makefile
>>>>> +++ b/drivers/media/platform/qcom/iris/Makefile
>>>>> @@ -31,3 +31,7 @@ qcom-iris-objs += iris_platform_gen1.o
>>>>>   endif
>>>>>   obj-$(CONFIG_VIDEO_QCOM_IRIS) += qcom-iris.o
>>>>> +
>>>>> +ifdef CONFIG_VIDEO_QCOM_IRIS
>>>>> +obj-y += iris_vpu_bus.o
>>>>> +endif
>>>>> diff --git a/drivers/media/platform/qcom/iris/iris_vpu_bus.c b/drivers/media/platform/qcom/iris/iris_vpu_bus.c
>>>>> new file mode 100644
>>>>> index 000000000000..b51bb4b82b0e
>>>>> --- /dev/null
>>>>> +++ b/drivers/media/platform/qcom/iris/iris_vpu_bus.c
>>>>> @@ -0,0 +1,32 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>>> +/*
>>>>> + * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.
>>>>> + */
>>>>> +
>>>>> +#include <linux/device.h>
>>>>> +#include <linux/of_device.h>
>>>>> +
>>>>> +#include "iris_platform_common.h"
>>>>> +
>>>>> +static int iris_vpu_bus_dma_configure(struct device *dev)
>>>>> +{
>>>>> +	const u32 *f_id = dev_get_drvdata(dev);
>>>>> +
>>>>> +	if (!f_id)
>>>>> +		return -ENODEV;
>>>>> +
>>>>> +	return of_dma_configure_id(dev, dev->parent->of_node, true, f_id);
>>>> I think it was discussed that this is not enough. Some of devices need
>>>> multiple function IDs.
>>> In this glymur series we are following the legacy way of handling IOMMUs
>>> and does not
>>> require multi map.
> Why can't we land the version that has multiple entries? It's as if the
> teams are totally not in sync. The corresponding version is in works, it
> has been implemented, etc.
The main idea is to introduce firmware stream ID with iommu-map, while keeping the
other stream IDs described in legacy iommus way.
or, are you suggesting that, going forward, we should have VPU stream IDs described
_only_ with iommu-map for any new dts/bindings?

Thanks,
Vishnu Reddy.


^ permalink raw reply

* Re: [PATCH 04/11] media: iris: Add helper to create a context bank device on iris vpu bus
From: Vishnu Reddy @ 2026-04-20 14:03 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Bryan O'Donoghue, Vikash Garodia, Dikshita Agarwal,
	Abhinav Kumar, Mauro Carvalho Chehab, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Joerg Roedel, Will Deacon,
	Robin Murphy, Bjorn Andersson, Konrad Dybcio, Stefan Schmidt,
	Hans Verkuil, linux-media, linux-arm-msm, devicetree,
	linux-kernel, iommu
In-Reply-To: <sqg2db63gsjg3cxfdfbmndhxibzlprgviarbcl4l6isza36nc3@ljgg23nkuooi>


On 4/17/2026 11:53 PM, Dmitry Baryshkov wrote:
> On Fri, Apr 17, 2026 at 08:49:44PM +0530, Vishnu Reddy wrote:
>> On 4/14/2026 8:48 PM, Dmitry Baryshkov wrote:
>>> On Tue, Apr 14, 2026 at 10:30:00AM +0530, Vishnu Reddy wrote:
>>>> From: Vikash Garodia<vikash.garodia@oss.qualcomm.com>
>>>>
>>>> Add a helper function to allocate and register context bank (CB) device
>>>> on the iris vpu bus. The function ID associated with the CB is specified
>>>> from the platform data, allowing the bus dma_configure callback to apply
>>>> correct stream ID mapping when device is registered.
>>>>
>>>> Signed-off-by: Vikash Garodia<vikash.garodia@oss.qualcomm.com>
>>>> Signed-off-by: Vishnu Reddy<busanna.reddy@oss.qualcomm.com>
>>>> ---
>>>>   drivers/media/platform/qcom/iris/iris_resources.c | 33 +++++++++++++++++++++++
>>>>   drivers/media/platform/qcom/iris/iris_resources.h |  1 +
>>>>   2 files changed, 34 insertions(+)
>>>>
>>>> diff --git a/drivers/media/platform/qcom/iris/iris_resources.c b/drivers/media/platform/qcom/iris/iris_resources.c
>>>> index 773f6548370a..a25e0f2e9d26 100644
>>>> --- a/drivers/media/platform/qcom/iris/iris_resources.c
>>>> +++ b/drivers/media/platform/qcom/iris/iris_resources.c
>>>> @@ -6,6 +6,7 @@
>>>>   #include <linux/clk.h>
>>>>   #include <linux/devfreq.h>
>>>>   #include <linux/interconnect.h>
>>>> +#include <linux/iris_vpu_bus.h>
>>>>   #include <linux/pm_domain.h>
>>>>   #include <linux/pm_opp.h>
>>>>   #include <linux/pm_runtime.h>
>>>> @@ -141,3 +142,35 @@ int iris_disable_unprepare_clock(struct iris_core *core, enum platform_clk_type
>>>>   	return 0;
>>>>   }
>>>> +
>>>> +static void iris_release_cb_dev(struct device *dev)
>>>> +{
>>>> +	kfree(dev);
>>>> +}
>>>> +
>>>> +struct device *iris_create_cb_dev(struct iris_core *core, const char *name, const u32 *f_id)
>>> Please move into the bus code and make it generic enough.
>> Do you suggest to add a wrapper to pass the varying inputs to the generic
>> bus, something like this
>> struct device* create_and_register_device(dma_mask, parent_dev, *release,
>> dev_name,...)
> Definitely not the release function. The devname is also not that
> important. The rest, yes, you are correct.
>
>>>> +{
>>>> +	struct device *dev;
>>>> +	int ret;
>>>> +
>>>> +	dev = kzalloc_obj(*dev);
>>>> +	if (!dev)
>>>> +		return ERR_PTR(-ENOMEM);
>>>> +
>>>> +	dev->release = iris_release_cb_dev;
>>>> +	dev->bus = &iris_vpu_bus_type;
>>>> +	dev->parent = core->dev;
>>>> +	dev->coherent_dma_mask = core->iris_platform_data->dma_mask;
>>>> +	dev->dma_mask = &dev->coherent_dma_mask;
>>> Would you also need to set the of_node? See
>>> device_set_of_node_from_dev()
>> It might be needed for FastRPC as they are following sub node approach, Iris
>> does not need.
> Wouldn't it save you from passing it to of_dma_configure_id()?
Iris will pass parent device of_node, setting parent device is enough.
>>>> +
>>>> +	dev_set_name(dev, "%s", name);
>>>> +	dev_set_drvdata(dev, (void *)f_id);
>>>> +
>>>> +	ret = device_register(dev);
>>>> +	if (ret) {
>>>> +		put_device(dev);
>>>> +		return ERR_PTR(ret);
>>>> +	}
>>>> +
>>>> +	return dev;
>>>> +}
>>>> diff --git a/drivers/media/platform/qcom/iris/iris_resources.h b/drivers/media/platform/qcom/iris/iris_resources.h
>>>> index 6bfbd2dc6db0..4a494627ff23 100644
>>>> --- a/drivers/media/platform/qcom/iris/iris_resources.h
>>>> +++ b/drivers/media/platform/qcom/iris/iris_resources.h
>>>> @@ -15,5 +15,6 @@ int iris_unset_icc_bw(struct iris_core *core);
>>>>   int iris_set_icc_bw(struct iris_core *core, unsigned long icc_bw);
>>>>   int iris_disable_unprepare_clock(struct iris_core *core, enum platform_clk_type clk_type);
>>>>   int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type clk_type);
>>>> +struct device *iris_create_cb_dev(struct iris_core *core, const char *name, const u32 *f_id);
>>>>   #endif
>>>>
>>>> -- 
>>>> 2.34.1
>>>>

^ permalink raw reply

* Re: [PATCH v3 1/2] dt-bindings: rng: mtk-rng: add SMC-based TRNG variants
From: Krzysztof Kozlowski @ 2026-04-20 14:07 UTC (permalink / raw)
  To: Daniel Golle
  Cc: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Matthias Brugger, AngeloGioacchino Del Regno,
	Sean Wang, linux-crypto, devicetree, linux-kernel,
	linux-arm-kernel, linux-mediatek
In-Reply-To: <585fc832e4e5d3656bd25ecee6bafb636993104a.1776600269.git.daniel@makrotopia.org>

On Sun, Apr 19, 2026 at 01:05:01PM +0100, Daniel Golle wrote:
> +    rng {
> +            compatible = "mediatek,mt7981-rng";

I asked at v1. Reminded at v2. Nothing serious, but repeating myself is
pointless and kind of waste of time.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v3 1/5] dt-bindings: vendor-prefixes: Add QST Corporation
From: Krzysztof Kozlowski @ 2026-04-20 14:08 UTC (permalink / raw)
  To: Hardik Phalet
  Cc: gregkh, jic23, andy, conor+dt, devicetree, dlechner, krzk+dt,
	linux-iio, linux-kernel, linux-staging, me, nuno.sa, robh, skhan,
	Hardik Phalet
In-Reply-To: <20260420-qmc5883p-driver-v3-1-da1e97088f8b@pm.me>

On Sun, Apr 19, 2026 at 10:32:20PM +0000, Hardik Phalet wrote:
> Add the vendor prefix 'qstcorp' for QST Corporation, a manufacturer of

(and here goes few-word explanation why you have chosen qstcorp or just
the link to the website)

> MEMS sensors.
> 
> Signed-off-by: Hardik Phalet <hardik.phalet@pm.me>
> ---
>  Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++

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

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v3 2/5] dt-bindings: iio: magnetometer: QSTCORP QMC5883P
From: Krzysztof Kozlowski @ 2026-04-20 14:10 UTC (permalink / raw)
  To: Hardik Phalet
  Cc: gregkh, jic23, andy, conor+dt, devicetree, dlechner, krzk+dt,
	linux-iio, linux-kernel, linux-staging, me, nuno.sa, robh, skhan,
	Hardik Phalet
In-Reply-To: <20260420-qmc5883p-driver-v3-2-da1e97088f8b@pm.me>

On Sun, Apr 19, 2026 at 10:32:32PM +0000, Hardik Phalet wrote:
> Add the device tree binding document for the QST QMC5883P, a 3-axis
> anisotropic magneto-resistive (AMR) sensor with a 16-bit ADC that
> communicates over I2C.
> 
> Add a MAINTAINERS entry for the QSTCORP QMC5883P devicetree binding.
> 
> Signed-off-by: Hardik Phalet <hardik.phalet@pm.me>
> ---
>  .../iio/magnetometer/qstcorp,qmc5883p.yaml         | 48 ++++++++++++++++++++++
>  MAINTAINERS                                        |  6 +++
>  2 files changed, 54 insertions(+)

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

<form letter>
This is an automated instruction, just in case, because many review
tags are being ignored. If you know the process, just skip it entirely
(please do not feel offended by me posting it here - no bad intentions
intended, no patronizing, I just want to avoid wasted efforts). If you
do not know the process, here is a short explanation:

Please add Acked-by/Reviewed-by/Tested-by tags when posting new
versions of patchset, under or above your Signed-off-by tag, unless
patch changed significantly (e.g. new properties added to the DT
bindings). Tag is "received", when provided in a message replied to you
on the mailing list. Tools like b4 can help here ('b4 trailers -u ...').
However, there's no need to repost patches *only* to add the tags. The
upstream maintainer will do that for tags received on the version they
apply.

https://elixir.bootlin.com/linux/v6.15/source/Documentation/process/submitting-patches.rst#L591
</form letter>

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v3 1/2] dt-bindings: spmi: glymur-spmi-pmic-arb: Add compatible for Qualcomm Hawi SoC
From: Krzysztof Kozlowski @ 2026-04-20 14:14 UTC (permalink / raw)
  To: Fenglin Wu
  Cc: Stephen Boyd, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Konrad Dybcio, Dmitry Baryshkov, Subbaraman Narayanamurthy,
	David Collins, linux-arm-msm, linux-kernel, devicetree, kernel
In-Reply-To: <20260419-hawi-spmi-v3-1-b04ee909cb87@oss.qualcomm.com>

On Sun, Apr 19, 2026 at 07:25:52PM -0700, Fenglin Wu wrote:
> The PMIC arbiter in the Qualcomm Hawi SoC is version v8.5, which
> introduces parity and CRC checks for data received from the PMIC,
> as well as NACK checks for command sequences except for read.
> All other features in PMIC arbiter remain the same as the one in
> the Qualcomm Glymur SoC, with the only differences being some
> additional error status checks.
> 
> Therefore, add a string for "qcom,hawi-spmi-pmic-arb" as a compatible
> entry for "qcom,glymur-spmi-pmic-arb".
> 
> Signed-off-by: Fenglin Wu <fenglin.wu@oss.qualcomm.com>
> ---
>  Documentation/devicetree/bindings/spmi/qcom,glymur-spmi-pmic-arb.yaml | 1 +
>  1 file changed, 1 insertion(+)

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

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v3 1/2] dt-bindings: bridge: This patch adds new content to the lontium,lt9611.yaml binding file
From: Krzysztof Kozlowski @ 2026-04-20 14:15 UTC (permalink / raw)
  To: syyang, robh, krzk+dt, conor+dt, andrzej.hajda, neil.armstrong,
	dmitry.baryshkov, maarten.lankhorst, rfoss, mripard
  Cc: Laurent.pinchart, tzimmermann, jonas, jernej.skrabec, devicetree,
	dri-devel, linux-kernel, yangsunyun1993, xmzhu
In-Reply-To: <192f1948-bb9a-4831-a1cd-5ef500cfb413@kernel.org>

On Mon, Apr 20, 2026 at 01:33:46PM +0200, Krzysztof Kozlowski wrote:
> > -audio
> >  1.sample rates of 32~192 KHz and sample sizes
> >    of 16~24 bits
> >  2.SPDIF interface supports PCM, Dolbydigital, DTS digital audio
> >    at up to 192KHz frame rate
> > 
> > -Miscellaneous
> >  1.CSC:RGB<->YUV444<->YUV422
> 
> Commit msg is not a datasheet introduction chapter. What does this all
> mean for compatibility? What is the benefit of listing all this?
> 
> You did not even explain the differences between C, UX and EXD, so all
> above is pointless. Explain the differences and why devices are not
> compatible.
>

Also, you have checkpatch errors.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH 0/4] Add hstimer support for H616 and T113-S3
From: Andre Przywara @ 2026-04-20 14:14 UTC (permalink / raw)
  To: Michal Piekos
  Cc: Daniel Lezcano, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Chen-Yu Tsai, Jernej Skrabec, Samuel Holland,
	Maxime Ripard, linux-kernel, devicetree, linux-arm-kernel,
	linux-sunxi
In-Reply-To: <xlvldmapdoql7nt3swube5vd6fdiosq7rt5afg246xertko5fa@4irmzupkar23>

Hi Michal,

On 4/20/26 13:27, Michal Piekos wrote:
> On Sun, Apr 19, 2026 at 10:55:39PM +0200, Andre Przywara wrote:
>> On Sun, 19 Apr 2026 14:46:06 +0200
>> Michal Piekos <michal.piekos@mmpsystems.pl> wrote:
>>
>> Hi Michal,
>>
>>> Add support for Allwinner H616 high speed timer in sun5i hstimer driver
>>> and describe corresponding nodes in dts for H616 and T113-S3.
>>>
>>> H616 uses same model as existing driver except register shift compared
>>> to older variants.
>>>
>>> Added register layout abstraction in the driver, extended the binding
>>> with new compatibles and wired up dts nodes for H616 and T113-S3 which
>>> uses H616 as fallback compatible.
>>
>> Can you say *why* we need this? IIUC Linux only ever uses one clock
>> source, and selects the (non-optional) Generic Timer (aka arch timer)
>> for that? So can you say what this hstimer clock source adds? I guess
>> higher resolution, but what is your use case, so why would you need the
>> 200 MHz? And does this offset the higher access cost of an MMIO
>> access, compared to the arch timer's sysreg based access? Also, IIUC,
>> people would need to manually select this as the clocksource, why and
>> when would they do so? (Given they even know about it in the first
>> place).
>> Also the hstimer hasn't been used since the A20, so nobody seemed to
>> have missed it meanwhile?
>>
>> Cheers,
>> Andre
>>
> I took the table from https://linux-sunxi.org/Linux_mainlining_effort as
> a todo list and wanted to help with it. I do not have own use case for
> this timer. If it is not needed then I will spin v2 to include your
> comments and abandon it.

Ah, that's good to know, and thanks for picking things from that list! I 
don't think there is a particular need to abandon your work, we could as 
well upstream it. At least the DT changes should be added, so that other 
DT users could make use of the timers - after all it's a Linux 
implementation choice to utilise just one timer. But please go ahead and 
post a complete v2, I don't think it hurts to have HSTIMER support in 
the kernel.
And while you are at it: can you figure out what the need is for using 
two timers? One is a clock source, the other is for clock events? And 
why do we limit the counters and timers to 32 bit? Even the A13 manual 
lists them as 56 bits, and a wraparound time of roughly 21 seconds (with 
32 bit counters) does not sound very long to me.


Not sure what your primary motivation for fixing Allwinner support is, 
but we could probably find more worthwhile targets. Do you have 
Allwinner boards other than the OrangePi Zero 3? There are not many low 
hanging fruits on the H616 left (MBUS and LDOs(?) maybe), but the A523 
has quite some missing drivers still, some of them probably more on the 
easy side.

If you are stuck with the OpiZero3, then you could just look and check 
the existing devices, and verify their operation. For instance I think 
USB-OTG is still broken - across most Allwinner SoCs actually, so it's a 
sunxi driver issue.

Thanks,
Andre

> 
> Michal
> 
>>>
>>> Signed-off-by: Michal Piekos <michal.piekos@mmpsystems.pl>
>>> ---
>>> Michal Piekos (4):
>>>        dt-bindings: timer: allwinner,sun5i-a13-hstimer: add H616 and T113-S3
>>>        clocksource/drivers/sun5i: add H616 hstimer support
>>>        arm64: dts: allwinner: h616: add hstimer node
>>>        arm: dts: allwinner: t113s: add hstimer node
>>>
>>>   .../timer/allwinner,sun5i-a13-hstimer.yaml         |  8 +++-
>>>   arch/arm/boot/dts/allwinner/sun8i-t113s.dtsi       | 12 +++++
>>>   arch/arm64/boot/dts/allwinner/sun50i-h616.dtsi     |  9 ++++
>>>   drivers/clocksource/timer-sun5i.c                  | 56 +++++++++++++++++++---
>>>   4 files changed, 78 insertions(+), 7 deletions(-)
>>> ---
>>> base-commit: faeab166167f5787719eb8683661fd41a3bb1514
>>> change-id: 20260413-h616-t113s-hstimer-62939948f91c
>>>
>>> Best regards,
>>
>>


^ permalink raw reply

* Re: [PATCH v3 1/2] dt-bindings: bridge: This patch adds new content to the lontium,lt9611.yaml binding file
From: Krzysztof Kozlowski @ 2026-04-20 14:18 UTC (permalink / raw)
  To: syyang, robh, krzk+dt, conor+dt, andrzej.hajda, neil.armstrong,
	dmitry.baryshkov, maarten.lankhorst, rfoss, mripard
  Cc: Laurent.pinchart, tzimmermann, jonas, jernej.skrabec, devicetree,
	dri-devel, linux-kernel, yangsunyun1993, xmzhu
In-Reply-To: <20260420-sophisticated-scorpion-from-camelot-b9f326@quoll>

On 20/04/2026 16:15, Krzysztof Kozlowski wrote:
> On Mon, Apr 20, 2026 at 01:33:46PM +0200, Krzysztof Kozlowski wrote:
>>> -audio
>>>  1.sample rates of 32~192 KHz and sample sizes
>>>    of 16~24 bits
>>>  2.SPDIF interface supports PCM, Dolbydigital, DTS digital audio
>>>    at up to 192KHz frame rate
>>>
>>> -Miscellaneous
>>>  1.CSC:RGB<->YUV444<->YUV422
>>
>> Commit msg is not a datasheet introduction chapter. What does this all
>> mean for compatibility? What is the benefit of listing all this?
>>
>> You did not even explain the differences between C, UX and EXD, so all
>> above is pointless. Explain the differences and why devices are not
>> compatible.
>>
> 
> Also, you have checkpatch errors.
> 

... and finally (apologies for making it in three emails, checkpatch
warnings come from different toolset): your subject is pretty breaking
every standard rule.

It's redundant, not informative in various ways. It misses proper
prefix, it misses even basic description WHAT. Everything can be a "new
content".

Please use subject prefixes matching the subsystem. You can get them for
example with `git log --oneline -- DIRECTORY_OR_FILE` on the directory
your patch is touching. For bindings, the preferred subjects are
explained here:
https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters

Please do not use "This commit/patch/change", but imperative mood. See
longer explanation here:
https://elixir.bootlin.com/linux/v6.16/source/Documentation/process/submitting-patches.rst#L94

Drop second/last, redundant "bindings". The "dt-bindings" prefix is
already stating that these are bindings.
See also:
https://elixir.bootlin.com/linux/v6.17-rc3/source/Documentation/devicetree/bindings/submitting-patches.rst#L18

And obviously "file" is pointless. Can you add content NOT to a file?
Like add it to a directory?


Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH] phy: qualcomm: qmp-combo: update DP PHY PLL programming on Glymur
From: Mahadevan P @ 2026-04-20 14:18 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Vinod Koul, Neil Armstrong, Wesley Cheng, Abel Vesa,
	Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, linux-arm-msm, linux-phy, linux-kernel, devicetree,
	Ritesh Kumar
In-Reply-To: <CAO9ioeXT0jxu875jBsAbOVrDqonASWSmmxJbP0AiitXb-gUANQ@mail.gmail.com>



On 4/19/2026 6:48 PM, Dmitry Baryshkov wrote:
> On Sun, 19 Apr 2026 at 13:16, Mahadevan P <mahadevan.p@oss.qualcomm.com> wrote:
>>
>> The existing DP PHY PLL and AUX configuration for the Glymur platform
>> does not fully follow the Hardware Programming Guide requirements for
>> DP over Type-C, which results in DP link bring-up failures.
>>
>> Update the DP PHY programming sequence and PLL-related register
>> settings to align with the latest HPG recommendations. With this
>> change, DP link training completes successfully on Glymur-based
>> platforms.
>>
>> Fixes: d10736db98d2 ("phy: qualcomm: qmp-combo: Add DP offsets and settings for Glymur platforms")
>> Signed-off-by: Ritesh Kumar <ritesh.kumar@oss.qualcomm.com>
>> Signed-off-by: Mahadevan P <mahadevan.p@oss.qualcomm.com>
>> ---
>> Tested on a Glymur CRD platform with two DisplayPorts over USB Type-C:
>> - Link training completes successfully
>> - Display comes up at expected resolutions
>> ---
>>   drivers/phy/qualcomm/phy-qcom-qmp-combo.c          | 213 +++++++++++++++------
>>   drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v8.h |   2 +
>>   2 files changed, 154 insertions(+), 61 deletions(-)
>>
>> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
>> index 93f1aa10d400..bb5e4091fd0c 100644
>> --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
>> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c
>> @@ -283,8 +283,8 @@ static const unsigned int qmp_v8_n3_usb43dpphy_regs_layout[QPHY_LAYOUT_SIZE] = {
>>          [QPHY_DP_AON_TOGGLE_ENABLE]             = QPHY_V8_PCS_AON_DP_AON_TOGGLE_ENABLE,
>>
>>          [QPHY_COM_RESETSM_CNTRL]                = QSERDES_V8_COM_RESETSM_CNTRL,
>> -       [QPHY_COM_C_READY_STATUS]               = QSERDES_V8_COM_C_READY_STATUS,
>> -       [QPHY_COM_CMN_STATUS]                   = QSERDES_V8_COM_CMN_STATUS,
>> +       [QPHY_COM_C_READY_STATUS]               = QSERDES_V8_COM_C_READY_STATUS_N3,
>> +       [QPHY_COM_CMN_STATUS]                   = QSERDES_V8_COM_CMN_STATUS_N3,
>>          [QPHY_COM_BIAS_EN_CLKBUFLR_EN]  = QSERDES_V8_COM_BIAS_EN_CLKBUFLR_EN,
>>
>>          [QPHY_DP_PHY_STATUS]                    = QSERDES_V8_DP_PHY_STATUS,
>> @@ -1386,10 +1386,10 @@ static const struct qmp_phy_init_tbl qmp_v6_n4_dp_serdes_tbl[] = {
>>   };
>>
>>   static const struct qmp_phy_init_tbl qmp_v8_dp_serdes_tbl[] = {
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SSC_STEP_SIZE2_MODE0, 0x00),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SSC_STEP_SIZE2_MODE0, 0x02),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CP_CTRL_MODE0, 0x06),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_PLL_RCTRL_MODE1, 0x10),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_PLL_CCTRL_MODE1, 0x01),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_PLL_RCTRL_MODE0, 0x16),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_PLL_CCTRL_MODE0, 0x36),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CORECLK_DIV_MODE0, 0x0a),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_DIV_FRAC_START1_MODE0, 0x00),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
>> @@ -1404,12 +1404,13 @@ static const struct qmp_phy_init_tbl qmp_v8_dp_serdes_tbl[] = {
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SYSCLK_BUF_ENABLE, 0x06),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_PLL_IVCO, 0x07),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SYSCLK_EN_SEL, 0x3b),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_RESETSM_CNTRL, 0x20),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP_EN, 0x00),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_VCO_TUNE_CTRL, 0x00),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_VCO_TUNE_MAP, 0x00),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CLK_SELECT, 0x30),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CORE_CLK_EN, 0x00),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CMN_CONFIG_1, 0x56),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CMN_CONFIG_1, 0x16),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SVS_MODE_CLK_SEL, 0x15),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CMN_MODE_CONTD1, 0x24),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_DCC_CAL_1, 0x40),
>> @@ -1445,26 +1446,26 @@ static const struct qmp_phy_init_tbl qmp_v6_n4_dp_tx_tbl[] = {
>>   };
>>
>>   static const struct qmp_phy_init_tbl qmp_v8_n3p_dp_tx_tbl[] = {
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TRANSMITTER_EN_CTRL, 0x3f),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TRANSMITTER_EN_CTRL, 0x1a),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_VMODE_CTRL1, 0x40),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_ANA_INTERFACE_SELECT1, 0x07),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_ANA_INTERFACE_SELECT2, 0x18),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_PCS_INTERFACE_SELECT1, 0x50),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_LANE_MODE_1, 0x0d),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_CLKBUF_ENABLE, 0x07),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_RESET_TSYNC_EN_CTRL, 0x0a),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_CLKBUF_ENABLE, 0x87),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_RESET_TSYNC_EN_CTRL, 0x0f),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX_LVL_UPDATE_CTRL, 0x0f),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TRAN_DRVR_EMP_EN, 0x5f),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX0_EMP_POST1_LVL, 0x20),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX1_EMP_POST1_LVL, 0x20),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX0_EMP_POST1_LVL, 0x2b),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX1_EMP_POST1_LVL, 0x2b),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX0_PRE1_EMPH, 0x20),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX1_PRE1_EMPH, 0x20),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX0_DRV_LVL, 0x00),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX1_DRV_LVL, 0x00),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_HIGHZ_DRVR_EN, 0x30),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_HIGHZ_DRVR_EN, 0x3f),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_LANE_MODE_2, 0x50),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_LANE_MODE_3, 0x51),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX_DCC_ANA_CTRL2, 0x00),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX_DCC_ANA_CTRL2, 0x0c),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX0_RESTRIM_CAL_CTRL, 0x20),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX1_RESTRIM_CAL_CTRL, 0x02),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_LALB_TX0_RESTRIM_POST_CAL_OFFSET, 0x10),
>> @@ -1589,11 +1590,12 @@ static const struct qmp_phy_init_tbl qmp_v6_n4_dp_serdes_tbl_hbr3[] = {
>>
>>   static const struct qmp_phy_init_tbl qmp_v8_dp_serdes_tbl_rbr[] = {
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_HSCLK_SEL_1, 0x05),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x7a),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x02),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x8d),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x27),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SSC_STEP_SIZE1_MODE0, 0x83),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP1_MODE0, 0x37),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP2_MODE0, 0x04),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SSC_STEP_SIZE2_MODE0, 0x02),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP1_MODE0, 0x1c),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP2_MODE0, 0x02),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_DEC_START_MODE0, 0x54),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_DIV_FRAC_START2_MODE0, 0x00),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_DIV_FRAC_START3_MODE0, 0x06),
>> @@ -1601,16 +1603,17 @@ static const struct qmp_phy_init_tbl qmp_v8_dp_serdes_tbl_rbr[] = {
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_VCO_TUNE2_MODE0, 0x00),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_HSCLK_SEL_1, 0x05),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CMN_MODE_CONTD3, 0x07),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CLK_FWD_CONFIG_1, 0x30),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_IP_CTRL_AND_DP_SEL, 0xa4),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CLK_FWD_CONFIG_1, 0x3f),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_IP_CTRL_AND_DP_SEL, 0xa7),
>>   };
>>
>>   static const struct qmp_phy_init_tbl qmp_v8_dp_serdes_tbl_hbr[] = {
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_HSCLK_SEL_1, 0x04),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x21),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x04),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xf6),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x20),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SSC_STEP_SIZE1_MODE0, 0x18),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP1_MODE0, 0x07),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SSC_STEP_SIZE2_MODE0, 0x02),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP1_MODE0, 0x08),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP2_MODE0, 0x07),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_DEC_START_MODE0, 0x46),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_DIV_FRAC_START2_MODE0, 0x00),
>> @@ -1627,7 +1630,8 @@ static const struct qmp_phy_init_tbl qmp_v8_dp_serdes_tbl_hbr2[] = {
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_HSCLK_SEL_1, 0x03),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xf6),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x20),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SSC_STEP_SIZE1_MODE0, 0x0),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SSC_STEP_SIZE1_MODE0, 0x18),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SSC_STEP_SIZE2_MODE0, 0x02),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_PLL_RCTRL_MODE0, 0x16),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_PLL_CCTRL_MODE0, 0x36),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP1_MODE0, 0x10),
>> @@ -1637,9 +1641,9 @@ static const struct qmp_phy_init_tbl qmp_v8_dp_serdes_tbl_hbr2[] = {
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_DIV_FRAC_START3_MODE0, 0x05),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_VCO_TUNE1_MODE0, 0xae),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_VCO_TUNE2_MODE0, 0x02),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_HSCLK_SEL_1, 0x00),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_IP_CTRL_AND_DP_SEL, 0xbf),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_HSCLK_SEL_1, 0x03),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_IP_CTRL_AND_DP_SEL, 0xab),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIAS_EN_CLKBUFLR_EN, 0x17),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_RESETSM_CNTRL, 0x20),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CMN_MODE_CONTD3, 0x03),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CLK_FWD_CONFIG_1, 0x3f),
>> @@ -1647,8 +1651,8 @@ static const struct qmp_phy_init_tbl qmp_v8_dp_serdes_tbl_hbr2[] = {
>>
>>   static const struct qmp_phy_init_tbl qmp_v8_dp_serdes_tbl_hbr3[] = {
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_HSCLK_SEL_1, 0x02),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x63),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x0c),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0x14),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x25),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SSC_STEP_SIZE1_MODE0, 0x5b),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SSC_STEP_SIZE2_MODE0, 0x02),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CP_CTRL_MODE0, 0x06),
>> @@ -1656,7 +1660,7 @@ static const struct qmp_phy_init_tbl qmp_v8_dp_serdes_tbl_hbr3[] = {
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_PLL_CCTRL_MODE0, 0x36),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CORECLK_DIV_MODE0, 0x0a),
>>
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP1_MODE0, 0x17),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP1_MODE0, 0x18),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_LOCK_CMP2_MODE0, 0x15),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_DEC_START_MODE0, 0x4f),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_DIV_FRAC_START1_MODE0, 0x00),
>> @@ -1675,19 +1679,19 @@ static const struct qmp_phy_init_tbl qmp_v8_dp_serdes_tbl_hbr3[] = {
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SYS_CLK_CTRL, 0x02),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SYSCLK_BUF_ENABLE, 0x06),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_PLL_IVCO, 0x07),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SYSCLK_EN_SEL, 0x04),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SYSCLK_EN_SEL, 0x3b),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_VCO_TUNE_CTRL, 0x00),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_VCO_TUNE_MAP, 0x00),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CLK_SELECT, 0x30),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CORE_CLK_EN, 0x00),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CMN_CONFIG_1, 0x16),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_SVS_MODE_CLK_SEL, 0x15),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CLK_FWD_CONFIG_1, 0x30),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIAS_EN_CLKBUFLR_EN, 0x10),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CLK_FWD_CONFIG_1, 0x3f),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIAS_EN_CLKBUFLR_EN, 0x17),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CMN_MODE_CONTD3, 0x05),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_CMN_MODE_CONTD1, 0x24),
>>          QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_BIN_VCOCAL_HSCLK_SEL_1, 0x02),
>> -       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_IP_CTRL_AND_DP_SEL, 0x84),
>> +       QMP_PHY_INIT_CFG(QSERDES_V8_USB43_COM_IP_CTRL_AND_DP_SEL, 0x87),
>>   };
>>
>>   static const struct qmp_phy_init_tbl sc8280xp_usb43dp_serdes_tbl[] = {
>> @@ -3145,6 +3149,30 @@ static int qmp_combo_configure_dp_swing(struct qmp_combo *qmp)
>>          return 0;
>>   }
>>
>> +static bool qmp_v8_combo_configure_dp_mode(struct qmp_combo *qmp)
>> +{
>> +       bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE);
>> +       const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
>> +       u32 val;
>> +
>> +       val = DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
>> +               DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN |
>> +               DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN;
>> +
>> +       if (dp_opts->lanes == 1 || dp_opts->lanes == 2) {
>> +               if (reverse)
>> +                       val &= ~DP_PHY_PD_CTL_LANE_2_3_PWRDN;
>> +               else
>> +                       val &= ~DP_PHY_PD_CTL_LANE_0_1_PWRDN;
>> +       }
>> +
>> +       writel(val, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);
>> +
>> +       writel(0x5c, qmp->dp_dp_phy + QSERDES_DP_PHY_MODE);
> 
> Are you saying that we don't need to write 0x4c here in case of the
> reverse mode? Was that changed and why?
Yes for glymur it is changed
DP2_PHY_DP_PHY_PD_CTL
Normal Orientation: 0x7D for 4lane; 0x75 for 1Lane or 2Lanne
Flip Orientation: 0x7D for 4Lane; 0x6D for 1Lane or 2Lane

> 
>> +
>> +       return reverse;
>> +}
>> +
>>   static void qmp_v3_configure_dp_tx(struct qmp_combo *qmp)
>>   {
>>          const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
>> @@ -3324,16 +3352,70 @@ static void qmp_v4_dp_aux_init(struct qmp_combo *qmp)
>>                 qmp->dp_dp_phy + QSERDES_V4_DP_PHY_AUX_INTERRUPT_MASK);
>>   }
>>
>> +static int qmp_v8_helper_configure_dp_phy(struct qmp_combo *qmp)
>> +{
>> +       const struct qmp_phy_cfg *cfg = qmp->cfg;
>> +       u32 status;
>> +       int ret;
>> +
>> +       writel(0x0f, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG_1);
>> +
>> +       qmp_v8_combo_configure_dp_mode(qmp);
>> +
>> +       writel(0x13, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG1);
>> +       writel(0xa4, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG2);
>> +
>> +       writel(0x05, qmp->dp_dp_phy + QSERDES_V4_DP_PHY_TX0_TX1_LANE_CTL);
>> +       writel(0x05, qmp->dp_dp_phy + QSERDES_V4_DP_PHY_TX2_TX3_LANE_CTL);
>> +
>> +       ret = qmp->cfg->configure_dp_clocks(qmp);
>> +       if (ret)
>> +               return ret;
>> +
>> +       writel(0x01, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG);
>> +       writel(0x05, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG);
>> +       writel(0x01, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG);
>> +       writel(0x09, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG);
>> +
>> +       writel(0x20, qmp->dp_serdes + cfg->regs[QPHY_COM_RESETSM_CNTRL]);
>> +
>> +       if (readl_poll_timeout(qmp->dp_serdes + cfg->regs[QPHY_COM_C_READY_STATUS],
>> +                       status,
>> +                       ((status & BIT(0)) > 0),
>> +                       500,
>> +                       10000))
>> +               return -ETIMEDOUT;
>> +
>> +       if (readl_poll_timeout(qmp->dp_serdes + cfg->regs[QPHY_COM_CMN_STATUS],
>> +                       status,
>> +                       ((status & BIT(0)) > 0),
>> +                       500,
>> +                       10000))
>> +               return -ETIMEDOUT;
>> +
>> +       if (readl_poll_timeout(qmp->dp_serdes + cfg->regs[QPHY_COM_CMN_STATUS],
>> +                       status,
>> +                       ((status & BIT(1)) > 0),
>> +                       500,
>> +                       10000))
>> +               return -ETIMEDOUT;
>> +
>> +       return 0;
>> +}
>> +
>>   static void qmp_v8_dp_aux_init(struct qmp_combo *qmp)
>>   {
>>          const struct qmp_phy_cfg *cfg = qmp->cfg;
>>
>> -       writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_PSR_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
>> -              DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
>> -              qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);
>> +       writel(0x3f, qmp->dp_serdes + QSERDES_V8_USB43_COM_CLK_FWD_CONFIG_1);
>> +
>> +       writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN |
>> +               DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN |
>> +               DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN,
>> +               qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL);
> 
> No AUX_PWRDN? Why?

Dropped PSR_PWRDN added LANE_0_1_PWRDN and LANE_2_3_PWRDN

Reffered latest Harware Programing guide its says we need to program 
0x7D to DP2_DP_PHY_PD_CTL register

> 
>>
>>          /* Turn on BIAS current for PHY/PLL */
>> -       writel(0x1c, qmp->dp_serdes + cfg->regs[QPHY_COM_BIAS_EN_CLKBUFLR_EN]);
>> +       writel(0x17, qmp->dp_serdes + cfg->regs[QPHY_COM_BIAS_EN_CLKBUFLR_EN]);
>>
>>          writel(0x00, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG0);
>>          writel(0x13, qmp->dp_dp_phy + QSERDES_DP_PHY_AUX_CFG1);
>> @@ -3361,8 +3443,8 @@ static void qmp_v4_configure_dp_tx(struct qmp_combo *qmp)
>>          writel(0x27, qmp->dp_tx + cfg->regs[QPHY_TX_TX_DRV_LVL]);
>>          writel(0x27, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_DRV_LVL]);
>>
>> -       writel(0x20, qmp->dp_tx + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]);
>> -       writel(0x20, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]);
>> +       writel(0x2b, qmp->dp_tx + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]);
>> +       writel(0x2b, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]);
>>
>>          qmp_combo_configure_dp_swing(qmp);
>>   }
>> @@ -3370,48 +3452,41 @@ static void qmp_v4_configure_dp_tx(struct qmp_combo *qmp)
>>   static int qmp_v8_configure_dp_clocks(struct qmp_combo *qmp)
>>   {
>>          const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts;
>> -       u32 phy_vco_div;
>>          unsigned long pixel_freq;
>> -       const struct qmp_phy_cfg *cfg = qmp->cfg;
>>
>>          switch (dp_opts->link_rate) {
>>          case 1620:
>> -               phy_vco_div = 0x4;
>>                  pixel_freq = 1620000000UL / 2;
>>                  break;
>>          case 2700:
>> -               phy_vco_div = 0x2;
>>                  pixel_freq = 2700000000UL / 2;
>>                  break;
>>          case 5400:
>> -               phy_vco_div = 0x4;
>>                  pixel_freq = 5400000000UL / 4;
>>                  break;
>>          case 8100:
>> -               phy_vco_div = 0x3;
>>                  pixel_freq = 8100000000UL / 6;
>>                  break;
>>          default:
>>                  /* Other link rates aren't supported */
>>                  return -EINVAL;
>>          }
>> -       writel(phy_vco_div, qmp->dp_dp_phy + cfg->regs[QPHY_DP_PHY_VCO_DIV]);
> 
> Hmm?
  In the Glymur PHY, the clock gen/div is entirely done within the PLL, 
removed the register writes for DP_PHY_VCO_DIV

> 
>>
>>          /* disable core reset tsync */
>>          writel(0x09, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG);
>>
>> -       writel(0x04, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_AUXLESS_SETUP_CYC);
>> -       writel(0x08, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_AUXLESS_SILENCE_CYC);
>> +       writel(0x09, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_AUXLESS_SETUP_CYC);
>> +       writel(0x11, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_AUXLESS_SILENCE_CYC);
>>          writel(0x08, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_LFPS_CYC);
>> -       writel(0x11, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_LFPS_PERIOD);
>> +       writel(0x33, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_LFPS_PERIOD);
>>
>>          writel(0x3e, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_TSYNC_OVRD);
>>          writel(0x05, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_TX2_TX3_LANE_CTL);
>>          writel(0x05, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_TX0_TX1_LANE_CTL);
>>          writel(0x01, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_AUXLESS_CFG1);
>> -       writel(0x11, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_LFPS_PERIOD);
>> +       writel(0x33, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_LFPS_PERIOD);
>>          writel(0x1f, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_LN0_DRV_LVL);
>> -       writel(0x1f, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_LN1_DRV_LVL);
>> +       writel(0x02, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_LN1_DRV_LVL);
>>
>>          clk_set_rate(qmp->dp_link_hw.clk, dp_opts->link_rate * 100000);
>>          clk_set_rate(qmp->dp_pixel_hw.clk, pixel_freq);
>> @@ -3558,7 +3633,7 @@ static int qmp_v8_configure_dp_phy(struct qmp_combo *qmp)
>>          u32 status;
>>          int ret;
>>
>> -       ret = qmp_v456_configure_dp_phy(qmp);
>> +       ret = qmp_v8_helper_configure_dp_phy(qmp);
>>          if (ret < 0)
>>                  return ret;
>>
>> @@ -3570,13 +3645,13 @@ static int qmp_v8_configure_dp_phy(struct qmp_combo *qmp)
>>          } else if (dp_opts->lanes == 2) {
>>                  bias0_en = reverse ? 0x3f : 0x15;
>>                  bias1_en = reverse ? 0x15 : 0x3f;
>> -               drvr0_en = 0x10;
>> -               drvr1_en = 0x10;
>> +               drvr0_en = 0x30;
>> +               drvr1_en = 0x30;
>>          } else {
>>                  bias0_en = 0x3f;
>>                  bias1_en = 0x3f;
>> -               drvr0_en = 0x34;
>> -               drvr1_en = 0x34;
>> +               drvr0_en = 0x30;
>> +               drvr1_en = 0x30;
>>          }
>>
>>          writel(drvr0_en, qmp->dp_tx + cfg->regs[QPHY_TX_HIGHZ_DRVR_EN]);
>> @@ -3584,6 +3659,14 @@ static int qmp_v8_configure_dp_phy(struct qmp_combo *qmp)
>>          writel(drvr1_en, qmp->dp_tx2 + cfg->regs[QPHY_TX_HIGHZ_DRVR_EN]);
>>          writel(bias1_en, qmp->dp_tx2 + cfg->regs[QPHY_TX_TRANSCEIVER_BIAS_EN]);
>>
>> +       writel(0x03, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_TSYNC_OVRD);
>> +       writel(0x23, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_TSYNC_OVRD);
>> +       writel(0x22, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_TSYNC_OVRD);
>> +
>> +       writel(0x0a, qmp->dp_tx + QSERDES_V8_LALB_RESET_TSYNC_EN_CTRL);
>> +       writel(0x0a, qmp->dp_tx2 + QSERDES_V8_LALB_RESET_TSYNC_EN_CTRL);
>> +
>> +       writel(0x3e, qmp->dp_dp_phy + QSERDES_V8_DP_PHY_TSYNC_OVRD);
>>          writel(0x08, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG);
>>          udelay(100);
>>          writel(0x09, qmp->dp_dp_phy + QSERDES_DP_PHY_CFG);
>> @@ -3591,16 +3674,24 @@ static int qmp_v8_configure_dp_phy(struct qmp_combo *qmp)
>>
>>          if (readl_poll_timeout(qmp->dp_dp_phy + cfg->regs[QPHY_DP_PHY_STATUS],
>>                          status,
>> -                       ((status & BIT(1)) > 0),
>> +                       ((status & BIT(0)) > 0),
>>                          500,
>>                          10000))
>>                  return -ETIMEDOUT;
>>
>> -       writel(0x00, qmp->dp_tx + cfg->regs[QPHY_TX_TX_DRV_LVL]);
>> -       writel(0x00, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_DRV_LVL]);
>> +       if (readl_poll_timeout(qmp->dp_serdes + cfg->regs[QPHY_COM_CMN_STATUS],
>> +                       status,
>> +                       ((status & BIT(0)) > 0),
>> +                       500,
>> +                       10000))
>> +               return -ETIMEDOUT;
>>
>> -       writel(0x2b, qmp->dp_tx + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]);
>> -       writel(0x2b, qmp->dp_tx2 + cfg->regs[QPHY_TX_TX_EMP_POST1_LVL]);
>> +       if (readl_poll_timeout(qmp->dp_serdes + cfg->regs[QPHY_COM_CMN_STATUS],
>> +                       status,
>> +                       ((status & BIT(1)) > 0),
>> +                       500,
>> +                       10000))
>> +               return -ETIMEDOUT;
>>
>>          return 0;
>>   }
>> diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v8.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v8.h
>> index d8ac4c4a2c31..b35b486cfa56 100644
>> --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v8.h
>> +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-com-v8.h
>> @@ -71,5 +71,7 @@
>>   #define QSERDES_V8_COM_ADDITIONAL_MISC                 0x1b4
>>   #define QSERDES_V8_COM_CMN_STATUS                      0x2c8
>>   #define QSERDES_V8_COM_C_READY_STATUS                  0x2f0
>> +#define QSERDES_V8_COM_CMN_STATUS_N3                   0x314
>> +#define QSERDES_V8_COM_C_READY_STATUS_N3               0x33c
>>
>>   #endif
>>
>> ---
>> base-commit: 1c7cc4904160c6fc6377564140062d68a3dc93a0
>> change-id: 20260414-glymur_dp-ffea83a75969
>>
>> Best regards,
>> --
>> Mahadevan P <mahadevan.p@oss.qualcomm.com>
>>
> 
> 

^ permalink raw reply

* Re: [PATCH v3 1/7] dt-bindings: spi: qcom,spi-qcom-qspi: Add qcom,qcs615-qspi compatible
From: Krzysztof Kozlowski @ 2026-04-20 14:19 UTC (permalink / raw)
  To: Viken Dadhaniya
  Cc: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, cros-qcom-dts-watchers,
	linux-arm-msm, linux-spi, devicetree, linux-kernel
In-Reply-To: <20260420-spi-nor-v3-1-7de325a29010@oss.qualcomm.com>

On Mon, Apr 20, 2026 at 11:42:48AM +0530, Viken Dadhaniya wrote:
> Add support for the QSPI controller on QCS615 SoC.
> 
> Move allOf section after required properties and add if:then constraint
> to require minimum 2 interconnects for qcs615 variant.
> 
> Signed-off-by: Viken Dadhaniya <viken.dadhaniya@oss.qualcomm.com>
> ---
>  .../devicetree/bindings/spi/qcom,spi-qcom-qspi.yaml | 21 ++++++++++++++++++---
>  1 file changed, 18 insertions(+), 3 deletions(-)

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

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v3 3/5] iio: magnetometer: add driver for QST QMC5883P
From: Jonathan Cameron @ 2026-04-20 14:22 UTC (permalink / raw)
  To: Hardik Phalet
  Cc: gregkh, andy, conor+dt, devicetree, dlechner, krzk+dt, linux-iio,
	linux-kernel, linux-staging, me, nuno.sa, robh, skhan,
	Hardik Phalet
In-Reply-To: <20260420-qmc5883p-driver-v3-3-da1e97088f8b@pm.me>

On Sun, 19 Apr 2026 22:32:43 +0000
Hardik Phalet <hardik.phalet@pm.me> wrote:

> Add an IIO driver for the QST QMC5883P, a 3-axis anisotropic
> magneto-resistive (AMR) magnetometer with a 16-bit ADC, communicating
> over I2C. There is no existing upstream driver for this device.
> 
> The driver supports:
>  - Raw magnetic field readings on X, Y and Z axes
>  - Four full-scale ranges (+/-2 G, +/-8 G, +/-12 G, +/-30 G) selectable
>    via IIO_CHAN_INFO_SCALE
>  - Output data rate configurable via IIO_CHAN_INFO_SAMP_FREQ (10, 50,
>    100, 200 Hz)
>  - vdd-supply regulator management
> 
> Regmap with an rbtree cache is used throughout. CTRL_1 and CTRL_2 bit
> fields are accessed via regmap_field to avoid read-modify-write races.
> The STATUS register is marked precious so regmap never reads it
> speculatively and clears the DRDY/OVFL bits unexpectedly.
> 
> The probe-time init sequence is: soft reset, wait 300 us for POR
> completion, deassert reset, then drop the register cache so subsequent
> RMW writes read fresh values from the device. After reset the chip is in
> MODE_SUSPEND per datasheet §6.2.4, and is left there; the first
> userspace access will wake it via runtime PM (added in a follow-up
> patch).
> 
> Cleanup is fully devm-managed via devm_regulator_get_enable() and
> devm_iio_device_register().
> 
> Oversampling ratio and runtime PM are added in follow-up patches.
> 
> Signed-off-by: Hardik Phalet <hardik.phalet@pm.me>
Hi Hardik,

Welcome to IIO. Various comments inline.

Thanks,

Jonathan

> diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
> index fb313e591e85..333c5e6f231d 100644
> --- a/drivers/iio/magnetometer/Kconfig
> +++ b/drivers/iio/magnetometer/Kconfig
> @@ -298,4 +298,15 @@ config YAMAHA_YAS530
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called yamaha-yas.
>  
> +config QMC5883P
Should be somewhere further up. In theory at least these files are in alphanumeric
order.

> +	tristate "QMC5883P 3-Axis Magnetometer"
> +	depends on I2C
> +	select REGMAP_I2C
> +	help
> +	  Say yes here to build support for QMC5883P I2C-based
> +	  3-axis magnetometer chip.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called qmc5883p.
> +
>  endmenu
> diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
> index 5bd227f8c120..ff519a055d77 100644
> --- a/drivers/iio/magnetometer/Makefile
> +++ b/drivers/iio/magnetometer/Makefile
> @@ -39,3 +39,5 @@ obj-$(CONFIG_SI7210)			+= si7210.o
>  obj-$(CONFIG_TI_TMAG5273)		+= tmag5273.o
>  
>  obj-$(CONFIG_YAMAHA_YAS530)		+= yamaha-yas530.o
> +
> +obj-$(CONFIG_QMC5883P) += qmc5883p.o
Alphabetical order.

> diff --git a/drivers/iio/magnetometer/qmc5883p.c b/drivers/iio/magnetometer/qmc5883p.c
> new file mode 100644
> index 000000000000..e4a76ae7c2cf
> --- /dev/null
> +++ b/drivers/iio/magnetometer/qmc5883p.c
> @@ -0,0 +1,574 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * qmc5883p.c - QMC5883P magnetometer driver

No point in having the file name in the file.  The only thing that can
do is become wrong in the longer term!

> + *
> + * Copyright 2026 Hardik Phalet <hardik.phalet@pm.me>
> + *
> + * TODO: add triggered buffer support, PM, OSR, DSR
> + *
Blank line above doesn't add anything. Mind you I'm not really
sure driver todo lists add much in general. It's perfectly ok to
never support some of these features.

> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/units.h>
> +#include <linux/unaligned.h>
Seems a very short list.  e.g. dev_printk.h is missing.
Give this another look and follow the (unfortunately) rather fuzzy
version of Include What You Use principles.

> +/*
> + * Oversampling rate
Single line comment seems fine here.
Same for other ones above.


> + */
> +#define QMC5883P_OSR_8 0x00
> +#define QMC5883P_OSR_4 0x01
> +#define QMC5883P_OSR_2 0x02
> +#define QMC5883P_OSR_1 0x03

> +
> +struct qmc5883p_data {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct mutex mutex; /* protects regmap and rf field accesses */

Need more on that comment.  Why does it mater?  regmap has it's own internal
locks.  Are there some sequences of accesses that must be done without
anything else in between?

> +	struct qmc5883p_rf rf;
> +};
> +
> +enum qmc5883p_channels {

I don't think the named nature of the enum is used. If not, just make
it anonymous.

> +	AXIS_X = 0,
> +	AXIS_Y,
> +	AXIS_Z,
> +};
>
> +
> +static const struct regmap_config qmc5883p_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 8,
> +	.max_register = 0x0B,
> +	.cache_type = REGCACHE_RBTREE,

Why this rather than MAPLE?  Unless you have strong reason just
go with REGCACHE_MAPLE so no one asks ;)


> +	.rd_table = &qmc5883p_readable_table,
> +	.wr_table = &qmc5883p_writable_table,
> +	.volatile_table = &qmc5883p_volatile_table,
> +	.precious_table = &qmc5883p_precious_table,
> +};
> +
> +static const struct reg_field qmc5883p_rf_osr =
> +	REG_FIELD(QMC5883P_REG_CTRL_1, 4, 5);
> +static const struct reg_field qmc5883p_rf_odr =
> +	REG_FIELD(QMC5883P_REG_CTRL_1, 2, 3);
> +static const struct reg_field qmc5883p_rf_mode =
> +	REG_FIELD(QMC5883P_REG_CTRL_1, 0, 1);
> +static const struct reg_field qmc5883p_rf_rng =
> +	REG_FIELD(QMC5883P_REG_CTRL_2, 2, 3);
> +static const struct reg_field qmc5883p_rf_sftrst =
> +	REG_FIELD(QMC5883P_REG_CTRL_2, 7, 7);
> +static const struct reg_field qmc5883p_rf_chip_id =
> +	REG_FIELD(QMC5883P_REG_CHIP_ID, 0, 7);
> +
> +/*
> + * qmc5883p_get_measure - read all three axes.

I think that bit is obvious from the function signature so I'd drop it.

> + * Must be called with data->mutex held.

Instead of a comment, use markings on the function so the compiler / static
analysis can check it.  Look at __must_hold()  That acts as both a real

check and documentation.


> + */
> +static int qmc5883p_get_measure(struct qmc5883p_data *data, s16 *x, s16 *y,
> +				s16 *z)
> +{
> +	int ret;
> +	u8 reg_data[6];

	__le16 reg_data[3];

and use aligned accessors below.

> +	unsigned int status;
> +
> +	/*
> +	 * Poll the status register until DRDY is set or timeout.
> +	 * Read the whole register in one shot so that OVFL is captured from
> +	 * the same read: reading 0x09 clears both DRDY and OVFL, so a second
> +	 * read would always see OVFL=0.
> +	 * At ODR=10Hz one period is 100ms; use 150ms as a safe upper bound.
> +	 */
> +	ret = regmap_read_poll_timeout(data->regmap, QMC5883P_REG_STATUS,
> +				       status, status & QMC5883P_STATUS_DRDY,
> +				       QMC5883P_DRDY_POLL_US,
> +				       150 * (MICRO / MILLI));
> +	if (ret)
> +		return ret;
> +
> +	if (status & QMC5883P_STATUS_OVFL) {
> +		dev_warn_ratelimited(data->dev,
> +			"data overflow, consider reducing field range\n");
> +		ret = -ERANGE;
> +		return ret;
	
		return -ERANGE;

> +	}
> +
> +	ret = regmap_bulk_read(data->regmap, QMC5883P_REG_X_LSB, reg_data,
> +			       ARRAY_SIZE(reg_data));
> +	if (ret)
> +		return ret;
> +
> +	*x = (s16)get_unaligned_le16(&reg_data[0]);
> +	*y = (s16)get_unaligned_le16(&reg_data[2]);
> +	*z = (s16)get_unaligned_le16(&reg_data[4]);
> +
> +	return ret;
> +}
> +
> +static int qmc5883p_read_raw(struct iio_dev *indio_dev,
> +			     const struct iio_chan_spec *chan, int *val,
> +			     int *val2, long mask)
> +{
> +	s16 x, y, z;
> +	struct qmc5883p_data *data = iio_priv(indio_dev);
> +	int ret;
> +	unsigned int regval;

When nothing else gives an order to definitions go with reverse
xmas tree. 

> +
> +	guard(mutex)(&data->mutex);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = qmc5883p_get_measure(data, &x, &y, &z);
> +		if (ret < 0)
> +			return ret;
> +		switch (chan->address) {
> +		case AXIS_X:
> +			*val = x;
> +			break;
> +		case AXIS_Y:
> +			*val = y;
> +			break;
> +		case AXIS_Z:
> +			*val = z;
> +			break;
> +		}
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		ret = regmap_field_read(data->rf.rng, &regval);
> +		if (ret < 0)
> +			return ret;
> +		*val = qmc5883p_scale[regval][0];
> +		*val2 = qmc5883p_scale[regval][1];
> +		return IIO_VAL_INT_PLUS_NANO;
> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		ret = regmap_field_read(data->rf.odr, &regval);
> +		if (ret < 0)
> +			return ret;
> +		*val = qmc5883p_odr[regval];
> +		return IIO_VAL_INT;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int qmc5883p_write_scale(struct qmc5883p_data *data, int val, int val2)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(qmc5883p_scale); i++) {
> +		if (qmc5883p_scale[i][0] == val && qmc5883p_scale[i][1] == val2)
> +			return regmap_field_write(data->rf.rng, i);

Another sashiko one I might have missed.  Given there is only one field
for all channels, is having per channel scale appropriate?

Under the ABI it's not wrong to do that, as any ABI element being modified
is allowed to modify any other (that's there to handle complex interactions)
but it might not be the best we can do wrt to a user friendly interface.


> +	}
> +
> +	return -EINVAL;
> +}

...

> +
> +static int qmc5883p_write_raw(struct iio_dev *indio_dev,
> +			      struct iio_chan_spec const *chan, int val,
> +			      int val2, long mask)
> +{
> +	struct qmc5883p_data *data = iio_priv(indio_dev);
> +	int ret, restore;
> +
> +	guard(mutex)(&data->mutex);
> +
> +	ret = regmap_field_write(data->rf.mode, QMC5883P_MODE_SUSPEND);

Sashiko was complaining about this and what mode we are in after reset
before calling this function. See below.  I think the solution
is probably to put it in normal mode in probe() and then deal with this
stuff as runtime pm later.


> +	if (ret)
> +		return ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		ret = qmc5883p_write_odr(data, val);
> +		break;
> +	case IIO_CHAN_INFO_SCALE:
> +		ret = qmc5883p_write_scale(data, val, val2);
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	restore = regmap_field_write(data->rf.mode, QMC5883P_MODE_NORMAL);
> +	if (restore && !ret)
> +		ret = restore;
> +
> +	return ret;
> +}
> +
> +/*
> + * qmc5883p_read_avail - expose available values to userspace.
> + *
> + * Creates the _available sysfs attributes automatically:
> + *   in_magn_sampling_frequency_available
> + *   in_magn_scale_available

Probably standard enough for an IIO driver to not need a comment
that is more about what the core code is doing with this than what
this function does.

I'd drop the comment. It is the sort of thing that will become out
of date as a driver evolves and doesn't bring enough value to make
that risk worthwhile.

> + */
> +static int qmc5883p_read_avail(struct iio_dev *indio_dev,
> +			       struct iio_chan_spec const *chan,
> +			       const int **vals, int *type, int *length,
> +			       long mask)
> +{
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*vals = qmc5883p_odr;
> +		*type = IIO_VAL_INT;
> +		*length = ARRAY_SIZE(qmc5883p_odr);
> +		return IIO_AVAIL_LIST;
> +
> +	case IIO_CHAN_INFO_SCALE:
> +		*vals = (const int *)qmc5883p_scale;
> +		*type = IIO_VAL_INT_PLUS_NANO;
> +		*length = ARRAY_SIZE(qmc5883p_scale) * 2;
> +		return IIO_AVAIL_LIST;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +/*
> + * Tell the IIO core how to parse sysfs writes. Without this, the core
> + * defaults to IIO_VAL_INT_PLUS_MICRO (6 fractional digits), which would
> + * silently truncate nano-scale writes like "0.000000040" to 0.

Not sure this comment is really needed given this is a
pretty standard IIO thing.

> + */
> +static int qmc5883p_write_raw_get_fmt(struct iio_dev *indio_dev,
> +				      struct iio_chan_spec const *chan,
> +				      long mask)
> +{
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SCALE:
> +		return IIO_VAL_INT_PLUS_NANO;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		return IIO_VAL_INT;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info qmc5883p_info = {
> +	.read_raw = qmc5883p_read_raw,
> +	.write_raw = qmc5883p_write_raw,
> +	.write_raw_get_fmt = qmc5883p_write_raw_get_fmt,
> +	.read_avail = qmc5883p_read_avail,
> +};
> +
> +static int qmc5883p_rf_init(struct qmc5883p_data *data)
> +{
> +	struct regmap *regmap = data->regmap;
> +	struct device *dev = data->dev;
> +	struct qmc5883p_rf *rf = &data->rf;
> +
> +	rf->osr = devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_osr);

Might be worth looking at whether you can use
devm_regmap_field_bulk_alloc() without impacting the code readability too
much. I'm fine with this if that option looks too ugly by needing an
enum to index the fields.  Main positive is it would avoid the
repetition in this function.



> +	if (IS_ERR(rf->osr))
> +		return PTR_ERR(rf->osr);
> +
> +	rf->odr = devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_odr);
> +	if (IS_ERR(rf->odr))
> +		return PTR_ERR(rf->odr);
> +
> +	rf->mode = devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_mode);
> +	if (IS_ERR(rf->mode))
> +		return PTR_ERR(rf->mode);
> +
> +	rf->rng = devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_rng);
> +	if (IS_ERR(rf->rng))
> +		return PTR_ERR(rf->rng);
> +
> +	rf->sftrst = devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_sftrst);
> +	if (IS_ERR(rf->sftrst))
> +		return PTR_ERR(rf->sftrst);
> +
> +	rf->chip_id = devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_chip_id);
> +	if (IS_ERR(rf->chip_id))
> +		return PTR_ERR(rf->chip_id);
> +
> +	return 0;
> +}
> +

> +
> +#define QMC5883P_CHAN(ch)                                                 \
> +	{                                                                 \
> +		.type = IIO_MAGN,                                         \
> +		.channel2 = IIO_MOD_##ch,                                 \
> +		.modified = 1,                                            \
> +		.address = AXIS_##ch,                                     \
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |            \
> +				      BIT(IIO_CHAN_INFO_SCALE),           \
> +		.info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), \

See above, whether scale is really separate of should be in shared_by_type.


> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> +		.info_mask_shared_by_type_available =                     \
> +			BIT(IIO_CHAN_INFO_SAMP_FREQ),                     \
> +	}
> +
> +static const struct iio_chan_spec qmc5883p_channels[] = {
> +	QMC5883P_CHAN(X),
> +	QMC5883P_CHAN(Y),
> +	QMC5883P_CHAN(Z),
> +};
> +
> +static int qmc5883p_chip_init(struct qmc5883p_data *data)
> +{
> +	int ret;
> +
> +	ret = regmap_field_write(data->rf.sftrst, 1);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * The datasheet does not specify a post-reset delay, but POR
> +	 * completion takes up to 250 microseconds. Use 300 microseconds
> +	 * to be safe.

Very minor, but wrap comments up to 80 chars. That gives us:
(my email client has a ruler which makes these easy to spot!)

	 * The datasheet does not specify a post-reset delay, but POR completion
	 * takes up to 250 microseconds. Use 300 microseconds to be safe.
> +	 */
> +	fsleep(300);
> +
> +	ret = regmap_field_write(data->rf.sftrst, 0);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Soft reset restored every register to its default. Drop the cache
> +	 * so subsequent RMW writes read fresh values from the device.
> +	 */
> +	regcache_drop_region(data->regmap, QMC5883P_REG_CHIP_ID,
> +			     QMC5883P_REG_CTRL_2);
> +
> +	/* Chip is now in MODE_SUSPEND per datasheet §6.2.4. Leave it there. */

As below, sashiko raises concern that we can't read if if suspend mode so after
this patch the driver doesn't function right.  I haven't looked into it in detail
but sounds plausible.

> +	return 0;
> +}
> +
> +static int qmc5883p_probe(struct i2c_client *client)
> +{
> +	struct device *dev = &client->dev;
> +	struct qmc5883p_data *data;
> +	struct iio_dev *indio_dev;
> +	struct regmap *regmap;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	regmap = devm_regmap_init_i2c(client, &qmc5883p_regmap_config);
> +	if (IS_ERR(regmap))
> +		return dev_err_probe(dev, PTR_ERR(regmap),
> +				     "regmap initialization failed\n");
> +
> +	data = iio_priv(indio_dev);
> +	i2c_set_clientdata(client, indio_dev);

Add this when it is used. I'd guess in the runtime pm patch.
Note that shashiko raised some concerns about bisectibility and they
make me wonder if there are problems if runtime pm is simply disabled.
To handle that we normally make sure the device is in whatever mode is
needed to take a reading before we turn on runtime pm.

https://sashiko.dev/#/patchset/20260420-qmc5883p-driver-v3-0-da1e97088f8b%40pm.me

(Note this wasn't running on linux-iio until a few days ago so your
earlier versions weren't covered!)

> +	data->dev = dev;
> +	data->regmap = regmap;
> +
> +	mutex_init(&data->mutex);
For new code prefer
	ret = devm_mutex_init(dev, &data->mutex);
	if (ret)
		return ret;

It brings minor benefits for lock debugging hence I'm not inclined
to update old code, but we might as well use best practice in
new code.

> +
> +	ret = qmc5883p_rf_init(data);
> +	if (ret)
...


> +static const struct of_device_id qmc5883p_of_match[] = {
> +	{ .compatible = "qstcorp,qmc5883p" },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, qmc5883p_of_match);
> +
> +static const struct i2c_device_id qmc5883p_id[] = {
> +	{ "qmc5883p", 0 },

	{ "qmc5883p" },

Is enough.  Under the C spec .driver_data gets set to 0 anyway
but more important is it isn't used, so we shouldn't set it.  Any change
to add more parts will require changing that line anyway so we aren't
saving on future churn by setting it.

> +	{ }
> +};
> +MODULE_DEVICE_TABLE(i2c, qmc5883p_id);


^ permalink raw reply

* Re: [PATCH v3 1/7] dt-bindings: spi: qcom,spi-qcom-qspi: Add qcom,qcs615-qspi compatible
From: Mark Brown @ 2026-04-20 14:23 UTC (permalink / raw)
  To: Viken Dadhaniya
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, cros-qcom-dts-watchers, linux-arm-msm, linux-spi,
	devicetree, linux-kernel
In-Reply-To: <20260420-spi-nor-v3-1-7de325a29010@oss.qualcomm.com>

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

On Mon, Apr 20, 2026 at 11:42:48AM +0530, Viken Dadhaniya wrote:
> Add support for the QSPI controller on QCS615 SoC.
> 
> Move allOf section after required properties and add if:then constraint
> to require minimum 2 interconnects for qcs615 variant.

Please submit patches using subject lines reflecting the style for the
subsystem, this makes it easier for people to identify relevant patches.
Look at what existing commits in the area you're changing are doing and
make sure your subject lines visually resemble what they're doing.
There's no need to resubmit to fix this alone.

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

^ permalink raw reply

* [PATCH v8 0/2] Introduce Synology Microp driver
From: Markus Probst @ 2026-04-20 14:24 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Greg Kroah-Hartman
  Cc: platform-driver-x86, linux-leds, devicetree, linux-kernel,
	rust-for-linux, Markus Probst

Synology uses a microcontroller in their NAS devices connected to a
serial port to control certain LEDs, fan speeds, a beeper, to handle
proper shutdown and restart, buttons and fan failures.

This patch series depends on the rust led abstraction [1] and the rust
serdev abstraction [2].

This is only a initial version of the driver able to control LEDs.
The following rust abstractions would be required, to implement the
remaining features:
- hwmon (include/linux/hwmon.h)
- input (include/linux/input.h)
- sysoff handler + hardware protection shutdown (include/linux/reboot.h)

[1] https://lore.kernel.org/rust-for-linux/20260329-rust_leds-v13-0-21a599c5b2d1@posteo.de/
[2] https://lore.kernel.org/rust-for-linux/20260411-rust_serdev-v4-0-845e960c6627@posteo.de/

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
Changes in v8:
- removed unnecessary Copy and Clone derive
- added `BLINK_DELAY` constant
- added compatible id fallbacks
- moved dt schema patch before the driver
- added ds411p
- Link to v7: https://lore.kernel.org/r/20260411-synology_microp_initial-v7-0-9a3a094e763a@posteo.de

Changes in v7:
- remove list of compatible ids from commit msg
- explain what makes the different models not compatible in the commit msg
- remove unnecessary examples
- Link to v6: https://lore.kernel.org/r/20260405-synology_microp_initial-v6-0-08fde474b6c9@posteo.de

Changes in v6:
- moved devicetree bindings patch at the end of the set
- remove several patches
- move of id table from model.rs to synology_microp.rs
- remove the model! macro
- use if blocks in devicetree schema to narrow down the
  fan-failure-gpios property
- add multiple devicetree examples to test if blocks
- Link to v5: https://lore.kernel.org/r/20260329-synology_microp_initial-v5-0-27cb80bdf591@posteo.de

Changes in v5:
- add esata led support
- use different compatible for each model
- add visibility modifier to of_device_table macro
- fix match data missing when using PRP0001
- Link to v4: https://lore.kernel.org/r/20260320-synology_microp_initial-v4-0-0423ddb83ca4@posteo.de

Changes in v4:
- convert to monolithic driver and moved it into drivers/platform
- removed mfd rust abstraction
- moved dt-bindings to embedded-controller
- Link to v3: https://lore.kernel.org/r/20260313-synology_microp_initial-v3-0-ad6ac463a201@posteo.de

Changes in v3:
- remove `default n` from Kconfig entry, as n is the default already.
- select RUST_SERIAL_DEV_BUS_ABSTRACTIONS in Kconfig
- add mfd rust abstraction
- split core and led parts into their own driver. It should now be considered a
  MFD device.
- split led part of dt binding into its own file
- Link to v2: https://lore.kernel.org/r/20260308-synology_microp_initial-v2-0-9389963f31c5@posteo.de

Changes in v2:
- fix missing tabs in MAINTAINERS file
- remove word binding from patch subject
- add missing signed-off-by
- add missing help entry in Kconfig
- add missing spdx license headers
- remove no-check{,-cpu}-fan properties from the dt-bindings and replace
  them with the check_fan module parameter
- use patternProperties for leds in dt-bindings
- license dt-binding as GPL-2.0-only OR BSD-2-Clause
- move driver from staging tree into mfd tree and mark it as work in
  progress inside Kconfig
- only register alert and usb led if fwnode is present
- Link to v1: https://lore.kernel.org/r/20260306-synology_microp_initial-v1-0-fcffede6448c@posteo.de

To: Markus Probst <markus.probst@posteo.de>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Miguel Ojeda <ojeda@kernel.org>
To: Boqun Feng <boqun@kernel.org>
To: Gary Guo <gary@garyguo.net>
To: Björn Roy Baron <bjorn3_gh@protonmail.com>
To: Benno Lossin <lossin@kernel.org>
To: Andreas Hindborg <a.hindborg@kernel.org>
To: Alice Ryhl <aliceryhl@google.com>
To: Trevor Gross <tmgross@umich.edu>
To: Danilo Krummrich <dakr@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: rust-for-linux@vger.kernel.org

---
Markus Probst (2):
      dt-bindings: embedded-controller: Add synology microp devices
      platform: Add initial synology microp driver

 .../synology,ds1825p-microp.yaml                   | 108 ++++++++
 MAINTAINERS                                        |   6 +
 drivers/platform/Kconfig                           |   2 +
 drivers/platform/Makefile                          |   1 +
 drivers/platform/synology_microp/Kconfig           |  13 +
 drivers/platform/synology_microp/Makefile          |   3 +
 drivers/platform/synology_microp/TODO              |   7 +
 drivers/platform/synology_microp/command.rs        |  54 ++++
 drivers/platform/synology_microp/led.rs            | 281 +++++++++++++++++++++
 drivers/platform/synology_microp/model.rs          |  49 ++++
 .../platform/synology_microp/synology_microp.rs    | 110 ++++++++
 11 files changed, 634 insertions(+)
---
base-commit: 3131ff5a117498bb4b9db3a238bb311cbf8383ce
change-id: 20260306-synology_microp_initial-0f7dac7b7496
prerequisite-change-id: 20251217-rust_serdev-ee5481e9085c:v4
prerequisite-patch-id: 52b17274481cc770c257d8f95335293eca32a2c5
prerequisite-patch-id: eec47e5051640d08bcd34a9670b98804449cad52
prerequisite-patch-id: f24b68c71c3f69371e8ac0251efca0a023b31cc4
prerequisite-patch-id: d0686cf451ef899a06d468adfba51ccd84e6ff98
prerequisite-change-id: 20251114-rust_leds-a959f7c2f7f9:v13
prerequisite-patch-id: 818700f22dcb9676157c985f82762d7c607b861e
prerequisite-patch-id: b15ffa7d95d9260151bfb116b259c4473f721c82
prerequisite-patch-id: 8c47e0d107530f577a1be0b79f8ee791f95d3cbe


^ permalink raw reply

* [PATCH v8 1/2] dt-bindings: embedded-controller: Add synology microp devices
From: Markus Probst @ 2026-04-20 14:24 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Greg Kroah-Hartman
  Cc: platform-driver-x86, linux-leds, devicetree, linux-kernel,
	rust-for-linux, Markus Probst
In-Reply-To: <20260420-synology_microp_initial-v8-0-7946a9124491@posteo.de>

Add the Synology Microp devicetree bindings. Those devices are
microcontrollers found on Synology NAS devices. They are connected to a
serial port on the host device.

Those devices are used to control certain LEDs, fan speeds, a beeper, to
handle buttons, fan failures and to properly shutdown and reboot the
device.

The device has a different feature set depending on the Synology NAS
model, like having different number of fans, buttons and leds. Depending
on the architecture of the model, they also need a different system
shutdown behaviour.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 .../synology,ds1825p-microp.yaml                   | 108 +++++++++++++++++++++
 1 file changed, 108 insertions(+)

diff --git a/Documentation/devicetree/bindings/embedded-controller/synology,ds1825p-microp.yaml b/Documentation/devicetree/bindings/embedded-controller/synology,ds1825p-microp.yaml
new file mode 100644
index 000000000000..76c671a42fbf
--- /dev/null
+++ b/Documentation/devicetree/bindings/embedded-controller/synology,ds1825p-microp.yaml
@@ -0,0 +1,108 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/embedded-controller/synology,ds1825p-microp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Synology NAS on-board Microcontroller
+
+maintainers:
+  - Markus Probst <markus.probst@posteo.de>
+
+description: |
+  Synology Microp is a microcontroller found in Synology NAS devices.
+  It is connected to a serial port on the host device.
+
+  It is necessary to properly shutdown and reboot the NAS device and
+  provides additional functionality such as led control, fan speed control,
+  a beeper and buttons on the NAS device.
+
+properties:
+  compatible:
+    oneOf:
+      - const: synology,ds223-microp
+      - const: synology,ds411p-microp
+      - const: synology,ds1010p-microp
+      - const: synology,ds710p-microp
+      - const: synology,ds723p-microp
+      - const: synology,ds225p-microp
+      - const: synology,rs422p-microp
+      - maxItems: 2
+        minItems: 2
+        items:
+          enum:
+            - synology,ds923p-microp
+            - synology,ds1522p-microp
+      - minItems: 4
+        maxItems: 4
+        items:
+          enum:
+            - synology,ds918p-microp
+            - synology,ds425p-microp
+            - synology,ds1525p-microp
+            - synology,ds925p-microp
+      - minItems: 2
+        maxItems: 2
+        items:
+          enum:
+            - synology,ds725p-microp
+            - synology,ds214play-microp
+      - minItems: 3
+        maxItems: 3
+        items:
+          enum:
+            - synology,ds223j-microp
+            - synology,ds124-microp
+            - synology,ds118-microp
+      - minItems: 3
+        maxItems: 3
+        items:
+          enum:
+            - synology,rs822p-microp
+            - synology,rs1221p-microp
+            - synology,rs1221rpp-microp
+      - minItems: 2
+        maxItems: 2
+        items:
+          enum:
+            - synology,ds1825p-microp
+            - synology,ds1823xsp-microp
+
+  fan-failure-gpios:
+    description: GPIOs needed to determine which fans stopped working on a fan failure event.
+    minItems: 2
+    maxItems: 3
+
+required:
+  - compatible
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - synology,ds425p-microp
+              - synology,rs422p-microp
+              - synology,ds1522p-microp
+              - synology,ds1010p-microp
+              - synology,ds411p-microp
+    then:
+      required:
+        - fan-failure-gpios
+    else:
+      properties:
+        fan-failure-gpios: false
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/leds/common.h>
+    #include <dt-bindings/gpio/gpio.h>
+
+    embedded-controller {
+      compatible = "synology,ds923p-microp", "synology,ds1522p-microp";
+
+      fan-failure-gpios = <&gpio 68 GPIO_ACTIVE_HIGH>, <&gpio 69 GPIO_ACTIVE_HIGH>;
+    };

-- 
2.52.0


^ permalink raw reply related

* [PATCH v8 2/2] platform: Add initial synology microp driver
From: Markus Probst @ 2026-04-20 14:24 UTC (permalink / raw)
  To: Hans de Goede, Ilpo Järvinen, Bryan O'Donoghue,
	Lee Jones, Pavel Machek, Miguel Ojeda, Boqun Feng, Gary Guo,
	Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
	Trevor Gross, Danilo Krummrich, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Greg Kroah-Hartman
  Cc: platform-driver-x86, linux-leds, devicetree, linux-kernel,
	rust-for-linux, Markus Probst
In-Reply-To: <20260420-synology_microp_initial-v8-0-7946a9124491@posteo.de>

Add a initial synology microp driver, written in Rust.
The driver targets a microcontroller found in Synology NAS devices. It
currently only supports controlling of the power led, status led, alert
led and usb led. Other components such as fan control or handling
on-device buttons will be added once the required rust abstractions are
there.

This driver can be used both on arm and x86, thus it goes into the root
directory of drivers/platform.

Tested successfully on a Synology DS923+.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 MAINTAINERS                                        |   6 +
 drivers/platform/Kconfig                           |   2 +
 drivers/platform/Makefile                          |   1 +
 drivers/platform/synology_microp/Kconfig           |  13 +
 drivers/platform/synology_microp/Makefile          |   3 +
 drivers/platform/synology_microp/TODO              |   7 +
 drivers/platform/synology_microp/command.rs        |  54 ++++
 drivers/platform/synology_microp/led.rs            | 281 +++++++++++++++++++++
 drivers/platform/synology_microp/model.rs          |  49 ++++
 .../platform/synology_microp/synology_microp.rs    | 110 ++++++++
 10 files changed, 526 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index c1c686846cdd..49f08290eed0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25555,6 +25555,12 @@ F:	drivers/dma-buf/sync_*
 F:	include/linux/sync_file.h
 F:	include/uapi/linux/sync_file.h
 
+SYNOLOGY MICROP DRIVER
+M:	Markus Probst <markus.probst@posteo.de>
+S:	Maintained
+F:	Documentation/devicetree/bindings/embedded-controller/synology,ds1825p-microp.yaml
+F:	drivers/platform/synology_microp/
+
 SYNOPSYS ARC ARCHITECTURE
 M:	Vineet Gupta <vgupta@kernel.org>
 L:	linux-snps-arc@lists.infradead.org
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig
index 312788f249c9..996050566a4a 100644
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -22,3 +22,5 @@ source "drivers/platform/arm64/Kconfig"
 source "drivers/platform/raspberrypi/Kconfig"
 
 source "drivers/platform/wmi/Kconfig"
+
+source "drivers/platform/synology_microp/Kconfig"
diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile
index fa322e7f8716..2381872e9133 100644
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -15,3 +15,4 @@ obj-$(CONFIG_SURFACE_PLATFORMS)	+= surface/
 obj-$(CONFIG_ARM64_PLATFORM_DEVICES)	+= arm64/
 obj-$(CONFIG_BCM2835_VCHIQ)	+= raspberrypi/
 obj-$(CONFIG_ACPI_WMI)		+= wmi/
+obj-$(CONFIG_SYNOLOGY_MICROP)	+= synology_microp/
diff --git a/drivers/platform/synology_microp/Kconfig b/drivers/platform/synology_microp/Kconfig
new file mode 100644
index 000000000000..7c4d8f2808f0
--- /dev/null
+++ b/drivers/platform/synology_microp/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config SYNOLOGY_MICROP
+	tristate "Synology Microp driver"
+	depends on LEDS_CLASS && LEDS_CLASS_MULTICOLOR
+	depends on RUST_SERIAL_DEV_BUS_ABSTRACTIONS
+	help
+	  Enable support for the MCU found in Synology NAS devices.
+
+	  This is needed to properly shutdown and reboot the device, as well as
+	  additional functionality like fan and LED control.
+
+	  This driver is work in progress and may not be fully functional.
diff --git a/drivers/platform/synology_microp/Makefile b/drivers/platform/synology_microp/Makefile
new file mode 100644
index 000000000000..63585ccf76e4
--- /dev/null
+++ b/drivers/platform/synology_microp/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += synology_microp.o
diff --git a/drivers/platform/synology_microp/TODO b/drivers/platform/synology_microp/TODO
new file mode 100644
index 000000000000..1961a33115db
--- /dev/null
+++ b/drivers/platform/synology_microp/TODO
@@ -0,0 +1,7 @@
+TODO:
+- add missing components:
+  - handle on-device buttons (Power, Factory reset, "USB Copy")
+  - handle fan failure
+  - beeper
+  - fan speed control
+  - correctly perform device power-off and restart on Synology devices
diff --git a/drivers/platform/synology_microp/command.rs b/drivers/platform/synology_microp/command.rs
new file mode 100644
index 000000000000..430cb858e1c3
--- /dev/null
+++ b/drivers/platform/synology_microp/command.rs
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+    device::Bound,
+    error::Result,
+    serdev, //
+};
+
+use crate::led;
+
+#[expect(
+    clippy::enum_variant_names,
+    reason = "future variants will not end with Led"
+)]
+pub(crate) enum Command {
+    PowerLed(led::State),
+    StatusLed(led::StatusLedColor, led::State),
+    AlertLed(led::State),
+    UsbLed(led::State),
+    EsataLed(led::State),
+}
+
+impl Command {
+    pub(crate) fn write(self, dev: &serdev::Device<Bound>) -> Result {
+        dev.write_all(
+            match self {
+                Self::PowerLed(led::State::On) => &[0x34],
+                Self::PowerLed(led::State::Blink) => &[0x35],
+                Self::PowerLed(led::State::Off) => &[0x36],
+
+                Self::StatusLed(_, led::State::Off) => &[0x37],
+                Self::StatusLed(led::StatusLedColor::Green, led::State::On) => &[0x38],
+                Self::StatusLed(led::StatusLedColor::Green, led::State::Blink) => &[0x39],
+                Self::StatusLed(led::StatusLedColor::Orange, led::State::On) => &[0x3A],
+                Self::StatusLed(led::StatusLedColor::Orange, led::State::Blink) => &[0x3B],
+
+                Self::AlertLed(led::State::On) => &[0x4C, 0x41, 0x31],
+                Self::AlertLed(led::State::Blink) => &[0x4C, 0x41, 0x32],
+                Self::AlertLed(led::State::Off) => &[0x4C, 0x41, 0x33],
+
+                Self::UsbLed(led::State::On) => &[0x40],
+                Self::UsbLed(led::State::Blink) => &[0x41],
+                Self::UsbLed(led::State::Off) => &[0x42],
+
+                Self::EsataLed(led::State::On) => &[0x4C, 0x45, 0x31],
+                Self::EsataLed(led::State::Blink) => &[0x4C, 0x45, 0x32],
+                Self::EsataLed(led::State::Off) => &[0x4C, 0x45, 0x33],
+            },
+            serdev::Timeout::Max,
+        )?;
+        dev.wait_until_sent(serdev::Timeout::Max);
+        Ok(())
+    }
+}
diff --git a/drivers/platform/synology_microp/led.rs b/drivers/platform/synology_microp/led.rs
new file mode 100644
index 000000000000..f89998a7e6b4
--- /dev/null
+++ b/drivers/platform/synology_microp/led.rs
@@ -0,0 +1,281 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::{
+    device::Bound,
+    devres::{
+        self,
+        Devres, //
+    },
+    led::{
+        self,
+        LedOps,
+        MultiColorSubLed, //
+    },
+    new_mutex,
+    prelude::*,
+    serdev,
+    str::CString,
+    sync::Mutex, //
+};
+use pin_init::pin_init_scope;
+
+use crate::{
+    command::Command,
+    model::Model, //
+};
+
+#[pin_data]
+pub(crate) struct Data {
+    #[pin]
+    status: Devres<led::MultiColorDevice<StatusLedHandler>>,
+    power_name: CString,
+    #[pin]
+    power: Devres<led::Device<LedHandler>>,
+}
+
+impl Data {
+    pub(super) fn register<'a>(
+        dev: &'a serdev::Device<Bound>,
+        model: &'a Model,
+    ) -> impl PinInit<Self, Error> + 'a {
+        pin_init_scope(move || {
+            if let Some(color) = model.led_alert {
+                let name = CString::try_from_fmt(fmt!("{}:alarm", color.as_c_str().to_str()?))?;
+                devres::register(
+                    dev.as_ref(),
+                    led::DeviceBuilder::new().color(color).name(&name).build(
+                        dev,
+                        try_pin_init!(LedHandler {
+                            blink <- new_mutex!(false),
+                            command: Command::AlertLed,
+                        }),
+                    ),
+                    GFP_KERNEL,
+                )?;
+            }
+
+            if model.led_usb_copy {
+                devres::register(
+                    dev.as_ref(),
+                    led::DeviceBuilder::new()
+                        .color(led::Color::Green)
+                        .name(c"green:usb")
+                        .build(
+                            dev,
+                            try_pin_init!(LedHandler {
+                                blink <- new_mutex!(false),
+                                command: Command::UsbLed,
+                            }),
+                        ),
+                    GFP_KERNEL,
+                )?;
+            }
+
+            if model.led_esata {
+                devres::register(
+                    dev.as_ref(),
+                    led::DeviceBuilder::new()
+                        .color(led::Color::Green)
+                        .name(c"green:esata")
+                        .build(
+                            dev,
+                            try_pin_init!(LedHandler {
+                                blink <- new_mutex!(false),
+                                command: Command::EsataLed,
+                            }),
+                        ),
+                    GFP_KERNEL,
+                )?;
+            }
+
+            Ok(try_pin_init!(Self {
+                status <- led::DeviceBuilder::new()
+                    .color(led::Color::Multi)
+                    .name(c"multicolor:status")
+                    .build_multicolor(
+                        dev,
+                        try_pin_init!(StatusLedHandler {
+                            blink <- new_mutex!(false),
+                        }),
+                        StatusLedHandler::SUBLEDS,
+                    ),
+                power_name: CString::try_from_fmt(fmt!(
+                    "{}:power",
+                    model.led_power.as_c_str().to_str()?
+                ))?,
+                power <- led::DeviceBuilder::new()
+                    .color(model.led_power)
+                    .name(power_name)
+                    .build(
+                        dev,
+                        try_pin_init!(LedHandler {
+                            blink <- new_mutex!(true),
+                            command: Command::PowerLed,
+                        }),
+                    ),
+            }))
+        })
+    }
+}
+
+#[derive(Copy, Clone)]
+pub(crate) enum StatusLedColor {
+    Green,
+    Orange,
+}
+
+#[derive(Copy, Clone)]
+pub(crate) enum State {
+    On,
+    Blink,
+    Off,
+}
+
+#[pin_data]
+struct LedHandler {
+    #[pin]
+    blink: Mutex<bool>,
+    command: fn(State) -> Command,
+}
+
+/// Blink delay measured using video recording on DS923+ for Power and Status Led.
+///
+/// We assume it is the same for all other leds and models.
+const BLINK_DELAY: usize = 167;
+
+#[vtable]
+impl LedOps for LedHandler {
+    type Bus = serdev::Device<Bound>;
+    type Mode = led::Normal;
+    const BLOCKING: bool = true;
+    const MAX_BRIGHTNESS: u32 = 1;
+
+    fn brightness_set(
+        &self,
+        dev: &Self::Bus,
+        _classdev: &led::Device<Self>,
+        brightness: u32,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+        (self.command)(if brightness == 0 {
+            *blink = false;
+            State::Off
+        } else if *blink {
+            State::Blink
+        } else {
+            State::On
+        })
+        .write(dev)?;
+
+        Ok(())
+    }
+
+    fn blink_set(
+        &self,
+        dev: &Self::Bus,
+        _classdev: &led::Device<Self>,
+        delay_on: &mut usize,
+        delay_off: &mut usize,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+
+        (self.command)(if *delay_on == 0 && *delay_off != 0 {
+            State::Off
+        } else if *delay_on != 0 && *delay_off == 0 {
+            State::On
+        } else {
+            *blink = true;
+            *delay_on = BLINK_DELAY;
+            *delay_off = BLINK_DELAY;
+
+            State::Blink
+        })
+        .write(dev)
+    }
+}
+
+#[pin_data]
+struct StatusLedHandler {
+    #[pin]
+    blink: Mutex<bool>,
+}
+
+impl StatusLedHandler {
+    const SUBLEDS: &[MultiColorSubLed] = &[
+        MultiColorSubLed::new(led::Color::Green).initial_intensity(1),
+        MultiColorSubLed::new(led::Color::Orange),
+    ];
+}
+
+#[vtable]
+impl LedOps for StatusLedHandler {
+    type Bus = serdev::Device<Bound>;
+    type Mode = led::MultiColor;
+    const BLOCKING: bool = true;
+    const MAX_BRIGHTNESS: u32 = 1;
+
+    fn brightness_set(
+        &self,
+        dev: &Self::Bus,
+        classdev: &led::MultiColorDevice<Self>,
+        brightness: u32,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+        if brightness == 0 {
+            *blink = false;
+        }
+
+        let (color, subled_brightness) = if classdev.subleds()[1].intensity == 0 {
+            (StatusLedColor::Green, classdev.subleds()[0].brightness)
+        } else {
+            (StatusLedColor::Orange, classdev.subleds()[1].brightness)
+        };
+
+        Command::StatusLed(
+            color,
+            if subled_brightness == 0 {
+                State::Off
+            } else if *blink {
+                State::Blink
+            } else {
+                State::On
+            },
+        )
+        .write(dev)
+    }
+
+    fn blink_set(
+        &self,
+        dev: &Self::Bus,
+        classdev: &led::MultiColorDevice<Self>,
+        delay_on: &mut usize,
+        delay_off: &mut usize,
+    ) -> Result<()> {
+        let mut blink = self.blink.lock();
+        *blink = true;
+
+        let (color, subled_intensity) = if classdev.subleds()[1].intensity == 0 {
+            (StatusLedColor::Green, classdev.subleds()[0].intensity)
+        } else {
+            (StatusLedColor::Orange, classdev.subleds()[1].intensity)
+        };
+        Command::StatusLed(
+            color,
+            if *delay_on == 0 && *delay_off != 0 {
+                *blink = false;
+                State::Off
+            } else if subled_intensity == 0 {
+                State::Off
+            } else if *delay_on != 0 && *delay_off == 0 {
+                *blink = false;
+                State::On
+            } else {
+                *delay_on = BLINK_DELAY;
+                *delay_off = BLINK_DELAY;
+
+                State::Blink
+            },
+        )
+        .write(dev)
+    }
+}
diff --git a/drivers/platform/synology_microp/model.rs b/drivers/platform/synology_microp/model.rs
new file mode 100644
index 000000000000..715d8840f56b
--- /dev/null
+++ b/drivers/platform/synology_microp/model.rs
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::led::Color;
+
+pub(crate) struct Model {
+    pub(crate) led_power: Color,
+    pub(crate) led_alert: Option<Color>,
+    pub(crate) led_usb_copy: bool,
+    pub(crate) led_esata: bool,
+}
+
+impl Model {
+    pub(super) const fn new() -> Self {
+        Self {
+            led_power: Color::Blue,
+            led_alert: None,
+            led_usb_copy: false,
+            led_esata: false,
+        }
+    }
+
+    pub(super) const fn led_power(self, color: Color) -> Self {
+        Self {
+            led_power: color,
+            ..self
+        }
+    }
+
+    pub(super) const fn led_alert(self, color: Color) -> Self {
+        Self {
+            led_alert: Some(color),
+            ..self
+        }
+    }
+
+    pub(super) const fn led_esata(self) -> Self {
+        Self {
+            led_esata: true,
+            ..self
+        }
+    }
+
+    pub(super) const fn led_usb_copy(self) -> Self {
+        Self {
+            led_usb_copy: true,
+            ..self
+        }
+    }
+}
diff --git a/drivers/platform/synology_microp/synology_microp.rs b/drivers/platform/synology_microp/synology_microp.rs
new file mode 100644
index 000000000000..1fd4fc658d85
--- /dev/null
+++ b/drivers/platform/synology_microp/synology_microp.rs
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Synology Microp driver
+
+use kernel::{
+    device,
+    led::Color,
+    of::{
+        DeviceId,
+        IdTable, //
+    },
+    of_device_table,
+    prelude::*,
+    serdev, //
+};
+use pin_init::pin_init_scope;
+
+use crate::model::Model;
+
+pub(crate) mod command;
+mod led;
+mod model;
+
+kernel::module_serdev_device_driver! {
+    type: SynologyMicropDriver,
+    name: "synology_microp",
+    authors: ["Markus Probst <markus.probst@posteo.de>"],
+    description: "Synology Microp driver",
+    license: "GPL v2",
+}
+
+#[rustfmt::skip]
+of_device_table!(
+    OF_TABLE,
+    MODULE_OF_TABLE,
+    Model,
+    [
+        // apollolake
+        (DeviceId::new(c"synology,ds918p-microp"), Model::new()),
+
+        // evansport
+        (DeviceId::new(c"synology,ds214play-microp"), Model::new()),
+
+        // geminilakenk
+        (DeviceId::new(c"synology,ds225p-microp"), Model::new().led_usb_copy()),
+        (DeviceId::new(c"synology,ds425p-microp"), Model::new()),
+
+        // pineview
+        (DeviceId::new(c"synology,ds710p-microp"), Model::new().led_esata()),
+        (DeviceId::new(c"synology,ds1010p-microp"), Model::new().led_alert(Color::Orange)),
+        (DeviceId::new(c"synology,ds411p-microp"), Model::new()),
+
+        // r1000
+        (DeviceId::new(c"synology,ds923p-microp"), Model::new()),
+        (DeviceId::new(c"synology,ds723p-microp"), Model::new()),
+        (DeviceId::new(c"synology,ds1522p-microp"), Model::new()),
+        (DeviceId::new(c"synology,rs422p-microp"), Model::new().led_power(Color::Green)),
+
+        // r1000nk
+        (DeviceId::new(c"synology,ds725p-microp"), Model::new()),
+
+        // rtd1296
+        (DeviceId::new(c"synology,ds118-microp"), Model::new()),
+
+        // rtd1619b
+        (DeviceId::new(c"synology,ds124-microp"), Model::new()),
+        (DeviceId::new(c"synolody,ds223-microp"), Model::new().led_usb_copy()),
+        (DeviceId::new(c"synology,ds223j-microp"), Model::new()),
+
+        // v1000
+        (DeviceId::new(c"synology,ds1823xsp-microp"), Model::new()),
+        (DeviceId::new(c"synology,rs822p-microp"), Model::new().led_power(Color::Green)),
+        (DeviceId::new(c"synology,rs1221p-microp"), Model::new().led_power(Color::Green)),
+        (DeviceId::new(c"synology,rs1221rpp-microp"), Model::new().led_power(Color::Green)),
+
+        // v1000nk
+        (DeviceId::new(c"synology,ds925p-microp"), Model::new()),
+        (DeviceId::new(c"synology,ds1525p-microp"), Model::new()),
+        (DeviceId::new(c"synology,ds1825p-microp"), Model::new()),
+    ]
+);
+
+#[pin_data]
+struct SynologyMicropDriver {
+    #[pin]
+    led: led::Data,
+}
+
+#[vtable]
+impl serdev::Driver for SynologyMicropDriver {
+    type IdInfo = Model;
+    const OF_ID_TABLE: Option<IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+
+    fn probe(
+        dev: &serdev::Device<device::Core>,
+        model: Option<&Model>,
+    ) -> impl PinInit<Self, kernel::error::Error> {
+        pin_init_scope(move || {
+            let model = model.ok_or(EINVAL)?;
+
+            dev.set_baudrate(9600).map_err(|_| EINVAL)?;
+            dev.set_flow_control(false);
+            dev.set_parity(serdev::Parity::None)?;
+
+            Ok(try_pin_init!(Self {
+                led <- led::Data::register(dev, model),
+            }))
+        })
+    }
+}

-- 
2.52.0


^ permalink raw reply related


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