Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH 1/3] arm64: dts: renesas: r8a77965-salvator-x: Enable GPU support
From: Niklas Söderlund @ 2026-06-11  9:47 UTC (permalink / raw)
  To: Marek Vasut
  Cc: linux-arm-kernel, Conor Dooley, David Airlie, Frank Binns,
	Geert Uytterhoeven, Krzysztof Kozlowski, Maarten Lankhorst,
	Magnus Damm, Matt Coster, Maxime Ripard, Rob Herring,
	Simona Vetter, Thomas Zimmermann, devicetree, dri-devel,
	linux-renesas-soc
In-Reply-To: <20260611005952.146825-1-marek.vasut+renesas@mailbox.org>

Hi Marek,

Thanks for your work.

On 2026-06-11 02:57:29 +0200, Marek Vasut wrote:
> Enable GPU on Salvator-X with R-Car M3-N.
> 
> Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>

For the whole series,

Reviewed-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>

> ---
> Cc: Conor Dooley <conor+dt@kernel.org>
> Cc: David Airlie <airlied@gmail.com>
> Cc: Frank Binns <frank.binns@imgtec.com>
> Cc: Geert Uytterhoeven <geert+renesas@glider.be>
> Cc: Krzysztof Kozlowski <krzk+dt@kernel.org>
> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
> Cc: Magnus Damm <magnus.damm@gmail.com>
> Cc: Matt Coster <matt.coster@imgtec.com>
> Cc: Maxime Ripard <mripard@kernel.org>
> Cc: "Niklas Söderlund" <niklas.soderlund@ragnatech.se>
> Cc: Rob Herring <robh@kernel.org>
> Cc: Simona Vetter <simona@ffwll.ch>
> Cc: Thomas Zimmermann <tzimmermann@suse.de>
> Cc: devicetree@vger.kernel.org
> Cc: dri-devel@lists.freedesktop.org
> Cc: linux-renesas-soc@vger.kernel.org
> ---
>  arch/arm64/boot/dts/renesas/r8a77965-salvator-x.dts | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/renesas/r8a77965-salvator-x.dts b/arch/arm64/boot/dts/renesas/r8a77965-salvator-x.dts
> index f84c64ed4df7b..af8cfdccd2103 100644
> --- a/arch/arm64/boot/dts/renesas/r8a77965-salvator-x.dts
> +++ b/arch/arm64/boot/dts/renesas/r8a77965-salvator-x.dts
> @@ -30,3 +30,7 @@ &du {
>  	clock-names = "du.0", "du.1", "du.3",
>  		      "dclkin.0", "dclkin.1", "dclkin.3";
>  };
> +
> +&gpu {
> +	status = "okay";
> +};
> -- 
> 2.53.0
> 

-- 
Kind Regards,
Niklas Söderlund

^ permalink raw reply

* Re: [PATCH 1/2] dt-bindings: display: Add Solomon SSD1351 OLED controller
From: Javier Martinez Canillas @ 2026-06-11  9:45 UTC (permalink / raw)
  To: Amit Barzilai, maarten.lankhorst, mripard, tzimmermann, airlied,
	simona
  Cc: robh, krzk+dt, conor+dt, dri-devel, devicetree, linux-kernel,
	Amit Barzilai
In-Reply-To: <20260610164428.180091-2-amit.barzilai22@gmail.com>

Amit Barzilai <amit.barzilai22@gmail.com> writes:

> Add a device tree binding for the Solomon SSD1351, a 128x128 65k-color
> RGB OLED display controller driven over a 4-wire SPI bus. The binding
> builds on the shared solomon,ssd-common.yaml properties already used by
> the other Solomon display controllers.
>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Amit Barzilai <amit.barzilai22@gmail.com>
> ---

The binding schema looks good to me.

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Core Platforms
Red Hat


^ permalink raw reply

* Re: [PATCH 0/2] drm: Add driver for the Solomon SSD1351 OLED controller
From: Javier Martinez Canillas @ 2026-06-11  9:44 UTC (permalink / raw)
  To: Amit Barzilai, maarten.lankhorst, mripard, tzimmermann, airlied,
	simona
  Cc: robh, krzk+dt, conor+dt, dri-devel, devicetree, linux-kernel,
	Amit Barzilai
In-Reply-To: <20260610164428.180091-1-amit.barzilai22@gmail.com>

Amit Barzilai <amit.barzilai22@gmail.com> writes:

Hello Amit,

Thanks for your patches.

> This series adds a new DRM/KMS driver for the Solomon SSD1351, a 128x128
> 65k-color RGB OLED controller driven over a 4-wire SPI bus. The SSD1351
> currently has no DRM driver; the only in-tree support is the legacy fbtft
> fb_ssd1351.
>
> The driver advertises XRGB8888 to userspace and converts to big-endian
> RGB565 on flush via drm_fb_xrgb8888_to_rgb565be(), building on the GEM
> SHMEM and atomic modeset/shadow-plane helpers with damage-clipped partial
> updates.
>
> The SSD1351 is implemented as a standalone driver rather than as part of
> ssd130x. ssd130x converts XRGB8888 down to a packed <= 8bpp hardware
> format (mono, grayscale, or 256-colour RGB332); the SSD1351 is driven in
> its native 65k-colour RGB565 and does not fit that pixel pipeline.
>

I think that makes more sense to have the SSD1351 support as a part of the
ssd130x driver, rather than as a separate one. There is already support for
SSD133X in ssd130x and this family also supports the same 65k format than
used by SSD1351.

The correct approach in my opinion to add SSD1351 support is as follows:

1) Add support for 65k (DRM_FORMAT_RGB565) for the SSD133X_FAMILY.

2) Add support for the SSD135X_FAMILY with DRM_FORMAT_RGB565.

3) Optionally add support for the 256k color format to SSD135X_FAMILY
   but I don't think there's a native fourcc code for this. So likely
   you will need to do a XRGB8888 to native 256k format conversion.

By doing this way, not only SSD1351 will be supported by ssd130x but also
you could make the existing SSD133X support to use the 65k color depth.

-- 
Best regards,

Javier Martinez Canillas
Core Platforms
Red Hat


^ permalink raw reply

* [PATCH 2/2] clk: ultrarisc: add DP1000 clock driver
From: Jia Wang via B4 Relay @ 2026-06-11  9:40 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-clk, devicetree, linux-kernel, Jia Wang
In-Reply-To: <20260611-ultrarisc-clock-v1-0-2d93ebb4cc13@ultrarisc.com>

From: Jia Wang <wangjia@ultrarisc.com>

Add a clock driver for the UltraRISC DP1000 SoC.

The clock tree is driven by a SYSPLL and provides fixed-factor clocks for
the subsystem and PCIe, divider-based root clocks for GMAC and the UART,
I2C, and SPI blocks, and per-instance gate clocks for UART0-3, I2C0-3,
and SPI0-1.

Signed-off-by: Jia Wang <wangjia@ultrarisc.com>
---
 MAINTAINERS                           |   1 +
 drivers/clk/Kconfig                   |   1 +
 drivers/clk/Makefile                  |   1 +
 drivers/clk/ultrarisc/Kconfig         |  18 ++
 drivers/clk/ultrarisc/Makefile        |   4 +
 drivers/clk/ultrarisc/clk-dp1000.c    | 152 +++++++++++++
 drivers/clk/ultrarisc/clk-ultrarisc.c | 417 ++++++++++++++++++++++++++++++++++
 drivers/clk/ultrarisc/clk-ultrarisc.h |  73 ++++++
 8 files changed, 667 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 3331f1edf002..ed2827094d7f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27362,6 +27362,7 @@ M:	Jia Wang <wangjia@ultrarisc.com>
 L:	linux-clk@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml
+F:	drivers/clk/ultrarisc/*
 F:	include/dt-bindings/clock/ultrarisc,dp1000-clk.h
 
 ULTRATRONIK BOARD SUPPORT
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index b2efbe9f6acb..75e336858420 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -535,6 +535,7 @@ source "drivers/clk/tenstorrent/Kconfig"
 source "drivers/clk/thead/Kconfig"
 source "drivers/clk/stm32/Kconfig"
 source "drivers/clk/ti/Kconfig"
+source "drivers/clk/ultrarisc/Kconfig"
 source "drivers/clk/uniphier/Kconfig"
 source "drivers/clk/visconti/Kconfig"
 source "drivers/clk/x86/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index a3e2862ebd7e..891272e8e1da 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -158,6 +158,7 @@ obj-$(CONFIG_ARCH_TEGRA)		+= tegra/
 obj-y					+= tenstorrent/
 obj-$(CONFIG_ARCH_THEAD)		+= thead/
 obj-y					+= ti/
+obj-y					+= ultrarisc/
 obj-$(CONFIG_CLK_UNIPHIER)		+= uniphier/
 obj-$(CONFIG_ARCH_U8500)		+= ux500/
 obj-y					+= versatile/
diff --git a/drivers/clk/ultrarisc/Kconfig b/drivers/clk/ultrarisc/Kconfig
new file mode 100644
index 000000000000..2eecc6ac3119
--- /dev/null
+++ b/drivers/clk/ultrarisc/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config CLK_ULTRARISC
+	tristate
+	depends on OF
+	depends on ARCH_ULTRARISC || COMPILE_TEST
+
+config CLK_ULTRARISC_DP1000
+	tristate "UltraRISC DP1000 clock controller"
+	select CLK_ULTRARISC
+	depends on OF && HAS_IOMEM
+	depends on ARCH_ULTRARISC || COMPILE_TEST
+	default ARCH_ULTRARISC
+	help
+	  This driver provides the clock controller for the UltraRISC
+	  DP1000 SoC. It exposes the PLL output, derived fixed-factor
+	  clocks, programmable divider clocks, and peripheral gate
+	  clocks to Linux consumers.
diff --git a/drivers/clk/ultrarisc/Makefile b/drivers/clk/ultrarisc/Makefile
new file mode 100644
index 000000000000..b013708c9444
--- /dev/null
+++ b/drivers/clk/ultrarisc/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_CLK_ULTRARISC) += clk-ultrarisc.o
+obj-$(CONFIG_CLK_ULTRARISC_DP1000) += clk-dp1000.o
diff --git a/drivers/clk/ultrarisc/clk-dp1000.c b/drivers/clk/ultrarisc/clk-dp1000.c
new file mode 100644
index 000000000000..0d4531f30023
--- /dev/null
+++ b/drivers/clk/ultrarisc/clk-dp1000.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd.
+ */
+
+#include <linux/module.h>
+
+#include <dt-bindings/clock/ultrarisc,dp1000-clk.h>
+
+#include "clk-ultrarisc.h"
+
+#define DP1000_PLL_CFG1_OFFSET		0x400
+#define DP1000_PLL_CFG2_OFFSET		0x404
+
+#define DP1000_CCR_UART_OFFSET		0x220
+#define DP1000_CCR_I2C_OFFSET		0x224
+#define DP1000_CCR_GMAC_OFFSET		0x228
+#define DP1000_CCR_SPI_OFFSET		0x22c
+#define DP1000_PERI_CLKENA_OFFSET	0x270
+
+#define DP1000_CCR_LOAD			BIT(16)
+
+#define DP1000_PERI_MAX_RATE		62500000UL
+
+static const struct ultrarisc_pll_layout dp1000_pll_layout = {
+	.cfg1_offset = DP1000_PLL_CFG1_OFFSET,
+	.cfg2_offset = DP1000_PLL_CFG2_OFFSET,
+	.frac_mask = GENMASK(23, 0),
+	.m_mask = GENMASK(23, 16),
+	.n_mask = GENMASK(11, 6),
+	.oddiv1_mask = GENMASK(4, 3),
+	.oddiv2_mask = GENMASK(1, 0),
+};
+
+static const struct ultrarisc_pll_desc dp1000_plls[] = {
+	{
+		.id = DP1000_CLK_SYSPLL,
+		.name = "syspll_clk",
+	},
+};
+
+#define DP1000_FIXED_FACTOR(_id, _name, _parent, _mult, _div)	\
+	{							\
+		.id = (_id),					\
+		.name = (_name),				\
+		.parent_id = (_parent),				\
+		.mult = (_mult),				\
+		.div = (_div),					\
+	}
+
+#define DP1000_DIV(_id, _name, _offset, _parent, _max_rate)	\
+	{							\
+		.id = (_id),					\
+		.name = (_name),				\
+		.offset = (_offset),				\
+		.parent_id = (_parent),				\
+		.max_rate = (_max_rate),			\
+		.load_mask = DP1000_CCR_LOAD,			\
+		.div_shift = 8,					\
+		.div_width = 4,					\
+		.gate_bit = 0,					\
+		.divider_flags = CLK_DIVIDER_ONE_BASED,		\
+		.gate_flags = 0,				\
+	}
+
+#define DP1000_GATE(_id, _name, _parent, _bit)		\
+	{							\
+		.id = (_id),					\
+		.name = (_name),				\
+		.offset = DP1000_PERI_CLKENA_OFFSET,		\
+		.parent_id = (_parent),				\
+		.gate_bit = (_bit),				\
+		.gate_flags = 0,				\
+	}
+
+static const struct ultrarisc_fixed_factor_desc dp1000_fixed_factor_clks[] = {
+	DP1000_FIXED_FACTOR(DP1000_CLK_SYSPLL_DIV2, "syspll_div2_clk",
+			    DP1000_CLK_SYSPLL, 1, 2),
+	DP1000_FIXED_FACTOR(DP1000_CLK_SUBSYS, "subsys_clk",
+			    DP1000_CLK_SYSPLL_DIV2, 1, 2),
+	DP1000_FIXED_FACTOR(DP1000_CLK_PCIE_DBI, "pcie_dbi_clk",
+			    DP1000_CLK_SYSPLL, 1, 10),
+	DP1000_FIXED_FACTOR(DP1000_CLK_PCIEX4_CORE, "pciex4_core_clk",
+			    DP1000_CLK_SYSPLL, 1, 2),
+	DP1000_FIXED_FACTOR(DP1000_CLK_PCIEX16_CORE, "pciex16_core_clk",
+			    DP1000_CLK_SYSPLL, 1, 1),
+	DP1000_FIXED_FACTOR(DP1000_CLK_PCIE_AUX, "pcie_aux_clk",
+			    DP1000_CLK_SYSPLL, 1, 40),
+};
+
+static const struct ultrarisc_divider_desc dp1000_divider_clks[] = {
+	DP1000_DIV(DP1000_CLK_GMAC, "gmac_clk", DP1000_CCR_GMAC_OFFSET,
+		   DP1000_CLK_SYSPLL_DIV2, 0),
+	DP1000_DIV(DP1000_CLK_UART_ROOT, "uart_root_clk",
+		   DP1000_CCR_UART_OFFSET, DP1000_CLK_SUBSYS,
+		   DP1000_PERI_MAX_RATE),
+	DP1000_DIV(DP1000_CLK_I2C_ROOT, "i2c_root_clk",
+		   DP1000_CCR_I2C_OFFSET, DP1000_CLK_SUBSYS,
+		   DP1000_PERI_MAX_RATE),
+	DP1000_DIV(DP1000_CLK_SPI_ROOT, "spi_root_clk",
+		   DP1000_CCR_SPI_OFFSET, DP1000_CLK_SUBSYS,
+		   DP1000_PERI_MAX_RATE),
+};
+
+static const struct ultrarisc_gate_desc dp1000_gate_clks[] = {
+	DP1000_GATE(DP1000_CLK_UART0, "uart0_clk", DP1000_CLK_UART_ROOT, 0),
+	DP1000_GATE(DP1000_CLK_UART1, "uart1_clk", DP1000_CLK_UART_ROOT, 1),
+	DP1000_GATE(DP1000_CLK_UART2, "uart2_clk", DP1000_CLK_UART_ROOT, 2),
+	DP1000_GATE(DP1000_CLK_UART3, "uart3_clk", DP1000_CLK_UART_ROOT, 3),
+	DP1000_GATE(DP1000_CLK_I2C0, "i2c0_clk", DP1000_CLK_I2C_ROOT, 4),
+	DP1000_GATE(DP1000_CLK_I2C1, "i2c1_clk", DP1000_CLK_I2C_ROOT, 5),
+	DP1000_GATE(DP1000_CLK_I2C2, "i2c2_clk", DP1000_CLK_I2C_ROOT, 6),
+	DP1000_GATE(DP1000_CLK_I2C3, "i2c3_clk", DP1000_CLK_I2C_ROOT, 7),
+	DP1000_GATE(DP1000_CLK_SPI0, "spi0_clk", DP1000_CLK_SPI_ROOT, 8),
+	DP1000_GATE(DP1000_CLK_SPI1, "spi1_clk", DP1000_CLK_SPI_ROOT, 9),
+};
+
+static const struct ultrarisc_clk_soc_data dp1000_clk_soc_data = {
+	.num_clks = DP1000_CLK_NUM,
+	.pll_layout = &dp1000_pll_layout,
+	.plls = dp1000_plls,
+	.num_plls = ARRAY_SIZE(dp1000_plls),
+	.fixed_factors = dp1000_fixed_factor_clks,
+	.num_fixed_factors = ARRAY_SIZE(dp1000_fixed_factor_clks),
+	.dividers = dp1000_divider_clks,
+	.num_dividers = ARRAY_SIZE(dp1000_divider_clks),
+	.gates = dp1000_gate_clks,
+	.num_gates = ARRAY_SIZE(dp1000_gate_clks),
+};
+
+static int dp1000_clk_probe(struct platform_device *pdev)
+{
+	return ultrarisc_clk_probe(pdev, &dp1000_clk_soc_data);
+}
+
+static const struct of_device_id dp1000_clk_of_match[] = {
+	{ .compatible = "ultrarisc,dp1000-clk" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, dp1000_clk_of_match);
+
+static struct platform_driver dp1000_clk_driver = {
+	.probe = dp1000_clk_probe,
+	.driver = {
+		.name = "ultrarisc-dp1000-clk",
+		.of_match_table = dp1000_clk_of_match,
+	},
+};
+module_platform_driver(dp1000_clk_driver);
+
+MODULE_DESCRIPTION("UltraRISC DP1000 clock controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/ultrarisc/clk-ultrarisc.c b/drivers/clk/ultrarisc/clk-ultrarisc.c
new file mode 100644
index 000000000000..2b970565a4be
--- /dev/null
+++ b/drivers/clk/ultrarisc/clk-ultrarisc.c
@@ -0,0 +1,417 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/of_clk.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "clk-ultrarisc.h"
+
+struct ultrarisc_pll_clk {
+	struct clk_hw hw;
+	void __iomem *base;
+	const struct ultrarisc_pll_layout *layout;
+};
+
+struct ultrarisc_divider_clk {
+	struct clk_divider divider;
+	struct clk_gate gate;
+	u32 load_mask;
+};
+
+#define to_ultrarisc_pll_clk(_hw) \
+	container_of(_hw, struct ultrarisc_pll_clk, hw)
+
+static inline struct ultrarisc_divider_clk *
+to_ultrarisc_divider_clk(struct clk_hw *hw)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+
+	return container_of(divider, struct ultrarisc_divider_clk, divider);
+}
+
+static unsigned long ultrarisc_pll_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	struct ultrarisc_pll_clk *pll = to_ultrarisc_pll_clk(hw);
+	const struct ultrarisc_pll_layout *layout = pll->layout;
+	u32 oddiv1_div, oddiv2_div;
+	u64 mult, rate, den;
+	u32 frac, m, n;
+	u32 cfg1, cfg2;
+
+	cfg1 = readl_relaxed(pll->base + layout->cfg1_offset);
+	cfg2 = readl_relaxed(pll->base + layout->cfg2_offset);
+
+	frac = field_get(layout->frac_mask, cfg1);
+	m = field_get(layout->m_mask, cfg2);
+	n = field_get(layout->n_mask, cfg2);
+	oddiv1_div = 1U << field_get(layout->oddiv1_mask, cfg2);
+	oddiv2_div = 1U << field_get(layout->oddiv2_mask, cfg2);
+
+	if (!n)
+		return 0;
+
+	/*
+	 * The output frequency is calculated as:
+	 * fvco = parent * (m + frac / 2^24) / n
+	 * fout = fvco / (2^oddiv1_raw * 2^oddiv2_raw)
+	 *
+	 * The output divider values are derived from the raw register field
+	 * values as:
+	 * oddivX_div = 1 << oddivX_raw
+	 */
+	mult = ((u64)m << 24) + frac;
+	rate = (u64)parent_rate * mult;
+	den = ((u64)n << 24) * oddiv1_div * oddiv2_div;
+
+	return div64_u64(rate + (den >> 1), den);
+}
+
+static const struct clk_ops ultrarisc_pll_ro_ops = {
+	.recalc_rate = ultrarisc_pll_recalc_rate,
+};
+
+static unsigned long
+ultrarisc_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+	u32 val;
+
+	val = readl_relaxed(divider->reg) >> divider->shift;
+	val &= clk_div_mask(divider->width);
+
+	return divider_recalc_rate(hw, parent_rate, val, divider->table,
+				   divider->flags, divider->width);
+}
+
+static int ultrarisc_divider_determine_rate(struct clk_hw *hw,
+					    struct clk_rate_request *req)
+{
+	struct clk_divider *divider = to_clk_divider(hw);
+
+	return divider_determine_rate(hw, req, divider->table, divider->width,
+				      divider->flags);
+}
+
+static int ultrarisc_divider_set_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long parent_rate)
+{
+	struct ultrarisc_divider_clk *divider_clk = to_ultrarisc_divider_clk(hw);
+	struct clk_divider *divider = &divider_clk->divider;
+	int value;
+	u32 val;
+
+	value = divider_get_val(rate, parent_rate, divider->table,
+				divider->width, divider->flags);
+	if (value < 0)
+		return value;
+
+	scoped_guard(spinlock_irqsave, divider->lock) {
+		val = readl_relaxed(divider->reg);
+		val &= ~(clk_div_mask(divider->width) << divider->shift);
+		val |= value << divider->shift;
+		writel_relaxed(val, divider->reg);
+
+		if (divider_clk->load_mask) {
+			/*
+			 * The divider update is triggered by writing 1 to the
+			 * self-clearing load bit after programming the new
+			 * divider field.
+			 */
+			writel_relaxed(val | divider_clk->load_mask,
+				       divider->reg);
+		}
+	}
+
+	return 0;
+}
+
+static const struct clk_ops ultrarisc_divider_ops = {
+	.recalc_rate = ultrarisc_divider_recalc_rate,
+	.determine_rate = ultrarisc_divider_determine_rate,
+	.set_rate = ultrarisc_divider_set_rate,
+};
+
+static struct clk_hw *ultrarisc_clk_register_pll(struct device *dev,
+						 const struct ultrarisc_pll_desc *desc,
+						 const struct ultrarisc_pll_layout *layout,
+						 void __iomem *base)
+{
+	struct clk_parent_data pdata = { .index = 0 };
+	struct ultrarisc_pll_clk *pll;
+	struct clk_init_data init = {
+		.name = desc->name,
+		.ops = &ultrarisc_pll_ro_ops,
+		.parent_data = &pdata,
+		.num_parents = 1,
+		.flags = CLK_GET_RATE_NOCACHE,
+	};
+	int ret;
+
+	pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	pll->base = base;
+	pll->layout = layout;
+	pll->hw.init = &init;
+
+	ret = devm_clk_hw_register(dev, &pll->hw);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return &pll->hw;
+}
+
+static struct clk_hw *
+ultrarisc_clk_register_divider(struct device *dev,
+			       const struct ultrarisc_divider_desc *desc,
+			       struct clk_hw *parent_hw, void __iomem *base,
+			       spinlock_t *lock)
+{
+	struct clk_parent_data pdata = { .hw = parent_hw };
+	void __iomem *reg = base + desc->offset;
+	struct ultrarisc_divider_clk *divider;
+	const struct clk_ops *gate_ops = NULL;
+	struct clk_hw *gate_hw = NULL;
+
+	if (!desc->div_width)
+		return ERR_PTR(-EINVAL);
+
+	if (!lock)
+		return ERR_PTR(-EINVAL);
+
+	divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
+	if (!divider)
+		return ERR_PTR(-ENOMEM);
+
+	divider->divider.reg = reg;
+	divider->divider.shift = desc->div_shift;
+	divider->divider.width = desc->div_width;
+	divider->divider.flags = desc->divider_flags;
+	divider->divider.lock = lock;
+	divider->load_mask = desc->load_mask;
+
+	if (desc->gate_bit != ULTRARISC_CLK_NO_GATE) {
+		divider->gate.reg = reg;
+		divider->gate.bit_idx = desc->gate_bit;
+		divider->gate.flags = desc->gate_flags;
+		divider->gate.lock = lock;
+		gate_hw = &divider->gate.hw;
+		gate_ops = &clk_gate_ops;
+	}
+
+	return devm_clk_hw_register_composite_pdata(dev, desc->name, &pdata, 1,
+						    NULL, NULL,
+						    &divider->divider.hw,
+						    &ultrarisc_divider_ops,
+						    gate_hw, gate_ops,
+						    CLK_GET_RATE_NOCACHE);
+}
+
+static int ultrarisc_clk_register_fixed_factors(struct device *dev,
+						struct clk_hw_onecell_data *clk_data,
+						const struct ultrarisc_clk_soc_data *soc_data)
+{
+	u32 i;
+
+	for (i = 0; i < soc_data->num_fixed_factors; i++) {
+		const struct ultrarisc_fixed_factor_desc *desc;
+		struct clk_hw *parent_hw;
+		struct clk_hw *hw;
+
+		desc = &soc_data->fixed_factors[i];
+		if (desc->id >= clk_data->num ||
+		    desc->parent_id >= clk_data->num)
+			return -EINVAL;
+
+		parent_hw = clk_data->hws[desc->parent_id];
+		if (!parent_hw)
+			return -EINVAL;
+
+		hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, desc->name,
+								 parent_hw,
+								 CLK_GET_RATE_NOCACHE,
+								 desc->mult,
+								 desc->div);
+		if (IS_ERR(hw))
+			return PTR_ERR(hw);
+
+		clk_data->hws[desc->id] = hw;
+	}
+
+	return 0;
+}
+
+static int ultrarisc_clk_register_plls(struct platform_device *pdev,
+				       struct clk_hw_onecell_data *clk_data,
+				       const struct ultrarisc_clk_soc_data *soc_data,
+				       void __iomem *base)
+{
+	struct device *dev = &pdev->dev;
+	u32 i;
+
+	for (i = 0; i < soc_data->num_plls; i++) {
+		const struct ultrarisc_pll_desc *desc = &soc_data->plls[i];
+		struct clk_hw *hw;
+
+		if (desc->id >= clk_data->num)
+			return -EINVAL;
+
+		hw = ultrarisc_clk_register_pll(dev, desc, soc_data->pll_layout,
+						base);
+		if (IS_ERR(hw))
+			return PTR_ERR(hw);
+
+		clk_data->hws[desc->id] = hw;
+	}
+
+	return 0;
+}
+
+static int ultrarisc_clk_register_dividers(struct platform_device *pdev,
+					   struct clk_hw_onecell_data *clk_data,
+					   const struct ultrarisc_clk_soc_data *soc_data,
+					   void __iomem *base,
+					   spinlock_t *lock)
+{
+	struct device *dev = &pdev->dev;
+	u32 i;
+
+	for (i = 0; i < soc_data->num_dividers; i++) {
+		const struct ultrarisc_divider_desc *desc;
+		struct clk_hw *parent_hw;
+		struct clk_hw *hw;
+
+		desc = &soc_data->dividers[i];
+		if (desc->id >= clk_data->num ||
+		    desc->parent_id >= clk_data->num)
+			return -EINVAL;
+
+		parent_hw = clk_data->hws[desc->parent_id];
+		if (!parent_hw)
+			return -EINVAL;
+
+		hw = ultrarisc_clk_register_divider(dev, desc, parent_hw, base,
+						    lock);
+		if (IS_ERR(hw))
+			return PTR_ERR(hw);
+
+		if (desc->max_rate) {
+			unsigned long rate;
+
+			clk_hw_set_rate_range(hw, 0, desc->max_rate);
+
+			rate = clk_hw_get_rate(hw);
+			if (rate > desc->max_rate)
+				dev_warn(dev, "%s rate %lu exceeds max %lu\n",
+					 desc->name, rate, desc->max_rate);
+		}
+
+		clk_data->hws[desc->id] = hw;
+	}
+
+	return 0;
+}
+
+static int ultrarisc_clk_register_gates(struct platform_device *pdev,
+					struct clk_hw_onecell_data *clk_data,
+					const struct ultrarisc_clk_soc_data *soc_data,
+					void __iomem *base,
+					spinlock_t *lock)
+{
+	struct device *dev = &pdev->dev;
+	u32 i;
+
+	for (i = 0; i < soc_data->num_gates; i++) {
+		const struct ultrarisc_gate_desc *desc;
+		struct clk_hw *parent_hw;
+		struct clk_hw *hw;
+
+		desc = &soc_data->gates[i];
+		if (desc->id >= clk_data->num ||
+		    desc->parent_id >= clk_data->num)
+			return -EINVAL;
+
+		parent_hw = clk_data->hws[desc->parent_id];
+		if (!parent_hw)
+			return -EINVAL;
+
+		hw = devm_clk_hw_register_gate_parent_hw(dev, desc->name,
+							 parent_hw,
+							 CLK_GET_RATE_NOCACHE,
+							 base + desc->offset,
+							 desc->gate_bit,
+							 desc->gate_flags,
+							 lock);
+		if (IS_ERR(hw))
+			return PTR_ERR(hw);
+
+		clk_data->hws[desc->id] = hw;
+	}
+
+	return 0;
+}
+
+int ultrarisc_clk_probe(struct platform_device *pdev,
+			const struct ultrarisc_clk_soc_data *soc_data)
+{
+	struct clk_hw_onecell_data *clk_data;
+	struct device *dev = &pdev->dev;
+	void __iomem *base;
+	spinlock_t *lock;
+	int ret;
+
+	if (!soc_data)
+		return -EINVAL;
+
+	lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
+	if (!lock)
+		return -ENOMEM;
+
+	spin_lock_init(lock);
+
+	clk_data = devm_kzalloc(dev, struct_size(clk_data, hws,
+						 soc_data->num_clks),
+				GFP_KERNEL);
+	if (!clk_data)
+		return -ENOMEM;
+
+	clk_data->num = soc_data->num_clks;
+
+	base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	ret = ultrarisc_clk_register_plls(pdev, clk_data, soc_data, base);
+	if (ret)
+		return ret;
+
+	ret = ultrarisc_clk_register_fixed_factors(dev, clk_data, soc_data);
+	if (ret)
+		return ret;
+
+	ret = ultrarisc_clk_register_dividers(pdev, clk_data, soc_data, base,
+					      lock);
+	if (ret)
+		return ret;
+
+	ret = ultrarisc_clk_register_gates(pdev, clk_data, soc_data, base,
+					   lock);
+	if (ret)
+		return ret;
+
+	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+					   clk_data);
+}
+EXPORT_SYMBOL_GPL(ultrarisc_clk_probe);
+
+MODULE_DESCRIPTION("UltraRISC clock core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/ultrarisc/clk-ultrarisc.h b/drivers/clk/ultrarisc/clk-ultrarisc.h
new file mode 100644
index 000000000000..1281196bb414
--- /dev/null
+++ b/drivers/clk/ultrarisc/clk-ultrarisc.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __ULTRARISC_CLK_ULTRARISC_H
+#define __ULTRARISC_CLK_ULTRARISC_H
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#define ULTRARISC_CLK_NO_GATE		(-1)
+
+struct ultrarisc_pll_layout {
+	u32 cfg1_offset;
+	u32 cfg2_offset;
+	u32 frac_mask;
+	u32 m_mask;
+	u32 n_mask;
+	u32 oddiv1_mask;
+	u32 oddiv2_mask;
+};
+
+struct ultrarisc_pll_desc {
+	u32 id;
+	const char *name;
+};
+
+struct ultrarisc_fixed_factor_desc {
+	u32 id;
+	const char *name;
+	u32 parent_id;
+	u32 mult;
+	u32 div;
+};
+
+struct ultrarisc_divider_desc {
+	u32 id;
+	const char *name;
+	u32 offset;
+	u32 parent_id;
+	unsigned long max_rate;
+	u32 load_mask;
+	u8 div_shift;
+	u8 div_width;
+	s8 gate_bit;
+	u16 divider_flags;
+	u8 gate_flags;
+};
+
+struct ultrarisc_gate_desc {
+	u32 id;
+	const char *name;
+	u32 offset;
+	u32 parent_id;
+	u8 gate_bit;
+	u8 gate_flags;
+};
+
+struct ultrarisc_clk_soc_data {
+	const struct ultrarisc_pll_layout *pll_layout;
+	const struct ultrarisc_pll_desc *plls;
+	u32 num_plls;
+	const struct ultrarisc_fixed_factor_desc *fixed_factors;
+	u32 num_fixed_factors;
+	const struct ultrarisc_divider_desc *dividers;
+	u32 num_dividers;
+	const struct ultrarisc_gate_desc *gates;
+	u32 num_gates;
+	u32 num_clks;
+};
+
+int ultrarisc_clk_probe(struct platform_device *pdev,
+			const struct ultrarisc_clk_soc_data *soc_data);
+
+#endif /* __ULTRARISC_CLK_ULTRARISC_H */

-- 
2.34.1



^ permalink raw reply related

* [PATCH 1/2] dt-bindings: clock: ultrarisc: Add DP1000 Clock Controller
From: Jia Wang via B4 Relay @ 2026-06-11  9:40 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-clk, devicetree, linux-kernel, Jia Wang
In-Reply-To: <20260611-ultrarisc-clock-v1-0-2d93ebb4cc13@ultrarisc.com>

From: Jia Wang <wangjia@ultrarisc.com>

Add doc for the clock controller on the UltraRISC DP1000 RISC-V SoC.

Signed-off-by: Jia Wang <wangjia@ultrarisc.com>
---
 .../bindings/clock/ultrarisc,dp1000-clk.yaml       | 72 ++++++++++++++++++++++
 MAINTAINERS                                        |  7 +++
 include/dt-bindings/clock/ultrarisc,dp1000-clk.h   | 29 +++++++++
 3 files changed, 108 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml b/Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml
new file mode 100644
index 000000000000..d4d3d851a079
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/ultrarisc,dp1000-clk.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: UltraRISC DP1000 Clock Controller
+
+maintainers:
+  - Jia Wang <wangjia@ultrarisc.com>
+
+description: |
+  The UltraRISC DP1000 clock controller is driven from a single external
+  oscillator input. It provides a system PLL with fractional multiplier
+  and post-divider stages, several fixed-ratio derived clocks for
+  the on-chip subsystem, Clock Configuration Register (CCR) divider
+  outputs for GMAC and the UART, I2C, and SPI root clocks, and
+  per-instance gate clocks for UART0-3, I2C0-3, and SPI0-1.
+
+  All available clocks are defined as preprocessor macros in
+  include/dt-bindings/clock/ultrarisc,dp1000-clk.h
+
+properties:
+  compatible:
+    const: ultrarisc,dp1000-clk
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+    description:
+      External oscillator input clock used as the parent of the PLLs.
+
+  clock-names:
+    items:
+      - const: osc
+
+  "#clock-cells":
+    const: 1
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - "#clock-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/ultrarisc,dp1000-clk.h>
+
+    osc: oscillator {
+      compatible = "fixed-clock";
+      #clock-cells = <0>;
+      clock-frequency = <24000000>;
+    };
+
+    soc {
+      #address-cells = <2>;
+      #size-cells = <2>;
+
+      clock-controller@11080000 {
+        compatible = "ultrarisc,dp1000-clk";
+        reg = <0x0 0x11080000 0x0 0x1000>;
+        clocks = <&osc>;
+        clock-names = "osc";
+        #clock-cells = <1>;
+      };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index e035a3be797c..3331f1edf002 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27357,6 +27357,13 @@ S:	Maintained
 F:	drivers/usb/common/ulpi.c
 F:	include/linux/ulpi/
 
+ULTRARISC DP1000 CLOCK DRIVER
+M:	Jia Wang <wangjia@ultrarisc.com>
+L:	linux-clk@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/clock/ultrarisc,dp1000-clk.yaml
+F:	include/dt-bindings/clock/ultrarisc,dp1000-clk.h
+
 ULTRATRONIK BOARD SUPPORT
 M:	Goran Rađenović <goran.radni@gmail.com>
 M:	Börge Strümpfel <boerge.struempfel@gmail.com>
diff --git a/include/dt-bindings/clock/ultrarisc,dp1000-clk.h b/include/dt-bindings/clock/ultrarisc,dp1000-clk.h
new file mode 100644
index 000000000000..5e484f652b08
--- /dev/null
+++ b/include/dt-bindings/clock/ultrarisc,dp1000-clk.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+#ifndef _DT_BINDINGS_CLOCK_ULTRARISC_DP1000_CLK_H
+#define _DT_BINDINGS_CLOCK_ULTRARISC_DP1000_CLK_H
+
+#define DP1000_CLK_SYSPLL		0
+#define DP1000_CLK_SYSPLL_DIV2		1
+#define DP1000_CLK_SUBSYS		2
+#define DP1000_CLK_GMAC			3
+#define DP1000_CLK_UART_ROOT		4
+#define DP1000_CLK_I2C_ROOT		5
+#define DP1000_CLK_SPI_ROOT		6
+#define DP1000_CLK_PCIE_DBI		7
+#define DP1000_CLK_PCIEX4_CORE		8
+#define DP1000_CLK_PCIEX16_CORE		9
+#define DP1000_CLK_PCIE_AUX		10
+#define DP1000_CLK_UART0		11
+#define DP1000_CLK_UART1		12
+#define DP1000_CLK_UART2		13
+#define DP1000_CLK_UART3		14
+#define DP1000_CLK_I2C0			15
+#define DP1000_CLK_I2C1			16
+#define DP1000_CLK_I2C2			17
+#define DP1000_CLK_I2C3			18
+#define DP1000_CLK_SPI0			19
+#define DP1000_CLK_SPI1			20
+
+#define DP1000_CLK_NUM			21
+
+#endif /* _DT_BINDINGS_CLOCK_ULTRARISC_DP1000_CLK_H */

-- 
2.34.1



^ permalink raw reply related

* [PATCH 0/2] clk: ultrarisc: add DP1000 clock support
From: Jia Wang via B4 Relay @ 2026-06-11  9:40 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-clk, devicetree, linux-kernel, Jia Wang

This series adds the devicetree binding and the clock driver for the
UltraRISC DP1000 SoC.

The clock tree is driven by a single SYSPLL and provides fixed-factor
clocks for the subsystem including PCIe, divider-based root clocks for
GMAC and the UART, I2C, and SPI blocks, and per-instance gate clocks
for UART0-3, I2C0-3, and SPI0-1.

Testing:
- dt_binding_check and dtbs_check on ultrarisc,dp1000-clk
- Kernel build for RISC-V and boot-tested on DP1000

Signed-off-by: Jia Wang <wangjia@ultrarisc.com>
---
Jia Wang (2):
      dt-bindings: clock: ultrarisc: Add DP1000 Clock Controller
      clk: ultrarisc: add DP1000 clock driver

 .../bindings/clock/ultrarisc,dp1000-clk.yaml       |  72 ++++
 MAINTAINERS                                        |   8 +
 drivers/clk/Kconfig                                |   1 +
 drivers/clk/Makefile                               |   1 +
 drivers/clk/ultrarisc/Kconfig                      |  18 +
 drivers/clk/ultrarisc/Makefile                     |   4 +
 drivers/clk/ultrarisc/clk-dp1000.c                 | 152 ++++++++
 drivers/clk/ultrarisc/clk-ultrarisc.c              | 417 +++++++++++++++++++++
 drivers/clk/ultrarisc/clk-ultrarisc.h              |  73 ++++
 include/dt-bindings/clock/ultrarisc,dp1000-clk.h   |  29 ++
 10 files changed, 775 insertions(+)
---
base-commit: 2d3090a8aeb596a26935db0955d46c9a5db5c6ce
change-id: 20260522-ultrarisc-clock-a1b7aa59f60b

Best regards,
--  
Jia Wang <wangjia@ultrarisc.com>



^ permalink raw reply

* Re: [PATCH v5 3/4] clk: qcom: smd-rpm: Add missing RF_CLK1/RF_CLK2 clocks support on Agatti
From: Konrad Dybcio @ 2026-06-11  9:40 UTC (permalink / raw)
  To: Imran Shaik, Bjorn Andersson, Michael Turquette, Stephen Boyd,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Richard Cochran,
	Brian Masney
  Cc: Ajit Pandey, Taniya Das, Jagadeesh Kona, Krzysztof Kozlowski,
	linux-arm-msm, linux-clk, devicetree, linux-kernel, netdev
In-Reply-To: <20260608-shikra-gcc-rpmcc-clks-v5-3-94cefe092ee3@oss.qualcomm.com>

On 6/8/26 2:21 PM, Imran Shaik wrote:
> Add support for missing RF_CLK1/RF_CLK2 clocks on Qualcomm Agatti (QCM2290)
> SoC.
> 
> Signed-off-by: Imran Shaik <imran.shaik@oss.qualcomm.com>
> ---
>  drivers/clk/qcom/clk-smd-rpm.c | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c
> index 103db984a40b950bd33fba668a292be46af6326e..0b624ed4715c75042b92ec49c073b281533cace4 100644
> --- a/drivers/clk/qcom/clk-smd-rpm.c
> +++ b/drivers/clk/qcom/clk-smd-rpm.c
> @@ -495,6 +495,7 @@ DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(rf_clk2, 5, 19200000);
>  DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(rf_clk3, 6, 19200000);
>  DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(ln_bb_clk, 8, 19200000);
>  
> +DEFINE_CLK_SMD_RPM_XO_BUFFER_PREFIX(38m4_, rf_clk2, 5, 38400000);
>  DEFINE_CLK_SMD_RPM_XO_BUFFER_PREFIX(38m4_, rf_clk3, 6, 38400000);
>  
>  DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(cxo_d0, 1, 19200000);
> @@ -1262,6 +1263,10 @@ static struct clk_smd_rpm *qcm2290_clks[] = {
>  	[RPM_SMD_QDSS_A_CLK] = &clk_smd_rpm_branch_qdss_a_clk,
>  	[RPM_SMD_LN_BB_CLK2] = &clk_smd_rpm_ln_bb_clk2,
>  	[RPM_SMD_LN_BB_CLK2_A] = &clk_smd_rpm_ln_bb_clk2_a,
> +	[RPM_SMD_RF_CLK1] = &clk_smd_rpm_rf_clk1,
> +	[RPM_SMD_RF_CLK1_A] = &clk_smd_rpm_rf_clk1_a,

Is RF_CLK1 19.2 MHz (with the other RFCLKs being 38.4) on purpose?

Konrad

^ permalink raw reply

* Re: [PATCH v1 2/2] ASoC: codecs: add Qualcomm WSA885X I2C codec driver
From: Krzysztof Kozlowski @ 2026-06-11  9:39 UTC (permalink / raw)
  To: Prasad Kumpatla
  Cc: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai,
	Linus Walleij, Bartosz Golaszewski, Srinivas Kandagatla,
	linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-gpio
In-Reply-To: <20260610155708.151067-3-prasad.kumpatla@oss.qualcomm.com>

On Wed, Jun 10, 2026 at 09:27:08PM +0530, Prasad Kumpatla wrote:
> +};
> +
> +static void wsa885x_gpio_set(struct wsa885x_i2c_priv *wsa885x, bool val)
> +{
> +	if (!wsa885x || !wsa885x->sd_n)

How wsa885x can be NULL?

This wrapper is pointless. Avoid creating abstraction layers over single
call to standard kernel interfaces.

> +		return;
> +
> +	gpiod_set_value_cansleep(wsa885x->sd_n, val);
> +}
> +

...

> +
> +static void wsa885x_gpio_powerdown(void *data)
> +{
> +	struct wsa885x_i2c_priv *wsa885x = data;
> +
> +	if (!wsa885x)
> +		return;

How is this possible?

> +
> +	wsa885x_gpio_set(wsa885x, true);
> +}
> +

...

> +	if (count > 0) {
> +		if (count % 2) {
> +			dev_err(dev, "%s: Invalid number of elements in %s (%d)\n",
> +				__func__, init_table_prop, count);
> +			return -EINVAL;
> +		}
> +		if (count > WSA885X_INIT_TABLE_MAX_ITEMS) {
> +			dev_err(dev, "%s: %s has too many elements (%d > %u)\n",
> +				__func__, init_table_prop, count,
> +				WSA885X_INIT_TABLE_MAX_ITEMS);
> +			return -EINVAL;
> +		}
> +		wsa885x->init_table_size = count;
> +
> +		wsa885x->init_table = devm_kcalloc(dev, wsa885x->init_table_size,
> +						   sizeof(*wsa885x->init_table), GFP_KERNEL);
> +		if (!wsa885x->init_table)
> +			return -ENOMEM;
> +
> +		if (device_property_read_u32_array(dev, init_table_prop,
> +						   wsa885x->init_table,
> +						   wsa885x->init_table_size)) {
> +			dev_err(dev, "%s: Failed to read %s\n",
> +				__func__, init_table_prop);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	ret = device_property_read_u32(dev, "qcom,battery-config",
> +				       &wsa885x->batt_conf);
> +	if (ret) {
> +		wsa885x->batt_conf = WSA885X_BATT_1S;
> +	} else if (wsa885x->batt_conf != WSA885X_BATT_1S &&
> +		   wsa885x->batt_conf != WSA885X_BATT_2S) {
> +		return dev_err_probe(dev, -EINVAL,
> +				     "Invalid battery config %u (expected 1S or 2S)\n",
> +				     wsa885x->batt_conf);
> +	}
> +
> +	for (i = 0; i < WSA885X_SUPPLIES_NUM; i++)
> +		wsa885x->supplies[i].supply = wsa885x_supply_name[i];
> +
> +	ret = devm_regulator_bulk_get(dev, WSA885X_SUPPLIES_NUM, wsa885x->supplies);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to get regulators\n");
> +
> +	ret = regulator_bulk_enable(WSA885X_SUPPLIES_NUM, wsa885x->supplies);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to enable regulators\n");
> +
> +	ret = devm_add_action_or_reset(dev, wsa885x_regulator_disable, wsa885x);

Why you cannot simply use devm_regulator_get_enable?

> +	if (ret)
> +		return dev_err_probe(dev, ret, "devm_add_action_or_reset failed\n");
> +
> +	wsa885x->sd_n = devm_gpiod_get(dev, "powerdown", GPIOD_OUT_HIGH);
> +	if (IS_ERR(wsa885x->sd_n))
> +		return dev_err_probe(dev, PTR_ERR(wsa885x->sd_n),
> +							 "Shutdown Control GPIO not found\n");

Messed/misaligned indentation.

> +
> +	wsa885x_gpio_set(wsa885x, false);
> +
> +	ret = devm_add_action_or_reset(dev, wsa885x_gpio_powerdown, wsa885x);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "devm_add_action_or_reset failed\n");
> +
> +	i2c_set_clientdata(client, wsa885x);
> +
> +	wsa885x->intr_pin = devm_gpiod_get(dev, "interrupt", GPIOD_IN);
> +	if (IS_ERR(wsa885x->intr_pin))
> +		return dev_err_probe(dev, PTR_ERR(wsa885x->intr_pin),
> +							 "Interrupt GPIO not found\n");
> +
> +	ret = wsa885x_register_irq(wsa885x);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "wsa885x irq registration failed\n");
> +
> +	ret = devm_snd_soc_register_component(dev, component_driver,
> +					      wsa885x_i2c_dai,
> +					      ARRAY_SIZE(wsa885x_i2c_dai));
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Codec component registration failed\n");
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id wsa885x_i2c_dt_match[] = {
> +	{
> +		.compatible = "qcom,wsa885x-i2c",
> +	},
> +	{}
> +};
> +
> +static const struct i2c_device_id wsa885x_id_i2c[] = {
> +	{"wsa885x_i2c", 0},

Used named initializers.

> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, wsa885x_id_i2c);
> +MODULE_DEVICE_TABLE(of, wsa885x_i2c_dt_match);

Don't come with own coding style. Each above goes IMMEDIATELY after the table.

Best regards,
Krzysztof


^ permalink raw reply

* [PATCH v2 1/2] dt-bindings: iio: adc: add Axiado AX3000/AX3005 SARADC
From: Petar Stepanovic @ 2026-06-11  9:37 UTC (permalink / raw)
  To: Akhila Kavi, Prasad Bolisetty, Jonathan Cameron, David Lechner,
	Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Harshit Shah
  Cc: linux-iio, devicetree, linux-arm-kernel, linux-kernel,
	Petar Stepanovic, Conor Dooley
In-Reply-To: <20260611-axiado-ax3000-ax3005-saradc-v2-0-913c9de7c64c@axiado.com>

The Axiado AX3000 and AX3005 SoCs include a 10-bit SAR ADC controller.
AX3000 supports 16 input channels, while AX3005 supports 8 input
channels.

Document the compatible strings, register region, clock, reference
voltage supply, and IIO channel cells.

Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
 .../bindings/iio/adc/axiado,ax3000-saradc.yaml     | 63 ++++++++++++++++++++++
 MAINTAINERS                                        |  7 +++
 2 files changed, 70 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
new file mode 100644
index 000000000000..b910852aa56f
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/axiado,ax3000-saradc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Axiado AX3000/AX3005 Successive Approximation Register ADC
+
+description:
+  The Axiado AX3000/AX3005 SAR ADC is a 10-bit ADC with sixteen input
+  channels on AX3000 and eight input channels on AX3005.
+
+maintainers:
+  - Petar Stepanovic <pstepanovic@axiado.com>
+  - Akhila Kavi <akavi@axiado.com>
+  - Prasad Bolisetty <pbolisetty@axiado.com>
+
+properties:
+  compatible:
+    enum:
+      - axiado,ax3000-saradc
+      - axiado,ax3005-saradc
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    const: saradc
+
+  '#io-channel-cells':
+    const: 1
+
+  vref-supply:
+    description: Reference voltage regulator supplying the ADC
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - '#io-channel-cells'
+  - vref-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    soc {
+      #address-cells = <2>;
+      #size-cells = <2>;
+
+      adc@806a0000 {
+        compatible = "axiado,ax3000-saradc";
+        reg = <0x0 0x806a0000 0x0 0x400>;
+        clocks = <&pclk>;
+        clock-names = "saradc";
+        vref-supply = <&vref_reg>;
+        #io-channel-cells = <1>;
+      };
+    };
diff --git a/MAINTAINERS b/MAINTAINERS
index b2040011a386..932bba890780 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4312,6 +4312,13 @@ S:	Orphan
 F:	Documentation/devicetree/bindings/sound/axentia,*
 F:	sound/soc/atmel/tse850-pcm5142.c
 
+AXIADO SARADC DRIVER
+M:	Petar Stepanovic <pstepanovic@axiado.com>
+M:	Akhila Kavi <akavi@axiado.com>
+M:	Prasad Bolisetty <pbolisetty@axiado.com>
+S:	Maintained
+F:	Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
+
 AXIS ARTPEC ARM64 SoC SUPPORT
 M:	Jesper Nilsson <jesper.nilsson@axis.com>
 M:	Lars Persson <lars.persson@axis.com>

-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 2/2] iio: adc: add Axiado SARADC driver
From: Petar Stepanovic @ 2026-06-11  9:37 UTC (permalink / raw)
  To: Akhila Kavi, Prasad Bolisetty, Jonathan Cameron, David Lechner,
	Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Harshit Shah
  Cc: linux-iio, devicetree, linux-arm-kernel, linux-kernel,
	Petar Stepanovic
In-Reply-To: <20260611-axiado-ax3000-ax3005-saradc-v2-0-913c9de7c64c@axiado.com>

Add support for the SARADC controller found on Axiado AX3000 and
AX3005 SoCs.

The driver supports single-shot voltage reads through the IIO
subsystem. The number of available input channels is selected from
the SoC match data, allowing AX3000 and AX3005 variants to use the
same driver.

Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com>
---
 MAINTAINERS                     |   1 +
 drivers/iio/adc/Kconfig         |  10 ++
 drivers/iio/adc/Makefile        |   1 +
 drivers/iio/adc/axiado_saradc.c | 244 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 256 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 932bba890780..e6dadfa65ee0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4318,6 +4318,7 @@ M:	Akhila Kavi <akavi@axiado.com>
 M:	Prasad Bolisetty <pbolisetty@axiado.com>
 S:	Maintained
 F:	Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
+F:	drivers/iio/adc/axiado_saradc.c
 
 AXIS ARTPEC ARM64 SoC SUPPORT
 M:	Jesper Nilsson <jesper.nilsson@axis.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index a9dedbb8eb46..a9ba600a5f64 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -631,6 +631,16 @@ config AT91_SAMA5D2_ADC
 	  To compile this driver as a module, choose M here: the module will be
 	  called at91-sama5d2_adc.
 
+config AXIADO_SARADC
+	tristate "Axiado SARADC driver"
+	depends on ARCH_AXIADO || COMPILE_TEST
+	help
+	  Say yes here to build support for the SARADC found in Axiado
+	  SoCs.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called axiado_saradc.
+
 config AXP20X_ADC
 	tristate "X-Powers AXP20X and AXP22X ADC driver"
 	depends on MFD_AXP20X
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 097357d146ba..96de0ce1d90a 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_ADI_AXI_ADC) += adi-axi-adc.o
 obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
 obj-$(CONFIG_AT91_ADC) += at91_adc.o
 obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
+obj-$(CONFIG_AXIADO_SARADC) += axiado_saradc.o
 obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
 obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
 obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
diff --git a/drivers/iio/adc/axiado_saradc.c b/drivers/iio/adc/axiado_saradc.c
new file mode 100644
index 000000000000..d2f4071c932c
--- /dev/null
+++ b/drivers/iio/adc/axiado_saradc.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021-2026 Axiado Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+
+/* Register offsets */
+#define AX_SARADC_GLOBAL_CTRL_REG 0x0004
+#define AX_SARADC_MANUAL_CTRL_REG 0x0008
+#define AX_SARADC_DOUT_REG 0x001C
+
+/* GLOBAL_CTRL register fields */
+#define AX_SARADC_GLOBAL_CTRL_CH_EN_MASK	GENMASK(31, 16)
+#define AX_SARADC_GLOBAL_CTRL_SAMPLE_MASK	GENMASK(6, 5)
+#define AX_SARADC_GLOBAL_CTRL_MODE_MASK		GENMASK(4, 3)
+#define AX_SARADC_GLOBAL_CTRL_PD		BIT(2)
+#define AX_SARADC_GLOBAL_CTRL_ENABLE		BIT(0)
+
+/* GLOBAL_CTRL register values */
+#define AX_SARADC_GLOBAL_CTRL_SAMPLE_16		\
+	FIELD_PREP(AX_SARADC_GLOBAL_CTRL_SAMPLE_MASK, 0)
+
+#define AX_SARADC_GLOBAL_CTRL_MODE_MANUAL	\
+	FIELD_PREP(AX_SARADC_GLOBAL_CTRL_MODE_MASK, 1)
+
+/* MANUAL_CTRL register fields */
+#define AX_SARADC_MANUAL_CTRL_ENABLE           BIT(0)
+#define AX_SARADC_MANUAL_CTRL_CH_SEL_MASK      GENMASK(4, 1)
+
+#define AX_SARADC_MANUAL_CTRL_EN(ch)           \
+	(AX_SARADC_MANUAL_CTRL_ENABLE |          \
+	 FIELD_PREP(AX_SARADC_MANUAL_CTRL_CH_SEL_MASK, ch))
+
+#define AX_RESOLUTION_BITS 10
+#define AX_SARADC_CONV_CYCLES 13
+#define AX_SARADC_CONV_DELAY_MARGIN_US 10
+
+struct axiado_saradc {
+	void __iomem *regs;
+	struct clk *clk;
+	unsigned long clk_rate;
+	int vref_uV;
+	struct mutex lock; /* Serializes ADC conversions. */
+};
+
+static int axiado_saradc_conversion(struct axiado_saradc *info,
+				    struct iio_chan_spec const *chan, int *val)
+{
+	unsigned long usecs;
+
+	guard(mutex)(&info->lock);
+
+	/* Select the channel to be used and trigger conversion */
+	writel(AX_SARADC_MANUAL_CTRL_EN(chan->channel),
+	       info->regs + AX_SARADC_MANUAL_CTRL_REG);
+
+	/* Hardware requires 13 conversion cycles at clk_rate */
+	usecs = DIV_ROUND_UP(AX_SARADC_CONV_CYCLES * USEC_PER_SEC,
+			     info->clk_rate);
+	fsleep(usecs + AX_SARADC_CONV_DELAY_MARGIN_US);
+
+	*val = readl(info->regs + AX_SARADC_DOUT_REG) &
+	       GENMASK(AX_RESOLUTION_BITS - 1, 0);
+
+	/* Stop manual conversion */
+	writel(0, info->regs + AX_SARADC_MANUAL_CTRL_REG);
+
+	return 0;
+}
+
+static int axiado_saradc_read_raw(struct iio_dev *indio_dev,
+				  struct iio_chan_spec const *chan, int *val,
+				  int *val2, long mask)
+{
+	struct axiado_saradc *info = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = axiado_saradc_conversion(info, chan, val);
+		return ret ? ret : IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = info->vref_uV / 1000;
+		*val2 = AX_RESOLUTION_BITS;
+		return IIO_VAL_FRACTIONAL_LOG2;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info axiado_saradc_iio_info = {
+	.read_raw = axiado_saradc_read_raw,
+};
+
+struct axiado_saradc_soc_data {
+	const char *name;
+	unsigned int num_channels;
+};
+
+static const struct axiado_saradc_soc_data ax3000_saradc_data = {
+	.name = "ax3000_saradc",
+	.num_channels = 16,
+};
+
+static const struct axiado_saradc_soc_data ax3005_saradc_data = {
+	.name = "ax3005_saradc",
+	.num_channels = 8,
+};
+
+#define AX_SARADC_CH(_index, _id)                                       \
+	{                                                               \
+		.type = IIO_VOLTAGE,                                    \
+		.indexed = 1,                                           \
+		.channel = (_index),                                    \
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
+		.datasheet_name = (_id),                                \
+	}
+
+static const struct iio_chan_spec axiado_saradc_iio_channels[] = {
+	AX_SARADC_CH(0, "adc0"),   AX_SARADC_CH(1, "adc1"),
+	AX_SARADC_CH(2, "adc2"),   AX_SARADC_CH(3, "adc3"),
+	AX_SARADC_CH(4, "adc4"),   AX_SARADC_CH(5, "adc5"),
+	AX_SARADC_CH(6, "adc6"),   AX_SARADC_CH(7, "adc7"),
+	AX_SARADC_CH(8, "adc8"),   AX_SARADC_CH(9, "adc9"),
+	AX_SARADC_CH(10, "adc10"), AX_SARADC_CH(11, "adc11"),
+	AX_SARADC_CH(12, "adc12"), AX_SARADC_CH(13, "adc13"),
+	AX_SARADC_CH(14, "adc14"), AX_SARADC_CH(15, "adc15"),
+};
+
+static void axiado_saradc_disable(void *data)
+{
+	struct axiado_saradc *info = data;
+
+	writel(AX_SARADC_GLOBAL_CTRL_PD,
+	       info->regs + AX_SARADC_GLOBAL_CTRL_REG);
+}
+
+static int axiado_saradc_probe(struct platform_device *pdev)
+{
+	const struct axiado_saradc_soc_data *soc_data;
+	struct device *dev = &pdev->dev;
+	struct axiado_saradc *info;
+	struct iio_dev *indio_dev;
+	u32 regval;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	info = iio_priv(indio_dev);
+
+	info->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(info->regs))
+		return PTR_ERR(info->regs);
+
+	info->clk = devm_clk_get_enabled(dev, NULL);
+	if (IS_ERR(info->clk))
+		return PTR_ERR(info->clk);
+
+	info->clk_rate = clk_get_rate(info->clk);
+	if (!info->clk_rate)
+		return dev_err_probe(dev, -EINVAL, "invalid clock rate\n");
+
+	info->vref_uV = devm_regulator_get_enable_read_voltage(dev, "vref");
+	if (info->vref_uV < 0)
+		return dev_err_probe(dev, info->vref_uV,
+				     "failed to get vref voltage\n");
+
+	soc_data = device_get_match_data(dev);
+	if (!soc_data)
+		return dev_err_probe(dev, -EINVAL, "failed to get match data\n");
+
+	ret = devm_mutex_init(dev, &info->lock);
+	if (ret)
+		return ret;
+
+	regval = FIELD_PREP(AX_SARADC_GLOBAL_CTRL_CH_EN_MASK,
+			 GENMASK(soc_data->num_channels - 1, 0)) |
+	      AX_SARADC_GLOBAL_CTRL_SAMPLE_16 |
+	      AX_SARADC_GLOBAL_CTRL_MODE_MANUAL |
+	      AX_SARADC_GLOBAL_CTRL_ENABLE;
+
+	writel(AX_SARADC_GLOBAL_CTRL_PD,
+		  info->regs + AX_SARADC_GLOBAL_CTRL_REG);
+	writel(regval, info->regs + AX_SARADC_GLOBAL_CTRL_REG);
+
+	ret = devm_add_action_or_reset(dev, axiado_saradc_disable, info);
+	if (ret)
+		return ret;
+
+	indio_dev->name = soc_data->name;
+	indio_dev->info = &axiado_saradc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = axiado_saradc_iio_channels;
+	indio_dev->num_channels = soc_data->num_channels;
+
+	return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id axiado_saradc_match[] = {
+	{
+		.compatible = "axiado,ax3000-saradc",
+		.data = &ax3000_saradc_data,
+	},
+	{
+		.compatible = "axiado,ax3005-saradc",
+		.data = &ax3005_saradc_data,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, axiado_saradc_match);
+
+static struct platform_driver axiado_saradc_driver = {
+	.driver = {
+		.name =  "axiado-saradc",
+		.of_match_table = axiado_saradc_match,
+	},
+	.probe = axiado_saradc_probe,
+};
+
+module_platform_driver(axiado_saradc_driver);
+
+MODULE_AUTHOR("AXIADO CORPORATION");
+MODULE_DESCRIPTION("AXIADO SARADC driver");
+MODULE_LICENSE("GPL");

-- 
2.34.1


^ permalink raw reply related

* [PATCH v2 0/2] iio: adc: Add Axiado SARADC driver
From: Petar Stepanovic @ 2026-06-11  9:37 UTC (permalink / raw)
  To: Akhila Kavi, Prasad Bolisetty, Jonathan Cameron, David Lechner,
	Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Harshit Shah
  Cc: linux-iio, devicetree, linux-arm-kernel, linux-kernel,
	Petar Stepanovic, Conor Dooley

This series adds support for the SAR ADC controller found on Axiado
AX3000 and AX3005 SoCs.

A new driver is needed because this SAR ADC controller is a SoC-specific
hardware block used on Axiado SoCs. It has its own register layout,
channel enable handling, conversion control, and data readout sequence,
and it does not match any existing upstream IIO ADC driver.

AX3000 provides sixteen input channels, while AX3005 provides eight
input channels. The driver uses SoC match data to select the number of
available channels for each compatible.

The driver supports single-shot voltage reads through the IIO subsystem
and uses the reference voltage regulator for scale calculation.

The datasheet is not publicly available. Public high-level product
information is available at:

  https://axiado.com/products/#AX3080

The register definitions and programming sequence used by this driver
are based on Axiado internal SoC documentation.

Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com>
---
Changes in v2:
- Fixed the devicetree example node name to use the generic ADC node name.
- Removed the explicit `depends on OF` from Kconfig.
- Cleaned up and reordered header includes.
- Added missing includes for `bits.h`, `clk.h`, `cleanup.h`, and `err.h`.
- Removed unused `linux/kernel.h` include.
- Renamed register offset macros to use the `_REG` suffix.
- Renamed register bitfield macros to include the register name prefix.
- Added separate macros for `GLOBAL_CTRL` and `MANUAL_CTRL` register
  fields and values.
- Replaced `iowrite32()` / `ioread32()` with `writel()` / `readl()`.
- Moved ADC conversion locking into `axiado_saradc_conversion()` using
  `guard(mutex)`.
- Replaced `usleep_range()` with `fsleep()`.
- Renamed `vref_uv` to `vref_uV`.
- Added SoC-specific device names in `axiado_saradc_soc_data`.
- Used the fixed SoC-specific name for `indio_dev->name`.
- Removed unused buffered scan configuration from IIO channels.
- Added a managed cleanup action to disable the SARADC hardware on driver
  unbind or probe failure.
- Switched to a local `struct device *dev` helper in probe.
- Used `devm_mutex_init()` for mutex initialization.
- Simplified error handling by using `dev_err_probe()`.
- Updated probe variable declarations to follow reverse Christmas tree
  order.
- Fixed the `of_device_id` terminator style.
- Replaced `KBUILD_MODNAME` with a fixed driver name string.
- Link to v1: https://lore.kernel.org/r/20260528-axiado-ax3000-ax3005-saradc-v1-0-345dd5f6608a@axiado.com

---
Petar Stepanovic (2):
      dt-bindings: iio: adc: add Axiado AX3000/AX3005 SARADC
      iio: adc: add Axiado SARADC driver

 .../bindings/iio/adc/axiado,ax3000-saradc.yaml     |  63 ++++++
 MAINTAINERS                                        |   8 +
 drivers/iio/adc/Kconfig                            |  10 +
 drivers/iio/adc/Makefile                           |   1 +
 drivers/iio/adc/axiado_saradc.c                    | 244 +++++++++++++++++++++
 5 files changed, 326 insertions(+)
---
base-commit: 51f0c0b8545b23963afd5d43a8f56ee05bfa54da
change-id: 20260508-axiado-ax3000-ax3005-saradc-151aed5d25da

Best regards,
-- 
Petar Stepanovic <pstepanovic@axiado.com>


^ permalink raw reply

* Re: [PATCH] dt-bindings: cache: l2c2x0: Add missing power-domains
From: Geert Uytterhoeven @ 2026-06-11  9:37 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Conor Dooley, Jonathan Cameron, Rob Herring, Krzysztof Kozlowski,
	devicetree, linux-renesas-soc
In-Reply-To: <20260611-serious-divergent-sidewinder-ec7ccb@quoll>

Hi Krzysztof,

On Thu, 11 Jun 2026 at 11:28, Krzysztof Kozlowski <krzk@kernel.org> wrote:
> On Wed, Jun 10, 2026 at 05:29:20PM +0200, Geert Uytterhoeven wrote:
> > On Renesas SH-Mobile and R-Mobile SoCs, the ARM PL310 L2 Cache
> > Controller is located in a controllable power area.
>
> Interesting... so to turn on L2 cache, OS would need to boot, setup
> power domains handling, turn on power domain and then turn on L2 cache?

On R-Mobile A1, the L2 cache is even located in the same power area
as the Cortex A9 CPU core ;-)

> I understand that bootloader actually handles it, but I really doubt
> that it is "controllable".

It may also depends on which CPU core is started first.  These SoCs have
both ARM and SH CPU cores, and the SH core can power down all ARM parts.

But hey, DT describes hardware ;-)

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

Thanks!

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH v4 3/3] arm64: dts: qcom: Add Vicharak Axon Mini
From: Ajit Singh @ 2026-06-11  9:35 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: Bjorn Andersson, Dmitry Baryshkov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-arm-msm, devicetree,
	linux-kernel
In-Reply-To: <cc48189f-421c-4d04-945a-5905fa52357c@oss.qualcomm.com>

On Wed, Jun 10, 2026 at 03:01:48PM +0530, Konrad Dybcio wrote:
> On 6/7/26 1:36 PM, Ajit Singh wrote:
Thanks for review, Konrad!
> > Add DTS for the Vicharak Axon Mini board based on the Qualcomm
> > QCS6490 SoC.
> > 
> > This adds debug UART, eMMC, UFS, SDIO WLAN, USB 2.0 host, PCIe
> > support along with regulators.
> > 
> > The UFS ICE block is kept disabled because enabling it currently causes
> > an SError during qcom_ice_create() on this board. UFS works without ICE.
> > 
> > Signed-off-by: Ajit Singh <blfizzyy@gmail.com>
> > ---
>
> [...]
>
> > +		vreg_l16b_1p1: ldo16 {
> > +			regulator-name = "vreg_l16b_1p1";
> > +			regulator-min-microvolt = <1100000>;
> > +			regulator-max-microvolt = <1300000>;
> > +			regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
> > +
> > +			regulator-always-on;
> > +			regulator-boot-on;
> > +		};
> > +
> > +		vreg_l17b_1p7: ldo17 {
> > +			regulator-name = "vreg_l17b_1p7";
> > +			regulator-min-microvolt = <1700000>;
> > +			regulator-max-microvolt = <1900000>;
> > +			regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>;
> > +
> > +			regulator-always-on;
> > +			regulator-boot-on;
> > +		};
>
> Any reason for these two to be always-on?
vreg_l17b_1p7 supplies VDDD of the ES8388 audio codec, and vreg_l16b_1p1
supplies the CH7218A eDP-to-HDMI bridge. Both consumers are not described in
this initial DTS and will be added in follow-up patches.

So yes, these do not need to be always-on in this patch. I will drop
regulator-always-on and regulator-boot-on for both.

>
> [...]
> 
> > +&usb_2 {
> > +	/* Routed to an onboard USB hub for two USB-A host ports. */
> > +	dr_mode = "host";
> 
> Do they need to be powered/power sequenced in any way?
> see drivers/usb/misc/onboard_usb_dev.c and e.g. qcs6490-rb3gen2.dts
> -> usb5e3,610
Thanks, I checked it. For Axon-mini USB hub is powered from the shared
VCC_3V3 rail, which is enabled by board hardware. There is no hub-specific
reset so I think no power sequencing is required here.

> 
> Konrad

Ajit

^ permalink raw reply

* Re: [PATCH v2 05/16] usb: hub: Associate port@ fwnode with USB port device
From: Bartosz Golaszewski @ 2026-06-11  9:35 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Greg Kroah-Hartman, Daniel Scally, Heikki Krogerus, Sakari Ailus,
	Rafael J. Wysocki, Danilo Krummrich, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno, Alan Stern, linux-acpi, driver-core,
	linux-pm, linux-usb, devicetree, linux-mediatek, linux-arm-kernel,
	linux-kernel, Manivannan Sadhasivam, Chen-Yu Tsai
In-Reply-To: <aipz2zMFcdnZYTxS@ashevche-desk.local>

On Thu, Jun 11, 2026 at 10:37 AM Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
>
> On Thu, Jun 11, 2026 at 04:20:58AM -0400, Bartosz Golaszewski wrote:
> > On Wed, 10 Jun 2026 16:16:12 +0200, Andy Shevchenko
> > <andriy.shevchenko@linux.intel.com> said:
> > > On Wed, Jun 10, 2026 at 04:40:39PM +0800, Chen-Yu Tsai wrote:
> > >> When a USB hub port is connected to a connector in a firmware node
> > >> graph, the port itself has a node in the graph.
> > >>
> > >> Associate the port's firmware node with the USB port's device,
> > >> usb_port::dev. This is used in later changes for the M.2 slot power
> > >> sequencing provider to match against the requesting port.
> > >
> > > Okay, would this affect ACPI-based systems? if so, how?
> > > Can you elaborate on that, please?
> >
> > Is it possible that there's an ACPI device node associated with the port like
> > on some DT systems? I don't think so and there should be no impact IMO but I
> > also don't know enough about ACPI.
>
> The API is agnostic. There is a possibility to have software nodes associated
> with the port. I think the best is to be sure that ACPI-aware people who are
> experts in USB will check this (Heikki?).
>

Even if there is a software node - it shouldn't really matter. It will
just be assigned to the port device.

Bart

> Also note Sashiko complain on reference count leakage.
>
> --
> With Best Regards,
> Andy Shevchenko
>
>

^ permalink raw reply

* Re: [PATCH v1 1/2] dt-bindings: sound: add qcom,wsa885x-i2c
From: Krzysztof Kozlowski @ 2026-06-11  9:34 UTC (permalink / raw)
  To: Prasad Kumpatla
  Cc: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai,
	Linus Walleij, Bartosz Golaszewski, Srinivas Kandagatla,
	linux-arm-msm, linux-sound, devicetree, linux-kernel, linux-gpio
In-Reply-To: <20260610155708.151067-2-prasad.kumpatla@oss.qualcomm.com>

On Wed, Jun 10, 2026 at 09:27:07PM +0530, Prasad Kumpatla wrote:
> Document the Qualcomm WSA885X I2C smart amplifier binding.
> 
> Describe the required supplies, powerdown and interrupt GPIOs, the
> optional battery configuration, and the optional init-table property
> used to program the device during codec initialization.
> 
> This matches the driver programming model and documents the DT data

Binding matches hardware, not driver. Please describe the hardware.

> needed to use the codec on platforms with Audio IF playback.
> 
> Signed-off-by: Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>
> ---
>  .../bindings/sound/qcom,wsa885x-i2c.yaml      | 89 +++++++++++++++++++
>  1 file changed, 89 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/sound/qcom,wsa885x-i2c.yaml
> 
> diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa885x-i2c.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa885x-i2c.yaml
> new file mode 100644
> index 000000000..1069f470d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/sound/qcom,wsa885x-i2c.yaml

There is no I2C in device name.

> @@ -0,0 +1,89 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/sound/qcom,wsa885x-i2c.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm WSA885x I2C smart speaker amplifier
> +
> +maintainers:
> +  - Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
> +  - Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>
> +
> +description: |

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

> +  WSA885x is a Qualcomm Aqstic smart speaker amplifier with an I2C control
> +  interface and a digital audio interface exposed through ASoC DAI callbacks.
> +
> +allOf:
> +  - $ref: dai-common.yaml#
> +
> +properties:
> +  compatible:
> +    const: qcom,wsa885x-i2c

Same here

Also, incorrect usage of wildcard. Look at other bindings how this is
written, so you will not repeat the same comments:
https://lore.kernel.org/all/20250522-rb2_audio_v3-v3-3-9eeb08cab9dc@linaro.org/

Read writing bindings before posting next version.

I also cannot find traces of internal review of this. Did it happen? Did
you receive toolset comments?

> +
> +  reg:
> +    maxItems: 1
> +
> +  '#sound-dai-cells':
> +    const: 0
> +
> +  powerdown-gpios:
> +    description: GPIO controlling the SD_N powerdown pin.
> +    maxItems: 1
> +
> +  interrupt-gpios:

No, interrupts are never written as GPIOs.

Where is this binding coming from?

> +    description: GPIO used for the codec interrupt output.
> +    maxItems: 1
> +
> +  vdd-1p8-supply: true
> +
> +  vdd-io-supply: true
> +
> +  qcom,battery-config:
> +    $ref: /schemas/types.yaml#/definitions/uint32
> +    description: Speaker battery configuration, 1 for 1S and 2 for 2S.

Use string

> +    default: 1
> +    enum: [1, 2]
> +
> +  qcom,wsa885x-init-table:
> +    $ref: /schemas/types.yaml#/definitions/uint32-array
> +    minItems: 2
> +    maxItems: 256
> +    description: |
> +      Sequence of register/value pairs applied during codec hardware

No, we don't store register values usually.

> +      initialization. Entries are encoded as alternating register address and
> +      register value cells. The number of entries must be even (register/value
> +      pairs); maxItems is 256 (128 pairs).
> +
> +required:
> +  - compatible
> +  - reg
> +  - '#sound-dai-cells'
> +  - powerdown-gpios
> +  - interrupt-gpios
> +  - vdd-1p8-supply
> +  - vdd-io-supply
> +
> +additionalProperties: false

unevaluated instead. Again, OPEN other existing bindings. Why doing
something completely different? Is there any WSA88xx binding with
additionalProperties? No.

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v5] arm64: dts: qcom: glymur: add coresight nodes
From: Konrad Dybcio @ 2026-06-11  9:28 UTC (permalink / raw)
  To: Jie Gan, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang
  Cc: linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <20260519-add-coresight-nodes-for-glymur-v5-1-3dad99fe31e0@oss.qualcomm.com>

On 5/19/26 3:14 PM, Jie Gan wrote:
> Add CoreSight nodes to enable trace paths like TPDM->ETF/STM->ETF.
> These devices are part of the AOSS, CDSP, QDSS, PCIe5, TraceNoc and
> some small subsystems, such as GCC, IPCC, PMU and so on.
> 
> Delete cti_wpss DT node on Mahua since this device will cause NoC issue
> on Mahua device.

Another good explanation is that it simply doesn't exist there!

> Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
> ---

[...]


> +	tpdm-cdsp-llm {

Nodes without a unit address should be sorted alphabetically

> +		compatible = "qcom,coresight-static-tpdm";
> +		qcom,cmb-element-bits = <32>;

Do these other TPDMs not have the "DSB element" as the driver
calls it?

> +
> +		out-ports {
> +			port {
> +				cdsp_llm_tpdm_out: endpoint {
> +					remote-endpoint = <&cdsp_tpda_in1>;
> +				};
> +			};
> +		};
> +	};
> +
> +	tpdm-cdsp-llm2 {
> +		compatible = "qcom,coresight-static-tpdm";
> +		qcom,cmb-element-bits = <32>;
> +
> +		out-ports {
> +			port {
> +				cdsp_llm2_tpdm_out: endpoint {
> +					remote-endpoint = <&cdsp_tpda_in2>;
> +				};
> +			};
> +		};
> +	};
> +
> +	tpdm-cdsp-cmsr {
> +		compatible = "qcom,coresight-static-tpdm";
> +
> +		qcom,cmb-element-bits = <32>;
> +		qcom,dsb-element-bits = <32>;

Some nodes have a \n between compatible and other properties, some don't
Please unify the style (fwiw I prefer the one with a \n)

> base-commit: e98d21c170b01ddef366f023bbfcf6b31509fa83

Please rebase, doesn't apply on next-20260610

Konrad

^ permalink raw reply

* Re: [PATCH v3 3/4] arm64: qcom: ipq5018: Add GEPHY RX and TX clocks
From: Paolo Abeni @ 2026-06-11  9:28 UTC (permalink / raw)
  To: george.moussalem, Andrew Lunn, Heiner Kallweit, Russell King,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
	Bjorn Andersson, Konrad Dybcio
  Cc: netdev, devicetree, linux-kernel, Konrad Dybcio, linux-arm-msm,
	Dmitry Baryshkov
In-Reply-To: <20260605-ipq5018-gephy-clocks-v3-3-f232d9ca0966@outlook.com>

On 6/5/26 2:41 PM, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
> 
> Add RX and TX clocks for the IPQ5018 GEPHY to enable the datapath.
> 
> Fixes: f5f2b835e316 ("arm64: dts: qcom: ipq5018: Add GE PHY to internal mdio bus")
> Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>

I think here we need an explicit ack from arm people to take this patch
(and the whole series).

/P


^ permalink raw reply

* Re: [PATCH] dt-bindings: cache: l2c2x0: Add missing power-domains
From: Krzysztof Kozlowski @ 2026-06-11  9:28 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Conor Dooley, Jonathan Cameron, Rob Herring, Krzysztof Kozlowski,
	devicetree, linux-renesas-soc
In-Reply-To: <0a57ab356e5f426e28ead373b809f88a63e55380.1781105151.git.geert+renesas@glider.be>

On Wed, Jun 10, 2026 at 05:29:20PM +0200, Geert Uytterhoeven wrote:
> On Renesas SH-Mobile and R-Mobile SoCs, the ARM PL310 L2 Cache
> Controller is located in a controllable power area.

Interesting... so to turn on L2 cache, OS would need to boot, setup
power domains handling, turn on power domain and then turn on L2 cache?

I understand that bootloader actually handles it, but I really doubt
that it is "controllable".

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

Best regards,
Krzysztof


^ permalink raw reply

* Re: [PATCH v3 2/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller support
From: sashiko-bot @ 2026-06-11  9:27 UTC (permalink / raw)
  To: Chi-Wen Weng; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260611091246.2070485-3-cwweng.linux@gmail.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Inconsistent chip-select polarity logic between SPI core and spi-mem operations leads to inverted CS levels.
- [High] Data race on the Slave Select Control (SSCTL) register due to concurrent `spi_setup()` and transfers.
- [High] The PIO transfer loop uses atomic polling for bulk data transfers without yielding the CPU, risking soft lockups on large memory operations.
--

commit 870f4cc6d9d6ab3e7f64bb28977c07e0fcfd4027
Author: Chi-Wen Weng <cwweng@nuvoton.com>

spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller support

Add SPI controller driver support for the Nuvoton MA35D1 Quad SPI
controller. The controller supports standard SPI transfers and spi-mem
operations in single, dual, and quad modes.

diff --git a/drivers/spi/spi-ma35d1-qspi.c b/drivers/spi/spi-ma35d1-qspi.c
new file mode 100644
index 0000000000000..d3e687b6a06d7
--- /dev/null
+++ b/drivers/spi/spi-ma35d1-qspi.c
[ ... ]
> +static int nuvoton_qspi_txrx(struct nuvoton_qspi *qspi, const void *txbuf,
> +			     void *rxbuf, unsigned int len)
> +{
> +	unsigned int bytes_per_word = DIV_ROUND_UP(qspi->bits_per_word, 8);
> +	unsigned int words;
> +	u32 val;
> +	int ret;
> +	int i;
> +
> +	if (!len)
> +		return 0;
> +
> +	if (len % bytes_per_word)
> +		return -EINVAL;
> +
> +	words = len / bytes_per_word;
> +
> +	ret = nuvoton_qspi_reset_fifo(qspi);
> +	if (ret) {
> +		dev_err(qspi->dev, "FIFO reset timed out\n");
> +		return ret;
> +	}
> +
> +	/*
> +	 * Use conservative word-by-word PIO access. This keeps the initial driver
> +	 * simple and avoids relying on FIFO threshold interrupts or DMA support.
> +	 *
> +	 * The MA35D1 QSPI controller pushes one RX FIFO entry for each TX word in
> +	 * single, dual-output and quad-output modes. Drain RX after every TX word
> +	 * and discard the value for TX-only transfers to avoid RX FIFO overflow.
> +	 */
> +	for (i = 0; i < words; i++) {
> +		ret = nuvoton_qspi_wait_tx_not_full(qspi);
> +		if (ret) {
> +			dev_err(qspi->dev, "TX FIFO full timeout\n");
> +			return ret;
> +		}
> +
> +		nuvoton_qspi_write(qspi, nuvoton_qspi_tx_word(txbuf, i,
> +							      qspi->bits_per_word),
> +				   NUVOTON_QSPI_TX_OFFSET);
> +
> +		ret = nuvoton_qspi_wait_rx_not_empty(qspi);

[Severity: High]
Will this loop trigger kernel soft lockups on large memory operations?

The wait helpers nuvoton_qspi_wait_tx_not_full() and
nuvoton_qspi_wait_rx_not_empty() use readl_poll_timeout_atomic(), which
busy-waits without yielding the CPU.

Since the driver does not set max_transfer_size limits, the SPI framework
can pass down arbitrarily large buffers (e.g., several megabytes for firmware
flashes or filesystems).

Does this loop need a cond_resched(), or should the controller set
appropriate transfer size limits to prevent CPU stalls?

> +		if (ret) {
> +			dev_err(qspi->dev, "RX FIFO empty timeout\n");
> +			return ret;
> +		}
> +
> +		val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_RX_OFFSET);
> +		if (rxbuf)
> +			nuvoton_qspi_rx_word(rxbuf, i, val, qspi->bits_per_word);
> +	}
[ ... ]
> +static void nuvoton_qspi_set_cs_level(struct nuvoton_qspi *qspi,
> +				      unsigned int cs, bool assert)
> +{
> +	u32 mask;
> +	u32 val;
> +
> +	switch (cs) {
> +	case 0:
> +		mask = NUVOTON_QSPI_SSCTL_SS0_MASK;
> +		break;
> +	case 1:
> +		mask = NUVOTON_QSPI_SSCTL_SS1_MASK;
> +		break;
> +	default:
> +		dev_warn(qspi->dev, "invalid chip select %u\n", cs);
> +		return;
> +	}
> +
> +	val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_SSCTL_OFFSET);
> +
> +	if (assert)
> +		val |= mask;
> +	else
> +		val &= ~mask;
> +
> +	nuvoton_qspi_write(qspi, val, NUVOTON_QSPI_SSCTL_OFFSET);

[Severity: High]
Can this unprotected read-modify-write corrupt the active chip-select state?

The SPI core invokes spi_set_cs() directly during spi_setup() without holding
controller-level locks.

If a transfer is active for CS0, and a concurrent setup call occurs for CS1
(such as an ioctl(SPI_IOC_WR_MODE) from spidev), could this read-modify-write
on the shared SSCTL register race and cause silent data corruption or aborted
transfers?

> +}
> +
> +static void nuvoton_qspi_set_cs(struct spi_device *spi, bool enable)
> +{
> +	struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
> +
> +	nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), enable);
> +}
> +
> +static void nuvoton_qspi_mem_set_cs(struct spi_device *spi, bool enable)
> +{
> +	struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
> +	bool assert = enable;
> +
> +	if (spi->mode & SPI_CS_HIGH)
> +		assert = !assert;
> +
> +	nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), assert);

[Severity: High]
Does this cause contradictory chip-select polarity between standard SPI
transfers and SPI memory operations?

The SPI framework passes the physical level to drive to ctlr->set_cs(),
where false means drive low and true means drive high. nuvoton_qspi_set_cs()
passes this directly to nuvoton_qspi_set_cs_level().

However, nuvoton_qspi_mem_set_cs() treats its enable parameter as a logical
assertion flag and performs a manual inversion.

For an active-low device, will one of these paths end up driving the
chip-select line with the wrong polarity?

> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260611091246.2070485-1-cwweng.linux@gmail.com?part=2

^ permalink raw reply

* Re: [PATCH] arm64: defconfig: Enable USB_UAS in arm64 configuration
From: Krzysztof Kozlowski @ 2026-06-11  9:24 UTC (permalink / raw)
  To: sashiko-reviews, Akash Kumar
  Cc: robh, conor+dt, devicetree, linux-arm-msm, Bjorn Andersson,
	Konrad Dybcio
In-Reply-To: <20260611091509.6E09F1F00898@smtp.kernel.org>

On 11/06/2026 11:15, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Low] Mismatch between Author email and Signed-off-by email.
> --
> 
> commit 8dd2d2a3035d6deca9f623e43d26b74e60f0f6d7
> Author: Akash Kumar <akash.kumar@oss.qualcomm.com>
> 
> arm64: defconfig: Enable USB_UAS in arm64 configuration
> 
> This commit enables CONFIG_USB_UAS=y in the default arm64 configuration
> to provide better out-of-the-box performance for USB storage devices.
> 
>> Author: Akash Kumar <akash.kumar@oss.qualcomm.com>
> [ ... ]
>>     Signed-off-by: Akash Kumar <akakum@qti.qualcomm.com>
> 
> [Severity: Low]
> This isn't a bug, but there is a mismatch between the Author email and the
> Signed-off-by email. 

This is a bug... DCO email must match.

Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH v3 4/4] net: phy: at803x: add RX and TX clock management for IPQ5018 PHY
From: Paolo Abeni @ 2026-06-11  9:24 UTC (permalink / raw)
  To: george.moussalem, Andrew Lunn, Heiner Kallweit, Russell King,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
	Bjorn Andersson, Konrad Dybcio
  Cc: netdev, devicetree, linux-kernel, Konrad Dybcio, linux-arm-msm
In-Reply-To: <20260605-ipq5018-gephy-clocks-v3-4-f232d9ca0966@outlook.com>

On 6/5/26 2:41 PM, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
> 
> Acquire and manage the RX and TX clocks for the IPQ5018 PHY.
> These clocks are required for the PHY's datapath to function correctly.
> Gate the clocks upon link state changes for improved power management.
> 
> Fixes: d46502279a11 ("net: phy: qcom: at803x: Add Qualcomm IPQ5018 Internal PHY support")
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>

I read the above as this device never worked. If so I think this should
probably target the net-next tree with no fixes tag (basically it
enables new H/W).

/P


^ permalink raw reply

* Re: [PATCH] ASoC: dt-bindings: everest,es8389: Document audio graph port
From: Mark Brown @ 2026-06-10 11:12 UTC (permalink / raw)
  To: Liam Girdwood, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Michael Zhang, Diederik de Haas
  Cc: linux-sound, devicetree, linux-kernel, linux-rockchip
In-Reply-To: <20260607105913.355966-1-diederik@cknow-tech.com>

On Sun, 07 Jun 2026 12:58:49 +0200, Diederik de Haas wrote:
> ASoC: dt-bindings: everest,es8389: Document audio graph port

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-7.2

Thanks!

[1/1] ASoC: dt-bindings: everest,es8389: Document audio graph port
      https://git.kernel.org/broonie/sound/c/aa283db3bb8d

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark


^ permalink raw reply

* Re: [PATCH] arm64: defconfig: Enable USB_UAS in arm64 configuration
From: Krzysztof Kozlowski @ 2026-06-11  9:20 UTC (permalink / raw)
  To: Akash Kumar, Greg Kroah-Hartman, Bjorn Andersson, Konrad Dybcio,
	Rob Herring, Dmitry Baryshkov, Krzysztof Kozlowski, Conor Dooley
  Cc: linux-usb, linux-kernel, linux-arm-msm, devicetree
In-Reply-To: <20260611090946.2790631-1-akash.kumar@oss.qualcomm.com>

On 11/06/2026 11:09, Akash Kumar wrote:
> The USB Attached SCSI (UAS) protocol is supported by many modern USB
> storage devices. It provides significantly higher performance than the
> legacy Mass Storage Class (MSC) by supporting multiple outstanding
> commands and command queuing.
> 
> Enable CONFIG_USB_UAS=y in the default arm64 configuration to provide
> better out-of-the-box performance for USB storage devices on arm64
> platforms.

Anyone in upstream actually uses it for development? Care to show some
examples?

This is not a distro config, you know...

> 
> Signed-off-by: Akash Kumar <akakum@qti.qualcomm.com>
> ---
>  arch/arm64/configs/defconfig | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
> index d905a0777f93..f82e9df77941 100644
> --- a/arch/arm64/configs/defconfig
> +++ b/arch/arm64/configs/defconfig
> @@ -1216,6 +1216,7 @@ CONFIG_USB_RENESAS_USBHS_HCD=m
>  CONFIG_USB_RENESAS_USBHS=m
>  CONFIG_USB_ACM=m
>  CONFIG_USB_STORAGE=y
> +CONFIG_USB_UAS=y

For sure not built-in...


Best regards,
Krzysztof

^ permalink raw reply

* Re: [PATCH v7 2/2] hwmon: Add Eswin EIC7700 PVT sensor driver
From: sashiko-bot @ 2026-06-11  9:17 UTC (permalink / raw)
  To: hehuan1; +Cc: robh, devicetree, linux-hwmon, conor+dt
In-Reply-To: <20260611090639.777-1-hehuan1@eswincomputing.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The IRQ is requested before the PM runtime cleanup action is registered, leading to a possible use-after-disable of the hardware clock in the ISR on driver removal.
- [Medium] The interrupt handler `eic7700_pvt_hard_isr` does not check the hardware interrupt status bit to verify if the device actually generated the interrupt.
--

commit 0f4c8aefb83780ee09652639eb2843f01c2baf38
Author: Huan He <hehuan1@eswincomputing.com>

hwmon: Add Eswin EIC7700 PVT sensor driver

Add support for ESWIN EIC7700 Voltage and Temperature sensor. The driver
supports temperature and voltage monitoring with polynomial conversion,
and provides sysfs interface for sensor data access.

> diff --git a/drivers/hwmon/eic7700-pvt.c b/drivers/hwmon/eic7700-pvt.c
> new file mode 100644
> index 0000000000000..3b3aed74c3941
> --- /dev/null
> +++ b/drivers/hwmon/eic7700-pvt.c

[ ... ]

> +static irqreturn_t eic7700_pvt_hard_isr(int irq, void *data)
> +{
> +	struct pvt_hwmon *pvt = data;
> +	int active;
> +	u32 val;
> +
> +	if (IS_ENABLED(CONFIG_PM)) {
> +		active = pm_runtime_get_if_active(pvt->dev);
> +		if (active <= 0)
> +			return IRQ_NONE;
> +	}
> +
> +	eic7700_pvt_update(pvt->regs + PVT_INT, PVT_INT_CLR, PVT_INT_CLR);
> +	/*
> +	 * Read the data, update the cache and notify a waiter of this event.
> +	 */
> +	val = readl(pvt->regs + PVT_DATA);
> +	WRITE_ONCE(pvt->data_cache, FIELD_GET(PVT_DATA_OUT, val));
> +	complete(&pvt->conversion);

[Severity: Medium]
Does this unconditionally process the interrupt without checking if the device
actually generated it?

If a spurious interrupt occurs, the ISR will prematurely clear the interrupt,
read whatever stale value is in PVT_DATA, and call complete(&pvt->conversion).
This will cause the waiting thread in eic7700_pvt_read_data() to wake up early
and return stale or garbage data to userspace.

Should the ISR read the status register and return IRQ_NONE if its device did
not assert the interrupt?

[ ... ]

> +	ret = eic7700_pvt_request_irq(pvt);
> +	if (ret) {
> +		if (!IS_ENABLED(CONFIG_PM))
> +			clk_disable_unprepare(pvt->clk);
> +		return ret;
> +	}
> +
> +	pm_runtime_enable(&pdev->dev);
> +	pm_runtime_set_autosuspend_delay(&pdev->dev, 3000);
> +	pm_runtime_use_autosuspend(&pdev->dev);
> +	pm_runtime_get_noresume(&pdev->dev);
> +
> +	ret = devm_add_action_or_reset(pvt->dev, eic7700_pvt_disable_pm_runtime,
> +				       pvt);

[Severity: High]
Can this devres setup lead to accessing unclocked registers during device
removal?

Because devres executes teardown callbacks in reverse order of their
registration, eic7700_pvt_disable_pm_runtime() will run before the IRQ
is freed by devm_request_threaded_irq()'s teardown.

During removal, eic7700_pvt_disable_pm_runtime() disables the clock:

eic7700_pvt_disable_pm_runtime() {
    ...
    if (!pm_runtime_status_suspended(pvt->dev)) {
        clk_disable_unprepare(pvt->clk);
        pm_runtime_set_suspended(pvt->dev);
    }
}

If an interrupt fires concurrently and the ISR executes past the
pm_runtime_get_if_active() check before the clock is disabled, the ISR
will attempt to access hardware registers while unclocked, which can lead
to an external abort or kernel panic:

eic7700_pvt_hard_isr() {
    ...
    eic7700_pvt_update(pvt->regs + PVT_INT, ...);
}

Should the PM runtime cleanup action be registered before the IRQ is requested?

> +	if (ret) {
> +		pm_runtime_put_noidle(&pdev->dev);
> +		return dev_err_probe(&pdev->dev, ret,
> +				     "Can't register PM cleanup\n");
> +	}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260611090505.734-1-hehuan1@eswincomputing.com?part=2

^ permalink raw reply

* Re: [PATCH v2 3/3] regulator: qcom-refgen: add support for the IPQ9650 SoC
From: sashiko-bot @ 2026-06-11  9:16 UTC (permalink / raw)
  To: Kathiravan Thirumoorthy; +Cc: conor+dt, robh, devicetree
In-Reply-To: <20260611-ipq9650_refgen-v2-3-d96a91d5b99e@oss.qualcomm.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] Unclocked MMIO access in `is_enabled` causes synchronous external abort.
- [High] Clock reference count underflow due to unbalanced disable.
- [Medium] Missing validation for zero clocks returned by `devm_clk_bulk_get_all()`.
--

commit 0bec5912036c4b180d8ea012798dcb554afa8119
Author: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>

regulator: qcom-refgen: add support for the IPQ9650 SoC

This commit adds support for the IPQ9650 SoC to the qcom-refgen regulator
driver. It introduces clock handling since this SoC requires explicitly
enabling clocks to bring up the PHYs properly.

> diff --git a/drivers/regulator/qcom-refgen-regulator.c b/drivers/regulator/qcom-refgen-regulator.c
> index 6a3795469927e..e8821f159ff16 100644
> --- a/drivers/regulator/qcom-refgen-regulator.c
> +++ b/drivers/regulator/qcom-refgen-regulator.c

[ ... ]

> @@ -62,6 +77,42 @@ static int qcom_sdm845_refgen_is_enabled(struct regulator_dev *rdev)
>  	return 1;
>  }
>  
> +static int qcom_ipq9650_refgen_enable(struct regulator_dev *rdev)
> +{
> +	struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev);
> +	int ret;
> +
> +	ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int qcom_ipq9650_refgen_disable(struct regulator_dev *rdev)
> +{
> +	struct qcom_refgen_drvdata *drvdata = rdev_get_drvdata(rdev);
> +
> +	clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);

[Severity: High]
If the bootloader leaves the REFGEN block enabled, the regulator framework
will treat this as boot-on and might skip calling the enable callback when
the first consumer enables it. When a consumer later disables it (or when
regulator_disable_unused() runs), this disable callback will execute
clk_bulk_disable_unprepare().

Since the driver never called clk_bulk_prepare_enable() itself, will this
cause the Linux clock framework's internal reference counts to underflow
and trigger warnings?

> +
> +	return 0;
> +}
> +
> +static const struct regulator_desc ipq9650_refgen_desc = {
> +	.enable_reg = REFGEN_REG_REFGEN_STATUS,
> +	.enable_mask = REFGEN_STATUS_OUT_MASK,
> +	.enable_val = REFGEN_STATUS_OUT_ENABLE,
> +	.enable_time = 5,
> +	.name = "refgen",
> +	.owner = THIS_MODULE,
> +	.type = REGULATOR_CURRENT,
> +	.ops = &(const struct regulator_ops) {
> +		.enable		= qcom_ipq9650_refgen_enable,
> +		.disable	= qcom_ipq9650_refgen_disable,
> +		.is_enabled	= regulator_is_enabled_regmap,

[Severity: Critical]
During devm_regulator_register(), the regulator framework invokes the
is_enabled callback to determine the initial hardware state. Because
regulator_is_enabled_regmap() directly performs a regmap_read(), and
qcom_refgen_probe() only retrieves the clocks without enabling them,
will this read occur while the clocks are gated?

On Qualcomm platforms, does accessing MMIO registers of a clock-gated
peripheral stall the bus and trigger a synchronous external abort?

> +	},
> +};
> +
>  static const struct regulator_desc sdm845_refgen_desc = {

[ ... ]

> @@ -106,10 +172,23 @@ static int qcom_refgen_probe(struct platform_device *pdev)
>  	struct regmap *regmap;
>  	void __iomem *base;
>  
> -	rdesc = of_device_get_match_data(dev);
> -	if (!rdesc)
> +	data = of_device_get_match_data(dev);
> +	if (!data)
>  		return -ENODATA;
>  
> +	if (data->has_clocks) {
> +		drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> +		if (!drvdata)
> +			return -ENOMEM;
> +
> +		drvdata->num_clks = devm_clk_bulk_get_all(dev, &drvdata->clks);
> +		if (drvdata->num_clks < 0)

[Severity: Medium]
If the device tree node improperly omits the clocks property,
devm_clk_bulk_get_all() will return 0. Does this allow the probe
function to silently succeed?

Later calls to clk_bulk_prepare_enable() will also succeed without
enabling any hardware. Since clocks are mandatory on IPQ9650, should
this treat a return value of 0 as an error?

> +			return dev_err_probe(dev, drvdata->num_clks,
> +					     "failed to get clocks\n");
> +	}
> +
> +	rdesc = data->rdesc;
> +
>  	base = devm_platform_ioremap_resource(pdev, 0);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260611-ipq9650_refgen-v2-0-d96a91d5b99e@oss.qualcomm.com?part=3

^ permalink raw reply


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