Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [DMARC error]Re: [PATCH 0/2] Add PWM support Amlogic S7 S7D S6
From: George Stark @ 2026-03-31  7:33 UTC (permalink / raw)
  To: Xianwei Zhao, Martin Blumenstingl
  Cc: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Heiner Kallweit, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, linux-pwm, devicetree, linux-kernel,
	linux-arm-kernel, linux-amlogic, Junyi Zhao
In-Reply-To: <70a637b1-a76a-470c-9a97-0b4599a40a1c@amlogic.com>

Hello Martin, Xianwei


On 3/31/26 10:10, Xianwei Zhao wrote:
> Hi Martin,
>      I confirmed with Junyi Zhao that the current implementation counts 
> from zero, so this submission is correct.
> We agree this should be fixed and will address it in a follow-up patch.
> Thanks for pointing it out.
> 
> On 2026/3/31 05:54, Martin Blumenstingl wrote:
>> Hi Xianwei Zhao,
>>
>> thanks for your contribution!
>>
>> On Thu, Mar 26, 2026 at 7:35 AM Xianwei Zhao via B4 Relay
>> <devnull+xianwei.zhao.amlogic.com@kernel.org>  wrote:
>>> Add bindings and driver support Amlogic S7/S7D/S6 SoCs.
>> There is an old report that got lost, stating that the current

Xianwei Zhao thanks for the confirmation.
I am the author of the old report and the corresponding patch and it's 
not lost. So if the patch is correct I'll be glad to add relevant 
tested-by tags.

>> pwm-meson driver has an off-by-one error with the hi and lo fields:
>> [0]
>> Since you are working on bringing up a new platform: is this something
>> you can verify in your lab?
>> To be clear: I'm not expecting you to work on this ad-hoc or bring a
>> patch into this series. However, it would be great if you could verify
>> if the findings from [0] are correct and send an updated patch in
>> future.
>>
>> Thank you and best regards
>> Martin
> 
> _______________________________________________
> linux-amlogic mailing list
> linux-amlogic@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-amlogic

-- 
Best regards
George


^ permalink raw reply

* [PATCH v5 3/3] pinctrl: aspeed: Add AST2700 SoC0 support
From: Billy Tsai @ 2026-03-31  7:31 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Joel Stanley, Andrew Jeffery, Linus Walleij, Billy Tsai,
	Bartosz Golaszewski, Ryan Chen
  Cc: Andrew Jeffery, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, openbmc, linux-gpio, linux-clk
In-Reply-To: <20260331-upstream_pinctrl-v5-0-8994f59ff367@aspeedtech.com>

Add pinctrl support for the SoC0 instance of the ASPEED AST2700.

AST2700 consists of two interconnected SoC instances, each with its own
pinctrl register block.

The SoC0 pinctrl hardware closely follows the design found in previous
ASPEED BMC generations, allowing the driver to build upon the common
ASPEED pinctrl infrastructure.

Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
 drivers/pinctrl/aspeed/Kconfig                  |   9 +
 drivers/pinctrl/aspeed/Makefile                 |   1 +
 drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c | 710 ++++++++++++++++++++++++
 3 files changed, 720 insertions(+)

diff --git a/drivers/pinctrl/aspeed/Kconfig b/drivers/pinctrl/aspeed/Kconfig
index 1a4e5b9ed471..f9672cca891e 100644
--- a/drivers/pinctrl/aspeed/Kconfig
+++ b/drivers/pinctrl/aspeed/Kconfig
@@ -31,3 +31,12 @@ config PINCTRL_ASPEED_G6
 	help
 	  Say Y here to enable pin controller support for Aspeed's 6th
 	  generation SoCs. GPIO is provided by a separate GPIO driver.
+
+config PINCTRL_ASPEED_G7_SOC0
+	bool "Aspeed G7 SoC pin control"
+	depends on (ARCH_ASPEED || COMPILE_TEST) && OF
+	select PINCTRL_ASPEED
+	help
+	  Say Y here to enable pin controller support for the SoC0 instance
+	  of Aspeed's 7th generation SoCs. GPIO is provided by a separate
+	  GPIO driver.
diff --git a/drivers/pinctrl/aspeed/Makefile b/drivers/pinctrl/aspeed/Makefile
index db2a7600ae2b..0de524ca2c72 100644
--- a/drivers/pinctrl/aspeed/Makefile
+++ b/drivers/pinctrl/aspeed/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_PINCTRL_ASPEED)	+= pinctrl-aspeed.o pinmux-aspeed.o
 obj-$(CONFIG_PINCTRL_ASPEED_G4)	+= pinctrl-aspeed-g4.o
 obj-$(CONFIG_PINCTRL_ASPEED_G5)	+= pinctrl-aspeed-g5.o
 obj-$(CONFIG_PINCTRL_ASPEED_G6)	+= pinctrl-aspeed-g6.o
+obj-$(CONFIG_PINCTRL_ASPEED_G7_SOC0) += pinctrl-aspeed-g7-soc0.o
diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c
new file mode 100644
index 000000000000..b1a09db65635
--- /dev/null
+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c
@@ -0,0 +1,710 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "pinctrl-aspeed.h"
+#include "pinmux-aspeed.h"
+#include "../pinctrl-utils.h"
+
+#define SCU200 0x200 /* System Reset Control #1  */
+
+#define SCU010 0x010 /* Hardware Strap Register */
+#define SCU400 0x400 /* Multi-function Pin Control #1  */
+#define SCU404 0x404 /* Multi-function Pin Control #2  */
+#define SCU408 0x408 /* Multi-function Pin Control #3  */
+#define SCU40C 0x40C /* Multi-function Pin Control #3  */
+#define SCU410 0x410 /* USB Multi-function Control Register  */
+#define SCU414 0x414 /* VGA Function Control Register  */
+
+#define SCU480 0x480 /* GPIO18A0 IO Control Register */
+#define SCU484 0x484 /* GPIO18A1 IO Control Register */
+#define SCU488 0x488 /* GPIO18A2 IO Control Register */
+#define SCU48C 0x48c /* GPIO18A3 IO Control Register */
+#define SCU490 0x490 /* GPIO18A4 IO Control Register */
+#define SCU494 0x494 /* GPIO18A5 IO Control Register */
+#define SCU498 0x498 /* GPIO18A6 IO Control Register */
+#define SCU49C 0x49c /* GPIO18A7 IO Control Register */
+#define SCU4A0 0x4A0 /* GPIO18B0 IO Control Register */
+#define SCU4A4 0x4A4 /* GPIO18B1 IO Control Register */
+#define SCU4A8 0x4A8 /* GPIO18B2 IO Control Register */
+#define SCU4AC 0x4AC /* GPIO18B3 IO Control Register */
+
+enum {
+	AC14,
+	AE15,
+	AD14,
+	AE14,
+	AF14,
+	AB13,
+	AB14,
+	AF15,
+	AF13,
+	AC13,
+	AD13,
+	AE13,
+	JTAG_PORT,
+	PCIERC0_PERST,
+	PCIERC1_PERST,
+	PORTA_MODE,
+	PORTA_U2,
+	PORTB_MODE,
+	PORTB_U2,
+	PORTA_U2_PHY,
+	PORTB_U2_PHY,
+	PORTA_U3,
+	PORTB_U3,
+	PORTA_U3_PHY,
+	PORTB_U3_PHY,
+};
+
+SIG_EXPR_LIST_DECL_SEMG(AC14, EMMCCLK, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 0));
+SIG_EXPR_LIST_DECL_SESG(AC14, VB1CS, VB1, SIG_DESC_SET(SCU404, 0));
+PIN_DECL_2(AC14, GPIO18A0, EMMCCLK, VB1CS);
+
+SIG_EXPR_LIST_DECL_SEMG(AE15, EMMCCMD, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 1));
+SIG_EXPR_LIST_DECL_SESG(AE15, VB1CK, VB1, SIG_DESC_SET(SCU404, 1));
+PIN_DECL_2(AE15, GPIO18A1, EMMCCMD, VB1CK);
+
+SIG_EXPR_LIST_DECL_SEMG(AD14, EMMCDAT0, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 2));
+SIG_EXPR_LIST_DECL_SESG(AD14, VB1MOSI, VB1, SIG_DESC_SET(SCU404, 2));
+PIN_DECL_2(AD14, GPIO18A2, EMMCDAT0, VB1MOSI);
+
+SIG_EXPR_LIST_DECL_SEMG(AE14, EMMCDAT1, EMMCG4, EMMC, SIG_DESC_SET(SCU400, 3));
+SIG_EXPR_LIST_DECL_SESG(AE14, VB1MISO, VB1, SIG_DESC_SET(SCU404, 3));
+PIN_DECL_2(AE14, GPIO18A3, EMMCDAT1, VB1MISO);
+
+SIG_EXPR_LIST_DECL_SEMG(AF14, EMMCDAT2, EMMCG4, EMMC, SIG_DESC_SET(SCU400, 4));
+PIN_DECL_1(AF14, GPIO18A4, EMMCDAT2);
+
+SIG_EXPR_LIST_DECL_SEMG(AB13, EMMCDAT3, EMMCG4, EMMC, SIG_DESC_SET(SCU400, 5));
+PIN_DECL_1(AB13, GPIO18A5, EMMCDAT3);
+
+SIG_EXPR_LIST_DECL_SEMG(AB14, EMMCCDN, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 6));
+SIG_EXPR_LIST_DECL_SESG(AB14, VB0CS, VB0, SIG_DESC_SET(SCU010, 17));
+PIN_DECL_2(AB14, GPIO18A6, EMMCCDN, VB0CS);
+
+SIG_EXPR_LIST_DECL_SEMG(AF15, EMMCWPN, EMMCG1, EMMC, SIG_DESC_SET(SCU400, 7));
+SIG_EXPR_LIST_DECL_SESG(AF15, VB0CK, VB0, SIG_DESC_SET(SCU010, 17));
+PIN_DECL_2(AF15, GPIO18A7, EMMCWPN, VB0CK);
+
+SIG_EXPR_LIST_DECL_SESG(AF13, TSPRSTN, TSPRSTN, SIG_DESC_SET(SCU010, 9));
+SIG_EXPR_LIST_DECL_SEMG(AF13, EMMCDAT4, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 8));
+SIG_EXPR_LIST_DECL_SESG(AF13, VB0MOSI, VB0, SIG_DESC_SET(SCU010, 17));
+PIN_DECL_3(AF13, GPIO18B0, TSPRSTN, EMMCDAT4, VB0MOSI);
+
+SIG_EXPR_LIST_DECL_SESG(AC13, UFSCLKI, UFSCLKI, SIG_DESC_SET(SCU010, 19));
+SIG_EXPR_LIST_DECL_SEMG(AC13, EMMCDAT5, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 9));
+SIG_EXPR_LIST_DECL_SESG(AC13, VB0MISO, VB0, SIG_DESC_SET(SCU010, 17));
+PIN_DECL_3(AC13, GPIO18B1, UFSCLKI, EMMCDAT5, VB0MISO);
+
+SIG_EXPR_LIST_DECL_SEMG(AD13, EMMCDAT6, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 10));
+SIG_EXPR_LIST_DECL_SESG(AD13, DDCCLK, VGADDC, SIG_DESC_SET(SCU404, 10));
+PIN_DECL_2(AD13, GPIO18B2, EMMCDAT6, DDCCLK);
+
+SIG_EXPR_LIST_DECL_SEMG(AE13, EMMCDAT7, EMMCG8, EMMC, SIG_DESC_SET(SCU400, 11));
+SIG_EXPR_LIST_DECL_SESG(AE13, DDCDAT, VGADDC, SIG_DESC_SET(SCU404, 11));
+PIN_DECL_2(AE13, GPIO18B3, EMMCDAT7, DDCDAT);
+
+GROUP_DECL(EMMCG1, AC14, AE15, AD14);
+GROUP_DECL(EMMCG4, AC14, AE15, AD14, AE14, AF14, AB13);
+GROUP_DECL(EMMCG8, AC14, AE15, AD14, AE14, AF14, AB13, AF13, AC13, AD13, AE13);
+GROUP_DECL(EMMCWPN, AF15);
+GROUP_DECL(EMMCCDN, AB14);
+FUNC_DECL_(EMMC, "EMMCG1", "EMMCG4", "EMMCG8", "EMMCWPN", "EMMCCDN");
+
+GROUP_DECL(VB1, AC14, AE15, AD14, AE14);
+GROUP_DECL(VB0, AF15, AB14, AF13, AC13);
+FUNC_DECL_2(VB, VB1, VB0);
+
+FUNC_GROUP_DECL(TSPRSTN, AF13);
+
+FUNC_GROUP_DECL(UFSCLKI, AC13);
+
+FUNC_GROUP_DECL(VGADDC, AD13, AE13);
+
+/* JTAG Port Selection */
+#define JTAG_PORT_PSP_DESC   { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x0, 0 }
+#define JTAG_PORT_SSP_DESC   { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x41, 0 }
+#define JTAG_PORT_TSP_DESC   { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x42, 0 }
+#define JTAG_PORT_DDR_DESC   { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x43, 0 }
+#define JTAG_PORT_USB3A_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x44, 0 }
+#define JTAG_PORT_USB3B_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x45, 0 }
+#define JTAG_PORT_PCIEA_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x46, 0 }
+#define JTAG_PORT_PCIEB_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x47, 0 }
+#define JTAG_PORT_JTAGM0_DESC { ASPEED_IP_SCU, SCU408, GENMASK(12, 5), 0x8, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGPSP, JTAG0, JTAGPSP, JTAG_PORT_PSP_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGSSP, JTAG0, JTAGSSP, JTAG_PORT_SSP_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGTSP, JTAG0, JTAGTSP, JTAG_PORT_TSP_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGDDR, JTAG0, JTAGDDR, JTAG_PORT_DDR_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGUSB3A, JTAG0, JTAGUSB3A, JTAG_PORT_USB3A_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGUSB3B, JTAG0, JTAGUSB3B, JTAG_PORT_USB3B_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGPCIEA, JTAG0, JTAGPCIEA, JTAG_PORT_PCIEA_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGPCIEB, JTAG0, JTAGPCIEB, JTAG_PORT_PCIEB_DESC);
+SIG_EXPR_LIST_DECL_SEMG(JTAG_PORT, JTAGM0, JTAG0, JTAGM0, JTAG_PORT_JTAGM0_DESC);
+PIN_DECL_(JTAG_PORT, SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGPSP), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGSSP),
+	  SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGTSP), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGDDR),
+	  SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGUSB3A), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGUSB3B),
+	  SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGPCIEA), SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGPCIEB),
+	  SIG_EXPR_LIST_PTR(JTAG_PORT, JTAGM0));
+
+GROUP_DECL(JTAG0, JTAG_PORT);
+
+FUNC_DECL_1(JTAGPSP, JTAG0);
+FUNC_DECL_1(JTAGSSP, JTAG0);
+FUNC_DECL_1(JTAGTSP, JTAG0);
+FUNC_DECL_1(JTAGDDR, JTAG0);
+FUNC_DECL_1(JTAGUSB3A, JTAG0);
+FUNC_DECL_1(JTAGUSB3B, JTAG0);
+FUNC_DECL_1(JTAGPCIEA, JTAG0);
+FUNC_DECL_1(JTAGPCIEB, JTAG0);
+FUNC_DECL_1(JTAGM0, JTAG0);
+
+/* PCIe Reset Control */
+SIG_EXPR_LIST_DECL_SESG(PCIERC0_PERST, PCIERC0PERST, PCIERC0PERST, SIG_DESC_SET(SCU200, 21));
+PIN_DECL_(PCIERC0_PERST, SIG_EXPR_LIST_PTR(PCIERC0_PERST, PCIERC0PERST));
+FUNC_GROUP_DECL(PCIERC0PERST, PCIERC0_PERST);
+
+SIG_EXPR_LIST_DECL_SESG(PCIERC1_PERST, PCIERC1PERST, PCIERC1PERST, SIG_DESC_SET(SCU200, 19));
+PIN_DECL_(PCIERC1_PERST, SIG_EXPR_LIST_PTR(PCIERC1_PERST, PCIERC1PERST));
+FUNC_GROUP_DECL(PCIERC1PERST, PCIERC1_PERST);
+
+#define PORTA_MODE_HPD0_DESC { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 0, 0 }
+#define PORTA_MODE_D0_DESC   { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 1, 0 }
+#define PORTA_MODE_H_DESC    { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 2, 0 }
+#define PORTA_MODE_HP_DESC    { ASPEED_IP_SCU, SCU410, GENMASK(25, 24), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AHPD0, USB2AH, USB2AHPD0, PORTA_MODE_HPD0_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AH, USB2AHAP, USB2AH, PORTA_MODE_H_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AHP, USB2AHAP, USB2AHP, PORTA_MODE_HP_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTA_MODE, USB2AD0, USB2AHAP, USB2AD0, PORTA_MODE_D0_DESC);
+PIN_DECL_(PORTA_MODE, SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AHPD0),
+	  SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AH), SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AHP),
+	  SIG_EXPR_LIST_PTR(PORTA_MODE, USB2AD0));
+
+#define PORTA_U2_XHD_DESC   { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 0, 0 }
+#define PORTA_U2_D1_DESC    { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 1, 0 }
+#define PORTA_U2_XH_DESC    { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 2, 0 }
+#define PORTA_U2_XH2E_DESC   { ASPEED_IP_SCU, SCU410, GENMASK(3, 2), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHD1, USB2A, USB2AXHD1, PORTA_U2_XHD_DESC,
+			SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHPD1, USB2A, USB2AXHPD1, PORTA_U2_XHD_DESC,
+			SIG_DESC_CLEAR(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXH, USB2AAP, USB2AXH, PORTA_U2_XH_DESC,
+			SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHP, USB2AAP, USB2AXHP, PORTA_U2_XH_DESC,
+			SIG_DESC_CLEAR(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXH2B, USB2ABP, USB2AXH2B, PORTA_U2_XH2E_DESC,
+			SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AXHP2B, USB2ABP, USB2AXHP2B, PORTA_U2_XH2E_DESC,
+			SIG_DESC_CLEAR(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U2, USB2AD1, USB2ADAP, USB2AD1, PORTA_U2_D1_DESC);
+PIN_DECL_(PORTA_U2, SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHD1), SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHPD1),
+	  SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXH), SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHP),
+	  SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXH2B), SIG_EXPR_LIST_PTR(PORTA_U2, USB2AXHP2B),
+	  SIG_EXPR_LIST_PTR(PORTA_U2, USB2AD1));
+
+#define PORTB_MODE_HPD0_DESC { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 0, 0 }
+#define PORTB_MODE_D0_DESC   { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 1, 0 }
+#define PORTB_MODE_H_DESC    { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 2, 0 }
+#define PORTB_MODE_HP_DESC    { ASPEED_IP_SCU, SCU410, GENMASK(29, 28), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BHPD0, USB2BH, USB2BHPD0, PORTB_MODE_HPD0_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BH, USB2BHBP, USB2BH, PORTB_MODE_H_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BHP, USB2BHBP, USB2BHP, PORTB_MODE_HP_DESC);
+SIG_EXPR_LIST_DECL_SEMG(PORTB_MODE, USB2BD0, USB2BHBP, USB2BD0, PORTB_MODE_D0_DESC);
+PIN_DECL_(PORTB_MODE, SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BHPD0),
+	  SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BH), SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BHP),
+	  SIG_EXPR_LIST_PTR(PORTB_MODE, USB2BD0));
+
+#define PORTB_U2_XHD_DESC   { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 0, 0 }
+#define PORTB_U2_D1_DESC    { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 1, 0 }
+#define PORTB_U2_XH_DESC    { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 2, 0 }
+#define PORTB_U2_XH2E_DESC   { ASPEED_IP_SCU, SCU410, GENMASK(7, 6), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHD1, USB2B, USB2BXHD1, PORTB_U2_XHD_DESC,
+			SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHPD1, USB2B, USB2BXHPD1, PORTB_U2_XHD_DESC,
+			SIG_DESC_CLEAR(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXH, USB2BBP, USB2BXH, PORTB_U2_XHD_DESC,
+			SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHP, USB2BBP, USB2BXHP, PORTB_U2_XHD_DESC,
+			SIG_DESC_CLEAR(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXH2A, USB2BAP, USB2BXH2A, PORTB_U2_XH2E_DESC,
+			SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BXHP2A, USB2BAP, USB2BXHP2A, PORTB_U2_XH2E_DESC,
+			SIG_DESC_CLEAR(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U2, USB2BD1, USB2BDBP, USB2BD1, PORTB_U2_D1_DESC);
+PIN_DECL_(PORTB_U2, SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHD1), SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHPD1),
+	  SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXH), SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHP),
+	  SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXH2A), SIG_EXPR_LIST_PTR(PORTB_U2, USB2BXHP2A),
+	  SIG_EXPR_LIST_PTR(PORTB_U2, USB2BD1));
+/*
+ * USB2 virtual PHY pins.
+ *
+ * PORTA_U2_PHY and PORTB_U2_PHY are logical endpoints, not package pins.
+ * They alias existing USB2 expressions so pin groups can model direct and
+ * cross-coupled routing for host and mode paths.
+ *
+ * - USB2AAP/USB2ADAP/USB2AHAP: use PORTA_U2_PHY
+ * - USB2ABP                  : use PORTB_U2_PHY
+ * - USB2BBP/USB2BDBP/USB2BHBP: use PORTB_U2_PHY
+ * - USB2BAP                  : use PORTA_U2_PHY
+ *
+ * They do not have any registers to configure this behaviour; the goal is
+ * simply for the driver to prevent conflicting selections. For example,
+ * selecting group USB2ABP and USB2BBP at the same time should not be
+ * allowed.
+ */
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AXH, USB2AAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AXHP, USB2AAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2BXH2A, USB2BAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2BXHP2A, USB2BAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AD1, USB2ADAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AH, USB2AHAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AHP, USB2AHAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U2_PHY, USB2AD0, USB2AHAP);
+PIN_DECL_(PORTA_U2_PHY, SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AXH),
+	  SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AXHP), SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2BXH2A),
+	  SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2BXHP2A), SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AD1),
+	  SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AH), SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AHP),
+	  SIG_EXPR_LIST_PTR(PORTA_U2_PHY, USB2AD0));
+
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2AXH2B, USB2ABP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2AXHP2B, USB2ABP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BXH, USB2BBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BXHP, USB2BBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BD1, USB2BDBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BH, USB2BHBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BHP, USB2BHBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U2_PHY, USB2BD0, USB2BHBP);
+PIN_DECL_(PORTB_U2_PHY, SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2AXH2B),
+	  SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2AXHP2B), SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BXH),
+	  SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BXHP), SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BD1),
+	  SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BH), SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BHP),
+	  SIG_EXPR_LIST_PTR(PORTB_U2_PHY, USB2BD0));
+
+GROUP_DECL(USB2A, PORTA_U2);
+GROUP_DECL(USB2AAP, PORTA_U2, PORTA_U2_PHY);
+GROUP_DECL(USB2ABP, PORTA_U2, PORTB_U2_PHY);
+GROUP_DECL(USB2ADAP, PORTA_U2, PORTA_U2_PHY);
+GROUP_DECL(USB2AH, PORTA_MODE);
+GROUP_DECL(USB2AHAP, PORTA_MODE, PORTA_U2_PHY);
+
+FUNC_DECL_1(USB2AXHD1, USB2A);
+FUNC_DECL_1(USB2AXHPD1, USB2A);
+FUNC_DECL_1(USB2AXH, USB2AAP);
+FUNC_DECL_1(USB2AXHP, USB2AAP);
+FUNC_DECL_1(USB2AXH2B, USB2ABP);
+FUNC_DECL_1(USB2AXHP2B, USB2ABP);
+FUNC_DECL_1(USB2AD1, USB2ADAP);
+FUNC_DECL_1(USB2AHPD0, USB2AH);
+FUNC_DECL_1(USB2AH, USB2AHAP);
+FUNC_DECL_1(USB2AHP, USB2AHAP);
+FUNC_DECL_1(USB2AD0, USB2AHAP);
+
+GROUP_DECL(USB2B, PORTB_U2);
+GROUP_DECL(USB2BBP, PORTB_U2, PORTB_U2_PHY);
+GROUP_DECL(USB2BAP, PORTB_U2, PORTA_U2_PHY);
+GROUP_DECL(USB2BDBP, PORTB_U2, PORTB_U2_PHY);
+GROUP_DECL(USB2BH, PORTB_MODE);
+GROUP_DECL(USB2BHBP, PORTB_MODE, PORTB_U2_PHY);
+
+FUNC_DECL_1(USB2BXHD1, USB2B);
+FUNC_DECL_1(USB2BXHPD1, USB2B);
+FUNC_DECL_1(USB2BXH, USB2BBP);
+FUNC_DECL_1(USB2BXHP, USB2BBP);
+FUNC_DECL_1(USB2BXH2A, USB2BAP);
+FUNC_DECL_1(USB2BXHP2A, USB2BAP);
+FUNC_DECL_1(USB2BD1, USB2BDBP);
+FUNC_DECL_1(USB2BHPD0, USB2BH);
+FUNC_DECL_1(USB2BH, USB2BHBP);
+FUNC_DECL_1(USB2BHP, USB2BHBP);
+FUNC_DECL_1(USB2BD0, USB2BHBP);
+
+#define PORTA_U3_XHD_DESC   { ASPEED_IP_SCU, SCU410, GENMASK(1, 0), 0, 0 }
+#define PORTA_U3_XH_DESC    { ASPEED_IP_SCU, SCU410, GENMASK(1, 0), 2, 0 }
+#define PORTA_U3_XH2E_DESC   { ASPEED_IP_SCU, SCU410, GENMASK(1, 0), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHD, USB3A, USB3AXHD, PORTA_U3_XHD_DESC,
+			SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHPD, USB3A, USB3AXHPD, PORTA_U3_XHD_DESC,
+			SIG_DESC_CLEAR(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXH, USB3AAP, USB3AXH, PORTA_U3_XH_DESC,
+			SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHP, USB3AAP, USB3AXHP, PORTA_U3_XH_DESC,
+			SIG_DESC_CLEAR(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXH2B, USB3ABP, USB3AXH2B, PORTA_U3_XH2E_DESC,
+			SIG_DESC_SET(SCU410, 9));
+SIG_EXPR_LIST_DECL_SEMG(PORTA_U3, USB3AXHP2B, USB3ABP, USB3AXHP2B, PORTA_U3_XH2E_DESC,
+			SIG_DESC_CLEAR(SCU410, 9));
+PIN_DECL_(PORTA_U3, SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHD), SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHPD),
+	  SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXH), SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHP),
+	  SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXH2B), SIG_EXPR_LIST_PTR(PORTA_U3, USB3AXHP2B));
+
+#define PORTB_U3_XHD_DESC   { ASPEED_IP_SCU, SCU410, GENMASK(5, 4), 0, 0 }
+#define PORTB_U3_XH_DESC    { ASPEED_IP_SCU, SCU410, GENMASK(5, 4), 2, 0 }
+#define PORTB_U3_XH2E_DESC   { ASPEED_IP_SCU, SCU410, GENMASK(5, 4), 3, 0 }
+
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHD, USB3B, USB3BXHD, PORTB_U3_XHD_DESC,
+			SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHPD, USB3B, USB3BXHPD, PORTB_U3_XHD_DESC,
+			SIG_DESC_CLEAR(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXH, USB3BBP, USB3BXH, PORTB_U3_XH_DESC,
+			SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHP, USB3BBP, USB3BXHP, PORTB_U3_XH_DESC,
+			SIG_DESC_CLEAR(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXH2A, USB3BAP, USB3BXH2A, PORTB_U3_XH2E_DESC,
+			SIG_DESC_SET(SCU410, 10));
+SIG_EXPR_LIST_DECL_SEMG(PORTB_U3, USB3BXHP2A, USB3BAP, USB3BXHP2A, PORTB_U3_XH2E_DESC,
+			SIG_DESC_CLEAR(SCU410, 10));
+PIN_DECL_(PORTB_U3, SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHD), SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHPD),
+	  SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXH), SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHP),
+	  SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXH2A), SIG_EXPR_LIST_PTR(PORTB_U3, USB3BXHP2A));
+
+/*
+ * USB3 virtual PHY pins.
+ *
+ * PORTA_U3_PHY and PORTB_U3_PHY are logical endpoints, not package pins.
+ * They alias existing USB3 expressions so pin groups can model both direct and
+ * cross-coupled routing to PHY A/B.
+ *
+ * - USB3AAP: PORTA_U3 + PORTA_U3_PHY   (A -> PHY A)
+ * - USB3ABP: PORTA_U3 + PORTB_U3_PHY   (A -> PHY B)
+ * - USB3BBP: PORTB_U3 + PORTB_U3_PHY   (B -> PHY B)
+ * - USB3BAP: PORTB_U3 + PORTA_U3_PHY   (B -> PHY A)
+ *
+ * They do not have any registers to configure this behavior; the goal is
+ * simply for the driver to prevent conflicting selections. For example,
+ * selecting group USB3ABP and USB3BBP at the same time should not be
+ * allowed.
+ */
+SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3AXH, USB3AAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3AXHP, USB3AAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3BXH2A, USB3BAP);
+SIG_EXPR_LIST_ALIAS(PORTA_U3_PHY, USB3BXHP2A, USB3BAP);
+PIN_DECL_(PORTA_U3_PHY, SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3AXH),
+	  SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3AXHP), SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3BXH2A),
+	  SIG_EXPR_LIST_PTR(PORTA_U3_PHY, USB3BXHP2A));
+
+SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3AXH2B, USB3ABP);
+SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3AXHP2B, USB3ABP);
+SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3BXH, USB3BBP);
+SIG_EXPR_LIST_ALIAS(PORTB_U3_PHY, USB3BXHP, USB3BBP);
+PIN_DECL_(PORTB_U3_PHY, SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3AXH2B),
+	  SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3AXHP2B), SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3BXH),
+	  SIG_EXPR_LIST_PTR(PORTB_U3_PHY, USB3BXHP));
+
+/* USB3A xHCI to vHUB */
+GROUP_DECL(USB3A, PORTA_U3);
+/* USB3A xHCI to USB3A PHY */
+GROUP_DECL(USB3AAP, PORTA_U3, PORTA_U3_PHY);
+/* USB3A xHCI to USB3B PHY */
+GROUP_DECL(USB3ABP, PORTA_U3, PORTB_U3_PHY);
+
+FUNC_DECL_1(USB3AXHD, USB3A);
+FUNC_DECL_1(USB3AXHPD, USB3A);
+FUNC_DECL_1(USB3AXH, USB3AAP);
+FUNC_DECL_1(USB3AXHP, USB3AAP);
+FUNC_DECL_1(USB3AXH2B, USB3ABP);
+FUNC_DECL_1(USB3AXHP2B, USB3ABP);
+
+/* USB3B xHCI to vHUB */
+GROUP_DECL(USB3B, PORTB_U3);
+/* USB3B xHCI to USB3A PHY */
+GROUP_DECL(USB3BAP, PORTB_U3, PORTA_U3_PHY);
+/* USB3B xHCI to USB3B PHY */
+GROUP_DECL(USB3BBP, PORTB_U3, PORTB_U3_PHY);
+
+FUNC_DECL_1(USB3BXHD, USB3B);
+FUNC_DECL_1(USB3BXHPD, USB3B);
+FUNC_DECL_1(USB3BXH, USB3BBP);
+FUNC_DECL_1(USB3BXHP, USB3BBP);
+FUNC_DECL_1(USB3BXH2A, USB3BAP);
+FUNC_DECL_1(USB3BXHP2A, USB3BAP);
+
+static const struct pinctrl_pin_desc aspeed_g7_soc0_pins[] = {
+	ASPEED_PINCTRL_PIN(AC14),
+	ASPEED_PINCTRL_PIN(AE15),
+	ASPEED_PINCTRL_PIN(AD14),
+	ASPEED_PINCTRL_PIN(AE14),
+	ASPEED_PINCTRL_PIN(AF14),
+	ASPEED_PINCTRL_PIN(AB13),
+	ASPEED_PINCTRL_PIN(AB14),
+	ASPEED_PINCTRL_PIN(AF15),
+	ASPEED_PINCTRL_PIN(AF13),
+	ASPEED_PINCTRL_PIN(AC13),
+	ASPEED_PINCTRL_PIN(AD13),
+	ASPEED_PINCTRL_PIN(AE13),
+	ASPEED_PINCTRL_PIN(JTAG_PORT),
+	ASPEED_PINCTRL_PIN(PCIERC0_PERST),
+	ASPEED_PINCTRL_PIN(PCIERC1_PERST),
+	ASPEED_PINCTRL_PIN(PORTA_MODE),
+	ASPEED_PINCTRL_PIN(PORTA_U2),
+	ASPEED_PINCTRL_PIN(PORTA_U3),
+	ASPEED_PINCTRL_PIN(PORTA_U2_PHY),
+	ASPEED_PINCTRL_PIN(PORTA_U3_PHY),
+	ASPEED_PINCTRL_PIN(PORTB_MODE),
+	ASPEED_PINCTRL_PIN(PORTB_U2),
+	ASPEED_PINCTRL_PIN(PORTB_U3),
+	ASPEED_PINCTRL_PIN(PORTB_U2_PHY),
+	ASPEED_PINCTRL_PIN(PORTB_U3_PHY),
+};
+
+static const struct aspeed_pin_group aspeed_g7_soc0_groups[] = {
+	ASPEED_PINCTRL_GROUP(EMMCCDN),
+	ASPEED_PINCTRL_GROUP(EMMCG1),
+	ASPEED_PINCTRL_GROUP(EMMCG4),
+	ASPEED_PINCTRL_GROUP(EMMCG8),
+	ASPEED_PINCTRL_GROUP(EMMCWPN),
+	ASPEED_PINCTRL_GROUP(TSPRSTN),
+	ASPEED_PINCTRL_GROUP(UFSCLKI),
+	ASPEED_PINCTRL_GROUP(VB0),
+	ASPEED_PINCTRL_GROUP(VB1),
+	ASPEED_PINCTRL_GROUP(VGADDC),
+	/* JTAG groups */
+	ASPEED_PINCTRL_GROUP(JTAG0),
+	/* PCIE RC groups */
+	ASPEED_PINCTRL_GROUP(PCIERC0PERST),
+	ASPEED_PINCTRL_GROUP(PCIERC1PERST),
+	/* USB3A groups */
+	ASPEED_PINCTRL_GROUP(USB3A),
+	ASPEED_PINCTRL_GROUP(USB3AAP),
+	ASPEED_PINCTRL_GROUP(USB3ABP),
+	/* USB3B groups */
+	ASPEED_PINCTRL_GROUP(USB3B),
+	ASPEED_PINCTRL_GROUP(USB3BAP),
+	ASPEED_PINCTRL_GROUP(USB3BBP),
+	/* USB2A groups */
+	ASPEED_PINCTRL_GROUP(USB2A),
+	ASPEED_PINCTRL_GROUP(USB2AAP),
+	ASPEED_PINCTRL_GROUP(USB2ABP),
+	ASPEED_PINCTRL_GROUP(USB2ADAP),
+	ASPEED_PINCTRL_GROUP(USB2AH),
+	ASPEED_PINCTRL_GROUP(USB2AHAP),
+	/* USB2B groups */
+	ASPEED_PINCTRL_GROUP(USB2B),
+	ASPEED_PINCTRL_GROUP(USB2BAP),
+	ASPEED_PINCTRL_GROUP(USB2BBP),
+	ASPEED_PINCTRL_GROUP(USB2BDBP),
+	ASPEED_PINCTRL_GROUP(USB2BH),
+	ASPEED_PINCTRL_GROUP(USB2BHBP),
+};
+
+static const struct aspeed_pin_function aspeed_g7_soc0_functions[] = {
+	ASPEED_PINCTRL_FUNC(EMMC),
+	ASPEED_PINCTRL_FUNC(TSPRSTN),
+	ASPEED_PINCTRL_FUNC(UFSCLKI),
+	ASPEED_PINCTRL_FUNC(VB),
+	ASPEED_PINCTRL_FUNC(VGADDC),
+	/* JTAG functions */
+	ASPEED_PINCTRL_FUNC(JTAGDDR),
+	ASPEED_PINCTRL_FUNC(JTAGM0),
+	ASPEED_PINCTRL_FUNC(JTAGPCIEA),
+	ASPEED_PINCTRL_FUNC(JTAGPCIEB),
+	ASPEED_PINCTRL_FUNC(JTAGPSP),
+	ASPEED_PINCTRL_FUNC(JTAGSSP),
+	ASPEED_PINCTRL_FUNC(JTAGTSP),
+	ASPEED_PINCTRL_FUNC(JTAGUSB3A),
+	ASPEED_PINCTRL_FUNC(JTAGUSB3B),
+	/* PCIE RC functions */
+	ASPEED_PINCTRL_FUNC(PCIERC0PERST),
+	ASPEED_PINCTRL_FUNC(PCIERC1PERST),
+	/* USB3A functions */
+	ASPEED_PINCTRL_FUNC(USB3AXH),
+	ASPEED_PINCTRL_FUNC(USB3AXH2B),
+	ASPEED_PINCTRL_FUNC(USB3AXHD),
+	ASPEED_PINCTRL_FUNC(USB3AXHP),
+	ASPEED_PINCTRL_FUNC(USB3AXHP2B),
+	ASPEED_PINCTRL_FUNC(USB3AXHPD),
+	/* USB3B functions */
+	ASPEED_PINCTRL_FUNC(USB3BXH),
+	ASPEED_PINCTRL_FUNC(USB3BXH2A),
+	ASPEED_PINCTRL_FUNC(USB3BXHD),
+	ASPEED_PINCTRL_FUNC(USB3BXHP),
+	ASPEED_PINCTRL_FUNC(USB3BXHP2A),
+	ASPEED_PINCTRL_FUNC(USB3BXHPD),
+	/* USB2A functions */
+	ASPEED_PINCTRL_FUNC(USB2AD0),
+	ASPEED_PINCTRL_FUNC(USB2AD1),
+	ASPEED_PINCTRL_FUNC(USB2AH),
+	ASPEED_PINCTRL_FUNC(USB2AHP),
+	ASPEED_PINCTRL_FUNC(USB2AHPD0),
+	ASPEED_PINCTRL_FUNC(USB2AXH),
+	ASPEED_PINCTRL_FUNC(USB2AXH2B),
+	ASPEED_PINCTRL_FUNC(USB2AXHD1),
+	ASPEED_PINCTRL_FUNC(USB2AXHP),
+	ASPEED_PINCTRL_FUNC(USB2AXHP2B),
+	ASPEED_PINCTRL_FUNC(USB2AXHPD1),
+	/* USB2B functions */
+	ASPEED_PINCTRL_FUNC(USB2BD0),
+	ASPEED_PINCTRL_FUNC(USB2BD1),
+	ASPEED_PINCTRL_FUNC(USB2BH),
+	ASPEED_PINCTRL_FUNC(USB2BHP),
+	ASPEED_PINCTRL_FUNC(USB2BHPD0),
+	ASPEED_PINCTRL_FUNC(USB2BXH),
+	ASPEED_PINCTRL_FUNC(USB2BXH2A),
+	ASPEED_PINCTRL_FUNC(USB2BXHD1),
+	ASPEED_PINCTRL_FUNC(USB2BXHP),
+	ASPEED_PINCTRL_FUNC(USB2BXHP2A),
+	ASPEED_PINCTRL_FUNC(USB2BXHPD1),
+};
+
+static const struct pinmux_ops aspeed_g7_soc0_pinmux_ops = {
+	.get_functions_count = aspeed_pinmux_get_fn_count,
+	.get_function_name = aspeed_pinmux_get_fn_name,
+	.get_function_groups = aspeed_pinmux_get_fn_groups,
+	.set_mux = aspeed_pinmux_set_mux,
+	.gpio_request_enable = aspeed_gpio_request_enable,
+	.strict = true,
+};
+
+static const struct pinctrl_ops aspeed_g7_soc0_pinctrl_ops = {
+	.get_groups_count = aspeed_pinctrl_get_groups_count,
+	.get_group_name = aspeed_pinctrl_get_group_name,
+	.get_group_pins = aspeed_pinctrl_get_group_pins,
+	.pin_dbg_show = aspeed_pinctrl_pin_dbg_show,
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+	.dt_free_map = pinctrl_utils_free_map,
+};
+
+static const struct pinconf_ops aspeed_g7_soc0_pinconf_ops = {
+	.is_generic = true,
+	.pin_config_get = aspeed_pin_config_get,
+	.pin_config_set = aspeed_pin_config_set,
+	.pin_config_group_get = aspeed_pin_config_group_get,
+	.pin_config_group_set = aspeed_pin_config_group_set,
+};
+
+/* pinctrl_desc */
+static const struct pinctrl_desc aspeed_g7_soc0_pinctrl_desc = {
+	.name = "aspeed-g7-soc0-pinctrl",
+	.pins = aspeed_g7_soc0_pins,
+	.npins = ARRAY_SIZE(aspeed_g7_soc0_pins),
+	.pctlops = &aspeed_g7_soc0_pinctrl_ops,
+	.pmxops = &aspeed_g7_soc0_pinmux_ops,
+	.confops = &aspeed_g7_soc0_pinconf_ops,
+};
+
+static const struct aspeed_pin_config aspeed_g7_soc0_configs[] = {
+	/* GPIO18A */
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AC14, AC14 }, SCU480, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AE15, AE15 }, SCU484, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AD14, AD14 }, SCU488, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AE14, AE14 }, SCU48C, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AF14, AF14 }, SCU490, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AB13, AB13 }, SCU494, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AB14, AB14 }, SCU498, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AF15, AF15 }, SCU49C, GENMASK(3, 0) },
+	/* GPIO18B */
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AF13, AF13 }, SCU4A0, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AC13, AC13 }, SCU4A4, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AD13, AD13 }, SCU4A8, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, { AE13, AE13 }, SCU4AC, GENMASK(3, 0) },
+};
+
+static const struct aspeed_pin_config_map aspeed_g7_soc0_pin_config_map[] = {
+	{ PIN_CONFIG_DRIVE_STRENGTH, 3, 0, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 6, 1, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 8, 2, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 11, 3, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 16, 4, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 18, 5, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 20, 6, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 23, 7, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 30, 8, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 32, 9, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 33, 10, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 35, 11, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 37, 12, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 38, 13, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 39, 14, GENMASK(3, 0) },
+	{ PIN_CONFIG_DRIVE_STRENGTH, 41, 15, GENMASK(3, 0) },
+
+};
+
+static int aspeed_g7_soc0_sig_expr_set(struct aspeed_pinmux_data *ctx,
+				       const struct aspeed_sig_expr *expr, bool enable)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i < expr->ndescs; i++) {
+		const struct aspeed_sig_desc *desc = &expr->descs[i];
+		u32 pattern = enable ? desc->enable : desc->disable;
+		u32 val = (pattern << __ffs(desc->mask));
+
+		if (!ctx->maps[desc->ip])
+			return -ENODEV;
+
+		WARN_ON_ONCE(desc->ip != ASPEED_IP_SCU);
+
+		ret = regmap_update_bits(ctx->maps[desc->ip], desc->reg,
+					 desc->mask, val);
+		if (ret)
+			return ret;
+	}
+
+	ret = aspeed_sig_expr_eval(ctx, expr, enable);
+	if (ret < 0)
+		return ret;
+
+	return ret ? 0 : -EPERM;
+}
+
+static const struct aspeed_pinmux_ops aspeed_g7_soc0_ops = {
+	.set = aspeed_g7_soc0_sig_expr_set,
+};
+
+static struct aspeed_pinctrl_data aspeed_g7_soc0_pinctrl_data = {
+	.pins = aspeed_g7_soc0_pins,
+	.npins = ARRAY_SIZE(aspeed_g7_soc0_pins),
+	.pinmux = {
+		.ops = &aspeed_g7_soc0_ops,
+		.groups = aspeed_g7_soc0_groups,
+		.ngroups = ARRAY_SIZE(aspeed_g7_soc0_groups),
+		.functions = aspeed_g7_soc0_functions,
+		.nfunctions = ARRAY_SIZE(aspeed_g7_soc0_functions),
+	},
+	.configs = aspeed_g7_soc0_configs,
+	.nconfigs = ARRAY_SIZE(aspeed_g7_soc0_configs),
+	.confmaps = aspeed_g7_soc0_pin_config_map,
+	.nconfmaps = ARRAY_SIZE(aspeed_g7_soc0_pin_config_map),
+};
+
+static int aspeed_g7_soc0_pinctrl_probe(struct platform_device *pdev)
+{
+	return aspeed_pinctrl_probe(pdev, &aspeed_g7_soc0_pinctrl_desc,
+				    &aspeed_g7_soc0_pinctrl_data);
+}
+
+static const struct of_device_id aspeed_g7_soc0_pinctrl_match[] = {
+	{ .compatible = "aspeed,ast2700-soc0-pinctrl" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, aspeed_g7_soc0_pinctrl_match);
+
+static struct platform_driver aspeed_g7_soc0_pinctrl_driver = {
+	.probe = aspeed_g7_soc0_pinctrl_probe,
+	.driver = {
+		.name = "aspeed-g7-soc0-pinctrl",
+		.of_match_table = aspeed_g7_soc0_pinctrl_match,
+		.suppress_bind_attrs = true,
+	},
+};
+
+static int __init aspeed_g7_soc0_pinctrl_init(void)
+{
+	return platform_driver_register(&aspeed_g7_soc0_pinctrl_driver);
+}
+arch_initcall(aspeed_g7_soc0_pinctrl_init);

-- 
2.34.1



^ permalink raw reply related

* [PATCH v5 2/3] dt-bindings: mfd: aspeed,ast2x00-scu: Describe AST2700 SCU0
From: Billy Tsai @ 2026-03-31  7:31 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Joel Stanley, Andrew Jeffery, Linus Walleij, Billy Tsai,
	Bartosz Golaszewski, Ryan Chen
  Cc: Andrew Jeffery, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, openbmc, linux-gpio, linux-clk
In-Reply-To: <20260331-upstream_pinctrl-v5-0-8994f59ff367@aspeedtech.com>

AST2700 consists of two interconnected SoC instances, each with its own
System Control Unit (SCU). The SCU0 provides pin control, interrupt
controllers, clocks, resets, and address-space mappings for the
Secondary and Tertiary Service Processors (SSP and TSP).

Describe the SSP/TSP address mappings using the standard
memory-region and memory-region-names properties.

Disallow legacy child nodes that are not present on AST2700, including
p2a-control and smp-memram. The latter is unnecessary as software can
access the scratch registers via the SCU syscon.

Also allow the AST2700 SoC0 pin controller to be described as a child
node of the SCU0, and add an example illustrating the SCU0 layout,
including reserved-memory, interrupt controllers, and pinctrl.

Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
 .../bindings/mfd/aspeed,ast2x00-scu.yaml           | 117 +++++++++++++++++++++
 1 file changed, 117 insertions(+)

diff --git a/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml b/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml
index a87f31fce019..86d51389689c 100644
--- a/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml
+++ b/Documentation/devicetree/bindings/mfd/aspeed,ast2x00-scu.yaml
@@ -46,6 +46,9 @@ properties:
   '#reset-cells':
     const: 1
 
+  memory-region: true
+  memory-region-names: true
+
 patternProperties:
   '^p2a-control@[0-9a-f]+$':
     description: >
@@ -87,6 +90,7 @@ patternProperties:
             - aspeed,ast2400-pinctrl
             - aspeed,ast2500-pinctrl
             - aspeed,ast2600-pinctrl
+            - aspeed,ast2700-soc0-pinctrl
 
     required:
       - compatible
@@ -156,6 +160,42 @@ required:
   - '#clock-cells'
   - '#reset-cells'
 
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            anyOf:
+              - const: aspeed,ast2700-scu0
+              - const: aspeed,ast2700-scu1
+    then:
+      patternProperties:
+        '^p2a-control@[0-9a-f]+$': false
+        '^smp-memram@[0-9a-f]+$': false
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: aspeed,ast2700-scu0
+    then:
+      properties:
+        memory-region:
+          items:
+            - description: Region mapped through the first SSP address window.
+            - description: Region mapped through the second SSP address window.
+            - description: Region mapped through the TSP address window.
+
+        memory-region-names:
+          items:
+            - const: ssp-0
+            - const: ssp-1
+            - const: tsp
+    else:
+      properties:
+        memory-region: false
+        memory-region-names: false
+
 additionalProperties: false
 
 examples:
@@ -180,4 +220,81 @@ examples:
             reg = <0x7c 0x4>, <0x150 0x8>;
         };
     };
+
+  - |
+    / {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        reserved-memory {
+            #address-cells = <2>;
+            #size-cells = <2>;
+            ranges;
+
+            ssp_region_0: memory@400000000 {
+                reg = <0x4 0x00000000 0x0 0x01000000>;
+                no-map;
+            };
+
+            ssp_region_1: memory@401000000 {
+                reg = <0x4 0x01000000 0x0 0x01000000>;
+                no-map;
+            };
+
+            tsp_region: memory@402000000 {
+                reg = <0x4 0x02000000 0x0 0x01000000>;
+                no-map;
+            };
+        };
+
+        bus {
+            #address-cells = <2>;
+            #size-cells = <2>;
+
+            syscon@12c02000 {
+                compatible = "aspeed,ast2700-scu0", "syscon", "simple-mfd";
+                reg = <0 0x12c02000 0 0x1000>;
+                ranges = <0x0 0x0 0x12c02000 0x1000>;
+                #address-cells = <1>;
+                #size-cells = <1>;
+                #clock-cells = <1>;
+                #reset-cells = <1>;
+
+                memory-region = <&ssp_region_0>, <&ssp_region_1>,
+                                <&tsp_region>;
+                memory-region-names = "ssp-0", "ssp-1", "tsp";
+
+                silicon-id@0 {
+                    compatible = "aspeed,ast2700-silicon-id", "aspeed,silicon-id";
+                    reg = <0x0 0x4>;
+                };
+
+                interrupt-controller@1b0 {
+                    compatible = "aspeed,ast2700-scu-ic0";
+                    reg = <0x1b0 0x4>;
+                    #interrupt-cells = <1>;
+                    interrupts-extended = <&intc0 97>;
+                    interrupt-controller;
+                };
+
+                interrupt-controller@1e0 {
+                    compatible = "aspeed,ast2700-scu-ic1";
+                    reg = <0x1e0 0x4>;
+                    #interrupt-cells = <1>;
+                    interrupts-extended = <&intc0 98>;
+                    interrupt-controller;
+                };
+
+                pinctrl@400 {
+                    compatible = "aspeed,ast2700-soc0-pinctrl";
+                    reg = <0x400 0x318>;
+                    emmc-state {
+                        function = "EMMC";
+                        groups = "EMMCG1";
+                    };
+                };
+            };
+        };
+    };
+
 ...

-- 
2.34.1



^ permalink raw reply related

* [PATCH v5 1/3] dt-bindings: pinctrl: Add aspeed,ast2700-soc0-pinctrl
From: Billy Tsai @ 2026-03-31  7:31 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Joel Stanley, Andrew Jeffery, Linus Walleij, Billy Tsai,
	Bartosz Golaszewski, Ryan Chen
  Cc: Andrew Jeffery, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, openbmc, linux-gpio, linux-clk
In-Reply-To: <20260331-upstream_pinctrl-v5-0-8994f59ff367@aspeedtech.com>

Add a device tree binding for the pin controller found in the
ASPEED AST2700 SoC0.

The controller manages various peripheral functions such as eMMC, USB,
VGA DDC, JTAG, and PCIe root complex signals.

Describe the AST2700 SoC0 pin controller using standard pin multiplexing
and configuration properties.

Signed-off-by: Billy Tsai <billy_tsai@aspeedtech.com>
---
 .../pinctrl/aspeed,ast2700-soc0-pinctrl.yaml       | 161 +++++++++++++++++++++
 1 file changed, 161 insertions(+)

diff --git a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml
new file mode 100644
index 000000000000..e43a658fe02b
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml
@@ -0,0 +1,161 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/aspeed,ast2700-soc0-pinctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASPEED AST2700 SoC0 Pin Controller
+
+maintainers:
+  - Billy Tsai <billy_tsai@aspeedtech.com>
+
+description:
+  The AST2700 features a dual-SoC architecture with two interconnected SoCs,
+  each having its own System Control Unit (SCU) for independent pin control.
+  This pin controller manages the pin multiplexing for SoC0.
+
+  The SoC0 pin controller manages pin functions including eMMC, VGA DDC,
+  dual USB3/USB2 ports (A and B), JTAG, and PCIe root complex interfaces.
+
+properties:
+  compatible:
+    const: aspeed,ast2700-soc0-pinctrl
+  reg:
+    maxItems: 1
+
+patternProperties:
+  '-state$':
+    type: object
+    allOf:
+      - $ref: pinmux-node.yaml#
+      - $ref: pincfg-node.yaml#
+    additionalProperties: false
+
+    properties:
+      function:
+        enum:
+          - EMMC
+          - JTAGDDR
+          - JTAGM0
+          - JTAGPCIEA
+          - JTAGPCIEB
+          - JTAGPSP
+          - JTAGSSP
+          - JTAGTSP
+          - JTAGUSB3A
+          - JTAGUSB3B
+          - PCIERC0PERST
+          - PCIERC1PERST
+          - TSPRSTN
+          - UFSCLKI
+          - USB2AD0
+          - USB2AD1
+          - USB2AH
+          - USB2AHP
+          - USB2AHPD0
+          - USB2AXH
+          - USB2AXH2B
+          - USB2AXHD1
+          - USB2AXHP
+          - USB2AXHP2B
+          - USB2AXHPD1
+          - USB2BD0
+          - USB2BD1
+          - USB2BH
+          - USB2BHP
+          - USB2BHPD0
+          - USB2BXH
+          - USB2BXH2A
+          - USB2BXHD1
+          - USB2BXHP
+          - USB2BXHP2A
+          - USB2BXHPD1
+          - USB3AXH
+          - USB3AXH2B
+          - USB3AXHD
+          - USB3AXHP
+          - USB3AXHP2B
+          - USB3AXHPD
+          - USB3BXH
+          - USB3BXH2A
+          - USB3BXHD
+          - USB3BXHP
+          - USB3BXHP2A
+          - USB3BXHPD
+          - VB
+          - VGADDC
+
+      groups:
+        enum:
+          - EMMCCDN
+          - EMMCG1
+          - EMMCG4
+          - EMMCG8
+          - EMMCWPN
+          - JTAG0
+          - PCIERC0PERST
+          - PCIERC1PERST
+          - TSPRSTN
+          - UFSCLKI
+          - USB2A
+          - USB2AAP
+          - USB2ABP
+          - USB2ADAP
+          - USB2AH
+          - USB2AHAP
+          - USB2B
+          - USB2BAP
+          - USB2BBP
+          - USB2BDBP
+          - USB2BH
+          - USB2BHBP
+          - USB3A
+          - USB3AAP
+          - USB3ABP
+          - USB3B
+          - USB3BAP
+          - USB3BBP
+          - VB0
+          - VB1
+          - VGADDC
+      pins:
+        enum:
+          - AB13
+          - AB14
+          - AC13
+          - AC14
+          - AD13
+          - AD14
+          - AE13
+          - AE14
+          - AE15
+          - AF13
+          - AF14
+          - AF15
+
+      drive-strength:
+        enum: [3, 6, 8, 11, 16, 18, 20, 23, 30, 32, 33, 35, 37, 38, 39, 41]
+
+      bias-disable: true
+      bias-pull-up: true
+      bias-pull-down: true
+
+required:
+  - compatible
+  - reg
+
+allOf:
+  - $ref: pinctrl.yaml#
+
+additionalProperties: false
+
+examples:
+  - |
+    pinctrl@400 {
+        compatible = "aspeed,ast2700-soc0-pinctrl";
+        reg = <0x400 0x318>;
+        emmc-state {
+            function = "EMMC";
+            groups = "EMMCG1";
+        };
+    };

-- 
2.34.1



^ permalink raw reply related

* [PATCH v5 0/3] pinctrl: aspeed: Add AST2700 SoC0 support
From: Billy Tsai @ 2026-03-31  7:31 UTC (permalink / raw)
  To: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Joel Stanley, Andrew Jeffery, Linus Walleij, Billy Tsai,
	Bartosz Golaszewski, Ryan Chen
  Cc: Andrew Jeffery, devicetree, linux-arm-kernel, linux-aspeed,
	linux-kernel, openbmc, linux-gpio, linux-clk

AST2700 is composed of two interconnected SoC instances, each providing
its own pin control hardware. This series introduces bindings describing
the AST2700 pinctrl architecture and adds pinctrl driver support for the
SoC0 instance.

The bindings document the AST2700 dual-SoC design and follow common
pinctrl conventions, while the SoC0 driver implementation builds upon
the existing ASPEED pinctrl infrastructure.

---
Changes in v5:
- Complete the AST2700 SCU0 binding and disallow child nodes that are
  not relevant for the hardware (p2a-control and smp-memram).
- Add examples for both the AST2700 SCU0 binding and the pinctrl binding,
  ensuring they are valid against the schema.
- Rework the pinctrl binding example to be self-contained and independent
  of the SCU binding.
- Reorder the binding patches so the pinctrl binding is introduced before
  the SCU binding update, allowing the SCU example to be added cleanly.
- Adjust the binding accordingly to restrict drive-strength to the
  supported values.
- Update the drive-strength table to match hardware-defined values.
- Link to v4: https://lore.kernel.org/r/20260306-upstream_pinctrl-v4-0-ad4e8ab8b489@aspeedtech.com

Changes in v4:
- Rename series title to "pinctrl: aspeed: Add AST2700 SoC0 support"
  to make it specific to SoC0.
- Remove unnecessary SCU example from bindings.
- Fix Makefile newline to avoid patch warning.
- Make pinctrl data structures const and align with existing Aspeed drivers.
- Sort the arrays and enums alphabetically.
- Minor cleanups for consistency, no functional changes.
- Link to v3: https://lore.kernel.org/r/20260120-upstream_pinctrl-v3-0-868fbf8413b5@aspeedtech.com

Changes in v3:
dt-bindings: pinctrl: aspeed: AST2700 pinctrl improvements
- Improved binding descriptions for SoC0 and SoC1 to better explain the
  AST2700 dual-SoC architecture with independent pin control blocks
- Switched from additionalProperties to patternProperties using the
  '-state$' suffix to restrict child node naming
- Removed per-binding examples based on review feedback
- Added additionalProperties: false at the top level for stricter schema
  validation
- Dropped the aspeed,ast2700-soc1-pinctrl binding, as the SoC1 pinctrl
  registers follow a regular layout and can be described using an
  existing generic pinctrl binding
- Updated the function and group enum lists to match the definitions
  used by the AST2700 pinctrl driver

dt-bindings: mfd: aspeed: Add AST2700 SCU example with pinctrl
- Added a complete AST2700 SCU0 example demonstrating pinctrl integration
- Example covers both pin function/group configuration and pin
  drive-strength settings
- Updated child node naming to use the '-state' suffix, following common
  pinctrl conventions

pinctrl: aspeed: AST2700 SoC0 driver improvements
- Refactored pin and signal declarations to use common ASPEED pinmux
  macros (SIG_EXPR_LIST_DECL_SEMG, SIG_EXPR_LIST_DECL_SESG, PIN_DECL_*)
- Added SCU010 register definition for hardware strap control
- Reworked code structure to better align with existing ASPEED pinctrl
  drivers

- Link to v2: https://lore.kernel.org/r/20250904103401.88287-1-billy_tsai@aspeedtech.com

Changes in v2:
- Update pinctrl aspeed binding files.
- Update the commit message for pinctrl binding patch.
- Link to v1: https://lore.kernel.org/r/20250829073030.2749482-1-billy_tsai@aspeedtech.com

---
Billy Tsai (3):
      dt-bindings: pinctrl: Add aspeed,ast2700-soc0-pinctrl
      dt-bindings: mfd: aspeed,ast2x00-scu: Describe AST2700 SCU0
      pinctrl: aspeed: Add AST2700 SoC0 support

 .../bindings/mfd/aspeed,ast2x00-scu.yaml           | 117 ++++
 .../pinctrl/aspeed,ast2700-soc0-pinctrl.yaml       | 161 +++++
 drivers/pinctrl/aspeed/Kconfig                     |   9 +
 drivers/pinctrl/aspeed/Makefile                    |   1 +
 drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc0.c    | 710 +++++++++++++++++++++
 5 files changed, 998 insertions(+)
---
base-commit: af4e9ef3d78420feb8fe58cd9a1ab80c501b3c08
change-id: 20251215-upstream_pinctrl-8f195df0a975

Best regards,
-- 
Billy Tsai <billy_tsai@aspeedtech.com>



^ permalink raw reply

* RE: [PATCH v28 2/4] dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs and enable-dma properties
From: Ryan Chen @ 2026-03-31  7:30 UTC (permalink / raw)
  To: Jeremy Kerr, Krzysztof Kozlowski
  Cc: andriy.shevchenko@linux.intel.com, Andi Shyti, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
	Benjamin Herrenschmidt, Philipp Zabel, linux-i2c@vger.kernel.org,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	openbmc@lists.ozlabs.org
In-Reply-To: <72517fcf99a7453841ee75243dc54e735c10238f.camel@codeconstruct.com.au>

> Subject: Re: [PATCH v28 2/4] dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs
> and enable-dma properties
> 
> Hi Ryan,
> 
> > > Sounds reasonable, but before you do so, how are you planning to
> > > manage the allocation of DMA channels across multiple i2c peripherals?
> > >
> > The AST2600 I2C hardware has only one can use DMA at a time.
> > To avoid the complexity of managing DMA channel contention, I plan to
> > use buffer mode by default for all controllers, which still provides
> > better performance than byte mode without requiring DMA channel
> allocation.
> 
> OK, but your wording there ("by default") implies that DMA is still selectable
> for one controller peripheral. In which case: you still have the problem of
> managing DMA channel contention, but now it's at runtime instead.
> 
> So my question still stands: how are you planning to enforce that DMA is only
> enabled for one controller?
> 
> Or are you planning to disable I2C DMA entirely on AST2600?
Yes, This is my intent to do.
Disable I2C DMA entirely on AST2600.
If I remove DMA, should can I keep byte and buffer for sysfs? 

^ permalink raw reply

* Re: [PATCH v2 14/30] KVM: arm64: Kill fault->ipa
From: Anshuman Khandual @ 2026-03-31  7:28 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Fuad Tabba, Will Deacon, Quentin Perret
In-Reply-To: <20260327113618.4051534-15-maz@kernel.org>



On 27/03/26 5:06 PM, Marc Zyngier wrote:
> fault->ipa, in a nested contest, represents the output of the guest's
> S2 translation for the fault->fault_ipa input, and is equal to
> fault->fault_ipa otherwise,
> 
> Given that this is readily available from kvm_s2_trans_output(),
> drop fault->ipa and directly compute fault->gfn instead, which
> is really what we want.
> 
> Tested-by: Fuad Tabba <tabba@google.com>
> Reviewed-by: Fuad Tabba <tabba@google.com>
> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Signed-off-by: Marc Zyngier <maz@kernel.org>

Reviewed-by: Anshuman Khandual <anshuman.khandual@arm.com>

> ---
>  arch/arm64/kvm/mmu.c | 14 +++++---------
>  1 file changed, 5 insertions(+), 9 deletions(-)
> 
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index c6cd6ce5254be..67e5e867e01dc 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1643,7 +1643,7 @@ static short kvm_s2_resolve_vma_size(struct vm_area_struct *vma,
>  				     unsigned long hva,
>  				     struct kvm_memory_slot *memslot,
>  				     struct kvm_s2_trans *nested,
> -				     bool *force_pte, phys_addr_t *ipa)
> +				     bool *force_pte)
>  {
>  	short vma_shift;
>  
> @@ -1681,8 +1681,6 @@ static short kvm_s2_resolve_vma_size(struct vm_area_struct *vma,
>  
>  		max_map_size = *force_pte ? PAGE_SIZE : PUD_SIZE;
>  
> -		*ipa = kvm_s2_trans_output(nested);
> -
>  		/*
>  		 * If we're about to create a shadow stage 2 entry, then we
>  		 * can only create a block mapping if the guest stage 2 page
> @@ -1722,7 +1720,6 @@ struct kvm_s2_fault {
>  	bool is_vma_cacheable;
>  	bool s2_force_noncacheable;
>  	unsigned long mmu_seq;
> -	phys_addr_t ipa;
>  	gfn_t gfn;
>  	kvm_pfn_t pfn;
>  	bool logging_active;
> @@ -1738,6 +1735,7 @@ static int kvm_s2_fault_get_vma_info(struct kvm_s2_fault *fault)
>  {
>  	struct vm_area_struct *vma;
>  	struct kvm *kvm = fault->vcpu->kvm;
> +	phys_addr_t ipa;
>  
>  	mmap_read_lock(current->mm);
>  	vma = vma_lookup(current->mm, fault->hva);
> @@ -1748,8 +1746,7 @@ static int kvm_s2_fault_get_vma_info(struct kvm_s2_fault *fault)
>  	}
>  
>  	fault->vma_pagesize = 1UL << kvm_s2_resolve_vma_size(vma, fault->hva, fault->memslot,
> -							     fault->nested, &fault->force_pte,
> -							     &fault->ipa);
> +							     fault->nested, &fault->force_pte);
>  
>  	/*
>  	 * Both the canonical IPA and fault IPA must be aligned to the
> @@ -1757,9 +1754,9 @@ static int kvm_s2_fault_get_vma_info(struct kvm_s2_fault *fault)
>  	 * mapping in the right place.
>  	 */
>  	fault->fault_ipa = ALIGN_DOWN(fault->fault_ipa, fault->vma_pagesize);
> -	fault->ipa = ALIGN_DOWN(fault->ipa, fault->vma_pagesize);
> +	ipa = fault->nested ? kvm_s2_trans_output(fault->nested) : fault->fault_ipa;
> +	fault->gfn = ALIGN_DOWN(ipa, fault->vma_pagesize) >> PAGE_SHIFT;
>  
> -	fault->gfn = fault->ipa >> PAGE_SHIFT;
>  	fault->mte_allowed = kvm_vma_mte_allowed(vma);
>  
>  	fault->vm_flags = vma->vm_flags;
> @@ -1970,7 +1967,6 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
>  		.memslot = memslot,
>  		.hva = hva,
>  		.fault_is_perm = fault_is_perm,
> -		.ipa = fault_ipa,
>  		.logging_active = logging_active,
>  		.force_pte = logging_active,
>  		.prot = KVM_PGTABLE_PROT_R,



^ permalink raw reply

* [PATCH] soc: apple: cleanup dead code in Kconfig
From: Julian Braha @ 2026-03-31  7:28 UTC (permalink / raw)
  To: sven, j, neal; +Cc: asahi, linux-arm-kernel, linux-kernel, Julian Braha

There is already an 'if ARCH_APPLE || COMPILE_TEST' condition wrapping
these config options, making the 'depends on' statement for each a
duplicate dependency (dead code).

I propose leaving the outer 'if ARCH_APPLE || COMPILE_TEST...endif' and
removing the individual 'depends on' statement from each option.

This dead code was found by kconfirm, a static analysis tool for Kconfig.

Signed-off-by: Julian Braha <julianbraha@gmail.com>
---
 drivers/soc/apple/Kconfig | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig
index d0ff32182a2b..e04a60722e0b 100644
--- a/drivers/soc/apple/Kconfig
+++ b/drivers/soc/apple/Kconfig
@@ -7,7 +7,7 @@ menu "Apple SoC drivers"
 config APPLE_MAILBOX
 	tristate "Apple SoC mailboxes"
 	depends on PM
-	depends on ARCH_APPLE || (64BIT && COMPILE_TEST)
+	depends on 64BIT
 	help
 	  Apple SoCs have various co-processors required for certain
 	  peripherals to work (NVMe, display controller, etc.). This
@@ -19,7 +19,6 @@ config APPLE_MAILBOX
 config APPLE_RTKIT
 	tristate "Apple RTKit co-processor IPC protocol"
 	depends on APPLE_MAILBOX
-	depends on ARCH_APPLE || COMPILE_TEST
 	help
 	  Apple SoCs such as the M1 come with various co-processors running
 	  their proprietary RTKit operating system. This option enables support
@@ -30,7 +29,6 @@ config APPLE_RTKIT
 
 config APPLE_SART
 	tristate "Apple SART DMA address filter"
-	depends on ARCH_APPLE || COMPILE_TEST
 	help
 	  Apple SART is a simple DMA address filter used on Apple SoCs such
 	  as the M1. It is usually required for the NVMe coprocessor which does
@@ -40,8 +38,7 @@ config APPLE_SART
 
 config APPLE_TUNABLE
 	tristate
-	depends on ARCH_APPLE || COMPILE_TEST
 
 endmenu
 
-endif
+endif # ARCH_APPLE || COMPILE_TEST
-- 
2.51.2



^ permalink raw reply related

* [RFC PATCH 7/7] media: amphion: Add V4L2 memory tracking support
From: ming.qian @ 2026-03-31  7:23 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, hverkuil-cisco, nicolas, sebastian.fricke, shawnguo,
	s.hauer, kernel, festevam, linux-imx, Frank.li, xiahong.bao,
	eagle.zhou, imx, linux-kernel, linux-arm-kernel
In-Reply-To: <20260331072347.253-1-ming.qian@oss.nxp.com>

From: Ming Qian <ming.qian@oss.nxp.com>

Integrate V4L2 memtrack framework to track DMA buffer allocations in the
Amphion VPU driver. Memory usage is organized hierarchically (device ->
instance -> queue) and exposed via V4L2_CID_MEMORY_USAGE control and
debugfs.

Tracked buffers include firmware boot region, RPC, stream ring buffers,
and codec-specific frame buffers (MBI, DCP, enc/ref frames).

Signed-off-by: Ming Qian <ming.qian@oss.nxp.com>
---
 drivers/media/platform/amphion/Kconfig    |  1 +
 drivers/media/platform/amphion/vdec.c     |  9 ++++++
 drivers/media/platform/amphion/venc.c     |  9 ++++++
 drivers/media/platform/amphion/vpu.h      |  7 +++++
 drivers/media/platform/amphion/vpu_core.c |  6 ++++
 drivers/media/platform/amphion/vpu_dbg.c  |  5 ++++
 drivers/media/platform/amphion/vpu_drv.c  |  2 ++
 drivers/media/platform/amphion/vpu_v4l2.c | 35 ++++++++++++++++++++++-
 8 files changed, 73 insertions(+), 1 deletion(-)

diff --git a/drivers/media/platform/amphion/Kconfig b/drivers/media/platform/amphion/Kconfig
index 4a363e07ccc9..2835c25415c5 100644
--- a/drivers/media/platform/amphion/Kconfig
+++ b/drivers/media/platform/amphion/Kconfig
@@ -12,6 +12,7 @@ config VIDEO_AMPHION_VPU
 	select V4L2_MEM2MEM_DEV
 	select VIDEOBUF2_DMA_CONTIG
 	select VIDEOBUF2_VMALLOC
+	select V4L2_MEMTRACK
 	help
 	  Amphion VPU Codec IP contains two parts: Windsor and Malone.
 	  Windsor is encoder that supports H.264, and Malone is decoder
diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c
index a9f0521f2e1a..2b863fadb67f 100644
--- a/drivers/media/platform/amphion/vdec.c
+++ b/drivers/media/platform/amphion/vdec.c
@@ -279,6 +279,9 @@ static int vdec_ctrl_init(struct vpu_inst *inst)
 	if (ctrl)
 		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
 
+	v4l2_ctrl_new_std(&inst->ctrl_handler, NULL,
+			  V4L2_CID_MEMORY_USAGE, 0, S64_MAX, 1, 0);
+
 	if (inst->ctrl_handler.error) {
 		ret = inst->ctrl_handler.error;
 		v4l2_ctrl_handler_free(&inst->ctrl_handler);
@@ -1069,6 +1072,8 @@ static int vdec_alloc_fs_buffer(struct vpu_inst *inst, struct vdec_fs_info *fs)
 
 	vpu_free_dma(buffer);
 	buffer->length = fs->size;
+	buffer->memtrack = inst->memtrack;
+	buffer->label = fs->type == MEM_RES_MBI ? "mbi" : "dcp";
 	return vpu_alloc_dma(inst->core, buffer);
 }
 
@@ -1683,6 +1688,8 @@ static int vdec_start(struct vpu_inst *inst)
 	vpu_trace(inst->dev, "[%d]\n", inst->id);
 	if (!vdec->udata.virt) {
 		vdec->udata.length = 0x1000;
+		vdec->udata.memtrack = inst->memtrack;
+		vdec->udata.label = "udata";
 		ret = vpu_alloc_dma(inst->core, &vdec->udata);
 		if (ret) {
 			dev_err(inst->dev, "[%d] alloc udata fail\n", inst->id);
@@ -1694,6 +1701,8 @@ static int vdec_start(struct vpu_inst *inst)
 		stream_buffer_size = vpu_iface_get_stream_buffer_size(inst->core);
 		if (stream_buffer_size > 0) {
 			inst->stream_buffer.length = stream_buffer_size;
+			inst->stream_buffer.memtrack = inst->memtrack;
+			inst->stream_buffer.label = "bitstream-ring-buffer";
 			ret = vpu_alloc_dma(inst->core, &inst->stream_buffer);
 			if (ret) {
 				dev_err(inst->dev, "[%d] alloc stream buffer fail\n", inst->id);
diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c
index 0b3d58b9f2f7..193ee488eba4 100644
--- a/drivers/media/platform/amphion/venc.c
+++ b/drivers/media/platform/amphion/venc.c
@@ -678,6 +678,9 @@ static int venc_ctrl_init(struct vpu_inst *inst)
 	v4l2_ctrl_new_std(&inst->ctrl_handler, NULL,
 			  V4L2_CID_MPEG_VIDEO_AVERAGE_QP, 0, 51, 1, 0);
 
+	v4l2_ctrl_new_std(&inst->ctrl_handler, NULL,
+			  V4L2_CID_MEMORY_USAGE, 0, S64_MAX, 1, 0);
+
 	if (inst->ctrl_handler.error) {
 		ret = inst->ctrl_handler.error;
 		v4l2_ctrl_handler_free(&inst->ctrl_handler);
@@ -929,6 +932,8 @@ static int venc_start_session(struct vpu_inst *inst, u32 type)
 	stream_buffer_size = vpu_iface_get_stream_buffer_size(inst->core);
 	if (stream_buffer_size > 0) {
 		inst->stream_buffer.length = max_t(u32, stream_buffer_size, venc->cpb_size * 3);
+		inst->stream_buffer.memtrack = inst->memtrack;
+		inst->stream_buffer.label = "bitstream-ring-buffer";
 		ret = vpu_alloc_dma(inst->core, &inst->stream_buffer);
 		if (ret)
 			goto error;
@@ -1027,6 +1032,8 @@ static void venc_request_mem_resource(struct vpu_inst *inst,
 
 	for (i = 0; i < enc_frame_num; i++) {
 		venc->enc[i].length = enc_frame_size;
+		venc->enc[i].memtrack = inst->memtrack;
+		venc->enc[i].label = "enc-frame";
 		ret = vpu_alloc_dma(inst->core, &venc->enc[i]);
 		if (ret) {
 			venc_cleanup_mem_resource(inst);
@@ -1035,6 +1042,8 @@ static void venc_request_mem_resource(struct vpu_inst *inst,
 	}
 	for (i = 0; i < ref_frame_num; i++) {
 		venc->ref[i].length = ref_frame_size;
+		venc->ref[i].memtrack = inst->memtrack;
+		venc->ref[i].label = "ref-frame";
 		ret = vpu_alloc_dma(inst->core, &venc->ref[i]);
 		if (ret) {
 			venc_cleanup_mem_resource(inst);
diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h
index bfd171a3ded4..08913cc54cb1 100644
--- a/drivers/media/platform/amphion/vpu.h
+++ b/drivers/media/platform/amphion/vpu.h
@@ -9,6 +9,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-mem2mem.h>
+#include <media/v4l2-memtrack.h>
 #include <linux/mailbox_client.h>
 #include <linux/mailbox_controller.h>
 #include <linux/kfifo.h>
@@ -17,6 +18,7 @@
 #define VPU_TIMEOUT		msecs_to_jiffies(1000)
 #define VPU_INST_NULL_ID	(-1L)
 #define VPU_MSG_BUFFER_SIZE	(8192)
+#define VPU_NOTIFY_DELAY_MS	(200)
 
 enum imx_plat_type {
 	IMX8QXP = 0,
@@ -47,6 +49,8 @@ struct vpu_buffer {
 	u32 length;
 	u32 bytesused;
 	struct device *dev;
+	struct v4l2_memtrack_node *memtrack;
+	const char *label;
 };
 
 struct vpu_func {
@@ -81,6 +85,7 @@ struct vpu_dev {
 	atomic_t ref_dec;
 
 	struct dentry *debugfs;
+	struct v4l2_memtrack_node *memtrack;
 };
 
 struct vpu_format {
@@ -279,6 +284,8 @@ struct vpu_inst {
 	pid_t tgid;
 	struct dentry *debugfs;
 
+	struct v4l2_memtrack_node *memtrack;
+
 	void *priv;
 };
 
diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c
index 85cc4a14f8ed..16c2efc86feb 100644
--- a/drivers/media/platform/amphion/vpu_core.c
+++ b/drivers/media/platform/amphion/vpu_core.c
@@ -150,6 +150,8 @@ static int __vpu_alloc_dma(struct device *dev, struct vpu_buffer *buf)
 	if (!buf->virt)
 		return -ENOMEM;
 
+	if (buf->memtrack)
+		v4l2_memtrack_add(buf->memtrack, buf->length, buf->label);
 	buf->dev = dev;
 
 	return 0;
@@ -160,6 +162,8 @@ void vpu_free_dma(struct vpu_buffer *buf)
 	if (!buf->virt || !buf->dev)
 		return;
 
+	if (buf->memtrack)
+		v4l2_memtrack_sub(buf->memtrack, buf->length, buf->label);
 	dma_free_coherent(buf->dev, buf->length, buf->virt, buf->phys);
 	buf->virt = NULL;
 	buf->phys = 0;
@@ -550,6 +554,7 @@ static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np)
 
 	core->fw.phys = res.start;
 	core->fw.length = resource_size(&res);
+	v4l2_memtrack_add(core->vpu->memtrack, core->fw.length, "fw");
 
 	ret = of_reserved_mem_region_to_resource(np, 1, &res);
 	if (ret) {
@@ -559,6 +564,7 @@ static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np)
 
 	core->rpc.phys = res.start;
 	core->rpc.length = resource_size(&res);
+	v4l2_memtrack_add(core->vpu->memtrack, core->rpc.length, "rpc");
 
 	if (core->rpc.length < core->res->rpc_size + core->res->fwlog_size) {
 		dev_err(core->dev, "the rpc-region <%pad, 0x%x> is not enough\n",
diff --git a/drivers/media/platform/amphion/vpu_dbg.c b/drivers/media/platform/amphion/vpu_dbg.c
index 497ae4e8a229..a82e21cc8a67 100644
--- a/drivers/media/platform/amphion/vpu_dbg.c
+++ b/drivers/media/platform/amphion/vpu_dbg.c
@@ -212,6 +212,11 @@ static int vpu_dbg_instance(struct seq_file *s, void *data)
 	if (seq_write(s, str, num))
 		return 0;
 
+	num = scnprintf(str, sizeof(str), "memory usage = %ld\n",
+			v4l2_memtrack_read(inst->memtrack));
+	if (seq_write(s, str, num))
+		return 0;
+
 	num = scnprintf(str, sizeof(str), "flow :\n");
 	if (seq_write(s, str, num))
 		return 0;
diff --git a/drivers/media/platform/amphion/vpu_drv.c b/drivers/media/platform/amphion/vpu_drv.c
index 2cca61f41bea..73e01c55da82 100644
--- a/drivers/media/platform/amphion/vpu_drv.c
+++ b/drivers/media/platform/amphion/vpu_drv.c
@@ -138,6 +138,7 @@ static int vpu_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_vpu_media;
 	vpu->debugfs = debugfs_create_dir("amphion_vpu", NULL);
+	vpu->memtrack = v4l2_memtrack_create_root("amphion-vpu");
 
 	of_platform_populate(dev->of_node, NULL, NULL, dev);
 
@@ -162,6 +163,7 @@ static void vpu_remove(struct platform_device *pdev)
 	struct vpu_dev *vpu = platform_get_drvdata(pdev);
 	struct device *dev = &pdev->dev;
 
+	v4l2_memtrack_destroy_node(vpu->memtrack);
 	debugfs_remove_recursive(vpu->debugfs);
 	vpu->debugfs = NULL;
 
diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c
index 7cccc994fc50..431f5f64e683 100644
--- a/drivers/media/platform/amphion/vpu_v4l2.c
+++ b/drivers/media/platform/amphion/vpu_v4l2.c
@@ -651,6 +651,14 @@ static const struct vb2_ops vpu_vb2_ops = {
 	.buf_queue          = vpu_vb2_buf_queue,
 };
 
+static void vpu_memtrack_ctrl_notify(struct v4l2_memtrack_node *node, size_t total, void *priv)
+{
+	struct v4l2_ctrl *ctrl = priv;
+
+	if (ctrl)
+		v4l2_ctrl_s_ctrl_int64(ctrl, total);
+}
+
 static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
 {
 	struct vpu_inst *inst = priv;
@@ -668,9 +676,13 @@ static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_q
 	src_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer);
 	src_vq->dev = inst->vpu->dev;
 	src_vq->lock = &inst->lock;
+	if (inst->memtrack)
+		src_vq->memtrack = v4l2_memtrack_create_node(inst->memtrack, "output");
 	ret = vb2_queue_init(src_vq);
-	if (ret)
+	if (ret) {
+		v4l2_memtrack_destroy_node(src_vq->memtrack);
 		return ret;
+	}
 
 	dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
 	inst->cap_format.type = dst_vq->type;
@@ -684,8 +696,12 @@ static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_q
 	dst_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer);
 	dst_vq->dev = inst->vpu->dev;
 	dst_vq->lock = &inst->lock;
+	if (inst->memtrack)
+		dst_vq->memtrack = v4l2_memtrack_create_node(inst->memtrack, "capture");
 	ret = vb2_queue_init(dst_vq);
 	if (ret) {
+		v4l2_memtrack_destroy_node(src_vq->memtrack);
+		v4l2_memtrack_destroy_node(dst_vq->memtrack);
 		vb2_queue_release(src_vq);
 		return ret;
 	}
@@ -706,6 +722,12 @@ static int vpu_v4l2_release(struct vpu_inst *inst)
 	vpu_release_core(inst->core);
 	put_device(inst->dev);
 
+	if (inst->memtrack) {
+		v4l2_memtrack_unregister_notify(inst->memtrack);
+		v4l2_memtrack_destroy_node(inst->memtrack);
+		inst->memtrack = NULL;
+	}
+
 	v4l2_ctrl_handler_free(&inst->ctrl_handler);
 	mutex_destroy(&inst->lock);
 
@@ -745,6 +767,8 @@ int vpu_v4l2_open(struct file *file, struct vpu_inst *inst)
 	inst->min_buffer_out = 2;
 	v4l2_fh_init(&inst->fh, func->vfd);
 	v4l2_fh_add(&inst->fh, file);
+	if (vpu->memtrack)
+		inst->memtrack = v4l2_memtrack_create_node(vpu->memtrack, "instance");
 
 	ret = call_vop(inst, ctrl_init);
 	if (ret)
@@ -757,6 +781,14 @@ int vpu_v4l2_open(struct file *file, struct vpu_inst *inst)
 		goto error;
 	}
 
+	if (inst->memtrack) {
+		v4l2_memtrack_set_notify_delay(inst->memtrack, VPU_NOTIFY_DELAY_MS);
+		v4l2_memtrack_register_notify(inst->memtrack,
+					      vpu_memtrack_ctrl_notify,
+					      v4l2_ctrl_find(&inst->ctrl_handler,
+							     V4L2_CID_MEMORY_USAGE));
+	}
+
 	inst->fh.ctrl_handler = &inst->ctrl_handler;
 	inst->state = VPU_CODEC_STATE_DEINIT;
 	inst->workqueue = alloc_ordered_workqueue("vpu_inst", WQ_MEM_RECLAIM);
@@ -775,6 +807,7 @@ int vpu_v4l2_open(struct file *file, struct vpu_inst *inst)
 
 	return 0;
 error:
+	v4l2_memtrack_destroy_node(inst->memtrack);
 	v4l2_fh_del(&inst->fh, file);
 	v4l2_fh_exit(&inst->fh);
 	vpu_inst_put(inst);
-- 
2.53.0



^ permalink raw reply related

* [RFC PATCH 6/7] media: videobuf2: Add memory tracking support
From: ming.qian @ 2026-03-31  7:23 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, hverkuil-cisco, nicolas, sebastian.fricke, shawnguo,
	s.hauer, kernel, festevam, linux-imx, Frank.li, xiahong.bao,
	eagle.zhou, imx, linux-kernel, linux-arm-kernel
In-Reply-To: <20260331072347.253-1-ming.qian@oss.nxp.com>

From: Ming Qian <ming.qian@oss.nxp.com>

Add optional memtrack field to vb2_queue for tracking MMAP buffer
memory usage. When a driver sets q->memtrack, the core automatically
tracks allocations via v4l2_memtrack_add/sub calls.

Only MMAP type buffers are tracked since DMABUF memory is allocated
externally by userspace.

Signed-off-by: Ming Qian <ming.qian@oss.nxp.com>
---
 drivers/media/common/videobuf2/videobuf2-core.c | 13 +++++++++++++
 include/media/videobuf2-core.h                  |  4 ++++
 2 files changed, 17 insertions(+)

diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index adf668b213c2..e42fdf829b22 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -28,6 +28,7 @@
 
 #include <media/videobuf2-core.h>
 #include <media/v4l2-mc.h>
+#include <media/v4l2-memtrack.h>
 
 #include <trace/events/vb2.h>
 
@@ -251,12 +252,19 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
 
 		/* Associate allocator private data with this plane */
 		vb->planes[plane].mem_priv = mem_priv;
+
+		if (q->memtrack)
+			v4l2_memtrack_add(q->memtrack, size, NULL);
 	}
 
 	return 0;
 free:
 	/* Free already allocated memory if one of the allocations failed */
 	for (; plane > 0; --plane) {
+		unsigned long size = PAGE_ALIGN(vb->planes[plane - 1].length);
+
+		if (q->memtrack)
+			v4l2_memtrack_sub(q->memtrack, size, NULL);
 		call_void_memop(vb, put, vb->planes[plane - 1].mem_priv);
 		vb->planes[plane - 1].mem_priv = NULL;
 	}
@@ -269,9 +277,14 @@ static int __vb2_buf_mem_alloc(struct vb2_buffer *vb)
  */
 static void __vb2_buf_mem_free(struct vb2_buffer *vb)
 {
+	struct vb2_queue *q = vb->vb2_queue;
 	unsigned int plane;
 
 	for (plane = 0; plane < vb->num_planes; ++plane) {
+		unsigned long size = PAGE_ALIGN(vb->planes[plane].length);
+
+		if (q->memtrack)
+			v4l2_memtrack_sub(q->memtrack, size, NULL);
 		call_void_memop(vb, put, vb->planes[plane].mem_priv);
 		vb->planes[plane].mem_priv = NULL;
 		dprintk(vb->vb2_queue, 3, "freed plane %d of buffer %d\n",
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 4424d481d7f7..7cb6ff2fc892 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -47,6 +47,7 @@ enum vb2_memory {
 struct vb2_fileio_data;
 struct vb2_threadio_data;
 struct vb2_buffer;
+struct v4l2_memtrack_node;
 
 /**
  * struct vb2_mem_ops - memory handling/memory allocator operations.
@@ -558,6 +559,7 @@ struct vb2_buf_ops {
  *		driver implements the V4L2_CID_MIN_BUFFERS_FOR_CAPTURE/OUTPUT
  *		control.
  * @alloc_devs:	&struct device memory type/allocator-specific per-plane device
+ * @memtrack:   optional memory tracking node for debugging and monitoring.
  */
 /*
  * Private elements (won't appear at the uAPI book):
@@ -632,6 +634,8 @@ struct vb2_queue {
 
 	struct device			*alloc_devs[VB2_MAX_PLANES];
 
+	struct v4l2_memtrack_node	*memtrack;
+
 	/* private: internal use only */
 	struct mutex			mmap_lock;
 	unsigned int			memory;
-- 
2.53.0



^ permalink raw reply related

* [RFC PATCH 5/7] MAINTAINERS: Add entry for V4L2 memory usage tracker
From: ming.qian @ 2026-03-31  7:23 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, hverkuil-cisco, nicolas, sebastian.fricke, shawnguo,
	s.hauer, kernel, festevam, linux-imx, Frank.li, xiahong.bao,
	eagle.zhou, imx, linux-kernel, linux-arm-kernel
In-Reply-To: <20260331072347.253-1-ming.qian@oss.nxp.com>

From: Ming Qian <ming.qian@oss.nxp.com>

Add a MAINTAINERS entry for the V4L2 memory tracking infrastructure,
covering the driver API documentation, core implementation, and header
files.

Signed-off-by: Ming Qian <ming.qian@oss.nxp.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 32b1dfee8614..6de2f259415f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27667,6 +27667,14 @@ F:	drivers/media/v4l2-core/v4l2-isp.c
 F:	include/media/v4l2-isp.h
 F:	include/uapi/linux/media/v4l2-isp.h
 
+V4L2 MEMORY USAGE TRACKER
+M:	Ming Qian <ming.qian@oss.nxp.com>
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	Documentation/driver-api/media/v4l2-memtrack.rst
+F:	drivers/media/common/v4l2-memtrack/v4l2-memtrack.c
+F:	include/media/v4l2-memtrack.h
+
 VF610 NAND DRIVER
 M:	Stefan Agner <stefan@agner.ch>
 L:	linux-mtd@lists.infradead.org
-- 
2.53.0



^ permalink raw reply related

* [RFC PATCH 3/7] media: v4l2-memtrack: Add V4L2 memory tracking infrastructure
From: ming.qian @ 2026-03-31  7:23 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, hverkuil-cisco, nicolas, sebastian.fricke, shawnguo,
	s.hauer, kernel, festevam, linux-imx, Frank.li, xiahong.bao,
	eagle.zhou, imx, linux-kernel, linux-arm-kernel
In-Reply-To: <20260331072347.253-1-ming.qian@oss.nxp.com>

From: Ming Qian <ming.qian@oss.nxp.com>

Add a hierarchical memory tracking system for V4L2 devices that allows
drivers to monitor and report memory usage. This infrastructure provides:

- Tree-structured memory tracking with parent-child relationships
- Per-node memory accounting with allocation counts
- Automatic aggregation of memory usage up the hierarchy
- debugfs interface for inspecting memory usage trees
- Notification callbacks for memory usage changes

The tracking system uses a context-based design where each root node
creates its own tracking tree. Memory changes propagate notifications up
the tree, allowing drivers to update controls or take other actions.

The debugfs interface creates entries under <debugfs>/v4l2-memtrack/
showing hierarchical memory usage with indentation, allocation counts,
and process information.

Drivers can use this to track internal buffers, intermediate processing
data, and other allocations, providing visibility into memory consumption
for debugging and resource monitoring.

Signed-off-by: Ming Qian <ming.qian@oss.nxp.com>
---
 drivers/media/common/Kconfig                  |   1 +
 drivers/media/common/Makefile                 |   2 +-
 drivers/media/common/v4l2-memtrack/Kconfig    |  19 +
 drivers/media/common/v4l2-memtrack/Makefile   |   3 +
 .../common/v4l2-memtrack/v4l2-memtrack.c      | 825 ++++++++++++++++++
 include/media/v4l2-memtrack.h                 | 220 +++++
 6 files changed, 1069 insertions(+), 1 deletion(-)
 create mode 100644 drivers/media/common/v4l2-memtrack/Kconfig
 create mode 100644 drivers/media/common/v4l2-memtrack/Makefile
 create mode 100644 drivers/media/common/v4l2-memtrack/v4l2-memtrack.c
 create mode 100644 include/media/v4l2-memtrack.h

diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig
index adcb6655385a..4da632f53970 100644
--- a/drivers/media/common/Kconfig
+++ b/drivers/media/common/Kconfig
@@ -27,5 +27,6 @@ config VIDEO_TVEEPROM
 source "drivers/media/common/b2c2/Kconfig"
 source "drivers/media/common/saa7146/Kconfig"
 source "drivers/media/common/siano/Kconfig"
+source "drivers/media/common/v4l2-memtrack/Kconfig"
 source "drivers/media/common/v4l2-tpg/Kconfig"
 source "drivers/media/common/videobuf2/Kconfig"
diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile
index c5ab905e7c20..0010a4d904ca 100644
--- a/drivers/media/common/Makefile
+++ b/drivers/media/common/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
-obj-y += b2c2/ saa7146/ siano/ v4l2-tpg/ videobuf2/
+obj-y += b2c2/ saa7146/ siano/ v4l2-memtrack/ v4l2-tpg/ videobuf2/
 
 # Please keep it alphabetically sorted by Kconfig name
 # (e. g. LC_ALL=C sort Makefile)
diff --git a/drivers/media/common/v4l2-memtrack/Kconfig b/drivers/media/common/v4l2-memtrack/Kconfig
new file mode 100644
index 000000000000..f4c97117b779
--- /dev/null
+++ b/drivers/media/common/v4l2-memtrack/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config V4L2_MEMTRACK
+	tristate "V4L2 memory usage tracking support"
+	help
+	  This option enables hierarchical memory allocation tracking
+	  for V4L2 devices. It provides:
+
+	  - Tree-structured memory usage monitoring
+	  - Per-device and per-context tracking
+	  - debugfs interface at /sys/kernel/debug/v4l2-memtrack/
+	  - Optional V4L2 control for userspace queries
+
+	  This is useful for debugging memory leaks and monitoring
+	  buffer allocation in video drivers.
+
+	  To compile this as a module, choose M here.
+
+	  If unsure, say N.
diff --git a/drivers/media/common/v4l2-memtrack/Makefile b/drivers/media/common/v4l2-memtrack/Makefile
new file mode 100644
index 000000000000..5ed59453b35a
--- /dev/null
+++ b/drivers/media/common/v4l2-memtrack/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_V4L2_MEMTRACK) += v4l2-memtrack.o
diff --git a/drivers/media/common/v4l2-memtrack/v4l2-memtrack.c b/drivers/media/common/v4l2-memtrack/v4l2-memtrack.c
new file mode 100644
index 000000000000..96eeaef7a653
--- /dev/null
+++ b/drivers/media/common/v4l2-memtrack/v4l2-memtrack.c
@@ -0,0 +1,825 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * V4L2 Memory Usage Tracker
+ *
+ * Copyright 2026 NXP
+ *
+ * Provides hierarchical memory allocation tracking for V4L2 devices.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/kref.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/atomic.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+
+#include <media/v4l2-memtrack.h>
+
+#define V4L2_MEMTRACK_DEBUGFS_ROOT	"v4l2-memtrack"
+#define V4L2_MEMTRACK_MAX_DEPTH		16
+
+/**
+ * struct v4l2_memtrack_ctx - Memory tracker context
+ * @name: Tracker name (shown in debugfs)
+ * @root: Root node of tracking hierarchy
+ * @debugfs: debugfs file entry
+ * @lock: Protects the node tree
+ */
+struct v4l2_memtrack_ctx {
+	const char *name;
+	struct v4l2_memtrack_node *root;
+	struct dentry *debugfs;
+	struct mutex lock;
+};
+
+/**
+ * struct v4l2_memtrack_node - Memory tracking node
+ * @list: Linkage in parent's children list
+ * @parent: Parent node
+ * @children: List of child nodes
+ * @bytes: Memory usage in bytes
+ * @alloc_count: Number of allocations
+ * @name: Node name
+ * @depth: Depth of this node in the tree (root = 0)
+ * @pid: Process ID that created this node
+ * @tgid: Thread group ID that created this node
+ * @ctx: Back pointer to context
+ * @is_leaf: True if this is a leaf node (internal use only)
+ * @notify: Notification callback for memory changes
+ * @notify_priv: Private data for notification callback
+ * @notify_work: Per-node delayed work for coalesced notifications
+ * @notify_delay_ms: Notification delay (<0: off, 0: immediate, >0: delayed)
+ * @in_notify: Flag indicating if callback is currently executing
+ * @notify_done: Completion for waiting on callback completion
+ */
+struct v4l2_memtrack_node {
+	struct list_head list;
+	struct v4l2_memtrack_node *parent;
+	struct list_head children;
+
+	size_t bytes;
+	unsigned int alloc_count;
+
+	const char *name;
+	unsigned int depth;
+	pid_t pid;
+	pid_t tgid;
+
+	struct v4l2_memtrack_ctx *ctx;
+	bool is_leaf;
+
+	/* Notify fields - only used by non-leaf nodes */
+	v4l2_memtrack_notify_fn notify;
+	void *notify_priv;
+	struct delayed_work notify_work;
+	int notify_delay_ms;
+	bool in_notify;
+	struct completion notify_done;
+};
+
+static struct dentry *v4l2_memtrack_debugfs_root;
+static DEFINE_MUTEX(v4l2_memtrack_debugfs_lock);
+static struct kref v4l2_memtrack_debugfs_kref;
+static bool v4l2_memtrack_debugfs_initialized;
+
+struct v4l2_memtrack_iter_state {
+	struct v4l2_memtrack_node *node;
+	struct list_head *next_child;
+};
+
+typedef int (*v4l2_memtrack_visit_fn)(struct v4l2_memtrack_node *node, void *data);
+
+static int v4l2_memtrack_for_each_locked(struct v4l2_memtrack_node *root,
+					 v4l2_memtrack_visit_fn visit,
+					 void *data)
+{
+	struct v4l2_memtrack_iter_state stack[V4L2_MEMTRACK_MAX_DEPTH];
+	struct v4l2_memtrack_node *node;
+	int sp = 0;
+	int ret;
+
+	lockdep_assert_held(&root->ctx->lock);
+
+	ret = visit(root, data);
+	if (ret)
+		return ret;
+
+	if (list_empty(&root->children))
+		return 0;
+
+	stack[sp].node = root;
+	stack[sp].next_child = root->children.next;
+	sp++;
+
+	while (sp > 0) {
+		struct v4l2_memtrack_iter_state *top = &stack[sp - 1];
+
+		if (top->next_child == &top->node->children) {
+			sp--;
+			continue;
+		}
+
+		node = list_entry(top->next_child,
+				  struct v4l2_memtrack_node, list);
+		top->next_child = top->next_child->next;
+
+		ret = visit(node, data);
+		if (ret)
+			return ret;
+
+		if (!list_empty(&node->children) &&
+		    sp < V4L2_MEMTRACK_MAX_DEPTH) {
+			stack[sp].node = node;
+			stack[sp].next_child = node->children.next;
+			sp++;
+		}
+	}
+
+	return 0;
+}
+
+static void v4l2_memtrack_debugfs_release(struct kref *kref)
+{
+	debugfs_remove(v4l2_memtrack_debugfs_root);
+	v4l2_memtrack_debugfs_root = NULL;
+	v4l2_memtrack_debugfs_initialized = false;
+}
+
+static void v4l2_memtrack_debugfs_get(void)
+{
+	guard(mutex)(&v4l2_memtrack_debugfs_lock);
+
+	if (!v4l2_memtrack_debugfs_initialized) {
+		kref_init(&v4l2_memtrack_debugfs_kref);
+		v4l2_memtrack_debugfs_root =
+			debugfs_create_dir(V4L2_MEMTRACK_DEBUGFS_ROOT, NULL);
+		v4l2_memtrack_debugfs_initialized = true;
+	} else {
+		kref_get(&v4l2_memtrack_debugfs_kref);
+	}
+}
+
+static void v4l2_memtrack_debugfs_put(void)
+{
+	guard(mutex)(&v4l2_memtrack_debugfs_lock);
+	kref_put(&v4l2_memtrack_debugfs_kref, v4l2_memtrack_debugfs_release);
+}
+
+static void v4l2_memtrack_notify_work_handler(struct work_struct *work);
+
+static int v4l2_memtrack_node_init(struct v4l2_memtrack_node *node,
+				   struct v4l2_memtrack_ctx *ctx,
+				   unsigned int depth,
+				   const char *name,
+				   bool is_leaf)
+{
+	INIT_LIST_HEAD(&node->list);
+	INIT_LIST_HEAD(&node->children);
+	node->bytes = 0;
+	node->alloc_count = 0;
+	node->ctx = ctx;
+	node->depth = depth;
+	node->tgid = current->tgid;
+	node->pid = current->pid;
+	node->is_leaf = is_leaf;
+
+	/* Initialize notify fields only for non-leaf nodes */
+	if (!is_leaf) {
+		node->notify = NULL;
+		node->notify_priv = NULL;
+		INIT_DELAYED_WORK(&node->notify_work,
+				  v4l2_memtrack_notify_work_handler);
+		node->notify_delay_ms = 0;
+		node->in_notify = false;
+		init_completion(&node->notify_done);
+	}
+
+	if (name) {
+		node->name = kstrdup_const(name, GFP_KERNEL);
+		if (!node->name)
+			return -ENOMEM;
+	} else {
+		node->name = NULL;
+	}
+
+	return 0;
+}
+
+static inline const char *v4l2_memtrack_name(struct v4l2_memtrack_node *node)
+{
+	return node->name ? : "(unnamed)";
+}
+
+static int v4l2_memtrack_sum_visitor(struct v4l2_memtrack_node *node, void *data)
+{
+	size_t *total = data;
+
+	*total += node->bytes;
+	return 0;
+}
+
+static size_t v4l2_memtrack_sum_bytes_locked(struct v4l2_memtrack_node *root)
+{
+	size_t total = 0;
+
+	lockdep_assert_held(&root->ctx->lock);
+
+	v4l2_memtrack_for_each_locked(root, v4l2_memtrack_sum_visitor, &total);
+	return total;
+}
+
+static void v4l2_memtrack_do_notify_locked(struct v4l2_memtrack_node *node)
+{
+	size_t total;
+	v4l2_memtrack_notify_fn notify;
+	void *priv;
+	struct v4l2_memtrack_ctx *ctx = node->ctx;
+
+	lockdep_assert_held(&ctx->lock);
+
+	if (!node->notify)
+		return;
+
+	total = v4l2_memtrack_sum_bytes_locked(node);
+	notify = node->notify;
+	priv = node->notify_priv;
+
+	node->in_notify = true;
+	reinit_completion(&node->notify_done);
+
+	mutex_unlock(&ctx->lock);
+	notify(node, total, priv);
+	mutex_lock(&ctx->lock);
+
+	node->in_notify = false;
+	complete_all(&node->notify_done);
+}
+
+static void v4l2_memtrack_notify_work_handler(struct work_struct *work)
+{
+	struct v4l2_memtrack_node *node =
+		container_of(to_delayed_work(work),
+			     struct v4l2_memtrack_node, notify_work);
+	struct v4l2_memtrack_ctx *ctx = node->ctx;
+
+	guard(mutex)(&ctx->lock);
+	v4l2_memtrack_do_notify_locked(node);
+}
+
+static void v4l2_memtrack_schedule_notify(struct v4l2_memtrack_node *node)
+{
+	int delay;
+
+	if (node->is_leaf || !node->notify)
+		return;
+
+	delay = node->notify_delay_ms;
+	if (delay < 0)
+		return;
+
+	if (delay == 0)
+		v4l2_memtrack_do_notify_locked(node);
+	else
+		schedule_delayed_work(&node->notify_work,
+				      msecs_to_jiffies(delay));
+}
+
+static void v4l2_memtrack_notify_change_locked(struct v4l2_memtrack_node *node)
+{
+	struct v4l2_memtrack_node *cur;
+
+	lockdep_assert_held(&node->ctx->lock);
+
+	for (cur = node; cur; cur = cur->parent) {
+		if (!cur->is_leaf)
+			v4l2_memtrack_schedule_notify(cur);
+	}
+}
+
+static void v4l2_memtrack_do_notify(struct v4l2_memtrack_node *node)
+{
+	if (!node || !node->ctx)
+		return;
+
+	guard(mutex)(&node->ctx->lock);
+	v4l2_memtrack_notify_change_locked(node);
+}
+
+struct v4l2_memtrack_seq_ctx {
+	struct seq_file *seq;
+};
+
+static int v4l2_memtrack_seq_visitor(struct v4l2_memtrack_node *node, void *data)
+{
+	struct v4l2_memtrack_seq_ctx *ctx = data;
+	struct seq_file *s = ctx->seq;
+	size_t bytes;
+
+	bytes = v4l2_memtrack_sum_bytes_locked(node);
+
+	seq_printf(s, "%*s", node->depth * 2, "");
+
+	if (node->name)
+		seq_printf(s, "%s ", node->name);
+
+	if (node->is_leaf)
+		seq_puts(s, "[leaf] ");
+
+	if (node->tgid && node->pid)
+		seq_printf(s, "(tgid=%d, pid=%d) ", node->tgid, node->pid);
+
+	seq_printf(s, "usage: %zu", bytes);
+
+	if (node->alloc_count > 1 || (node->alloc_count == 1 && !list_empty(&node->children))) {
+		seq_printf(s, " (count=%u", node->alloc_count);
+		if (!list_empty(&node->children))
+			seq_printf(s, ", self=%zu", node->bytes);
+		seq_puts(s, ")");
+	}
+
+	seq_puts(s, "\n");
+	return 0;
+}
+
+static int v4l2_memtrack_seq_show(struct seq_file *s, void *data)
+{
+	struct v4l2_memtrack_ctx *ctx = s->private;
+	struct v4l2_memtrack_seq_ctx seq_ctx = { .seq = s };
+
+	guard(mutex)(&ctx->lock);
+
+	seq_printf(s, "Total memory usage: %zu bytes\n",
+		   v4l2_memtrack_sum_bytes_locked(ctx->root));
+	v4l2_memtrack_for_each_locked(ctx->root, v4l2_memtrack_seq_visitor, &seq_ctx);
+
+	return 0;
+}
+
+static int v4l2_memtrack_debugfs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, v4l2_memtrack_seq_show, inode->i_private);
+}
+
+static const struct file_operations v4l2_memtrack_debugfs_fops = {
+	.owner = THIS_MODULE,
+	.open = v4l2_memtrack_debugfs_open,
+	.release = single_release,
+	.read = seq_read,
+	.llseek = seq_lseek,
+};
+
+static void v4l2_memtrack_create_debugfs(struct v4l2_memtrack_ctx *ctx)
+{
+	guard(mutex)(&v4l2_memtrack_debugfs_lock);
+
+	if (IS_ERR_OR_NULL(v4l2_memtrack_debugfs_root))
+		return;
+
+	ctx->debugfs = debugfs_create_file(ctx->name, 0444,
+					   v4l2_memtrack_debugfs_root,
+					   ctx, &v4l2_memtrack_debugfs_fops);
+}
+
+static struct v4l2_memtrack_ctx *v4l2_memtrack_ctx_create(const char *name)
+{
+	struct v4l2_memtrack_ctx *ctx;
+
+	ctx = kzalloc_obj(*ctx);
+	if (!ctx)
+		return NULL;
+
+	if (name) {
+		ctx->name = kstrdup_const(name, GFP_KERNEL);
+		if (!ctx->name) {
+			kfree(ctx);
+			return NULL;
+		}
+	}
+
+	mutex_init(&ctx->lock);
+
+	return ctx;
+}
+
+static void v4l2_memtrack_ctx_destroy(struct v4l2_memtrack_ctx *ctx)
+{
+	debugfs_remove(ctx->debugfs);
+	mutex_destroy(&ctx->lock);
+	kfree_const(ctx->name);
+	kfree(ctx);
+	v4l2_memtrack_debugfs_put();
+}
+
+struct v4l2_memtrack_node *v4l2_memtrack_create_root(const char *name)
+{
+	struct v4l2_memtrack_ctx *ctx;
+	struct v4l2_memtrack_node *node;
+
+	v4l2_memtrack_debugfs_get();
+
+	ctx = v4l2_memtrack_ctx_create(name);
+	if (!ctx) {
+		v4l2_memtrack_debugfs_put();
+		return NULL;
+	}
+
+	node = kzalloc_obj(*node);
+	if (!node) {
+		v4l2_memtrack_ctx_destroy(ctx);
+		return NULL;
+	}
+
+	if (v4l2_memtrack_node_init(node, ctx, 0, name, false)) {
+		kfree(node);
+		v4l2_memtrack_ctx_destroy(ctx);
+		return NULL;
+	}
+
+	ctx->root = node;
+
+	if (name)
+		v4l2_memtrack_create_debugfs(ctx);
+
+	return node;
+}
+EXPORT_SYMBOL_GPL(v4l2_memtrack_create_root);
+
+struct v4l2_memtrack_node *v4l2_memtrack_create_node(struct v4l2_memtrack_node *parent,
+						     const char *name)
+{
+	struct v4l2_memtrack_ctx *ctx;
+	struct v4l2_memtrack_node *node;
+	unsigned int new_depth;
+
+	if (!parent || !parent->ctx)
+		return NULL;
+
+	/* Leaf nodes cannot have children */
+	if (parent->is_leaf) {
+		pr_warn_once("v4l2-memtrack: cannot create child under leaf node\n");
+		return NULL;
+	}
+
+	new_depth = parent->depth + 1;
+	if (new_depth >= V4L2_MEMTRACK_MAX_DEPTH) {
+		pr_warn_once("v4l2-memtrack: max depth %d reached, cannot create child node\n",
+			     V4L2_MEMTRACK_MAX_DEPTH);
+		return NULL;
+	}
+
+	ctx = parent->ctx;
+
+	node = kzalloc_obj(*node);
+	if (!node)
+		return NULL;
+
+	if (v4l2_memtrack_node_init(node, ctx, new_depth, name, false)) {
+		kfree(node);
+		return NULL;
+	}
+
+	node->parent = parent;
+
+	scoped_guard(mutex, &ctx->lock)
+		list_add_tail(&node->list, &parent->children);
+
+	return node;
+}
+EXPORT_SYMBOL_GPL(v4l2_memtrack_create_node);
+
+static void v4l2_memtrack_cancel_notify(struct v4l2_memtrack_node *node)
+{
+	bool wait_needed = false;
+
+	if (node->is_leaf)
+		return;
+
+	cancel_delayed_work_sync(&node->notify_work);
+
+	scoped_guard(mutex, &node->ctx->lock) {
+		node->notify = NULL;
+		node->notify_priv = NULL;
+		wait_needed = node->in_notify;
+	}
+
+	if (wait_needed)
+		wait_for_completion(&node->notify_done);
+}
+
+static void v4l2_memtrack_delete_subtree_locked(struct v4l2_memtrack_node *root)
+{
+	struct v4l2_memtrack_ctx *ctx = root->ctx;
+	struct v4l2_memtrack_node * const stop = root->parent;
+	struct v4l2_memtrack_node *node, *parent;
+
+	lockdep_assert_held(&ctx->lock);
+
+	node = root;
+	while (node) {
+		if (!list_empty(&node->children)) {
+			node = list_first_entry(&node->children,
+						struct v4l2_memtrack_node, list);
+		} else {
+			parent = node->parent;
+			list_del(&node->list);
+			kfree_const(node->name);
+			kfree(node);
+
+			if (parent == stop)
+				break;
+			node = parent;
+		}
+	}
+}
+
+static int v4l2_memtrack_find_notify_visitor(struct v4l2_memtrack_node *node,
+					     void *data)
+{
+	struct v4l2_memtrack_node **found = data;
+
+	if (!node->is_leaf && node->notify) {
+		*found = node;
+		return 1; /* Stop iteration */
+	}
+	return 0;
+}
+
+static struct v4l2_memtrack_node *
+v4l2_memtrack_get_next_notify_node_locked(struct v4l2_memtrack_node *root)
+{
+	struct v4l2_memtrack_node *found = NULL;
+
+	lockdep_assert_held(&root->ctx->lock);
+
+	v4l2_memtrack_for_each_locked(root, v4l2_memtrack_find_notify_visitor,
+				      &found);
+	return found;
+}
+
+void v4l2_memtrack_destroy_node(struct v4l2_memtrack_node *node)
+{
+	struct v4l2_memtrack_ctx *ctx;
+	struct v4l2_memtrack_node *parent;
+	struct v4l2_memtrack_node *notify_node;
+	bool is_root;
+
+	if (!node || !node->ctx)
+		return;
+
+	ctx = node->ctx;
+	parent = node->parent;
+	is_root = !parent;
+
+	if (v4l2_memtrack_read(node))
+		v4l2_memtrack_print_debug(node);
+
+	/*
+	 * Cancel all notify callbacks in the subtree.
+	 * Get one node with notify at a time while holding lock,
+	 * then cancel it without lock to avoid deadlock.
+	 */
+	if (!node->is_leaf) {
+		while (true) {
+			scoped_guard(mutex, &ctx->lock)
+				notify_node = v4l2_memtrack_get_next_notify_node_locked(node);
+
+			if (!notify_node)
+				break;
+
+			v4l2_memtrack_cancel_notify(notify_node);
+		}
+	}
+
+	scoped_guard(mutex, &ctx->lock)
+		v4l2_memtrack_delete_subtree_locked(node);
+
+	if (is_root)
+		v4l2_memtrack_ctx_destroy(ctx);
+	else
+		v4l2_memtrack_do_notify(parent);
+}
+EXPORT_SYMBOL_GPL(v4l2_memtrack_destroy_node);
+
+static int v4l2_memtrack_create_leaf_locked(struct v4l2_memtrack_node *node,
+					    size_t bytes, const char *name)
+{
+	struct v4l2_memtrack_ctx *ctx = node->ctx;
+	struct v4l2_memtrack_node *child;
+	unsigned int new_depth;
+
+	lockdep_assert_held(&ctx->lock);
+
+	new_depth = node->depth + 1;
+	if (new_depth >= V4L2_MEMTRACK_MAX_DEPTH) {
+		pr_warn_once("v4l2-memtrack: max depth %d reached\n",
+			     V4L2_MEMTRACK_MAX_DEPTH);
+		return -ENOSPC;
+	}
+
+	child = kzalloc(sizeof(*child), GFP_KERNEL);
+	if (!child)
+		return -ENOMEM;
+
+	if (v4l2_memtrack_node_init(child, ctx, new_depth, name, true)) {
+		kfree(child);
+		return -ENOMEM;
+	}
+
+	child->parent = node;
+	child->bytes = bytes;
+	child->alloc_count = 1;
+
+	list_add_tail(&child->list, &node->children);
+
+	return 0;
+}
+
+void v4l2_memtrack_add(struct v4l2_memtrack_node *node, size_t bytes,
+		       const char *name)
+{
+	if (!node || !node->ctx)
+		return;
+
+	/* Leaf nodes cannot have children */
+	if (node->is_leaf) {
+		pr_warn_once("v4l2-memtrack: cannot add to leaf node\n");
+		return;
+	}
+
+	guard(mutex)(&node->ctx->lock);
+
+	if (name) {
+		/* Create a leaf node for named allocations */
+		if (v4l2_memtrack_create_leaf_locked(node, bytes, name) < 0) {
+			/* Fallback to direct tracking on failure */
+			node->bytes += bytes;
+			node->alloc_count++;
+		}
+	} else {
+		/* Direct tracking for unnamed allocations */
+		node->bytes += bytes;
+		node->alloc_count++;
+	}
+
+	v4l2_memtrack_notify_change_locked(node);
+}
+EXPORT_SYMBOL_GPL(v4l2_memtrack_add);
+
+void v4l2_memtrack_sub(struct v4l2_memtrack_node *node, size_t bytes,
+		       const char *name)
+{
+	struct v4l2_memtrack_node *child;
+	struct v4l2_memtrack_node *found = NULL;
+
+	if (!node || !node->ctx)
+		return;
+
+	guard(mutex)(&node->ctx->lock);
+
+	/* Try to find and remove a matching leaf node if name is provided */
+	if (name && !node->is_leaf) {
+		list_for_each_entry(child, &node->children, list) {
+			if (!child->is_leaf)
+				continue;
+			if (child->bytes != bytes)
+				continue;
+			if (!child->name || strcmp(child->name, name))
+				continue;
+
+			list_del(&child->list);
+			found = child;
+			break;
+		}
+	}
+
+	if (found) {
+		kfree_const(found->name);
+		kfree(found);
+	} else {
+		/* Direct subtraction */
+		if (node->bytes < bytes) {
+			pr_warn_once("v4l2-memtrack: %s: bytes underflow\n",
+				     v4l2_memtrack_name(node));
+			node->bytes = 0;
+		} else {
+			node->bytes -= bytes;
+		}
+
+		if (node->alloc_count > 0)
+			node->alloc_count--;
+	}
+
+	v4l2_memtrack_notify_change_locked(node);
+}
+EXPORT_SYMBOL_GPL(v4l2_memtrack_sub);
+
+void v4l2_memtrack_set(struct v4l2_memtrack_node *node, size_t bytes)
+{
+	if (!node || !node->ctx)
+		return;
+
+	guard(mutex)(&node->ctx->lock);
+
+	node->bytes = bytes;
+	node->alloc_count = bytes ? 1 : 0;
+	v4l2_memtrack_notify_change_locked(node);
+}
+EXPORT_SYMBOL_GPL(v4l2_memtrack_set);
+
+size_t v4l2_memtrack_read(struct v4l2_memtrack_node *node)
+{
+	struct v4l2_memtrack_ctx *ctx;
+	size_t total;
+
+	if (!node || !node->ctx)
+		return 0;
+
+	ctx = node->ctx;
+
+	scoped_guard(mutex, &ctx->lock)
+		total = v4l2_memtrack_sum_bytes_locked(node);
+
+	return total;
+}
+EXPORT_SYMBOL_GPL(v4l2_memtrack_read);
+
+int v4l2_memtrack_register_notify(struct v4l2_memtrack_node *node,
+				  v4l2_memtrack_notify_fn notify,
+				  void *priv)
+{
+	if (!node || !node->ctx)
+		return -EINVAL;
+
+	/* Leaf nodes do not support notifications */
+	if (node->is_leaf)
+		return -EINVAL;
+
+	guard(mutex)(&node->ctx->lock);
+
+	if (node->notify)
+		return -EBUSY;
+
+	node->notify = notify;
+	node->notify_priv = priv;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_memtrack_register_notify);
+
+void v4l2_memtrack_unregister_notify(struct v4l2_memtrack_node *node)
+{
+	if (!node || !node->ctx || node->is_leaf)
+		return;
+
+	v4l2_memtrack_cancel_notify(node);
+}
+EXPORT_SYMBOL_GPL(v4l2_memtrack_unregister_notify);
+
+void v4l2_memtrack_set_notify_delay(struct v4l2_memtrack_node *node, int delay_ms)
+{
+	if (!node || !node->ctx || node->is_leaf)
+		return;
+
+	guard(mutex)(&node->ctx->lock);
+	node->notify_delay_ms = delay_ms;
+}
+EXPORT_SYMBOL_GPL(v4l2_memtrack_set_notify_delay);
+
+static int v4l2_memtrack_print_visitor(struct v4l2_memtrack_node *node, void *data)
+{
+	pr_debug("v4l2-memtrack: %*s%s%s: usage=%zu bytes, count=%u\n",
+		 node->depth * 2, "",
+		 v4l2_memtrack_name(node),
+		 node->is_leaf ? " [leaf]" : "",
+		 v4l2_memtrack_sum_bytes_locked(node),
+		 node->alloc_count);
+	return 0;
+}
+
+void v4l2_memtrack_print_debug(struct v4l2_memtrack_node *node)
+{
+	struct v4l2_memtrack_ctx *ctx;
+
+	if (!node || !node->ctx)
+		return;
+
+	ctx = node->ctx;
+
+	pr_debug("v4l2-memtrack: Memory Tracker: %s\n", ctx->name ? : "(unnamed)");
+
+	scoped_guard(mutex, &ctx->lock)
+		v4l2_memtrack_for_each_locked(node, v4l2_memtrack_print_visitor, NULL);
+}
+EXPORT_SYMBOL_GPL(v4l2_memtrack_print_debug);
+
+MODULE_DESCRIPTION("V4L2 Memory Usage Tracker");
+MODULE_AUTHOR("Ming Qian <ming.qian@oss.nxp.com>");
+MODULE_LICENSE("GPL");
diff --git a/include/media/v4l2-memtrack.h b/include/media/v4l2-memtrack.h
new file mode 100644
index 000000000000..b146277ab98f
--- /dev/null
+++ b/include/media/v4l2-memtrack.h
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * V4L2 Memory Usage Tracker
+ *
+ * Copyright 2026 NXP
+ *
+ * Provides hierarchical memory allocation tracking for V4L2 devices,
+ * useful for debugging memory leaks and monitoring buffer usage.
+ */
+
+#ifndef _V4L2_MEMTRACK_H
+#define _V4L2_MEMTRACK_H
+
+#include <linux/types.h>
+
+struct v4l2_memtrack_node;
+
+/**
+ * typedef v4l2_memtrack_notify_fn - Memory change notification callback
+ * @node: The node whose subtree memory usage changed
+ * @total: Total memory usage of the node's subtree in bytes
+ * @priv: Private data passed during registration
+ *
+ * This callback is invoked when memory usage changes in the node or any
+ * of its descendants. The callback is executed without holding internal
+ * locks, so it may call most kernel functions safely.
+ *
+ * However, the callback MUST NOT call v4l2_memtrack_unregister_notify()
+ * or v4l2_memtrack_destroy_node() on the same node, as this will cause
+ * a deadlock. The callback should be kept reasonably short to avoid
+ * delaying other pending notifications.
+ */
+typedef void (*v4l2_memtrack_notify_fn)(struct v4l2_memtrack_node *node, size_t total, void *priv);
+
+#if IS_REACHABLE(CONFIG_V4L2_MEMTRACK)
+
+/**
+ * v4l2_memtrack_create_root - Create a root tracking node
+ * @name: Name for this tracker (shows in debugfs)
+ *
+ * Creates a root node that serves as the top of a tracking hierarchy.
+ * Each root node creates its own debugfs entry under <debugfs>/v4l2-memtrack/.
+ *
+ * Return: Pointer to the root node, or NULL on failure
+ */
+struct v4l2_memtrack_node *v4l2_memtrack_create_root(const char *name);
+
+/**
+ * v4l2_memtrack_create_node - Create a child tracking node
+ * @parent: Parent node
+ * @name: Optional name for this node
+ *
+ * Creates a child node under the specified parent. Child nodes
+ * contribute to their parent's total memory count.
+ *
+ * Return: Pointer to the new node, or NULL on failure
+ */
+struct v4l2_memtrack_node *v4l2_memtrack_create_node(struct v4l2_memtrack_node *parent,
+						     const char *name);
+
+/**
+ * v4l2_memtrack_destroy_node - Destroy a tracking node
+ * @node: Node to destroy
+ *
+ * Destroys the node and all its children. If this is a root node,
+ * also removes the debugfs entry and frees all resources.
+ */
+void v4l2_memtrack_destroy_node(struct v4l2_memtrack_node *node);
+
+/**
+ * v4l2_memtrack_add - Record a memory allocation
+ * @node: Tracking node
+ * @bytes: Number of bytes allocated
+ * @name: Optional name for the allocation
+ *
+ * Records a memory allocation. If @name is provided, creates an internal
+ * leaf node to track this specific allocation, which can be matched and
+ * removed later by v4l2_memtrack_sub() with the same name and size.
+ * If @name is NULL, adds bytes directly to the node's counter.
+ *
+ * Triggers notification callbacks on this node and all ancestors.
+ */
+void v4l2_memtrack_add(struct v4l2_memtrack_node *node, size_t bytes,
+		       const char *name);
+
+/**
+ * v4l2_memtrack_sub - Record a memory free
+ * @node: Tracking node
+ * @bytes: Number of bytes freed
+ * @name: Optional name to match (must match what was passed to add)
+ *
+ * Records a memory deallocation. If @name is provided, searches for a
+ * matching internal leaf node (by name and size) and removes it.
+ * If no match is found or @name is NULL, subtracts directly from the
+ * node's counter.
+ *
+ * Triggers notification callbacks on this node and all ancestors.
+ */
+void v4l2_memtrack_sub(struct v4l2_memtrack_node *node, size_t bytes,
+		       const char *name);
+
+/**
+ * v4l2_memtrack_set - Set memory usage directly
+ * @node: Tracking node
+ * @bytes: Total bytes to set
+ *
+ * Sets the node's memory counter to the specified value.
+ * Allocation count is set to 1 if bytes > 0, otherwise 0.
+ */
+void v4l2_memtrack_set(struct v4l2_memtrack_node *node, size_t bytes);
+
+/**
+ * v4l2_memtrack_read - Read total memory usage
+ * @node: Tracking node
+ *
+ * Returns the total memory usage of this node plus all its children.
+ *
+ * Return: Total memory usage in bytes
+ */
+size_t v4l2_memtrack_read(struct v4l2_memtrack_node *node);
+
+/**
+ * v4l2_memtrack_register_notify - Register notification callback
+ * @node: Tracking node
+ * @notify: Callback function
+ * @priv: Private data passed to callback
+ *
+ * Register a callback that is called when memory usage changes on this
+ * node or any of its descendants. Only one callback per node is supported.
+ *
+ * Return: 0 on success, -EINVAL if node is invalid, -EBUSY if callback
+ *         already registered
+ */
+int v4l2_memtrack_register_notify(struct v4l2_memtrack_node *node,
+				  v4l2_memtrack_notify_fn notify,
+				  void *priv);
+
+/**
+ * v4l2_memtrack_unregister_notify - Unregister notification callback
+ * @node: The tracking node
+ *
+ * Unregisters the notification callback and waits for any in-progress
+ * callback to complete.
+ *
+ * NOTE: This function MUST be called before v4l2_memtrack_destroy_node()
+ * if a notify callback was registered. Failing to do so will trigger
+ * a WARN and may cause use-after-free.
+ */
+void v4l2_memtrack_unregister_notify(struct v4l2_memtrack_node *node);
+
+/**
+ * v4l2_memtrack_set_notify_delay - Set notification delay
+ * @node: Any node in the tracking tree
+ * @delay_ms: Delay in milliseconds
+ *            < 0: disable notifications
+ *            = 0: immediate notification (default)
+ *            > 0: coalesce notifications within delay period
+ *
+ * Sets the notification delay for the entire tracking tree.
+ * When delay > 0, multiple updates within the delay period are
+ * coalesced into a single notification, reducing overhead for
+ * batch operations.
+ */
+void v4l2_memtrack_set_notify_delay(struct v4l2_memtrack_node *node,
+				    int delay_ms);
+
+/**
+ * v4l2_memtrack_print_debug - Print memory usage to kernel log
+ * @node: Tracking node
+ *
+ * Prints the memory usage tree starting from this node using pr_debug().
+ * Supports dynamic debug when CONFIG_DYNAMIC_DEBUG is enabled.
+ *
+ * Enable output with:
+ *   echo 'file v4l2-memtrack.c +p' > /sys/kernel/debug/dynamic_debug/control
+ */
+void v4l2_memtrack_print_debug(struct v4l2_memtrack_node *node);
+
+#else /* !CONFIG_V4L2_MEMTRACK */
+
+static inline struct v4l2_memtrack_node *v4l2_memtrack_create_root(const char *name)
+{
+	return NULL;
+}
+
+static inline struct v4l2_memtrack_node *v4l2_memtrack_create_node(
+	struct v4l2_memtrack_node *parent, const char *name)
+{
+	return NULL;
+}
+
+static inline void v4l2_memtrack_destroy_node(struct v4l2_memtrack_node *node) {}
+
+static inline void v4l2_memtrack_add(struct v4l2_memtrack_node *node,
+				     size_t bytes, const char *name) {}
+
+static inline void v4l2_memtrack_sub(struct v4l2_memtrack_node *node,
+				     size_t bytes, const char *name) {}
+
+static inline void v4l2_memtrack_set(struct v4l2_memtrack_node *node, size_t bytes) {}
+
+static inline size_t v4l2_memtrack_read(struct v4l2_memtrack_node *node)
+{
+	return 0;
+}
+
+static inline int v4l2_memtrack_register_notify(struct v4l2_memtrack_node *node,
+						v4l2_memtrack_notify_fn notify,
+						void *priv)
+{
+	return 0;
+}
+
+static inline void v4l2_memtrack_unregister_notify(struct v4l2_memtrack_node *node) {}
+static inline void v4l2_memtrack_set_notify_delay(struct v4l2_memtrack_node *node, int delay_ms) {}
+static inline void v4l2_memtrack_print_debug(struct v4l2_memtrack_node *node) {}
+
+#endif /* CONFIG_V4L2_MEMTRACK */
+
+#endif /* _V4L2_MEMTRACK_H */
-- 
2.53.0



^ permalink raw reply related

* [RFC PATCH 4/7] docs: media: v4l2-memtrack: Add driver API documentation
From: ming.qian @ 2026-03-31  7:23 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, hverkuil-cisco, nicolas, sebastian.fricke, shawnguo,
	s.hauer, kernel, festevam, linux-imx, Frank.li, xiahong.bao,
	eagle.zhou, imx, linux-kernel, linux-arm-kernel
In-Reply-To: <20260331072347.253-1-ming.qian@oss.nxp.com>

From: Ming Qian <ming.qian@oss.nxp.com>

Add comprehensive documentation for the V4L2 memory tracking infrastructure
in the driver API documentation. This documentation covers:

- Overview of the hierarchical memory tracking system
- Basic usage examples showing root node creation and memory tracking
- Hierarchical tracking with parent-child node relationships
- debugfs interface for runtime memory inspection
- Notification callback registration and usage warnings
- Visual examples of tree structure and debugfs output

The documentation provides driver developers with practical examples
of how to integrate memory tracking into their V4L2 drivers, including
proper setup during probe/remove, buffer allocation tracking, and
per-context memory monitoring.

Add the new v4l2-memtrack documentation to the v4l2-core index to make
it accessible in the generated documentation.

Signed-off-by: Ming Qian <ming.qian@oss.nxp.com>
---
 Documentation/driver-api/media/v4l2-core.rst  |   1 +
 .../driver-api/media/v4l2-memtrack.rst        | 140 ++++++++++++++++++
 2 files changed, 141 insertions(+)
 create mode 100644 Documentation/driver-api/media/v4l2-memtrack.rst

diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
index a5f5102c64cc..09765d028375 100644
--- a/Documentation/driver-api/media/v4l2-core.rst
+++ b/Documentation/driver-api/media/v4l2-core.rst
@@ -28,3 +28,4 @@ Video4Linux devices
     v4l2-tveeprom
     v4l2-jpeg
     v4l2-isp
+    v4l2-memtrack
diff --git a/Documentation/driver-api/media/v4l2-memtrack.rst b/Documentation/driver-api/media/v4l2-memtrack.rst
new file mode 100644
index 000000000000..bca6954cfa0b
--- /dev/null
+++ b/Documentation/driver-api/media/v4l2-memtrack.rst
@@ -0,0 +1,140 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+V4L2 Memory Usage Tracker
+=========================
+
+Overview
+--------
+
+The V4L2 memory tracking module provides hierarchical memory allocation
+tracking for V4L2 devices. It is useful for debugging memory leaks and
+monitoring buffer usage in video drivers.
+
+Features:
+
+- Tree-structured memory usage monitoring
+- Per-device and per-context tracking
+- debugfs interface for runtime inspection
+- Optional notification callbacks for memory changes
+- V4L2 control integration
+
+Usage Example
+-------------
+
+Basic usage in a V4L2 driver:
+
+.. code-block:: c
+
+    #include <media/v4l2-memtrack.h>
+
+    struct my_device {
+        struct v4l2_memtrack_node *memtrack;
+        /* ... */
+    };
+
+    static int my_probe(struct platform_device *pdev)
+    {
+        struct my_device *dev;
+
+        dev->memtrack = v4l2_memtrack_create_root("my-device");
+        if (!dev->memtrack)
+            return -ENOMEM;
+
+        return 0;
+    }
+
+    static void my_remove(struct platform_device *pdev)
+    {
+        v4l2_memtrack_destroy_node(dev->memtrack);
+    }
+
+    static int my_alloc_buffer(struct my_device *dev, size_t size)
+    {
+        void *buf = dma_alloc_coherent(dev, size, &dma_addr, GFP_KERNEL);
+        if (!buf)
+            return -ENOMEM;
+
+        v4l2_memtrack_add(dev->memtrack, size);
+        return 0;
+    }
+
+    static void my_free_buffer(struct my_device *dev, size_t size)
+    {
+        dma_free_coherent(dev, size, buf, dma_addr);
+        v4l2_memtrack_sub(dev->memtrack, size);
+    }
+
+Hierarchical Tracking
+---------------------
+
+For more detailed tracking, create child nodes:
+
+.. code-block:: c
+
+    struct my_context {
+        struct v4l2_memtrack_node *memtrack;
+    };
+
+    static int my_open(struct file *file)
+    {
+        struct my_context *ctx;
+
+        ctx->memtrack = v4l2_memtrack_create_node(dev->memtrack, "context");
+        return 0;
+    }
+
+    static int my_close(struct file *file)
+    {
+        v4l2_memtrack_destroy_node(ctx->memtrack);
+        return 0;
+    }
+
+This creates a tree structure::
+
+    my-device (root)
+    ├── context (pid=1234)
+    │   ├── buffer: 4096 bytes
+    │   └── buffer: 8192 bytes
+    └── context (pid=5678)
+        └── buffer: 4096 bytes
+
+debugfs Interface
+-----------------
+
+Memory usage is exposed via debugfs at::
+
+    /sys/kernel/debug/v4l2-memtrack/<device-name>
+
+Example output::
+
+    Total memory usage: 16384 bytes
+    my-device (tgid=1000, pid=1000) usage: 16384
+      context (tgid=1234, pid=1234) usage: 12288 (count=2)
+      context (tgid=5678, pid=5678) usage: 4096 (count=1)
+
+Notification Callbacks
+----------------------
+
+Register callbacks to be notified of memory changes:
+
+.. code-block:: c
+
+    static void my_notify(struct v4l2_memtrack_node *node,
+                          size_t total, void *priv)
+    {
+        pr_info("Memory usage changed: %zu bytes\n", total);
+    }
+
+    v4l2_memtrack_register_notify(dev->memtrack, my_notify, dev);
+
+.. warning::
+
+   Callbacks are executed without holding internal locks, so most kernel
+   functions may be called safely. However, the callback MUST NOT call
+   v4l2_memtrack_unregister_notify() or v4l2_memtrack_destroy_node() on
+   the same node, as this will cause a deadlock.
+
+API Reference
+-------------
+
+.. kernel-doc:: include/media/v4l2-memtrack.h
-- 
2.53.0



^ permalink raw reply related

* [RFC PATCH 2/7] docs: media: v4l2-ctrls: Add V4L2_CID_MEMORY_USAGE control
From: ming.qian @ 2026-03-31  7:23 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, hverkuil-cisco, nicolas, sebastian.fricke, shawnguo,
	s.hauer, kernel, festevam, linux-imx, Frank.li, xiahong.bao,
	eagle.zhou, imx, linux-kernel, linux-arm-kernel
In-Reply-To: <20260331072347.253-1-ming.qian@oss.nxp.com>

From: Ming Qian <ming.qian@oss.nxp.com>

Add documentation for the V4L2_CID_MEMORY_USAGE control in the
userspace API documentation. This read-only control allows applications
to query the total amount of memory currently used by a device instance.

The documentation explains:
- The control reports memory usage in bytes for driver-managed allocations
- It includes internal buffers, intermediate processing data, and other
  driver allocations associated with the instance
- Applications can use it for debugging, resource monitoring, and making
  informed buffer allocation decisions
- The value may change dynamically during device operation
- It only reports driver-managed memory, not application-allocated buffers

Update V4L2_CID_LASTP1 definition to reflect the new control as the
last predefined control ID.

Signed-off-by: Ming Qian <ming.qian@oss.nxp.com>
---
 .../userspace-api/media/v4l/control.rst       | 22 ++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/Documentation/userspace-api/media/v4l/control.rst b/Documentation/userspace-api/media/v4l/control.rst
index 19372bb32c4b..6904c5bdbf37 100644
--- a/Documentation/userspace-api/media/v4l/control.rst
+++ b/Documentation/userspace-api/media/v4l/control.rst
@@ -316,9 +316,29 @@ Control IDs
     set the alpha component value of all pixels for further processing
     in the device.
 
+``V4L2_CID_MEMORY_USAGE`` ``(integer64)``
+    This is an optional read-only control that reports the total amount of
+    memory, in bytes, currently allocated and used by this device instance.
+    This includes memory used for internal buffers, intermediate processing
+    data, and any other driver-managed allocations associated with the
+    instance.
+
+    Applications can use this control to monitor memory consumption for
+    debugging purposes, resource management, or to make informed decisions
+    about buffer allocation strategies. The value may change dynamically
+    as the device operates, for example when buffers are allocated or
+    released during streaming.
+
+    Note that this control only reports memory managed by the driver for
+    this specific instance; it does not include memory allocated by the
+    application itself (e.g., userspace buffers passed to the driver).
+
+    Not all drivers support this control. Applications should query for
+    its availability before use.
+
 ``V4L2_CID_LASTP1``
     End of the predefined control IDs (currently
-    ``V4L2_CID_ALPHA_COMPONENT`` + 1).
+    ``V4L2_CID_MEMORY_USAGE`` + 1).
 
 ``V4L2_CID_PRIVATE_BASE``
     ID of the first custom (driver specific) control. Applications
-- 
2.53.0



^ permalink raw reply related

* [RFC PATCH 1/7] media: v4l2-ctrls: Add V4L2_CID_MEMORY_USAGE control
From: ming.qian @ 2026-03-31  7:23 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, hverkuil-cisco, nicolas, sebastian.fricke, shawnguo,
	s.hauer, kernel, festevam, linux-imx, Frank.li, xiahong.bao,
	eagle.zhou, imx, linux-kernel, linux-arm-kernel
In-Reply-To: <20260331072347.253-1-ming.qian@oss.nxp.com>

From: Ming Qian <ming.qian@oss.nxp.com>

Add a new read-only control V4L2_CID_MEMORY_USAGE that allows
applications to query the total amount of memory currently used
by a device instance.

This control reports the memory consumption in bytes, including
internal buffers, intermediate processing data, and other
driver-managed allocations. Applications can use this information
for debugging, resource monitoring, or making informed decisions
about buffer allocation strategies.

Signed-off-by: Ming Qian <ming.qian@oss.nxp.com>
---
 drivers/media/v4l2-core/v4l2-ctrls-defs.c | 8 ++++++++
 include/uapi/linux/v4l2-controls.h        | 4 +++-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
index 551426c4cd01..053db78ff661 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
@@ -831,6 +831,7 @@ const char *v4l2_ctrl_get_name(u32 id)
 	case V4L2_CID_ALPHA_COMPONENT:		return "Alpha Component";
 	case V4L2_CID_COLORFX_CBCR:		return "Color Effects, CbCr";
 	case V4L2_CID_COLORFX_RGB:              return "Color Effects, RGB";
+	case V4L2_CID_MEMORY_USAGE:		return "Memory Usage";
 
 	/*
 	 * Codec controls
@@ -1476,6 +1477,13 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
 		*min = 0;
 		*max = 0xffff;
 		break;
+	case V4L2_CID_MEMORY_USAGE:
+		*type = V4L2_CTRL_TYPE_INTEGER64;
+		*flags |= V4L2_CTRL_FLAG_READ_ONLY;
+		*min = 0;
+		*max = S64_MAX;
+		*step = 1;
+		break;
 	case V4L2_CID_FLASH_FAULT:
 	case V4L2_CID_JPEG_ACTIVE_MARKER:
 	case V4L2_CID_3A_LOCK:
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 68dd0c4e47b2..02c6f960d38e 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -110,8 +110,10 @@ enum v4l2_colorfx {
 #define V4L2_CID_COLORFX_CBCR			(V4L2_CID_BASE+42)
 #define V4L2_CID_COLORFX_RGB			(V4L2_CID_BASE+43)
 
+#define V4L2_CID_MEMORY_USAGE			(V4L2_CID_BASE+44)
+
 /* last CID + 1 */
-#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+44)
+#define V4L2_CID_LASTP1                         (V4L2_CID_BASE+45)
 
 /* USER-class private control IDs */
 
-- 
2.53.0



^ permalink raw reply related

* [RFC PATCH 0/7] media: amphion: Add DMA memory tracking support
From: ming.qian @ 2026-03-31  7:23 UTC (permalink / raw)
  To: linux-media
  Cc: mchehab, hverkuil-cisco, nicolas, sebastian.fricke, shawnguo,
	s.hauer, kernel, festevam, linux-imx, Frank.li, xiahong.bao,
	eagle.zhou, imx, linux-kernel, linux-arm-kernel

From: Ming Qian <ming.qian@oss.nxp.com>

This series adds DMA memory tracking support to the Amphion VPU driver,
allowing userspace to monitor memory consumption of codec instances.

To implement this feature in a reusable way, a new V4L2 memory tracking
infrastructure (v4l2-memtrack) is introduced, which can be adopted by
other V4L2 drivers in the future.

Background
==========

The Amphion VPU codec driver allocates various DMA buffers internally:

- Firmware boot region and RPC buffers
- Stream ring buffers
- Codec-specific buffers (MBI, DCP, reference frames)
- Encoder/decoder working buffers

These allocations are not visible to userspace, making it difficult to
debug memory issues or monitor resource usage in multi-instance scenarios
(e.g., transcoding servers with multiple simultaneous streams).

Solution
========

This series introduces:

1. V4L2_CID_MEMORY_USAGE control
   - New read-only control reporting memory usage in bytes
   - Standard V4L2 interface, queryable via VIDIOC_G_CTRL

2. v4l2-memtrack infrastructure
   - Hierarchical memory tracking (device -> instance -> queue)
   - debugfs interface at /sys/kernel/debug/v4l2-memtrack/
   - Reusable by other V4L2 drivers

3. videobuf2 integration
   - Automatic tracking of vb2 buffer allocations

4. Amphion VPU integration
   - Tracks all internal DMA allocations
   - Organized as: device -> instance -> buffers

Ming Qian (7):
  media: v4l2-ctrls: Add V4L2_CID_MEMORY_USAGE control
  docs: media: v4l2-ctrls: Add V4L2_CID_MEMORY_USAGE control
  media: v4l2-memtrack: Add V4L2 memory tracking infrastructure
  docs: media: v4l2-memtrack: Add driver API documentation
  MAINTAINERS: Add entry for V4L2 memory usage tracker
  media: videobuf2: Add memory tracking support
  media: amphion: Add V4L2 memory tracking support

 Documentation/driver-api/media/v4l2-core.rst  |   1 +
 .../driver-api/media/v4l2-memtrack.rst        | 140 +++
 .../userspace-api/media/v4l/control.rst       |  22 +-
 MAINTAINERS                                   |   8 +
 drivers/media/common/Kconfig                  |   1 +
 drivers/media/common/Makefile                 |   2 +-
 drivers/media/common/v4l2-memtrack/Kconfig    |  19 +
 drivers/media/common/v4l2-memtrack/Makefile   |   3 +
 .../common/v4l2-memtrack/v4l2-memtrack.c      | 825 ++++++++++++++++++
 .../media/common/videobuf2/videobuf2-core.c   |  13 +
 drivers/media/platform/amphion/Kconfig        |   1 +
 drivers/media/platform/amphion/vdec.c         |   9 +
 drivers/media/platform/amphion/venc.c         |   9 +
 drivers/media/platform/amphion/vpu.h          |   7 +
 drivers/media/platform/amphion/vpu_core.c     |   6 +
 drivers/media/platform/amphion/vpu_dbg.c      |   5 +
 drivers/media/platform/amphion/vpu_drv.c      |   2 +
 drivers/media/platform/amphion/vpu_v4l2.c     |  35 +-
 drivers/media/v4l2-core/v4l2-ctrls-defs.c     |   8 +
 include/media/v4l2-memtrack.h                 | 220 +++++
 include/media/videobuf2-core.h                |   4 +
 include/uapi/linux/v4l2-controls.h            |   4 +-
 22 files changed, 1340 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/driver-api/media/v4l2-memtrack.rst
 create mode 100644 drivers/media/common/v4l2-memtrack/Kconfig
 create mode 100644 drivers/media/common/v4l2-memtrack/Makefile
 create mode 100644 drivers/media/common/v4l2-memtrack/v4l2-memtrack.c
 create mode 100644 include/media/v4l2-memtrack.h


base-commit: 4fbeef21f5387234111b5d52924e77757626faa5
prerequisite-patch-id: 0000000000000000000000000000000000000000
-- 
2.53.0



^ permalink raw reply

* Re: [PATCH v3 2/2] ARM: dts: gemini: Rename power controller node to poweroff
From: Krzysztof Kozlowski @ 2026-03-31  7:22 UTC (permalink / raw)
  To: Khushal Chitturi
  Cc: sre, robh, krzk+dt, conor+dt, ulli.kroll, linusw, daniel.baluta,
	simona.toaca, d-gole, m-chawdhry, linux-pm, devicetree,
	linux-arm-kernel, linux-kernel
In-Reply-To: <20260330110135.10316-3-khushalchitturi@gmail.com>

On Mon, Mar 30, 2026 at 04:31:35PM +0530, Khushal Chitturi wrote:
> Update the node name for the Cortina Gemini power controller from
> power-controller to poweroff since node "power controller" is
> reserved for power domain controller.
> 
> Signed-off-by: Khushal Chitturi <khushalchitturi@gmail.com>

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

Best regards,
Krzysztof



^ permalink raw reply

* Re: [PATCH v3 1/2] dt-bindings: power: reset: cortina,gemini-power-controller: convert to DT schema
From: Krzysztof Kozlowski @ 2026-03-31  7:22 UTC (permalink / raw)
  To: Khushal Chitturi
  Cc: sre, robh, krzk+dt, conor+dt, ulli.kroll, linusw, daniel.baluta,
	simona.toaca, d-gole, m-chawdhry, linux-pm, devicetree,
	linux-arm-kernel, linux-kernel
In-Reply-To: <20260330110135.10316-2-khushalchitturi@gmail.com>

On Mon, Mar 30, 2026 at 04:31:34PM +0530, Khushal Chitturi wrote:
> Convert the Cortina Systems Gemini Poweroff Controller bindings to
> DT schema.
> 
> Signed-off-by: Khushal Chitturi <khushalchitturi@gmail.com>

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

Best regards,
Krzysztof



^ permalink raw reply

* Re: [PATCH v28 2/4] dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs and enable-dma properties
From: Jeremy Kerr @ 2026-03-31  7:17 UTC (permalink / raw)
  To: Ryan Chen, Krzysztof Kozlowski
  Cc: andriy.shevchenko@linux.intel.com, Andi Shyti, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
	Benjamin Herrenschmidt, Philipp Zabel, linux-i2c@vger.kernel.org,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	openbmc@lists.ozlabs.org
In-Reply-To: <TY2PPF5CB9A1BE6EA5BABE55EB8A7003D22F253A@TY2PPF5CB9A1BE6.apcprd06.prod.outlook.com>

Hi Ryan,

> > Sounds reasonable, but before you do so, how are you planning to manage the
> > allocation of DMA channels across multiple i2c peripherals?
> > 
> The AST2600 I2C hardware has only one can use DMA at a time.
> To avoid the complexity of managing DMA channel contention,
> I plan to use buffer mode by default for all controllers, which still provides
> better performance than byte mode without requiring DMA channel allocation.

OK, but your wording there ("by default") implies that DMA is still
selectable for one controller peripheral. In which case: you still have
the problem of managing DMA channel contention, but now it's at runtime
instead.

So my question still stands: how are you planning to enforce that DMA is
only enabled for one controller?

Or are you planning to disable I2C DMA entirely on AST2600?

Cheers,


Jeremy


^ permalink raw reply

* Re: [PATCH v5 2/3] crash: Align the declaration of crash_load_dm_crypt_keys with CONFIG_CRASH_DM_CRYPT
From: Baoquan He @ 2026-03-31  7:12 UTC (permalink / raw)
  To: Coiby Xu
  Cc: kexec, linux-arm-kernel, linuxppc-dev, devicetree,
	kernel test robot, Andrew Morton, Vivek Goyal, Dave Young,
	open list
In-Reply-To: <20260225060347.718905-3-coxu@redhat.com>

On 02/25/26 at 02:03pm, Coiby Xu wrote:
> This will prevent a compiling failure when CONFIG_CRASH_DUMP is enabled
> but CONFIG_CRASH_DM_CRYPT is disabled,
> 
>        arch/powerpc/kexec/elf_64.c: In function 'elf64_load':
>     >> arch/powerpc/kexec/elf_64.c:82:23: error: implicit declaration of function 'crash_load_dm_crypt_keys' [-Werror=implicit-function-declaration]
>           82 |                 ret = crash_load_dm_crypt_keys(image);
>              |                       ^~~~~~~~~~~~~~~~~~~~~~~~
>        cc1: some warnings being treated as errors
> 
> Reported-by: kernel test robot <lkp@intel.com>
> Closes: https://lore.kernel.org/oe-kbuild-all/202602120648.RgQALnnI-lkp@intel.com/
> Signed-off-by: Coiby Xu <coxu@redhat.com>
> ---
>  include/linux/crash_core.h | 14 +++++++-------
>  1 file changed, 7 insertions(+), 7 deletions(-)

Acked-by: Baoquan He <bhe@redhat.com>

> 
> diff --git a/include/linux/crash_core.h b/include/linux/crash_core.h
> index d35726d6a415..c1dee3f971a9 100644
> --- a/include/linux/crash_core.h
> +++ b/include/linux/crash_core.h
> @@ -34,13 +34,6 @@ static inline void arch_kexec_protect_crashkres(void) { }
>  static inline void arch_kexec_unprotect_crashkres(void) { }
>  #endif
>  
> -#ifdef CONFIG_CRASH_DM_CRYPT
> -int crash_load_dm_crypt_keys(struct kimage *image);
> -ssize_t dm_crypt_keys_read(char *buf, size_t count, u64 *ppos);
> -#else
> -static inline int crash_load_dm_crypt_keys(struct kimage *image) {return 0; }
> -#endif
> -
>  #ifndef arch_crash_handle_hotplug_event
>  static inline void arch_crash_handle_hotplug_event(struct kimage *image, void *arg) { }
>  #endif
> @@ -96,4 +89,11 @@ static inline void crash_save_cpu(struct pt_regs *regs, int cpu) {};
>  static inline int kimage_crash_copy_vmcoreinfo(struct kimage *image) { return 0; };
>  #endif /* CONFIG_CRASH_DUMP*/
>  
> +#ifdef CONFIG_CRASH_DM_CRYPT
> +int crash_load_dm_crypt_keys(struct kimage *image);
> +ssize_t dm_crypt_keys_read(char *buf, size_t count, u64 *ppos);
> +#else
> +static inline int crash_load_dm_crypt_keys(struct kimage *image) {return 0; }
> +#endif
> +
>  #endif /* LINUX_CRASH_CORE_H */
> -- 
> 2.53.0
> 



^ permalink raw reply

* Re: [PATCH v5 1/3] crash_dump/dm-crypt: Don't print in arch-specific code
From: Baoquan He @ 2026-03-31  7:12 UTC (permalink / raw)
  To: Coiby Xu
  Cc: kexec, linux-arm-kernel, linuxppc-dev, devicetree, Will Deacon,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen,
	maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT), H. Peter Anvin,
	Andrew Morton, Vivek Goyal, Dave Young,
	open list:X86 ARCHITECTURE (32-BIT AND 64-BIT)
In-Reply-To: <20260225060347.718905-2-coxu@redhat.com>

On 02/25/26 at 02:03pm, Coiby Xu wrote:
> When the vmcore dumping target is not a LUKS-encrypted target, it's
> expected that there is no dm-crypt key thus no need to return -ENOENT.
> Also print more logs in crash_load_dm_crypt_keys. The benefit is
> arch-specific code can be more succinct.
> 
> Suggested-by: Will Deacon <will@kernel.org>
> Signed-off-by: Coiby Xu <coxu@redhat.com>
> ---
>  arch/x86/kernel/kexec-bzimage64.c | 6 +-----
>  kernel/crash_dump_dm_crypt.c      | 7 +++++--
>  2 files changed, 6 insertions(+), 7 deletions(-)

Acked-by: Baoquan He <bhe@redhat.com>

> 
> diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c
> index 5630c7dca1f3..7e980ea49d8d 100644
> --- a/arch/x86/kernel/kexec-bzimage64.c
> +++ b/arch/x86/kernel/kexec-bzimage64.c
> @@ -525,12 +525,8 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
>  		if (ret)
>  			return ERR_PTR(ret);
>  		ret = crash_load_dm_crypt_keys(image);
> -		if (ret == -ENOENT) {
> -			kexec_dprintk("No dm crypt key to load\n");
> -		} else if (ret) {
> -			pr_err("Failed to load dm crypt keys\n");
> +		if (ret)
>  			return ERR_PTR(ret);
> -		}
>  		if (image->dm_crypt_keys_addr &&
>  		    cmdline_len + MAX_ELFCOREHDR_STR_LEN + MAX_DMCRYPTKEYS_STR_LEN >
>  			    header->cmdline_size) {
> diff --git a/kernel/crash_dump_dm_crypt.c b/kernel/crash_dump_dm_crypt.c
> index 1f4067fbdb94..2f7b42b09673 100644
> --- a/kernel/crash_dump_dm_crypt.c
> +++ b/kernel/crash_dump_dm_crypt.c
> @@ -414,14 +414,16 @@ int crash_load_dm_crypt_keys(struct kimage *image)
>  
>  	if (key_count <= 0) {
>  		kexec_dprintk("No dm-crypt keys\n");
> -		return -ENOENT;
> +		return 0;
>  	}
>  
>  	if (!is_dm_key_reused) {
>  		image->dm_crypt_keys_addr = 0;
>  		r = build_keys_header();
> -		if (r)
> +		if (r) {
> +			pr_err("Failed to build dm-crypt keys header, ret=%d\n", r);
>  			return r;
> +		}
>  	}
>  
>  	kbuf.buffer = keys_header;
> @@ -432,6 +434,7 @@ int crash_load_dm_crypt_keys(struct kimage *image)
>  	kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
>  	r = kexec_add_buffer(&kbuf);
>  	if (r) {
> +		pr_err("Failed to call kexec_add_buffer, ret=%d\n", r);
>  		kvfree((void *)kbuf.buffer);
>  		return r;
>  	}
> -- 
> 2.53.0
> 



^ permalink raw reply

* Re: [PATCH v3 2/2] arm64: dts: ti: Add audio overlay for k3-j721s2-evm
From: Krzysztof Kozlowski @ 2026-03-31  7:11 UTC (permalink / raw)
  To: Moteen Shah
  Cc: krzk+dt, robh, conor+dt, nm, vigneshr, kristo, devicetree,
	linux-arm-kernel, linux-kernel, u-kumar1, gehariprasath,
	y-abhilashchandra
In-Reply-To: <20260330094459.128648-3-m-shah@ti.com>

On Mon, Mar 30, 2026 at 03:14:59PM +0530, Moteen Shah wrote:
> +	p10-hog {
> +		/* P10 - MCASP/TRACE_MUX_S1 */
> +		gpio-hog;
> +		gpios = <10 GPIO_ACTIVE_HIGH>;
> +		output-high;
> +		line-name = "MCASP/TRACE_MUX_S1";
> +	};
> +};
> +
> +&mux0 {
> +	idle-state = <0>;
> +};
> +
> +&mux1 {
> +	idle-state = <0>;
> +};
> +
> +&scm_conf {
> +	#address-cells = <1>;
> +	#size-cells = <1>;
> +
> +	audio_refclk1: clock-controller@42e4 {
> +		compatible = "ti,am62-audio-refclk";

there is no am62 compatible or DTSI include in the parent SoC file, so
am62n seems to be completely different device than j721s2.

Why does am62b appear here in this context?

Best regards,
Krzysztof



^ permalink raw reply

* Re: [PATCH v2 13/30] KVM: arm64: Clean up control flow in kvm_s2_fault_map()
From: Anshuman Khandual @ 2026-03-31  7:11 UTC (permalink / raw)
  To: Marc Zyngier, kvmarm, linux-arm-kernel, kvm
  Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
	Fuad Tabba, Will Deacon, Quentin Perret
In-Reply-To: <20260327113618.4051534-14-maz@kernel.org>



On 27/03/26 5:06 PM, Marc Zyngier wrote:
> From: Fuad Tabba <tabba@google.com>
> 
> Clean up the KVM MMU lock retry loop by pre-assigning the error code.
> Add clear braces to the THP adjustment integration for readability, and
> safely unnest the transparent hugepage logic branches.
> 
> Signed-off-by: Fuad Tabba <tabba@google.com>
> Signed-off-by: Marc Zyngier <maz@kernel.org>

Reviewed-by: Anshuman Khandual <anshuman.khandual@arm.com>

> ---
>  arch/arm64/kvm/mmu.c | 20 +++++++++++---------
>  1 file changed, 11 insertions(+), 9 deletions(-)
> 
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index ee2a548999b1b..c6cd6ce5254be 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1897,10 +1897,9 @@ static int kvm_s2_fault_map(struct kvm_s2_fault *fault, void *memcache)
>  
>  	kvm_fault_lock(kvm);
>  	pgt = fault->vcpu->arch.hw_mmu->pgt;
> -	if (mmu_invalidate_retry(kvm, fault->mmu_seq)) {
> -		ret = -EAGAIN;
> +	ret = -EAGAIN;
> +	if (mmu_invalidate_retry(kvm, fault->mmu_seq))
>  		goto out_unlock;
> -	}
>  
>  	/*
>  	 * If we are not forced to use page mapping, check if we are
> @@ -1908,16 +1907,17 @@ static int kvm_s2_fault_map(struct kvm_s2_fault *fault, void *memcache)
>  	 */
>  	if (fault->vma_pagesize == PAGE_SIZE &&
>  	    !(fault->force_pte || fault->s2_force_noncacheable)) {
> -		if (fault->fault_is_perm && fault->fault_granule > PAGE_SIZE)
> +		if (fault->fault_is_perm && fault->fault_granule > PAGE_SIZE) {
>  			fault->vma_pagesize = fault->fault_granule;
> -		else
> +		} else {
>  			fault->vma_pagesize = transparent_hugepage_adjust(kvm, fault->memslot,
>  									  fault->hva, &fault->pfn,
>  									  &fault->fault_ipa);
>  
> -		if (fault->vma_pagesize < 0) {
> -			ret = fault->vma_pagesize;
> -			goto out_unlock;
> +			if (fault->vma_pagesize < 0) {
> +				ret = fault->vma_pagesize;
> +				goto out_unlock;
> +			}
>  		}
>  	}
>  
> @@ -1951,7 +1951,9 @@ static int kvm_s2_fault_map(struct kvm_s2_fault *fault, void *memcache)
>  	if (fault->writable && !ret)
>  		mark_page_dirty_in_slot(kvm, fault->memslot, fault->gfn);
>  
> -	return ret != -EAGAIN ? ret : 0;
> +	if (ret != -EAGAIN)
> +		return ret;
> +	return 0;
>  }
>  
>  static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,



^ permalink raw reply

* Re: [PATCH 0/2] Add PWM support Amlogic S7 S7D S6
From: Xianwei Zhao @ 2026-03-31  7:10 UTC (permalink / raw)
  To: Martin Blumenstingl
  Cc: Uwe Kleine-König, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Heiner Kallweit, Neil Armstrong, Kevin Hilman,
	Jerome Brunet, linux-pwm, devicetree, linux-kernel,
	linux-arm-kernel, linux-amlogic, Junyi Zhao
In-Reply-To: <CAFBinCD1GPP82MEBDHg3BwCJg6JY5k2HksEt+kCB=YjnYTO7Tw@mail.gmail.com>

Hi Martin,
     I confirmed with Junyi Zhao that the current implementation counts 
from zero, so this submission is correct.
We agree this should be fixed and will address it in a follow-up patch.
Thanks for pointing it out.

On 2026/3/31 05:54, Martin Blumenstingl wrote:
> Hi Xianwei Zhao,
> 
> thanks for your contribution!
> 
> On Thu, Mar 26, 2026 at 7:35 AM Xianwei Zhao via B4 Relay
> <devnull+xianwei.zhao.amlogic.com@kernel.org>  wrote:
>> Add bindings and driver support Amlogic S7/S7D/S6 SoCs.
> There is an old report that got lost, stating that the current
> pwm-meson driver has an off-by-one error with the hi and lo fields:
> [0]
> Since you are working on bringing up a new platform: is this something
> you can verify in your lab?
> To be clear: I'm not expecting you to work on this ad-hoc or bring a
> patch into this series. However, it would be great if you could verify
> if the findings from [0] are correct and send an updated patch in
> future.
> 
> Thank you and best regards
> Martin


^ permalink raw reply

* RE: [PATCH v28 2/4] dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs and enable-dma properties
From: Ryan Chen @ 2026-03-31  7:09 UTC (permalink / raw)
  To: Jeremy Kerr, Krzysztof Kozlowski
  Cc: andriy.shevchenko@linux.intel.com, Andi Shyti, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Joel Stanley, Andrew Jeffery,
	Benjamin Herrenschmidt, Philipp Zabel, linux-i2c@vger.kernel.org,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
	openbmc@lists.ozlabs.org
In-Reply-To: <09cbc12bea5707f794e139ea1bfafac82c2d2c12.camel@codeconstruct.com.au>

> Subject: Re: [PATCH v28 2/4] dt-bindings: i2c: ast2600-i2c.yaml: Add global-regs
> and enable-dma properties
> 
> Hi Ryan,
> 
> > All AST2600 I2C controller instances have DMA hardware.
> > I will remove the aspeed,enable-dma property and instead expose sysfs
> > attribute in driver to allow users to enable dma/buffer/byte.
> 
> Sounds reasonable, but before you do so, how are you planning to manage the
> allocation of DMA channels across multiple i2c peripherals?
> 
The AST2600 I2C hardware has only one can use DMA at a time.
To avoid the complexity of managing DMA channel contention,
I plan to use buffer mode by default for all controllers, which still provides
better performance than byte mode without requiring DMA channel allocation.

^ 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