[openwrt/openwrt] rockchip: backport driver updates for rk3576

LEDE Commits lede-commits at lists.infradead.org
Thu Oct 2 14:25:45 PDT 2025


hauke pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/0a6ed6db2615f4f531663b34f688bdaf5b7178b3

commit 0a6ed6db2615f4f531663b34f688bdaf5b7178b3
Author: Tianling Shen <cnsztl at immortalwrt.org>
AuthorDate: Sat Sep 13 19:49:35 2025 +0800

    rockchip: backport driver updates for rk3576
    
    Backport clk/phy/rng/ufs/usb driver updates for rk3576.
    
    Signed-off-by: Tianling Shen <cnsztl at immortalwrt.org>
    Link: https://github.com/openwrt/openwrt/pull/20041
    Signed-off-by: Hauke Mehrtens <hauke at hauke-m.de>
---
 ...13-of-property-add-of_graph_get_next_port.patch | 199 ++++++++
 ...vide-devm_clk_bulk_get_all_enabled-helper.patch | 118 +++++
 target/linux/generic/config-6.12                   |  16 +
 target/linux/generic/config-6.6                    |  15 +
 ...rovide-old-opp-to-config_clks-on-_set_opp.patch |  26 ++
 target/linux/rockchip/armv8/config-6.12            |   8 +-
 ...rng-rockchip-add-support-for-RK3576-s-RNG.patch | 164 +++++++
 ...add-header-for-suspend-mode-SIP-interface.patch |  29 ++
 ...-clk-rockchip-rk3576-define-clk_otp_phy_g.patch |  27 ++
 ...-dt-bindings-clock-rk3576-add-SCMI-clocks.patch |  31 ++
 ...indings-clock-rk3576-add-IOC-gated-clocks.patch |  39 ++
 ....16-clk-rockchip-introduce-auxiliary-GRFs.patch | 299 ++++++++++++
 ...15-v6.16-clk-rockchip-introduce-GRF-gates.patch | 213 +++++++++
 ...p-add-GATE_GRFs-for-SAI-MCLKOUT-to-rk3576.patch |  90 ++++
 ...ockchip-rk3576-add-missing-slab-h-include.patch |  27 ++
 ...-rockchip-Add-smc-call-to-inform-firmware.patch |  55 +++
 ...kchip-Check-if-SMC-could-be-handled-by-TA.patch |  37 ++
 ...3-v6.15-pmdomain-rockchip-Fix-build-error.patch |  26 ++
 ...ers-rockchip-Rename-rk_tsadcv3_tshut_mode.patch |  40 ++
 ...rockchip-Support-RK3576-SoC-in-the-therma.patch |  61 +++
 ...rockchip-Support-reading-trim-values-from.patch | 431 +++++++++++++++++
 ...-explan-the-format-of-the-GPIO-version-ID.patch |  37 ++
 ...ip-change-the-GPIO-version-judgment-logic.patch |  46 ++
 ...13-gpio-rockchip-support-new-version-GPIO.patch |  34 ++
 ...-inno-usb2-Handle-failed-extcon-allocatio.patch |  31 ++
 ...nno-usb2-convert-clock-management-to-bulk.patch | 119 +++++
 ...nno-usb2-Add-usb2-phys-support-for-rk3576.patch | 143 ++++++
 ...ckchip-usbdp-add-rk3576-device-match-data.patch |  73 +++
 ...-rockchip-naneng-combo-add-rk3576-support.patch | 355 ++++++++++++++
 ...scsi-ufs-core-Export-ufshcd_dme_reset-and.patch |  66 +++
 ...scsi-ufs-rockchip-Initial-support-for-UFS.patch | 511 +++++++++++++++++++++
 ...ockchip-Fix-devm_clk_bulk_get_all_enabled.patch |  26 ++
 32 files changed, 3390 insertions(+), 2 deletions(-)

diff --git a/target/linux/generic/backport-6.12/201-v6.13-of-property-add-of_graph_get_next_port.patch b/target/linux/generic/backport-6.12/201-v6.13-of-property-add-of_graph_get_next_port.patch
new file mode 100644
index 0000000000..5178dc83ae
--- /dev/null
+++ b/target/linux/generic/backport-6.12/201-v6.13-of-property-add-of_graph_get_next_port.patch
@@ -0,0 +1,199 @@
+From 02ac5f9d6caec96071103f7c62b5117526e47b64 Mon Sep 17 00:00:00 2001
+From: Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
+Date: Thu, 24 Oct 2024 02:20:02 +0000
+Subject: [PATCH] of: property: add of_graph_get_next_port()
+
+We have endpoint base functions
+	- of_graph_get_next_endpoint()
+	- of_graph_get_endpoint_count()
+	- for_each_endpoint_of_node()
+
+Here, for_each_endpoint_of_node() loop finds each endpoints
+
+	ports {
+		port at 0 {
+(1)			endpoint {...};
+		};
+		port at 1 {
+(2)			endpoint {...};
+		};
+		...
+	};
+
+In above case, it finds endpoint as (1) -> (2) -> ...
+
+Basically, user/driver knows which port is used for what, but not in
+all cases. For example on flexible/generic driver case, how many ports
+are used is not fixed.
+
+For example Sound Generic Card driver which is very flexible/generic and
+used from many venders can't know how many ports are used, and used for
+what, because it depends on each vender SoC and/or its used board.
+
+And more, the port can have multi endpoints. For example Generic Sound
+Card case, it supports many type of connection between CPU / Codec, and
+some of them uses multi endpoint in one port. see below.
+
+	ports {
+(A)		port at 0 {
+(1)			endpoint at 0 {...};
+(2)			endpoint at 1 {...};
+		};
+(B)		port at 1 {
+(3)			endpoint {...};
+		};
+		...
+	};
+
+Generic Sound Card want to handle each connection via "port" base instead
+of "endpoint" base. But, it is very difficult to handle each "port" via
+existing for_each_endpoint_of_node(). Because getting each "port" via
+of_get_parent() from each "endpoint" doesn't work. For example in above
+case, both (1) (2) endpoint has same "port" (= A).
+
+Add "port" base functions.
+
+Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
+Link: https://lore.kernel.org/r/87ldyeb5t9.wl-kuninori.morimoto.gx@renesas.com
+Signed-off-by: Rob Herring (Arm) <robh at kernel.org>
+---
+ drivers/of/property.c    | 54 ++++++++++++++++++++++++++++++++++++++++
+ include/linux/of_graph.h | 28 +++++++++++++++++++++
+ 2 files changed, 82 insertions(+)
+
+--- a/drivers/of/property.c
++++ b/drivers/of/property.c
+@@ -631,6 +631,43 @@ struct device_node *of_graph_get_port_by
+ EXPORT_SYMBOL(of_graph_get_port_by_id);
+ 
+ /**
++ * of_graph_get_next_port() - get next port node.
++ * @parent: pointer to the parent device node, or parent ports node
++ * @prev: previous port node, or NULL to get first
++ *
++ * Parent device node can be used as @parent whether device node has ports node
++ * or not. It will work same as ports at 0 node.
++ *
++ * Return: A 'port' node pointer with refcount incremented. Refcount
++ * of the passed @prev node is decremented.
++ */
++struct device_node *of_graph_get_next_port(const struct device_node *parent,
++					   struct device_node *prev)
++{
++	if (!parent)
++		return NULL;
++
++	if (!prev) {
++		struct device_node *node __free(device_node) =
++			of_get_child_by_name(parent, "ports");
++
++		if (node)
++			parent = node;
++
++		return of_get_child_by_name(parent, "port");
++	}
++
++	do {
++		prev = of_get_next_child(parent, prev);
++		if (!prev)
++			break;
++	} while (!of_node_name_eq(prev, "port"));
++
++	return prev;
++}
++EXPORT_SYMBOL(of_graph_get_next_port);
++
++/**
+  * of_graph_get_next_endpoint() - get next endpoint node
+  * @parent: pointer to the parent device node
+  * @prev: previous endpoint node, or NULL to get first
+@@ -824,6 +861,23 @@ unsigned int of_graph_get_endpoint_count
+ EXPORT_SYMBOL(of_graph_get_endpoint_count);
+ 
+ /**
++ * of_graph_get_port_count() - get the number of port in a device or ports node
++ * @np: pointer to the device or ports node
++ *
++ * Return: count of port of this device or ports node
++ */
++unsigned int of_graph_get_port_count(struct device_node *np)
++{
++	unsigned int num = 0;
++
++	for_each_of_graph_port(np, port)
++		num++;
++
++	return num;
++}
++EXPORT_SYMBOL(of_graph_get_port_count);
++
++/**
+  * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
+  * @node: pointer to parent device_node containing graph port/endpoint
+  * @port: identifier (value of reg property) of the parent port node
+--- a/include/linux/of_graph.h
++++ b/include/linux/of_graph.h
+@@ -11,6 +11,7 @@
+ #ifndef __LINUX_OF_GRAPH_H
+ #define __LINUX_OF_GRAPH_H
+ 
++#include <linux/cleanup.h>
+ #include <linux/types.h>
+ #include <linux/errno.h>
+ 
+@@ -37,14 +38,29 @@ struct of_endpoint {
+ 	for (child = of_graph_get_next_endpoint(parent, NULL); child != NULL; \
+ 	     child = of_graph_get_next_endpoint(parent, child))
+ 
++/**
++ * for_each_of_graph_port - iterate over every port in a device or ports node
++ * @parent: parent device or ports node containing port
++ * @child: loop variable pointing to the current port node
++ *
++ * When breaking out of the loop, and continue to use the @child, you need to
++ * use return_ptr(@child) or no_free_ptr(@child) not to call __free() for it.
++ */
++#define for_each_of_graph_port(parent, child)			\
++	for (struct device_node *child __free(device_node) = of_graph_get_next_port(parent, NULL);\
++	     child != NULL; child = of_graph_get_next_port(parent, child))
++
+ #ifdef CONFIG_OF
+ bool of_graph_is_present(const struct device_node *node);
+ int of_graph_parse_endpoint(const struct device_node *node,
+ 				struct of_endpoint *endpoint);
+ unsigned int of_graph_get_endpoint_count(const struct device_node *np);
++unsigned int of_graph_get_port_count(struct device_node *np);
+ struct device_node *of_graph_get_port_by_id(struct device_node *node, u32 id);
+ struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
+ 					struct device_node *previous);
++struct device_node *of_graph_get_next_port(const struct device_node *parent,
++					   struct device_node *port);
+ struct device_node *of_graph_get_endpoint_by_regs(
+ 		const struct device_node *parent, int port_reg, int reg);
+ struct device_node *of_graph_get_remote_endpoint(
+@@ -73,6 +89,11 @@ static inline unsigned int of_graph_get_
+ 	return 0;
+ }
+ 
++static inline unsigned int of_graph_get_port_count(struct device_node *np)
++{
++	return 0;
++}
++
+ static inline struct device_node *of_graph_get_port_by_id(
+ 					struct device_node *node, u32 id)
+ {
+@@ -83,6 +104,13 @@ static inline struct device_node *of_gra
+ 					const struct device_node *parent,
+ 					struct device_node *previous)
+ {
++	return NULL;
++}
++
++static inline struct device_node *of_graph_get_next_port(
++					const struct device_node *parent,
++					struct device_node *previous)
++{
+ 	return NULL;
+ }
+ 
diff --git a/target/linux/generic/backport-6.12/202-v6.13-clk-Provide-devm_clk_bulk_get_all_enabled-helper.patch b/target/linux/generic/backport-6.12/202-v6.13-clk-Provide-devm_clk_bulk_get_all_enabled-helper.patch
new file mode 100644
index 0000000000..cf52b592c4
--- /dev/null
+++ b/target/linux/generic/backport-6.12/202-v6.13-clk-Provide-devm_clk_bulk_get_all_enabled-helper.patch
@@ -0,0 +1,118 @@
+From 51e32e897539663957f7a0950f66b48f8896efee Mon Sep 17 00:00:00 2001
+From: Cristian Ciocaltea <cristian.ciocaltea at collabora.com>
+Date: Sat, 19 Oct 2024 14:16:00 +0300
+Subject: [PATCH] clk: Provide devm_clk_bulk_get_all_enabled() helper
+
+Commit 265b07df758a ("clk: Provide managed helper to get and enable bulk
+clocks") added devm_clk_bulk_get_all_enable() function, but missed to
+return the number of clocks stored in the clk_bulk_data table referenced
+by the clks argument.  Without knowing the number, it's not possible to
+iterate these clocks when needed, hence the argument is useless and
+could have been simply removed.
+
+Introduce devm_clk_bulk_get_all_enabled() variant, which is consistent
+with devm_clk_bulk_get_all() in terms of the returned value:
+
+ > 0 if one or more clocks have been stored
+ = 0 if there are no clocks
+ < 0 if an error occurred
+
+Moreover, the naming is consistent with devm_clk_get_enabled(), i.e. use
+the past form of 'enable'.
+
+To reduce code duplication and improve patch readability, make
+devm_clk_bulk_get_all_enable() use the new helper, as suggested by
+Stephen Boyd.
+
+Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno at collabora.com>
+Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam at linaro.org>
+Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea at collabora.com>
+Link: https://lore.kernel.org/r/20241019-clk_bulk_ena_fix-v4-1-57f108f64e70@collabora.com
+Signed-off-by: Stephen Boyd <sboyd at kernel.org>
+---
+ drivers/clk/clk-devres.c |  9 +++++----
+ include/linux/clk.h      | 21 ++++++++++++++++-----
+ 2 files changed, 21 insertions(+), 9 deletions(-)
+
+--- a/drivers/clk/clk-devres.c
++++ b/drivers/clk/clk-devres.c
+@@ -218,8 +218,8 @@ static void devm_clk_bulk_release_all_en
+ 	clk_bulk_put_all(devres->num_clks, devres->clks);
+ }
+ 
+-int __must_check devm_clk_bulk_get_all_enable(struct device *dev,
+-					      struct clk_bulk_data **clks)
++int __must_check devm_clk_bulk_get_all_enabled(struct device *dev,
++					       struct clk_bulk_data **clks)
+ {
+ 	struct clk_bulk_devres *devres;
+ 	int ret;
+@@ -244,11 +244,12 @@ int __must_check devm_clk_bulk_get_all_e
+ 	} else {
+ 		clk_bulk_put_all(devres->num_clks, devres->clks);
+ 		devres_free(devres);
++		return ret;
+ 	}
+ 
+-	return ret;
++	return devres->num_clks;
+ }
+-EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all_enable);
++EXPORT_SYMBOL_GPL(devm_clk_bulk_get_all_enabled);
+ 
+ static int devm_clk_match(struct device *dev, void *res, void *data)
+ {
+--- a/include/linux/clk.h
++++ b/include/linux/clk.h
+@@ -496,11 +496,13 @@ int __must_check devm_clk_bulk_get_all(s
+ 				       struct clk_bulk_data **clks);
+ 
+ /**
+- * devm_clk_bulk_get_all_enable - Get and enable all clocks of the consumer (managed)
++ * devm_clk_bulk_get_all_enabled - Get and enable all clocks of the consumer (managed)
+  * @dev: device for clock "consumer"
+  * @clks: pointer to the clk_bulk_data table of consumer
+  *
+- * Returns success (0) or negative errno.
++ * Returns a positive value for the number of clocks obtained while the
++ * clock references are stored in the clk_bulk_data table in @clks field.
++ * Returns 0 if there're none and a negative value if something failed.
+  *
+  * This helper function allows drivers to get all clocks of the
+  * consumer and enables them in one operation with management.
+@@ -508,8 +510,8 @@ int __must_check devm_clk_bulk_get_all(s
+  * is unbound.
+  */
+ 
+-int __must_check devm_clk_bulk_get_all_enable(struct device *dev,
+-					      struct clk_bulk_data **clks);
++int __must_check devm_clk_bulk_get_all_enabled(struct device *dev,
++					       struct clk_bulk_data **clks);
+ 
+ /**
+  * devm_clk_get - lookup and obtain a managed reference to a clock producer.
+@@ -1034,7 +1036,7 @@ static inline int __must_check devm_clk_
+ 	return 0;
+ }
+ 
+-static inline int __must_check devm_clk_bulk_get_all_enable(struct device *dev,
++static inline int __must_check devm_clk_bulk_get_all_enabled(struct device *dev,
+ 						struct clk_bulk_data **clks)
+ {
+ 	return 0;
+@@ -1136,6 +1138,15 @@ static inline void clk_restore_context(v
+ 
+ #endif
+ 
++/* Deprecated. Use devm_clk_bulk_get_all_enabled() */
++static inline int __must_check
++devm_clk_bulk_get_all_enable(struct device *dev, struct clk_bulk_data **clks)
++{
++	int ret = devm_clk_bulk_get_all_enabled(dev, clks);
++
++	return ret > 0 ? 0 : ret;
++}
++
+ /* clk_prepare_enable helps cases using clk_enable in non-atomic context. */
+ static inline int clk_prepare_enable(struct clk *clk)
+ {
diff --git a/target/linux/generic/config-6.12 b/target/linux/generic/config-6.12
index c1947b2cb0..b60d9c367e 100644
--- a/target/linux/generic/config-6.12
+++ b/target/linux/generic/config-6.12
@@ -5443,6 +5443,22 @@ CONFIG_SCSI_PROC_FS=y
 # CONFIG_SCSI_STEX is not set
 # CONFIG_SCSI_SYM53C8XX_2 is not set
 # CONFIG_SCSI_UFSHCD is not set
+# CONFIG_SCSI_UFSHCD_PCI is not set
+# CONFIG_SCSI_UFSHCD_PLATFORM is not set
+# CONFIG_SCSI_UFS_BSG is not set
+# CONFIG_SCSI_UFS_CDNS_PLATFORM is not set
+# CONFIG_SCSI_UFS_CRYPTO is not set
+# CONFIG_SCSI_UFS_DWC_TC_PLATFORM is not set
+# CONFIG_SCSI_UFS_EXYNOS is not set
+# CONFIG_SCSI_UFS_FAULT_INJECTION is not set
+# CONFIG_SCSI_UFS_HISI is not set
+# CONFIG_SCSI_UFS_HWMON is not set
+# CONFIG_SCSI_UFS_MEDIATEK is not set
+# CONFIG_SCSI_UFS_QCOM is not set
+# CONFIG_SCSI_UFS_RENESAS is not set
+# CONFIG_SCSI_UFS_ROCKCHIP is not set
+# CONFIG_SCSI_UFS_SPRD is not set
+# CONFIG_SCSI_UFS_TI_J721E is not set
 # CONFIG_SCSI_VIRTIO is not set
 # CONFIG_SCSI_WD719X is not set
 # CONFIG_SC_CAMCC_7180 is not set
diff --git a/target/linux/generic/config-6.6 b/target/linux/generic/config-6.6
index be73510dd0..f00af6d9c8 100644
--- a/target/linux/generic/config-6.6
+++ b/target/linux/generic/config-6.6
@@ -5300,6 +5300,21 @@ CONFIG_SCSI_PROC_FS=y
 # CONFIG_SCSI_STEX is not set
 # CONFIG_SCSI_SYM53C8XX_2 is not set
 # CONFIG_SCSI_UFSHCD is not set
+# CONFIG_SCSI_UFSHCD_PCI is not set
+# CONFIG_SCSI_UFSHCD_PLATFORM is not set
+# CONFIG_SCSI_UFS_BSG is not set
+# CONFIG_SCSI_UFS_CDNS_PLATFORM is not set
+# CONFIG_SCSI_UFS_CRYPTO is not set
+# CONFIG_SCSI_UFS_DWC_TC_PLATFORM is not set
+# CONFIG_SCSI_UFS_EXYNOS is not set
+# CONFIG_SCSI_UFS_FAULT_INJECTION is not set
+# CONFIG_SCSI_UFS_HISI is not set
+# CONFIG_SCSI_UFS_HWMON is not set
+# CONFIG_SCSI_UFS_MEDIATEK is not set
+# CONFIG_SCSI_UFS_QCOM is not set
+# CONFIG_SCSI_UFS_RENESAS is not set
+# CONFIG_SCSI_UFS_SPRD is not set
+# CONFIG_SCSI_UFS_TI_J721E is not set
 # CONFIG_SCSI_VIRTIO is not set
 # CONFIG_SCSI_WD719X is not set
 # CONFIG_SC_CAMCC_7180 is not set
diff --git a/target/linux/generic/pending-6.12/802-OPP-Provide-old-opp-to-config_clks-on-_set_opp.patch b/target/linux/generic/pending-6.12/802-OPP-Provide-old-opp-to-config_clks-on-_set_opp.patch
index aedeeb4e9f..7a25825537 100644
--- a/target/linux/generic/pending-6.12/802-OPP-Provide-old-opp-to-config_clks-on-_set_opp.patch
+++ b/target/linux/generic/pending-6.12/802-OPP-Provide-old-opp-to-config_clks-on-_set_opp.patch
@@ -72,6 +72,19 @@ Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
  						     &target_freq, false);
  			goto put_opp_table;
  		}
+--- a/drivers/ufs/core/ufshcd.c
++++ b/drivers/ufs/core/ufshcd.c
+@@ -1119,8 +1119,8 @@ out:
+ }
+ 
+ int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table,
+-			   struct dev_pm_opp *opp, void *data,
+-			   bool scaling_down)
++			   struct dev_pm_opp *old_opp, struct dev_pm_opp *opp,
++			   void *data, bool scaling_down)
+ {
+ 	struct ufs_hba *hba = dev_get_drvdata(dev);
+ 	struct list_head *head = &hba->clk_list_head;
 --- a/include/linux/pm_opp.h
 +++ b/include/linux/pm_opp.h
 @@ -50,7 +50,8 @@ typedef int (*config_regulators_t)(struc
@@ -106,3 +119,16 @@ Signed-off-by: Christian Marangi <ansuelsmth at gmail.com>
  {
  	return -EOPNOTSUPP;
  }
+--- a/include/ufs/ufshcd.h
++++ b/include/ufs/ufshcd.h
+@@ -1326,8 +1326,8 @@ void ufshcd_mcq_enable(struct ufs_hba *h
+ void ufshcd_mcq_config_esi(struct ufs_hba *hba, struct msi_msg *msg);
+ 
+ int ufshcd_opp_config_clks(struct device *dev, struct opp_table *opp_table,
+-			   struct dev_pm_opp *opp, void *data,
+-			   bool scaling_down);
++			   struct dev_pm_opp *old_opp, struct dev_pm_opp *opp,
++			   void *data, bool scaling_down);
+ /**
+  * ufshcd_set_variant - set variant specific data to the hba
+  * @hba: per adapter instance
diff --git a/target/linux/rockchip/armv8/config-6.12 b/target/linux/rockchip/armv8/config-6.12
index 326744093e..1200605efe 100644
--- a/target/linux/rockchip/armv8/config-6.12
+++ b/target/linux/rockchip/armv8/config-6.12
@@ -603,6 +603,10 @@ CONFIG_SCSI_COMMON=y
 CONFIG_SCSI_SAS_ATTRS=y
 CONFIG_SCSI_SAS_HOST_SMP=y
 CONFIG_SCSI_SAS_LIBSAS=y
+CONFIG_SCSI_UFSHCD=y
+CONFIG_SCSI_UFSHCD_PLATFORM=y
+CONFIG_SCSI_UFS_HWMON=y
+CONFIG_SCSI_UFS_ROCKCHIP=y
 # CONFIG_SECURITY_DMESG_RESTRICT is not set
 CONFIG_SENSORS_ARM_SCMI=y
 CONFIG_SENSORS_ARM_SCPI=y
@@ -612,10 +616,10 @@ CONFIG_SERIAL_8250_DWLIB=y
 CONFIG_SERIAL_8250_EXAR=y
 CONFIG_SERIAL_8250_EXTENDED=y
 CONFIG_SERIAL_8250_FSL=y
-CONFIG_SERIAL_8250_NR_UARTS=10
+CONFIG_SERIAL_8250_NR_UARTS=12
 CONFIG_SERIAL_8250_PCI=y
 CONFIG_SERIAL_8250_PCILIB=y
-CONFIG_SERIAL_8250_RUNTIME_UARTS=10
+CONFIG_SERIAL_8250_RUNTIME_UARTS=12
 CONFIG_SERIAL_8250_SHARE_IRQ=y
 CONFIG_SERIAL_AMBA_PL011=y
 CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
diff --git a/target/linux/rockchip/patches-6.12/031-07-v6.16-hwrng-rockchip-add-support-for-RK3576-s-RNG.patch b/target/linux/rockchip/patches-6.12/031-07-v6.16-hwrng-rockchip-add-support-for-RK3576-s-RNG.patch
new file mode 100644
index 0000000000..d85f52db64
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/031-07-v6.16-hwrng-rockchip-add-support-for-RK3576-s-RNG.patch
@@ -0,0 +1,164 @@
+From 8f66ccbd8f67ab41b29f54f383f8a8516be7696c Mon Sep 17 00:00:00 2001
+From: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Date: Wed, 30 Apr 2025 18:16:35 +0200
+Subject: [PATCH] hwrng: rockchip - add support for RK3576's RNG
+
+The Rockchip RK3576 SoC uses a new hardware random number generator IP.
+It's also used on the Rockchip RK3562 and the Rockchip RK3528.
+
+It has several modes of operation and self-checking features that are
+not implemented here. For starters, it has a DRNG output, which is an
+AES-CTR pseudo-random number generator that can be reseeded from the
+true entropy regularly.
+
+However, it also allows for access of the true entropy generator
+directly. This entropy is generated from an oscillator.
+
+There are several configuration registers which we don't touch here. The
+oscillator can be switched between a "CRO" and "STR" oscillator, and the
+length of the oscillator can be configured.
+
+The hardware also supports some automatic continuous entropy quality
+checking, which is also not implemented in this driver for the time
+being.
+
+The output as-is has been deemed sufficient to be useful:
+
+  rngtest: starting FIPS tests...
+  rngtest: bits received from input: 20000032
+  rngtest: FIPS 140-2 successes: 997
+  rngtest: FIPS 140-2 failures: 3
+  rngtest: FIPS 140-2(2001-10-10) Monobit: 0
+  rngtest: FIPS 140-2(2001-10-10) Poker: 1
+  rngtest: FIPS 140-2(2001-10-10) Runs: 1
+  rngtest: FIPS 140-2(2001-10-10) Long run: 1
+  rngtest: FIPS 140-2(2001-10-10) Continuous run: 0
+  rngtest: input channel speed: (min=17.050; avg=1897.272;
+           max=19531250.000)Kibits/s
+  rngtest: FIPS tests speed: (min=44.773; avg=71.179; max=96.820)Mibits/s
+  rngtest: Program run time: 11760715 microseconds
+  rngtest: bits received from input: 40000032
+  rngtest: FIPS 140-2 successes: 1997
+  rngtest: FIPS 140-2 failures: 3
+  rngtest: FIPS 140-2(2001-10-10) Monobit: 0
+  rngtest: FIPS 140-2(2001-10-10) Poker: 1
+  rngtest: FIPS 140-2(2001-10-10) Runs: 1
+  rngtest: FIPS 140-2(2001-10-10) Long run: 1
+  rngtest: FIPS 140-2(2001-10-10) Continuous run: 0
+  rngtest: input channel speed: (min=17.050; avg=1798.618;
+           max=19531250.000)Kibits/s
+  rngtest: FIPS tests speed: (min=44.773; avg=64.561; max=96.820)Mibits/s
+  rngtest: Program run time: 23507723 microseconds
+
+Stretching the entropy can then be left up to Linux's actual entropy
+pool.
+
+Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Signed-off-by: Herbert Xu <herbert at gondor.apana.org.au>
+---
+ drivers/char/hw_random/rockchip-rng.c | 73 +++++++++++++++++++++++++++
+ 1 file changed, 73 insertions(+)
+
+--- a/drivers/char/hw_random/rockchip-rng.c
++++ b/drivers/char/hw_random/rockchip-rng.c
+@@ -93,6 +93,30 @@
+ #define TRNG_v1_VERSION_CODE			0x46bc
+ /* end of TRNG_V1 register definitions */
+ 
++/*
++ * RKRNG register definitions
++ * The RKRNG IP is a stand-alone TRNG implementation (not part of a crypto IP)
++ * and can be found in the Rockchip RK3576, Rockchip RK3562 and Rockchip RK3528
++ * SoCs. It can either output true randomness (TRNG) or "deterministic"
++ * randomness derived from hashing the true entropy (DRNG). This driver
++ * implementation uses just the true entropy, and leaves stretching the entropy
++ * up to Linux.
++ */
++#define RKRNG_CFG				0x0000
++#define RKRNG_CTRL				0x0010
++#define RKRNG_CTRL_REQ_TRNG			BIT(4)
++#define RKRNG_STATE				0x0014
++#define RKRNG_STATE_TRNG_RDY			BIT(4)
++#define RKRNG_TRNG_DATA0			0x0050
++#define RKRNG_TRNG_DATA1			0x0054
++#define RKRNG_TRNG_DATA2			0x0058
++#define RKRNG_TRNG_DATA3			0x005C
++#define RKRNG_TRNG_DATA4			0x0060
++#define RKRNG_TRNG_DATA5			0x0064
++#define RKRNG_TRNG_DATA6			0x0068
++#define RKRNG_TRNG_DATA7			0x006C
++#define RKRNG_READ_LEN				32
++
+ /* Before removing this assert, give rk3588_rng_read an upper bound of 32 */
+ static_assert(RK_RNG_MAX_BYTE <= (TRNG_V1_RAND7 + 4 - TRNG_V1_RAND0),
+ 	      "You raised RK_RNG_MAX_BYTE and broke rk3588-rng, congrats.");
+@@ -205,6 +229,46 @@ out:
+ 	return (ret < 0) ? ret : to_read;
+ }
+ 
++static int rk3576_rng_init(struct hwrng *rng)
++{
++	struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
++
++	return rk_rng_enable_clks(rk_rng);
++}
++
++static int rk3576_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
++{
++	struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
++	size_t to_read = min_t(size_t, max, RKRNG_READ_LEN);
++	int ret = 0;
++	u32 val;
++
++	ret = pm_runtime_resume_and_get(rk_rng->dev);
++	if (ret < 0)
++		return ret;
++
++	rk_rng_writel(rk_rng, RKRNG_CTRL_REQ_TRNG | (RKRNG_CTRL_REQ_TRNG << 16),
++		      RKRNG_CTRL);
++
++	if (readl_poll_timeout(rk_rng->base + RKRNG_STATE, val,
++			       (val & RKRNG_STATE_TRNG_RDY), RK_RNG_POLL_PERIOD_US,
++			       RK_RNG_POLL_TIMEOUT_US)) {
++		dev_err(rk_rng->dev, "timed out waiting for data\n");
++		ret = -ETIMEDOUT;
++		goto out;
++	}
++
++	rk_rng_writel(rk_rng, RKRNG_STATE_TRNG_RDY, RKRNG_STATE);
++
++	memcpy_fromio(buf, rk_rng->base + RKRNG_TRNG_DATA0, to_read);
++
++out:
++	pm_runtime_mark_last_busy(rk_rng->dev);
++	pm_runtime_put_sync_autosuspend(rk_rng->dev);
++
++	return (ret < 0) ? ret : to_read;
++}
++
+ static int rk3588_rng_init(struct hwrng *rng)
+ {
+ 	struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng);
+@@ -305,6 +369,14 @@ static const struct rk_rng_soc_data rk35
+ 	.reset_optional = false,
+ };
+ 
++static const struct rk_rng_soc_data rk3576_soc_data = {
++	.rk_rng_init = rk3576_rng_init,
++	.rk_rng_read = rk3576_rng_read,
++	.rk_rng_cleanup = rk3588_rng_cleanup,
++	.quality = 999,		/* as determined by actual testing */
++	.reset_optional = true,
++};
++
+ static const struct rk_rng_soc_data rk3588_soc_data = {
+ 	.rk_rng_init = rk3588_rng_init,
+ 	.rk_rng_read = rk3588_rng_read,
+@@ -397,6 +469,7 @@ static const struct dev_pm_ops rk_rng_pm
+ 
+ static const struct of_device_id rk_rng_dt_match[] = {
+ 	{ .compatible = "rockchip,rk3568-rng", .data = (void *)&rk3568_soc_data },
++	{ .compatible = "rockchip,rk3576-rng", .data = (void *)&rk3576_soc_data },
+ 	{ .compatible = "rockchip,rk3588-rng", .data = (void *)&rk3588_soc_data },
+ 	{ /* sentinel */ },
+ };
diff --git a/target/linux/rockchip/patches-6.12/032-10-v6.15-soc-rockchip-add-header-for-suspend-mode-SIP-interface.patch b/target/linux/rockchip/patches-6.12/032-10-v6.15-soc-rockchip-add-header-for-suspend-mode-SIP-interface.patch
new file mode 100644
index 0000000000..8ff32dfc4d
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/032-10-v6.15-soc-rockchip-add-header-for-suspend-mode-SIP-interface.patch
@@ -0,0 +1,29 @@
+From 184055a9ae2b7b19f6fd6e9c0b7e1edce6930b2f Mon Sep 17 00:00:00 2001
+From: Shawn Lin <shawn.lin at rock-chips.com>
+Date: Wed, 5 Feb 2025 14:15:51 +0800
+Subject: [PATCH] soc: rockchip: add header for suspend mode SIP interface
+
+Add ROCKCHIP_SIP_SUSPEND_MODE to pass down parameters to Trusted Firmware
+in order to decide suspend mode. Currently only add ROCKCHIP_SLEEP_PD_CONFIG
+which teaches firmware to power down controllers or not.
+
+Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
+Acked-by: Heiko Stuebner <heiko at sntech.de>
+Link: https://lore.kernel.org/r/1738736156-119203-3-git-send-email-shawn.lin@rock-chips.com
+Signed-off-by: Ulf Hansson <ulf.hansson at linaro.org>
+---
+ include/soc/rockchip/rockchip_sip.h | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/include/soc/rockchip/rockchip_sip.h
++++ b/include/soc/rockchip/rockchip_sip.h
+@@ -6,6 +6,9 @@
+ #ifndef __SOC_ROCKCHIP_SIP_H
+ #define __SOC_ROCKCHIP_SIP_H
+ 
++#define ROCKCHIP_SIP_SUSPEND_MODE		0x82000003
++#define ROCKCHIP_SLEEP_PD_CONFIG		0xff
++
+ #define ROCKCHIP_SIP_DRAM_FREQ			0x82000008
+ #define ROCKCHIP_SIP_CONFIG_DRAM_INIT		0x00
+ #define ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE	0x01
diff --git a/target/linux/rockchip/patches-6.12/032-11-v6.15-clk-rockchip-rk3576-define-clk_otp_phy_g.patch b/target/linux/rockchip/patches-6.12/032-11-v6.15-clk-rockchip-rk3576-define-clk_otp_phy_g.patch
new file mode 100644
index 0000000000..5a343baf0b
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/032-11-v6.15-clk-rockchip-rk3576-define-clk_otp_phy_g.patch
@@ -0,0 +1,27 @@
+From d934a93bbcccd551c142206b8129903d18126261 Mon Sep 17 00:00:00 2001
+From: Heiko Stuebner <heiko at sntech.de>
+Date: Mon, 10 Feb 2025 23:45:05 +0100
+Subject: [PATCH] clk: rockchip: rk3576: define clk_otp_phy_g
+
+The phy clock of the OTP block is also present, but was not defined
+so far. Though its clk-id already existed, so just define its location.
+
+Tested-by: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Signed-off-by: Heiko Stuebner <heiko at sntech.de>
+Link: https://lore.kernel.org/r/20250210224510.1194963-2-heiko@sntech.de
+Signed-off-by: Heiko Stuebner <heiko at sntech.de>
+---
+ drivers/clk/rockchip/clk-rk3576.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/clk/rockchip/clk-rk3576.c
++++ b/drivers/clk/rockchip/clk-rk3576.c
+@@ -541,6 +541,8 @@ static struct rockchip_clk_branch rk3576
+ 			RK3576_CLKGATE_CON(5), 14, GFLAGS),
+ 	GATE(CLK_OTPC_AUTO_RD_G, "clk_otpc_auto_rd_g", "xin24m", 0,
+ 			RK3576_CLKGATE_CON(5), 15, GFLAGS),
++	GATE(CLK_OTP_PHY_G, "clk_otp_phy_g", "xin24m", 0,
++			RK3576_CLKGATE_CON(6), 0, GFLAGS),
+ 	COMPOSITE(CLK_MIPI_CAMERAOUT_M0, "clk_mipi_cameraout_m0", mux_24m_spll_gpll_cpll_p, 0,
+ 			RK3576_CLKSEL_CON(38), 8, 2, MFLAGS, 0, 8, DFLAGS,
+ 			RK3576_CLKGATE_CON(6), 3, GFLAGS),
diff --git a/target/linux/rockchip/patches-6.12/032-12-v6.15-dt-bindings-clock-rk3576-add-SCMI-clocks.patch b/target/linux/rockchip/patches-6.12/032-12-v6.15-dt-bindings-clock-rk3576-add-SCMI-clocks.patch
new file mode 100644
index 0000000000..524aa1c611
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/032-12-v6.15-dt-bindings-clock-rk3576-add-SCMI-clocks.patch
@@ -0,0 +1,31 @@
+From 28699ca6d9018201674787e7b6bdce68d9cf7256 Mon Sep 17 00:00:00 2001
+From: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Date: Mon, 10 Mar 2025 10:59:56 +0100
+Subject: [PATCH] dt-bindings: clock: rk3576: add SCMI clocks
+
+Mainline Linux uses different clock IDs from both downstream and
+mainline TF-A, which both got them from downstream Linux. If we want to
+control clocks through SCMI, we'll need to know about these IDs.
+
+Add the relevant ones prefixed with SCMI_ to the header.
+
+Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Acked-by: "Rob Herring (Arm)" <robh at kernel.org>
+Link: https://lore.kernel.org/r/20250310-rk3576-scmi-clocks-v1-1-e165deb034e8@collabora.com
+Signed-off-by: Heiko Stuebner <heiko at sntech.de>
+---
+ include/dt-bindings/clock/rockchip,rk3576-cru.h | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/include/dt-bindings/clock/rockchip,rk3576-cru.h
++++ b/include/dt-bindings/clock/rockchip,rk3576-cru.h
+@@ -589,4 +589,9 @@
+ #define PCLK_EDP_S			569
+ #define ACLK_KLAD			570
+ 
++/* SCMI clocks, use these when changing clocks through SCMI */
++#define SCMI_ARMCLK_L			10
++#define SCMI_ARMCLK_B			11
++#define SCMI_CLK_GPU			456
++
+ #endif
diff --git a/target/linux/rockchip/patches-6.12/032-13-v6.16-dt-bindings-clock-rk3576-add-IOC-gated-clocks.patch b/target/linux/rockchip/patches-6.12/032-13-v6.16-dt-bindings-clock-rk3576-add-IOC-gated-clocks.patch
new file mode 100644
index 0000000000..d7386ce725
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/032-13-v6.16-dt-bindings-clock-rk3576-add-IOC-gated-clocks.patch
@@ -0,0 +1,39 @@
+From 4210f21c004a18aad11c55bdaf552e649a4fd286 Mon Sep 17 00:00:00 2001
+From: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Date: Fri, 2 May 2025 13:03:07 +0200
+Subject: [PATCH] dt-bindings: clock: rk3576: add IOC gated clocks
+
+Certain clocks on the RK3576 are additionally essentially "gated" behind
+some bit toggles in the IOC GRF range. Downstream ungates these by
+adding a separate clock driver that maps over the GRF range and leaks
+their implementation of this into the DT.
+
+Instead, define some new clock IDs for these, so that consumers of these
+types of clocks can properly articulate which clock they're using, so
+that we can then add them to the clock driver for SoCs that need them.
+
+Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski at linaro.org>
+Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Link: https://lore.kernel.org/r/20250502-rk3576-sai-v3-1-376cef19dd7c@collabora.com
+Signed-off-by: Heiko Stuebner <heiko at sntech.de>
+---
+ include/dt-bindings/clock/rockchip,rk3576-cru.h | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/include/dt-bindings/clock/rockchip,rk3576-cru.h
++++ b/include/dt-bindings/clock/rockchip,rk3576-cru.h
+@@ -594,4 +594,14 @@
+ #define SCMI_ARMCLK_B			11
+ #define SCMI_CLK_GPU			456
+ 
++/* IOC-controlled output clocks */
++#define CLK_SAI0_MCLKOUT_TO_IO		571
++#define CLK_SAI1_MCLKOUT_TO_IO		572
++#define CLK_SAI2_MCLKOUT_TO_IO		573
++#define CLK_SAI3_MCLKOUT_TO_IO		574
++#define CLK_SAI4_MCLKOUT_TO_IO		575
++#define CLK_SAI4_MCLKOUT_TO_IO		575
++#define CLK_FSPI0_TO_IO			576
++#define CLK_FSPI1_TO_IO			577
++
+ #endif
diff --git a/target/linux/rockchip/patches-6.12/032-14-v6.16-clk-rockchip-introduce-auxiliary-GRFs.patch b/target/linux/rockchip/patches-6.12/032-14-v6.16-clk-rockchip-introduce-auxiliary-GRFs.patch
new file mode 100644
index 0000000000..b05c18a410
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/032-14-v6.16-clk-rockchip-introduce-auxiliary-GRFs.patch
@@ -0,0 +1,299 @@
+From 70a114daf2077472e58b3cac23ba8998e35352f4 Mon Sep 17 00:00:00 2001
+From: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Date: Fri, 2 May 2025 13:03:08 +0200
+Subject: [PATCH] clk: rockchip: introduce auxiliary GRFs
+
+The MUXGRF clock branch type depends on having access to some sort of
+GRF as a regmap to be registered. So far, we could easily get away with
+only ever having one GRF stowed away in the context.
+
+However, newer Rockchip SoCs, such as the RK3576, have several GRFs
+which are relevant for clock purposes. It already depends on the pmu0
+GRF for MUXGRF reasons, but could get away with not refactoring this
+because it didn't need the sysgrf at all, so could overwrite the pointer
+in the clock provider to the pmu0 grf regmap handle.
+
+In preparation for needing to finally access more than one GRF per SoC,
+let's untangle this. Introduce an auxiliary GRF hashmap, and a GRF type
+enum. The hashmap is keyed by the enum, and clock branches now have a
+struct member to store the value of that enum, which defaults to the
+system GRF.
+
+The SoC-specific _clk_init function can then insert pointers to GRF
+regmaps into the hashmap based on the grf type.
+
+During clock branch registration, we then pick the right GRF for each
+branch from the hashmap if something other than the sys GRF is
+requested.
+
+The reason for doing it with this grf type indirection in the clock
+branches is so that we don't need to define the MUXGRF branches in a
+separate step, just to have a direct pointer to a regmap available
+already.
+
+Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Link: https://lore.kernel.org/r/20250502-rk3576-sai-v3-2-376cef19dd7c@collabora.com
+Signed-off-by: Heiko Stuebner <heiko at sntech.de>
+---
+ drivers/clk/rockchip/clk-rk3288.c |  2 +-
+ drivers/clk/rockchip/clk-rk3328.c |  6 +++---
+ drivers/clk/rockchip/clk-rk3568.c |  2 +-
+ drivers/clk/rockchip/clk-rk3576.c | 32 +++++++++++++++++++++----------
+ drivers/clk/rockchip/clk-rv1126.c |  2 +-
+ drivers/clk/rockchip/clk.c        | 17 +++++++++++++++-
+ drivers/clk/rockchip/clk.h        | 29 +++++++++++++++++++++++++++-
+ 7 files changed, 72 insertions(+), 18 deletions(-)
+
+--- a/drivers/clk/rockchip/clk-rk3288.c
++++ b/drivers/clk/rockchip/clk-rk3288.c
+@@ -418,7 +418,7 @@ static struct rockchip_clk_branch rk3288
+ 			RK3288_CLKSEL_CON(32), 14, 2, MFLAGS, 8, 5, DFLAGS,
+ 			RK3288_CLKGATE_CON(3), 11, GFLAGS),
+ 	MUXGRF(0, "aclk_vcodec_pre", mux_aclk_vcodec_pre_p, CLK_SET_RATE_PARENT,
+-			RK3288_GRF_SOC_CON(0), 7, 1, MFLAGS),
++			RK3288_GRF_SOC_CON(0), 7, 1, MFLAGS, grf_type_sys),
+ 	GATE(ACLK_VCODEC, "aclk_vcodec", "aclk_vcodec_pre", 0,
+ 		RK3288_CLKGATE_CON(9), 0, GFLAGS),
+ 
+--- a/drivers/clk/rockchip/clk-rk3328.c
++++ b/drivers/clk/rockchip/clk-rk3328.c
+@@ -677,9 +677,9 @@ static struct rockchip_clk_branch rk3328
+ 			RK3328_CLKSEL_CON(27), 15, 1, MFLAGS, 8, 5, DFLAGS,
+ 			RK3328_CLKGATE_CON(3), 5, GFLAGS),
+ 	MUXGRF(SCLK_MAC2IO, "clk_mac2io", mux_mac2io_src_p, CLK_SET_RATE_NO_REPARENT,
+-			RK3328_GRF_MAC_CON1, 10, 1, MFLAGS),
++			RK3328_GRF_MAC_CON1, 10, 1, MFLAGS, grf_type_sys),
+ 	MUXGRF(SCLK_MAC2IO_EXT, "clk_mac2io_ext", mux_mac2io_ext_p, CLK_SET_RATE_NO_REPARENT,
+-			RK3328_GRF_SOC_CON4, 14, 1, MFLAGS),
++			RK3328_GRF_SOC_CON4, 14, 1, MFLAGS, grf_type_sys),
+ 
+ 	COMPOSITE(SCLK_MAC2PHY_SRC, "clk_mac2phy_src", mux_2plls_p, 0,
+ 			RK3328_CLKSEL_CON(26), 7, 1, MFLAGS, 0, 5, DFLAGS,
+@@ -692,7 +692,7 @@ static struct rockchip_clk_branch rk3328
+ 			RK3328_CLKSEL_CON(26), 8, 2, DFLAGS,
+ 			RK3328_CLKGATE_CON(9), 2, GFLAGS),
+ 	MUXGRF(SCLK_MAC2PHY, "clk_mac2phy", mux_mac2phy_src_p, CLK_SET_RATE_NO_REPARENT,
+-			RK3328_GRF_MAC_CON2, 10, 1, MFLAGS),
++			RK3328_GRF_MAC_CON2, 10, 1, MFLAGS, grf_type_sys),
+ 
+ 	FACTOR(0, "xin12m", "xin24m", 0, 1, 2),
+ 
+--- a/drivers/clk/rockchip/clk-rk3568.c
++++ b/drivers/clk/rockchip/clk-rk3568.c
+@@ -590,7 +590,7 @@ static struct rockchip_clk_branch rk3568
+ 			RK3568_CLKSEL_CON(9), 6, 2, MFLAGS, 0, 5, DFLAGS,
+ 			RK3568_CLKGATE_CON(4), 0, GFLAGS),
+ 	MUXGRF(CLK_DDR1X, "clk_ddr1x", clk_ddr1x_p, CLK_SET_RATE_PARENT,
+-			RK3568_CLKSEL_CON(9), 15, 1, MFLAGS),
++			RK3568_CLKSEL_CON(9), 15, 1, MFLAGS, grf_type_sys),
+ 
+ 	COMPOSITE_NOMUX(CLK_MSCH, "clk_msch", "clk_ddr1x", CLK_IGNORE_UNUSED,
+ 			RK3568_CLKSEL_CON(10), 0, 2, DFLAGS,
+--- a/drivers/clk/rockchip/clk-rk3576.c
++++ b/drivers/clk/rockchip/clk-rk3576.c
+@@ -1678,13 +1678,13 @@ static struct rockchip_clk_branch rk3576
+ 
+ 	/* phy ref */
+ 	MUXGRF(CLK_PHY_REF_SRC, "clk_phy_ref_src", clk_phy_ref_src_p,  0,
+-			RK3576_PMU0_GRF_OSC_CON6, 4, 1, MFLAGS),
++			RK3576_PMU0_GRF_OSC_CON6, 4, 1, MFLAGS, grf_type_pmu0),
+ 	MUXGRF(CLK_USBPHY_REF_SRC, "clk_usbphy_ref_src", clk_usbphy_ref_src_p,  0,
+-			RK3576_PMU0_GRF_OSC_CON6, 2, 1, MFLAGS),
++			RK3576_PMU0_GRF_OSC_CON6, 2, 1, MFLAGS, grf_type_pmu0),
+ 	MUXGRF(CLK_CPLL_REF_SRC, "clk_cpll_ref_src", clk_cpll_ref_src_p,  0,
+-			RK3576_PMU0_GRF_OSC_CON6, 1, 1, MFLAGS),
++			RK3576_PMU0_GRF_OSC_CON6, 1, 1, MFLAGS, grf_type_pmu0),
+ 	MUXGRF(CLK_AUPLL_REF_SRC, "clk_aupll_ref_src", clk_aupll_ref_src_p,  0,
+-			RK3576_PMU0_GRF_OSC_CON6, 0, 1, MFLAGS),
++			RK3576_PMU0_GRF_OSC_CON6, 0, 1, MFLAGS, grf_type_pmu0),
+ 
+ 	/* secure ns */
+ 	COMPOSITE_NODIV(ACLK_SECURE_NS, "aclk_secure_ns", mux_350m_175m_116m_24m_p, CLK_IS_CRITICAL,
+@@ -1727,13 +1727,14 @@ static void __init rk3576_clk_init(struc
+ 	struct rockchip_clk_provider *ctx;
+ 	unsigned long clk_nr_clks;
+ 	void __iomem *reg_base;
+-	struct regmap *grf;
++	struct rockchip_aux_grf *pmu0_grf_e;
++	struct regmap *pmu0_grf;
+ 
+ 	clk_nr_clks = rockchip_clk_find_max_clk_id(rk3576_clk_branches,
+ 					ARRAY_SIZE(rk3576_clk_branches)) + 1;
+ 
+-	grf = syscon_regmap_lookup_by_compatible("rockchip,rk3576-pmu0-grf");
+-	if (IS_ERR(grf)) {
++	pmu0_grf = syscon_regmap_lookup_by_compatible("rockchip,rk3576-pmu0-grf");
++	if (IS_ERR(pmu0_grf)) {
+ 		pr_err("%s: could not get PMU0 GRF syscon\n", __func__);
+ 		return;
+ 	}
+@@ -1747,11 +1748,16 @@ static void __init rk3576_clk_init(struc
+ 	ctx = rockchip_clk_init(np, reg_base, clk_nr_clks);
+ 	if (IS_ERR(ctx)) {
+ 		pr_err("%s: rockchip clk init failed\n", __func__);
+-		iounmap(reg_base);
+-		return;
++		goto err_unmap;
+ 	}
+ 
+-	ctx->grf = grf;
++	pmu0_grf_e = kzalloc(sizeof(*pmu0_grf_e), GFP_KERNEL);
++	if (!pmu0_grf_e)
++		goto err_unmap;
++
++	pmu0_grf_e->grf = pmu0_grf;
++	pmu0_grf_e->type = grf_type_pmu0;
++	hash_add(ctx->aux_grf_table, &pmu0_grf_e->node, grf_type_pmu0);
+ 
+ 	rockchip_clk_register_plls(ctx, rk3576_pll_clks,
+ 				   ARRAY_SIZE(rk3576_pll_clks),
+@@ -1774,6 +1780,12 @@ static void __init rk3576_clk_init(struc
+ 	rockchip_register_restart_notifier(ctx, RK3576_GLB_SRST_FST, NULL);
+ 
+ 	rockchip_clk_of_add_provider(np, ctx);
++
++	return;
++
++err_unmap:
++	iounmap(reg_base);
++	return;
+ }
+ 
+ CLK_OF_DECLARE(rk3576_cru, "rockchip,rk3576-cru", rk3576_clk_init);
+--- a/drivers/clk/rockchip/clk-rv1126.c
++++ b/drivers/clk/rockchip/clk-rv1126.c
+@@ -857,7 +857,7 @@ static struct rockchip_clk_branch rv1126
+ 			RV1126_GMAC_CON, 5, 1, MFLAGS),
+ 	MUXGRF(CLK_GMAC_SRC, "clk_gmac_src", mux_clk_gmac_src_p, CLK_SET_RATE_PARENT |
+ 			CLK_SET_RATE_NO_REPARENT,
+-			RV1126_GRF_IOFUNC_CON1, 12, 1, MFLAGS),
++			RV1126_GRF_IOFUNC_CON1, 12, 1, MFLAGS, grf_type_sys),
+ 
+ 	GATE(CLK_GMAC_REF, "clk_gmac_ref", "clk_gmac_src", 0,
+ 			RV1126_CLKGATE_CON(20), 7, GFLAGS),
+--- a/drivers/clk/rockchip/clk.c
++++ b/drivers/clk/rockchip/clk.c
+@@ -382,6 +382,8 @@ static struct rockchip_clk_provider *roc
+ 	ctx->cru_node = np;
+ 	spin_lock_init(&ctx->lock);
+ 
++	hash_init(ctx->aux_grf_table);
++
+ 	ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node,
+ 						   "rockchip,grf");
+ 
+@@ -496,6 +498,8 @@ void rockchip_clk_register_branches(stru
+ 				    struct rockchip_clk_branch *list,
+ 				    unsigned int nr_clk)
+ {
++	struct regmap *grf = ctx->grf;
++	struct rockchip_aux_grf *agrf;
+ 	struct clk *clk;
+ 	unsigned int idx;
+ 	unsigned long flags;
+@@ -504,6 +508,17 @@ void rockchip_clk_register_branches(stru
+ 		flags = list->flags;
+ 		clk = NULL;
+ 
++		/* for GRF-dependent branches, choose the right grf first */
++		if (list->branch_type == branch_muxgrf &&
++				list->grf_type != grf_type_sys) {
++			hash_for_each_possible(ctx->aux_grf_table, agrf, node, list->grf_type) {
++				if (agrf->type == list->grf_type) {
++					grf = agrf->grf;
++					break;
++				}
++			}
++		}
++
+ 		/* catch simple muxes */
+ 		switch (list->branch_type) {
+ 		case branch_mux:
+@@ -526,7 +541,7 @@ void rockchip_clk_register_branches(stru
+ 		case branch_muxgrf:
+ 			clk = rockchip_clk_register_muxgrf(list->name,
+ 				list->parent_names, list->num_parents,
+-				flags, ctx->grf, list->muxdiv_offset,
++				flags, grf, list->muxdiv_offset,
+ 				list->mux_shift, list->mux_width,
+ 				list->mux_flags);
+ 			break;
+--- a/drivers/clk/rockchip/clk.h
++++ b/drivers/clk/rockchip/clk.h
+@@ -19,6 +19,7 @@
+ 
+ #include <linux/io.h>
+ #include <linux/clk-provider.h>
++#include <linux/hashtable.h>
+ 
+ struct clk;
+ 
+@@ -381,12 +382,35 @@ enum rockchip_pll_type {
+ 	.k = _k,						\
+ }
+ 
++enum rockchip_grf_type {
++	grf_type_sys = 0,
++	grf_type_pmu0,
++	grf_type_pmu1,
++	grf_type_ioc,
++};
++
++/* ceil(sqrt(enums in rockchip_grf_type - 1)) */
++#define GRF_HASH_ORDER 2
++
++/**
++ * struct rockchip_aux_grf - entry for the aux_grf_table hashtable
++ * @grf: pointer to the grf this entry references
++ * @type: what type of GRF this is
++ * @node: hlist node
++ */
++struct rockchip_aux_grf {
++	struct regmap *grf;
++	enum rockchip_grf_type type;
++	struct hlist_node node;
++};
++
+ /**
+  * struct rockchip_clk_provider - information about clock provider
+  * @reg_base: virtual address for the register base.
+  * @clk_data: holds clock related data like clk* and number of clocks.
+  * @cru_node: device-node of the clock-provider
+  * @grf: regmap of the general-register-files syscon
++ * @aux_grf_table: hashtable of auxiliary GRF regmaps, indexed by grf_type
+  * @lock: maintains exclusion between callbacks for a given clock-provider.
+  */
+ struct rockchip_clk_provider {
+@@ -394,6 +418,7 @@ struct rockchip_clk_provider {
+ 	struct clk_onecell_data clk_data;
+ 	struct device_node *cru_node;
+ 	struct regmap *grf;
++	DECLARE_HASHTABLE(aux_grf_table, GRF_HASH_ORDER);
+ 	spinlock_t lock;
+ };
+ 
+@@ -599,6 +624,7 @@ struct rockchip_clk_branch {
+ 	u8				gate_shift;
+ 	u8				gate_flags;
+ 	unsigned int			linked_clk_id;
++	enum rockchip_grf_type		grf_type;
+ 	struct rockchip_clk_branch	*child;
+ };
+ 
+@@ -839,7 +865,7 @@ struct rockchip_clk_branch {
+ 		.mux_table	= mt,				\
+ 	}
+ 
+-#define MUXGRF(_id, cname, pnames, f, o, s, w, mf)		\
++#define MUXGRF(_id, cname, pnames, f, o, s, w, mf, gt)		\
+ 	{							\
+ 		.id		= _id,				\
+ 		.branch_type	= branch_muxgrf,		\
+@@ -852,6 +878,7 @@ struct rockchip_clk_branch {
+ 		.mux_width	= w,				\
+ 		.mux_flags	= mf,				\
+ 		.gate_offset	= -1,				\
++		.grf_type	= gt,				\
+ 	}
+ 
+ #define DIV(_id, cname, pname, f, o, s, w, df)			\
diff --git a/target/linux/rockchip/patches-6.12/032-15-v6.16-clk-rockchip-introduce-GRF-gates.patch b/target/linux/rockchip/patches-6.12/032-15-v6.16-clk-rockchip-introduce-GRF-gates.patch
new file mode 100644
index 0000000000..bb27fcc79a
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/032-15-v6.16-clk-rockchip-introduce-GRF-gates.patch
@@ -0,0 +1,213 @@
+From e277168cabe9fd99e647f5dad0bc846d5d6b0093 Mon Sep 17 00:00:00 2001
+From: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Date: Fri, 2 May 2025 13:03:09 +0200
+Subject: [PATCH] clk: rockchip: introduce GRF gates
+
+Some rockchip SoCs, namely the RK3576, have bits in a General Register
+File (GRF) that act just like clock gates. The downstream vendor kernel
+simply maps over the already mapped GRF range with a generic clock gate
+driver. This solution isn't suitable for upstream, as a memory range
+will be in use by multiple drivers at the same time, and it leaks
+implementation details into the device tree.
+
+Instead, implement this with a new clock branch type in the Rockchip
+clock driver: GRF gates. Somewhat akin to MUXGRF, this clock branch
+depends on the type of GRF, but functions like a gate instead.
+
+Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Link: https://lore.kernel.org/r/20250502-rk3576-sai-v3-3-376cef19dd7c@collabora.com
+Signed-off-by: Heiko Stuebner <heiko at sntech.de>
+---
+ drivers/clk/rockchip/Makefile   |   1 +
+ drivers/clk/rockchip/clk.c      |   9 ++-
+ drivers/clk/rockchip/clk.h      |  20 ++++++
+ drivers/clk/rockchip/gate-grf.c | 105 ++++++++++++++++++++++++++++++++
+ 4 files changed, 134 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/clk/rockchip/gate-grf.c
+
+--- a/drivers/clk/rockchip/Makefile
++++ b/drivers/clk/rockchip/Makefile
+@@ -14,6 +14,7 @@ clk-rockchip-y += clk-mmc-phase.o
+ clk-rockchip-y += clk-muxgrf.o
+ clk-rockchip-y += clk-ddr.o
+ clk-rockchip-y += gate-link.o
++clk-rockchip-y += gate-grf.o
+ clk-rockchip-$(CONFIG_RESET_CONTROLLER) += softrst.o
+ 
+ obj-$(CONFIG_CLK_PX30)          += clk-px30.o
+--- a/drivers/clk/rockchip/clk.c
++++ b/drivers/clk/rockchip/clk.c
+@@ -509,7 +509,7 @@ void rockchip_clk_register_branches(stru
+ 		clk = NULL;
+ 
+ 		/* for GRF-dependent branches, choose the right grf first */
+-		if (list->branch_type == branch_muxgrf &&
++		if ((list->branch_type == branch_muxgrf || list->branch_type == branch_grf_gate) &&
+ 				list->grf_type != grf_type_sys) {
+ 			hash_for_each_possible(ctx->aux_grf_table, agrf, node, list->grf_type) {
+ 				if (agrf->type == list->grf_type) {
+@@ -588,6 +588,13 @@ void rockchip_clk_register_branches(stru
+ 				ctx->reg_base + list->gate_offset,
+ 				list->gate_shift, list->gate_flags, &ctx->lock);
+ 			break;
++		case branch_grf_gate:
++			flags |= CLK_SET_RATE_PARENT;
++			clk = rockchip_clk_register_gate_grf(list->name,
++				list->parent_names[0], flags, grf,
++				list->gate_offset, list->gate_shift,
++				list->gate_flags);
++			break;
+ 		case branch_composite:
+ 			clk = rockchip_clk_register_branch(list->name,
+ 				list->parent_names, list->num_parents,
+--- a/drivers/clk/rockchip/clk.h
++++ b/drivers/clk/rockchip/clk.h
+@@ -586,6 +586,11 @@ struct clk *rockchip_clk_register_muxgrf
+ 				int flags, struct regmap *grf, int reg,
+ 				int shift, int width, int mux_flags);
+ 
++struct clk *rockchip_clk_register_gate_grf(const char *name,
++				const char *parent_name, unsigned long flags,
++				struct regmap *regmap, unsigned int reg,
++				unsigned int shift, u8 gate_flags);
++
+ #define PNAME(x) static const char *const x[] __initconst
+ 
+ enum rockchip_clk_branch_type {
+@@ -595,6 +600,7 @@ enum rockchip_clk_branch_type {
+ 	branch_divider,
+ 	branch_fraction_divider,
+ 	branch_gate,
++	branch_grf_gate,
+ 	branch_linked_gate,
+ 	branch_mmc,
+ 	branch_inverter,
+@@ -924,6 +930,20 @@ struct rockchip_clk_branch {
+ 		.gate_flags	= gf,				\
+ 	}
+ 
++#define GATE_GRF(_id, cname, pname, f, o, b, gf, gt)		\
++	{							\
++		.id		= _id,				\
++		.branch_type	= branch_grf_gate,		\
++		.name		= cname,			\
++		.parent_names	= (const char *[]){ pname },	\
++		.num_parents	= 1,				\
++		.flags		= f,				\
++		.gate_offset	= o,				\
++		.gate_shift	= b,				\
++		.gate_flags	= gf,				\
++		.grf_type	= gt,				\
++	}
++
+ #define GATE_LINK(_id, cname, pname, linkedclk, f, o, b, gf)	\
+ 	{							\
+ 		.id		= _id,				\
+--- /dev/null
++++ b/drivers/clk/rockchip/gate-grf.c
+@@ -0,0 +1,105 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (c) 2025 Collabora Ltd.
++ * Author: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
++ *
++ * Certain clocks on Rockchip are "gated" behind an additional register bit
++ * write in a GRF register, such as the SAI MCLKs on RK3576. This code
++ * implements a clock driver for these types of gates, based on regmaps.
++ */
++
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/regmap.h>
++#include <linux/slab.h>
++#include "clk.h"
++
++struct rockchip_gate_grf {
++	struct clk_hw		hw;
++	struct regmap		*regmap;
++	unsigned int		reg;
++	unsigned int		shift;
++	u8			flags;
++};
++
++#define to_gate_grf(_hw) container_of(_hw, struct rockchip_gate_grf, hw)
++
++static int rockchip_gate_grf_enable(struct clk_hw *hw)
++{
++	struct rockchip_gate_grf *gate = to_gate_grf(hw);
++	u32 val = !(gate->flags & CLK_GATE_SET_TO_DISABLE) ? BIT(gate->shift) : 0;
++	u32 hiword = ((gate->flags & CLK_GATE_HIWORD_MASK) ? 1 : 0) << (gate->shift + 16);
++	int ret;
++
++	ret = regmap_update_bits(gate->regmap, gate->reg,
++				 hiword | BIT(gate->shift), hiword | val);
++
++	return ret;
++}
++
++static void rockchip_gate_grf_disable(struct clk_hw *hw)
++{
++	struct rockchip_gate_grf *gate = to_gate_grf(hw);
++	u32 val = !(gate->flags & CLK_GATE_SET_TO_DISABLE) ? 0 : BIT(gate->shift);
++	u32 hiword = ((gate->flags & CLK_GATE_HIWORD_MASK) ? 1 : 0) << (gate->shift + 16);
++
++	regmap_update_bits(gate->regmap, gate->reg,
++			   hiword | BIT(gate->shift), hiword | val);
++}
++
++static int rockchip_gate_grf_is_enabled(struct clk_hw *hw)
++{
++	struct rockchip_gate_grf *gate = to_gate_grf(hw);
++	bool invert = !!(gate->flags & CLK_GATE_SET_TO_DISABLE);
++	int ret;
++
++	ret = regmap_test_bits(gate->regmap, gate->reg, BIT(gate->shift));
++	if (ret < 0)
++		ret = 0;
++
++	return invert ? 1 - ret : ret;
++
++}
++
++static const struct clk_ops rockchip_gate_grf_ops = {
++	.enable = rockchip_gate_grf_enable,
++	.disable = rockchip_gate_grf_disable,
++	.is_enabled = rockchip_gate_grf_is_enabled,
++};
++
++struct clk *rockchip_clk_register_gate_grf(const char *name,
++		const char *parent_name, unsigned long flags,
++		struct regmap *regmap, unsigned int reg, unsigned int shift,
++		u8 gate_flags)
++{
++	struct rockchip_gate_grf *gate;
++	struct clk_init_data init;
++	struct clk *clk;
++
++	if (IS_ERR(regmap)) {
++		pr_err("%s: regmap not available\n", __func__);
++		return ERR_PTR(-EOPNOTSUPP);
++	}
++
++	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
++	if (!gate)
++		return ERR_PTR(-ENOMEM);
++
++	init.name = name;
++	init.flags = flags;
++	init.num_parents = parent_name ? 1 : 0;
++	init.parent_names = parent_name ? &parent_name : NULL;
++	init.ops = &rockchip_gate_grf_ops;
++
++	gate->hw.init = &init;
++	gate->regmap = regmap;
++	gate->reg = reg;
++	gate->shift = shift;
++	gate->flags = gate_flags;
++
++	clk = clk_register(NULL, &gate->hw);
++	if (IS_ERR(clk))
++		kfree(gate);
++
++	return clk;
++}
diff --git a/target/linux/rockchip/patches-6.12/032-16-v6.16-clk-rockchip-add-GATE_GRFs-for-SAI-MCLKOUT-to-rk3576.patch b/target/linux/rockchip/patches-6.12/032-16-v6.16-clk-rockchip-add-GATE_GRFs-for-SAI-MCLKOUT-to-rk3576.patch
new file mode 100644
index 0000000000..efca0f07d4
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/032-16-v6.16-clk-rockchip-add-GATE_GRFs-for-SAI-MCLKOUT-to-rk3576.patch
@@ -0,0 +1,90 @@
+From 9199ec29f0977efee223791c9ee3eb402d23f8ba Mon Sep 17 00:00:00 2001
+From: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Date: Fri, 2 May 2025 13:03:10 +0200
+Subject: [PATCH] clk: rockchip: add GATE_GRFs for SAI MCLKOUT to rk3576
+
+The Rockchip RK3576 gates the SAI MCLKOUT clocks behind some IOC GRF
+writes.
+
+Add these clock branches, and add the IOC GRF to the auxiliary GRF
+hashtable.
+
+Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Link: https://lore.kernel.org/r/20250502-rk3576-sai-v3-4-376cef19dd7c@collabora.com
+Signed-off-by: Heiko Stuebner <heiko at sntech.de>
+---
+ drivers/clk/rockchip/clk-rk3576.c | 27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+--- a/drivers/clk/rockchip/clk-rk3576.c
++++ b/drivers/clk/rockchip/clk-rk3576.c
+@@ -15,6 +15,7 @@
+ 
+ #define RK3576_GRF_SOC_STATUS0		0x600
+ #define RK3576_PMU0_GRF_OSC_CON6	0x18
++#define RK3576_VCCIO_IOC_MISC_CON0	0x6400
+ 
+ enum rk3576_plls {
+ 	bpll, lpll, vpll, aupll, cpll, gpll, ppll,
+@@ -1481,6 +1482,14 @@ static struct rockchip_clk_branch rk3576
+ 			RK3576_CLKGATE_CON(10), 0, GFLAGS),
+ 	GATE(CLK_SAI0_MCLKOUT, "clk_sai0_mclkout", "mclk_sai0_8ch", 0,
+ 			RK3576_CLKGATE_CON(10), 1, GFLAGS),
++	GATE_GRF(CLK_SAI0_MCLKOUT_TO_IO, "mclk_sai0_to_io", "clk_sai0_mclkout",
++			0, RK3576_VCCIO_IOC_MISC_CON0, 0, GFLAGS, grf_type_ioc),
++	GATE_GRF(CLK_SAI1_MCLKOUT_TO_IO, "mclk_sai1_to_io", "clk_sai1_mclkout",
++			0, RK3576_VCCIO_IOC_MISC_CON0, 1, GFLAGS, grf_type_ioc),
++	GATE_GRF(CLK_SAI2_MCLKOUT_TO_IO, "mclk_sai2_to_io", "clk_sai2_mclkout",
++			0, RK3576_VCCIO_IOC_MISC_CON0, 2, GFLAGS, grf_type_ioc),
++	GATE_GRF(CLK_SAI3_MCLKOUT_TO_IO, "mclk_sai3_to_io", "clk_sai3_mclkout",
++			0, RK3576_VCCIO_IOC_MISC_CON0, 3, GFLAGS, grf_type_ioc),
+ 
+ 	/* sdgmac */
+ 	COMPOSITE_NODIV(HCLK_SDGMAC_ROOT, "hclk_sdgmac_root", mux_200m_100m_50m_24m_p, 0,
+@@ -1727,7 +1736,9 @@ static void __init rk3576_clk_init(struc
+ 	struct rockchip_clk_provider *ctx;
+ 	unsigned long clk_nr_clks;
+ 	void __iomem *reg_base;
++	struct rockchip_aux_grf *ioc_grf_e;
+ 	struct rockchip_aux_grf *pmu0_grf_e;
++	struct regmap *ioc_grf;
+ 	struct regmap *pmu0_grf;
+ 
+ 	clk_nr_clks = rockchip_clk_find_max_clk_id(rk3576_clk_branches,
+@@ -1739,6 +1750,12 @@ static void __init rk3576_clk_init(struc
+ 		return;
+ 	}
+ 
++	ioc_grf = syscon_regmap_lookup_by_compatible("rockchip,rk3576-ioc-grf");
++	if (IS_ERR(ioc_grf)) {
++		pr_err("%s: could not get IOC GRF syscon\n", __func__);
++		return;
++	}
++
+ 	reg_base = of_iomap(np, 0);
+ 	if (!reg_base) {
+ 		pr_err("%s: could not map cru region\n", __func__);
+@@ -1759,6 +1776,14 @@ static void __init rk3576_clk_init(struc
+ 	pmu0_grf_e->type = grf_type_pmu0;
+ 	hash_add(ctx->aux_grf_table, &pmu0_grf_e->node, grf_type_pmu0);
+ 
++	ioc_grf_e = kzalloc(sizeof(*ioc_grf_e), GFP_KERNEL);
++	if (!ioc_grf_e)
++		goto err_free_pmu0;
++
++	ioc_grf_e->grf = ioc_grf;
++	ioc_grf_e->type = grf_type_ioc;
++	hash_add(ctx->aux_grf_table, &ioc_grf_e->node, grf_type_ioc);
++
+ 	rockchip_clk_register_plls(ctx, rk3576_pll_clks,
+ 				   ARRAY_SIZE(rk3576_pll_clks),
+ 				   RK3576_GRF_SOC_STATUS0);
+@@ -1783,6 +1808,8 @@ static void __init rk3576_clk_init(struc
+ 
+ 	return;
+ 
++err_free_pmu0:
++	kfree(pmu0_grf_e);
+ err_unmap:
+ 	iounmap(reg_base);
+ 	return;
diff --git a/target/linux/rockchip/patches-6.12/032-17-v6.16-clk-rockchip-rk3576-add-missing-slab-h-include.patch b/target/linux/rockchip/patches-6.12/032-17-v6.16-clk-rockchip-rk3576-add-missing-slab-h-include.patch
new file mode 100644
index 0000000000..b6d8641ad5
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/032-17-v6.16-clk-rockchip-rk3576-add-missing-slab-h-include.patch
@@ -0,0 +1,27 @@
+From 92da5c3cba23ee4be2c043bb63a551c89c48de18 Mon Sep 17 00:00:00 2001
+From: Heiko Stuebner <heiko at sntech.de>
+Date: Thu, 15 May 2025 10:26:51 +0200
+Subject: [PATCH] clk: rockchip: rk3576: add missing slab.h include
+
+The change for auxiliary GRFs introduced kzalloc usage into the rk3576 clock
+driver, but missed adding the header for its prototype. Add it now.
+
+Reported-by: kernel test robot <lkp at intel.com>
+Closes: https://lore.kernel.org/oe-kbuild-all/202505150941.KWKskr2c-lkp@intel.com/
+Fixes: 70a114daf207 ("clk: rockchip: introduce auxiliary GRFs")
+Signed-off-by: Heiko Stuebner <heiko at sntech.de>
+Link: https://lore.kernel.org/r/20250515082652.2503063-1-heiko@sntech.de
+---
+ drivers/clk/rockchip/clk-rk3576.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/clk/rockchip/clk-rk3576.c
++++ b/drivers/clk/rockchip/clk-rk3576.c
+@@ -10,6 +10,7 @@
+ #include <linux/platform_device.h>
+ #include <linux/syscore_ops.h>
+ #include <linux/mfd/syscon.h>
++#include <linux/slab.h>
+ #include <dt-bindings/clock/rockchip,rk3576-cru.h>
+ #include "clk.h"
+ 
diff --git a/target/linux/rockchip/patches-6.12/033-01-v6.15-pmdomain-rockchip-Add-smc-call-to-inform-firmware.patch b/target/linux/rockchip/patches-6.12/033-01-v6.15-pmdomain-rockchip-Add-smc-call-to-inform-firmware.patch
new file mode 100644
index 0000000000..02fbd3d950
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/033-01-v6.15-pmdomain-rockchip-Add-smc-call-to-inform-firmware.patch
@@ -0,0 +1,55 @@
+From 58ebba35ddab4868c921f970b60a77032362ef4c Mon Sep 17 00:00:00 2001
+From: Shawn Lin <shawn.lin at rock-chips.com>
+Date: Wed, 5 Feb 2025 14:15:53 +0800
+Subject: [PATCH] pmdomain: rockchip: Add smc call to inform firmware
+
+Inform firmware to keep the power domain on or off.
+
+Suggested-by: Ulf Hansson <ulf.hansson at linaro.org>
+Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
+Reviewed-by: Ulf Hansson <ulf.hansson at linaro.org>
+Acked-by: Heiko Stuebner <heiko at sntech.de>
+Link: https://lore.kernel.org/r/1738736156-119203-5-git-send-email-shawn.lin@rock-chips.com
+Signed-off-by: Ulf Hansson <ulf.hansson at linaro.org>
+---
+ drivers/pmdomain/rockchip/pm-domains.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/pmdomain/rockchip/pm-domains.c
++++ b/drivers/pmdomain/rockchip/pm-domains.c
+@@ -5,6 +5,7 @@
+  * Copyright (c) 2015 ROCKCHIP, Co. Ltd.
+  */
+ 
++#include <linux/arm-smccc.h>
+ #include <linux/io.h>
+ #include <linux/iopoll.h>
+ #include <linux/err.h>
+@@ -20,6 +21,7 @@
+ #include <linux/regmap.h>
+ #include <linux/mfd/syscon.h>
+ #include <soc/rockchip/pm_domains.h>
++#include <soc/rockchip/rockchip_sip.h>
+ #include <dt-bindings/power/px30-power.h>
+ #include <dt-bindings/power/rockchip,rv1126-power.h>
+ #include <dt-bindings/power/rk3036-power.h>
+@@ -540,6 +542,7 @@ static void rockchip_do_pmu_set_power_do
+ 	struct generic_pm_domain *genpd = &pd->genpd;
+ 	u32 pd_pwr_offset = pd->info->pwr_offset;
+ 	bool is_on, is_mem_on = false;
++	struct arm_smccc_res res;
+ 
+ 	if (pd->info->pwr_mask == 0)
+ 		return;
+@@ -567,6 +570,11 @@ static void rockchip_do_pmu_set_power_do
+ 			genpd->name, is_on);
+ 		return;
+ 	}
++
++	/* Inform firmware to keep this pd on or off */
++	arm_smccc_smc(ROCKCHIP_SIP_SUSPEND_MODE, ROCKCHIP_SLEEP_PD_CONFIG,
++			pmu->info->pwr_offset + pd_pwr_offset,
++			pd->info->pwr_mask, on, 0, 0, 0, &res);
+ }
+ 
+ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
diff --git a/target/linux/rockchip/patches-6.12/033-02-v6.15-pmdomain-rockchip-Check-if-SMC-could-be-handled-by-TA.patch b/target/linux/rockchip/patches-6.12/033-02-v6.15-pmdomain-rockchip-Check-if-SMC-could-be-handled-by-TA.patch
new file mode 100644
index 0000000000..41517df2d2
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/033-02-v6.15-pmdomain-rockchip-Check-if-SMC-could-be-handled-by-TA.patch
@@ -0,0 +1,37 @@
+From 61eeb9678789644f118eff608d9031b5de4f719d Mon Sep 17 00:00:00 2001
+From: Shawn Lin <shawn.lin at rock-chips.com>
+Date: Wed, 19 Feb 2025 08:58:09 +0800
+Subject: [PATCH] pmdomain: rockchip: Check if SMC could be handled by TA
+
+Non-existent trusted-firmware could lead to SMC calls into some unset
+location, that breaks the system. Let's check that it's supported before
+executing the SMC.
+
+Reported-by: Steven Price <steven.price at arm.com>
+Suggested-by: Heiko Stuebner <heiko at sntech.de>
+Fixes: 58ebba35ddab ("pmdomain: rockchip: Add smc call to inform firmware")
+Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
+Reviewed-by: Heiko Stuebner <heiko at sntech.de>
+Tested-by: Steven Price <steven.price at arm.com>
+Link: https://lore.kernel.org/r/1739926689-151827-1-git-send-email-shawn.lin@rock-chips.com
+Signed-off-by: Ulf Hansson <ulf.hansson at linaro.org>
+---
+ drivers/pmdomain/rockchip/pm-domains.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/drivers/pmdomain/rockchip/pm-domains.c
++++ b/drivers/pmdomain/rockchip/pm-domains.c
+@@ -572,9 +572,10 @@ static void rockchip_do_pmu_set_power_do
+ 	}
+ 
+ 	/* Inform firmware to keep this pd on or off */
+-	arm_smccc_smc(ROCKCHIP_SIP_SUSPEND_MODE, ROCKCHIP_SLEEP_PD_CONFIG,
+-			pmu->info->pwr_offset + pd_pwr_offset,
+-			pd->info->pwr_mask, on, 0, 0, 0, &res);
++	if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_NONE)
++		arm_smccc_smc(ROCKCHIP_SIP_SUSPEND_MODE, ROCKCHIP_SLEEP_PD_CONFIG,
++				pmu->info->pwr_offset + pd_pwr_offset,
++				pd->info->pwr_mask, on, 0, 0, 0, &res);
+ }
+ 
+ static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
diff --git a/target/linux/rockchip/patches-6.12/033-03-v6.15-pmdomain-rockchip-Fix-build-error.patch b/target/linux/rockchip/patches-6.12/033-03-v6.15-pmdomain-rockchip-Fix-build-error.patch
new file mode 100644
index 0000000000..cc3f302b6c
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/033-03-v6.15-pmdomain-rockchip-Fix-build-error.patch
@@ -0,0 +1,26 @@
+From bc4bc2a1609712e6c5de01be8a20341b710dc99b Mon Sep 17 00:00:00 2001
+From: Ulf Hansson <ulf.hansson at linaro.org>
+Date: Mon, 24 Feb 2025 13:05:29 +0100
+Subject: [PATCH] pmdomain: rockchip: Fix build error
+
+To resolve the build error with undefined reference to
+`arm_smccc_1_1_get_conduit', let's add a build dependency to
+HAVE_ARM_SMCCC_DISCOVERY.
+
+Reported-by: Stephen Rothwell <sfr at canb.auug.org.au>
+Fixes: 61eeb9678789 ("pmdomain: rockchip: Check if SMC could be handled by TA")
+Signed-off-by: Ulf Hansson <ulf.hansson at linaro.org>
+---
+ drivers/pmdomain/rockchip/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/pmdomain/rockchip/Kconfig
++++ b/drivers/pmdomain/rockchip/Kconfig
+@@ -4,6 +4,7 @@ if ARCH_ROCKCHIP || COMPILE_TEST
+ config ROCKCHIP_PM_DOMAINS
+ 	bool "Rockchip generic power domain"
+ 	depends on PM
++	depends on HAVE_ARM_SMCCC_DISCOVERY
+ 	select PM_GENERIC_DOMAINS
+ 	help
+ 	  Say y here to enable power domain support.
diff --git a/target/linux/rockchip/patches-6.12/034-01-v6.17-thermal-drivers-rockchip-Rename-rk_tsadcv3_tshut_mode.patch b/target/linux/rockchip/patches-6.12/034-01-v6.17-thermal-drivers-rockchip-Rename-rk_tsadcv3_tshut_mode.patch
new file mode 100644
index 0000000000..853e3b1997
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/034-01-v6.17-thermal-drivers-rockchip-Rename-rk_tsadcv3_tshut_mode.patch
@@ -0,0 +1,40 @@
+From 9a9f71b2a3a7491c10ceea699e1999298db5c596 Mon Sep 17 00:00:00 2001
+From: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Date: Tue, 10 Jun 2025 14:32:37 +0200
+Subject: [PATCH] thermal/drivers/rockchip: Rename rk_tsadcv3_tshut_mode
+
+The "v" version specifier here refers to the hardware IP revision.
+Mainline deviated from downstream here by calling the v4 revision v3 as
+it didn't support the v3 hardware revision at all.
+
+This creates needless confusion, so rename it to rk_tsadcv4_tshut_mode
+to be consistent with what the hardware wants to be called.
+
+Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Reviewed-by: Heiko Stuebner <heiko at sntech.de>
+Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-1-b6e9efbf1015@collabora.com
+Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
+---
+ drivers/thermal/rockchip_thermal.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/thermal/rockchip_thermal.c
++++ b/drivers/thermal/rockchip_thermal.c
+@@ -1045,7 +1045,7 @@ static void rk_tsadcv2_tshut_mode(int ch
+ 	writel_relaxed(val, regs + TSADCV2_INT_EN);
+ }
+ 
+-static void rk_tsadcv3_tshut_mode(int chn, void __iomem *regs,
++static void rk_tsadcv4_tshut_mode(int chn, void __iomem *regs,
+ 				  enum tshut_mode mode)
+ {
+ 	u32 val_gpio, val_cru;
+@@ -1297,7 +1297,7 @@ static const struct rockchip_tsadc_chip
+ 	.get_temp = rk_tsadcv4_get_temp,
+ 	.set_alarm_temp = rk_tsadcv3_alarm_temp,
+ 	.set_tshut_temp = rk_tsadcv3_tshut_temp,
+-	.set_tshut_mode = rk_tsadcv3_tshut_mode,
++	.set_tshut_mode = rk_tsadcv4_tshut_mode,
+ 	.table = {
+ 		.id = rk3588_code_table,
+ 		.length = ARRAY_SIZE(rk3588_code_table),
diff --git a/target/linux/rockchip/patches-6.12/034-02-v6.17-thermal-drivers-rockchip-Support-RK3576-SoC-in-the-therma.patch b/target/linux/rockchip/patches-6.12/034-02-v6.17-thermal-drivers-rockchip-Support-RK3576-SoC-in-the-therma.patch
new file mode 100644
index 0000000000..9f35e21ee0
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/034-02-v6.17-thermal-drivers-rockchip-Support-RK3576-SoC-in-the-therma.patch
@@ -0,0 +1,61 @@
+From feb69bccf5d3eb31918df86638abc82594390ba5 Mon Sep 17 00:00:00 2001
+From: Ye Zhang <ye.zhang at rock-chips.com>
+Date: Tue, 10 Jun 2025 14:32:39 +0200
+Subject: [PATCH] thermal/drivers/rockchip: Support RK3576 SoC in the thermal
+ driver
+
+The RK3576 SoC has six TS-ADC channels: TOP, BIG_CORE, LITTLE_CORE,
+DDR, NPU and GPU.
+
+Signed-off-by: Ye Zhang <ye.zhang at rock-chips.com>
+[ported to mainline, reworded commit message]
+Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Reviewed-by: Heiko Stuebner <heiko at sntech.de>
+Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-3-b6e9efbf1015@collabora.com
+Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
+---
+ drivers/thermal/rockchip_thermal.c | 26 ++++++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+--- a/drivers/thermal/rockchip_thermal.c
++++ b/drivers/thermal/rockchip_thermal.c
+@@ -1284,6 +1284,28 @@ static const struct rockchip_tsadc_chip
+ 	},
+ };
+ 
++static const struct rockchip_tsadc_chip rk3576_tsadc_data = {
++	/* top, big_core, little_core, ddr, npu, gpu */
++	.chn_offset = 0,
++	.chn_num = 6, /* six channels for tsadc */
++	.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
++	.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
++	.tshut_temp = 95000,
++	.initialize = rk_tsadcv8_initialize,
++	.irq_ack = rk_tsadcv4_irq_ack,
++	.control = rk_tsadcv4_control,
++	.get_temp = rk_tsadcv4_get_temp,
++	.set_alarm_temp = rk_tsadcv3_alarm_temp,
++	.set_tshut_temp = rk_tsadcv3_tshut_temp,
++	.set_tshut_mode = rk_tsadcv4_tshut_mode,
++	.table = {
++		.id = rk3588_code_table,
++		.length = ARRAY_SIZE(rk3588_code_table),
++		.data_mask = TSADCV4_DATA_MASK,
++		.mode = ADC_INCREMENT,
++	},
++};
++
+ static const struct rockchip_tsadc_chip rk3588_tsadc_data = {
+ 	/* top, big_core0, big_core1, little_core, center, gpu, npu */
+ 	.chn_offset = 0,
+@@ -1343,6 +1365,10 @@ static const struct of_device_id of_rock
+ 		.data = (void *)&rk3568_tsadc_data,
+ 	},
+ 	{
++		.compatible = "rockchip,rk3576-tsadc",
++		.data = (void *)&rk3576_tsadc_data,
++	},
++	{
+ 		.compatible = "rockchip,rk3588-tsadc",
+ 		.data = (void *)&rk3588_tsadc_data,
+ 	},
diff --git a/target/linux/rockchip/patches-6.12/034-03-v6.17-thermal-drivers-rockchip-Support-reading-trim-values-from.patch b/target/linux/rockchip/patches-6.12/034-03-v6.17-thermal-drivers-rockchip-Support-reading-trim-values-from.patch
new file mode 100644
index 0000000000..4d6b5b8435
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/034-03-v6.17-thermal-drivers-rockchip-Support-reading-trim-values-from.patch
@@ -0,0 +1,431 @@
+From ae332ec0009d762982540635411caefeafa92a5b Mon Sep 17 00:00:00 2001
+From: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Date: Tue, 10 Jun 2025 14:32:41 +0200
+Subject: [PATCH] thermal/drivers/rockchip: Support reading trim values from
+ OTP
+
+Many of the Rockchip SoCs support storing trim values for the sensors in
+factory programmable memory. These values specify a fixed offset from
+the sensor's returned temperature to get a more accurate picture of what
+temperature the silicon is actually at.
+
+The way this is implemented is with various OTP cells, which may be
+absent. There may both be whole-TSADC trim values, as well as per-sensor
+trim values.
+
+In the downstream driver, whole-chip trim values override the per-sensor
+trim values. This rewrite of the functionality changes the semantics to
+something I see as slightly more useful: allow the whole-chip trim
+values to serve as a fallback for lacking per-sensor trim values,
+instead of overriding already present sensor trim values.
+
+Additionally, the chip may specify an offset (trim_base, trim_base_frac)
+in degrees celsius and degrees decicelsius respectively which defines
+what the basis is from which the trim, if any, should be calculated
+from. By default, this is 30 degrees Celsius, but the chip can once
+again specify a different value through OTP cells.
+
+The implementation of these trim calculations have been tested
+extensively on an RK3576, where it was confirmed to get rid of pesky 1.8
+degree Celsius offsets between certain sensors.
+
+Signed-off-by: Nicolas Frattaroli <nicolas.frattaroli at collabora.com>
+Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-5-b6e9efbf1015@collabora.com
+Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
+---
+ drivers/thermal/rockchip_thermal.c | 221 ++++++++++++++++++++++++++---
+ 1 file changed, 202 insertions(+), 19 deletions(-)
+
+--- a/drivers/thermal/rockchip_thermal.c
++++ b/drivers/thermal/rockchip_thermal.c
+@@ -9,6 +9,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/io.h>
+ #include <linux/module.h>
++#include <linux/nvmem-consumer.h>
+ #include <linux/of.h>
+ #include <linux/of_address.h>
+ #include <linux/of_irq.h>
+@@ -69,16 +70,18 @@ struct chip_tsadc_table {
+  * struct rockchip_tsadc_chip - hold the private data of tsadc chip
+  * @chn_offset: the channel offset of the first channel
+  * @chn_num: the channel number of tsadc chip
+- * @tshut_temp: the hardware-controlled shutdown temperature value
++ * @trim_slope: used to convert the trim code to a temperature in millicelsius
++ * @tshut_temp: the hardware-controlled shutdown temperature value, with no trim
+  * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
+  * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
+  * @initialize: SoC special initialize tsadc controller method
+  * @irq_ack: clear the interrupt
+  * @control: enable/disable method for the tsadc controller
+- * @get_temp: get the temperature
++ * @get_temp: get the raw temperature, unadjusted by trim
+  * @set_alarm_temp: set the high temperature interrupt
+  * @set_tshut_temp: set the hardware-controlled shutdown temperature
+  * @set_tshut_mode: set the hardware-controlled shutdown mode
++ * @get_trim_code: convert a hardware temperature code to one adjusted for by trim
+  * @table: the chip-specific conversion table
+  */
+ struct rockchip_tsadc_chip {
+@@ -86,6 +89,9 @@ struct rockchip_tsadc_chip {
+ 	int chn_offset;
+ 	int chn_num;
+ 
++	/* Used to convert trim code to trim temp */
++	int trim_slope;
++
+ 	/* The hardware-controlled tshut property */
+ 	int tshut_temp;
+ 	enum tshut_mode tshut_mode;
+@@ -105,6 +111,8 @@ struct rockchip_tsadc_chip {
+ 	int (*set_tshut_temp)(const struct chip_tsadc_table *table,
+ 			      int chn, void __iomem *reg, int temp);
+ 	void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m);
++	int (*get_trim_code)(const struct chip_tsadc_table *table,
++			     int code, int trim_base, int trim_base_frac);
+ 
+ 	/* Per-table methods */
+ 	struct chip_tsadc_table table;
+@@ -114,12 +122,16 @@ struct rockchip_tsadc_chip {
+  * struct rockchip_thermal_sensor - hold the information of thermal sensor
+  * @thermal:  pointer to the platform/configuration data
+  * @tzd: pointer to a thermal zone
++ * @of_node: pointer to the device_node representing this sensor, if any
+  * @id: identifier of the thermal sensor
++ * @trim_temp: per-sensor trim temperature value
+  */
+ struct rockchip_thermal_sensor {
+ 	struct rockchip_thermal_data *thermal;
+ 	struct thermal_zone_device *tzd;
++	struct device_node *of_node;
+ 	int id;
++	int trim_temp;
+ };
+ 
+ /**
+@@ -132,7 +144,11 @@ struct rockchip_thermal_sensor {
+  * @pclk: the advanced peripherals bus clock
+  * @grf: the general register file will be used to do static set by software
+  * @regs: the base address of tsadc controller
+- * @tshut_temp: the hardware-controlled shutdown temperature value
++ * @trim_base: major component of sensor trim value, in Celsius
++ * @trim_base_frac: minor component of sensor trim value, in Decicelsius
++ * @trim: fallback thermal trim value for each channel
++ * @tshut_temp: the hardware-controlled shutdown temperature value, with no trim
++ * @trim_temp: the fallback trim temperature for the whole sensor
+  * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
+  * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
+  */
+@@ -149,7 +165,12 @@ struct rockchip_thermal_data {
+ 	struct regmap *grf;
+ 	void __iomem *regs;
+ 
++	int trim_base;
++	int trim_base_frac;
++	int trim;
++
+ 	int tshut_temp;
++	int trim_temp;
+ 	enum tshut_mode tshut_mode;
+ 	enum tshut_polarity tshut_polarity;
+ };
+@@ -249,6 +270,9 @@ struct rockchip_thermal_data {
+ 
+ #define GRF_CON_TSADC_CH_INV			(0x10001 << 1)
+ 
++
++#define RK_MAX_TEMP				(180000)
++
+ /**
+  * struct tsadc_table - code to temperature conversion table
+  * @code: the value of adc channel
+@@ -1061,6 +1085,15 @@ static void rk_tsadcv4_tshut_mode(int ch
+ 	writel_relaxed(val_cru, regs + TSADCV3_HSHUT_CRU_INT_EN);
+ }
+ 
++static int rk_tsadcv2_get_trim_code(const struct chip_tsadc_table *table,
++				    int code, int trim_base, int trim_base_frac)
++{
++	int temp = trim_base * 1000 + trim_base_frac * 100;
++	u32 base_code = rk_tsadcv2_temp_to_code(table, temp);
++
++	return code - base_code;
++}
++
+ static const struct rockchip_tsadc_chip px30_tsadc_data = {
+ 	/* cpu, gpu */
+ 	.chn_offset = 0,
+@@ -1298,6 +1331,8 @@ static const struct rockchip_tsadc_chip
+ 	.set_alarm_temp = rk_tsadcv3_alarm_temp,
+ 	.set_tshut_temp = rk_tsadcv3_tshut_temp,
+ 	.set_tshut_mode = rk_tsadcv4_tshut_mode,
++	.get_trim_code = rk_tsadcv2_get_trim_code,
++	.trim_slope = 923,
+ 	.table = {
+ 		.id = rk3588_code_table,
+ 		.length = ARRAY_SIZE(rk3588_code_table),
+@@ -1413,7 +1448,7 @@ static int rockchip_thermal_set_trips(st
+ 		__func__, sensor->id, low, high);
+ 
+ 	return tsadc->set_alarm_temp(&tsadc->table,
+-				     sensor->id, thermal->regs, high);
++				     sensor->id, thermal->regs, high + sensor->trim_temp);
+ }
+ 
+ static int rockchip_thermal_get_temp(struct thermal_zone_device *tz, int *out_temp)
+@@ -1425,6 +1460,8 @@ static int rockchip_thermal_get_temp(str
+ 
+ 	retval = tsadc->get_temp(&tsadc->table,
+ 				 sensor->id, thermal->regs, out_temp);
++	*out_temp -= sensor->trim_temp;
++
+ 	return retval;
+ }
+ 
+@@ -1433,6 +1470,104 @@ static const struct thermal_zone_device_
+ 	.set_trips = rockchip_thermal_set_trips,
+ };
+ 
++/**
++ * rockchip_get_efuse_value - read an OTP cell from a device node
++ * @np: pointer to the device node with the nvmem-cells property
++ * @cell_name: name of cell that should be read
++ * @value: pointer to where the read value will be placed
++ *
++ * Return: Negative errno on failure, during which *value will not be touched,
++ * or 0 on success.
++ */
++static int rockchip_get_efuse_value(struct device_node *np, const char *cell_name,
++				    int *value)
++{
++	struct nvmem_cell *cell;
++	int ret = 0;
++	size_t len;
++	u8 *buf;
++	int i;
++
++	cell = of_nvmem_cell_get(np, cell_name);
++	if (IS_ERR(cell))
++		return PTR_ERR(cell);
++
++	buf = nvmem_cell_read(cell, &len);
++
++	nvmem_cell_put(cell);
++
++	if (IS_ERR(buf))
++		return PTR_ERR(buf);
++
++	if (len > sizeof(*value)) {
++		ret = -ERANGE;
++		goto exit;
++	}
++
++	/* Copy with implicit endian conversion */
++	*value = 0;
++	for (i = 0; i < len; i++)
++		*value |= (int) buf[i] << (8 * i);
++
++exit:
++	kfree(buf);
++	return ret;
++}
++
++static int rockchip_get_trim_configuration(struct device *dev, struct device_node *np,
++					   struct rockchip_thermal_data *thermal)
++{
++	const struct rockchip_tsadc_chip *tsadc = thermal->chip;
++	int trim_base = 0, trim_base_frac = 0, trim = 0;
++	int trim_code;
++	int ret;
++
++	thermal->trim_base = 0;
++	thermal->trim_base_frac = 0;
++	thermal->trim = 0;
++
++	if (!tsadc->get_trim_code)
++		return 0;
++
++	ret = rockchip_get_efuse_value(np, "trim_base", &trim_base);
++	if (ret < 0) {
++		if (ret == -ENOENT) {
++			trim_base = 30;
++			dev_dbg(dev, "trim_base is absent, defaulting to 30\n");
++		} else {
++			dev_err(dev, "failed reading nvmem value of trim_base: %pe\n",
++				ERR_PTR(ret));
++			return ret;
++		}
++	}
++	ret = rockchip_get_efuse_value(np, "trim_base_frac", &trim_base_frac);
++	if (ret < 0) {
++		if (ret == -ENOENT) {
++			dev_dbg(dev, "trim_base_frac is absent, defaulting to 0\n");
++		} else {
++			dev_err(dev, "failed reading nvmem value of trim_base_frac: %pe\n",
++				ERR_PTR(ret));
++			return ret;
++		}
++	}
++	thermal->trim_base = trim_base;
++	thermal->trim_base_frac = trim_base_frac;
++
++	/*
++	 * If the tsadc node contains the trim property, then it is used in the
++	 * absence of per-channel trim values
++	 */
++	if (!rockchip_get_efuse_value(np, "trim", &trim))
++		thermal->trim = trim;
++	if (trim) {
++		trim_code = tsadc->get_trim_code(&tsadc->table, trim,
++						 trim_base, trim_base_frac);
++		thermal->trim_temp = thermal->chip->trim_slope * trim_code;
++	}
++
++	return 0;
++}
++
+ static int rockchip_configure_from_dt(struct device *dev,
+ 				      struct device_node *np,
+ 				      struct rockchip_thermal_data *thermal)
+@@ -1493,6 +1628,8 @@ static int rockchip_configure_from_dt(st
+ 	if (IS_ERR(thermal->grf))
+ 		dev_warn(dev, "Missing rockchip,grf property\n");
+ 
++	rockchip_get_trim_configuration(dev, np, thermal);
++
+ 	return 0;
+ }
+ 
+@@ -1503,23 +1640,50 @@ rockchip_thermal_register_sensor(struct
+ 				 int id)
+ {
+ 	const struct rockchip_tsadc_chip *tsadc = thermal->chip;
++	struct device *dev = &pdev->dev;
++	int trim = thermal->trim;
++	int trim_code, tshut_temp;
++	int trim_temp = 0;
+ 	int error;
+ 
++	if (thermal->trim_temp)
++		trim_temp = thermal->trim_temp;
++
++	if (tsadc->get_trim_code && sensor->of_node) {
++		error = rockchip_get_efuse_value(sensor->of_node, "trim", &trim);
++		if (error < 0 && error != -ENOENT) {
++			dev_err(dev, "failed reading trim of sensor %d: %pe\n",
++				id, ERR_PTR(error));
++			return error;
++		}
++		if (trim) {
++			trim_code = tsadc->get_trim_code(&tsadc->table, trim,
++							 thermal->trim_base,
++							 thermal->trim_base_frac);
++			trim_temp = thermal->chip->trim_slope * trim_code;
++		}
++	}
++
++	sensor->trim_temp = trim_temp;
++
++	dev_dbg(dev, "trim of sensor %d is %d\n", id, sensor->trim_temp);
++
++	tshut_temp = min(thermal->tshut_temp + sensor->trim_temp, RK_MAX_TEMP);
++
+ 	tsadc->set_tshut_mode(id, thermal->regs, thermal->tshut_mode);
+ 
+-	error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs,
+-			      thermal->tshut_temp);
++	error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs, tshut_temp);
+ 	if (error)
+-		dev_err(&pdev->dev, "%s: invalid tshut=%d, error=%d\n",
+-			__func__, thermal->tshut_temp, error);
++		dev_err(dev, "%s: invalid tshut=%d, error=%d\n",
++			__func__, tshut_temp, error);
+ 
+ 	sensor->thermal = thermal;
+ 	sensor->id = id;
+-	sensor->tzd = devm_thermal_of_zone_register(&pdev->dev, id, sensor,
++	sensor->tzd = devm_thermal_of_zone_register(dev, id, sensor,
+ 						    &rockchip_of_thermal_ops);
+ 	if (IS_ERR(sensor->tzd)) {
+ 		error = PTR_ERR(sensor->tzd);
+-		dev_err(&pdev->dev, "failed to register sensor %d: %d\n",
++		dev_err(dev, "failed to register sensor %d: %d\n",
+ 			id, error);
+ 		return error;
+ 	}
+@@ -1542,9 +1706,11 @@ static int rockchip_thermal_probe(struct
+ {
+ 	struct device_node *np = pdev->dev.of_node;
+ 	struct rockchip_thermal_data *thermal;
++	struct device_node *child;
+ 	int irq;
+ 	int i;
+ 	int error;
++	u32 chn;
+ 
+ 	irq = platform_get_irq(pdev, 0);
+ 	if (irq < 0)
+@@ -1595,6 +1761,18 @@ static int rockchip_thermal_probe(struct
+ 	thermal->chip->initialize(thermal->grf, thermal->regs,
+ 				  thermal->tshut_polarity);
+ 
++	for_each_available_child_of_node(np, child) {
++		if (!of_property_read_u32(child, "reg", &chn)) {
++			if (chn < thermal->chip->chn_num)
++				thermal->sensors[chn].of_node = child;
++			else
++				dev_warn(&pdev->dev,
++					 "sensor address (%d) too large, ignoring its trim\n",
++					 chn);
++		}
++
++	}
++
+ 	for (i = 0; i < thermal->chip->chn_num; i++) {
+ 		error = rockchip_thermal_register_sensor(pdev, thermal,
+ 						&thermal->sensors[i],
+@@ -1664,8 +1842,11 @@ static int __maybe_unused rockchip_therm
+ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
+ {
+ 	struct rockchip_thermal_data *thermal = dev_get_drvdata(dev);
+-	int i;
++	const struct rockchip_tsadc_chip *tsadc = thermal->chip;
++	struct rockchip_thermal_sensor *sensor;
++	int tshut_temp;
+ 	int error;
++	int i;
+ 
+ 	error = clk_enable(thermal->clk);
+ 	if (error)
+@@ -1679,21 +1860,23 @@ static int __maybe_unused rockchip_therm
+ 
+ 	rockchip_thermal_reset_controller(thermal->reset);
+ 
+-	thermal->chip->initialize(thermal->grf, thermal->regs,
+-				  thermal->tshut_polarity);
++	tsadc->initialize(thermal->grf, thermal->regs, thermal->tshut_polarity);
+ 
+ 	for (i = 0; i < thermal->chip->chn_num; i++) {
+-		int id = thermal->sensors[i].id;
++		sensor = &thermal->sensors[i];
++
++		tshut_temp = min(thermal->tshut_temp + sensor->trim_temp,
++				 RK_MAX_TEMP);
+ 
+-		thermal->chip->set_tshut_mode(id, thermal->regs,
++		tsadc->set_tshut_mode(sensor->id, thermal->regs,
+ 					      thermal->tshut_mode);
+ 
+-		error = thermal->chip->set_tshut_temp(&thermal->chip->table,
+-					      id, thermal->regs,
+-					      thermal->tshut_temp);
++		error = tsadc->set_tshut_temp(&thermal->chip->table,
++					      sensor->id, thermal->regs,
++					      tshut_temp);
+ 		if (error)
+ 			dev_err(dev, "%s: invalid tshut=%d, error=%d\n",
+-				__func__, thermal->tshut_temp, error);
++				__func__, tshut_temp, error);
+ 	}
+ 
+ 	thermal->chip->control(thermal->regs, true);
diff --git a/target/linux/rockchip/patches-6.12/035-01-v6.13-gpio-rockchip-explan-the-format-of-the-GPIO-version-ID.patch b/target/linux/rockchip/patches-6.12/035-01-v6.13-gpio-rockchip-explan-the-format-of-the-GPIO-version-ID.patch
new file mode 100644
index 0000000000..38a171acff
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/035-01-v6.13-gpio-rockchip-explan-the-format-of-the-GPIO-version-ID.patch
@@ -0,0 +1,37 @@
+From 591ae6bed250e4067db926313ff7279d23a1c7d1 Mon Sep 17 00:00:00 2001
+From: Ye Zhang <ye.zhang at rock-chips.com>
+Date: Tue, 12 Nov 2024 09:54:05 +0800
+Subject: [PATCH] gpio: rockchip: explan the format of the GPIO version ID
+
+Remove redundant comments and provide a detailed explanation of the
+GPIO version ID.
+
+Signed-off-by: Ye Zhang <ye.zhang at rock-chips.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko at linux.intel.com>
+Reviewed-by: Sebastian Reichel <sebastian.reichel at collabora.com>
+Link: https://lore.kernel.org/r/20241112015408.3139996-2-ye.zhang@rock-chips.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski at linaro.org>
+---
+ drivers/gpio/gpio-rockchip.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpio/gpio-rockchip.c
++++ b/drivers/gpio/gpio-rockchip.c
+@@ -26,9 +26,15 @@
+ #include "../pinctrl/core.h"
+ #include "../pinctrl/pinctrl-rockchip.h"
+ 
++/*
++ * Version ID Register
++ * Bits [31:24] - Major Version
++ * Bits [23:16] - Minor Version
++ * Bits [15:0]  - Revision Number
++ */
+ #define GPIO_TYPE_V1		(0)           /* GPIO Version ID reserved */
+-#define GPIO_TYPE_V2		(0x01000C2B)  /* GPIO Version ID 0x01000C2B */
+-#define GPIO_TYPE_V2_1		(0x0101157C)  /* GPIO Version ID 0x0101157C */
++#define GPIO_TYPE_V2		(0x01000C2B)
++#define GPIO_TYPE_V2_1		(0x0101157C)
+ 
+ static const struct rockchip_gpio_regs gpio_regs_v1 = {
+ 	.port_dr = 0x00,
diff --git a/target/linux/rockchip/patches-6.12/035-02-v6.13-gpio-rockchip-change-the-GPIO-version-judgment-logic.patch b/target/linux/rockchip/patches-6.12/035-02-v6.13-gpio-rockchip-change-the-GPIO-version-judgment-logic.patch
new file mode 100644
index 0000000000..13bfa981e9
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/035-02-v6.13-gpio-rockchip-change-the-GPIO-version-judgment-logic.patch
@@ -0,0 +1,46 @@
+From 41209307cad7f14c387c68375a93b50e54261a53 Mon Sep 17 00:00:00 2001
+From: Ye Zhang <ye.zhang at rock-chips.com>
+Date: Tue, 12 Nov 2024 09:54:06 +0800
+Subject: [PATCH] gpio: rockchip: change the GPIO version judgment logic
+
+Have a list of valid IDs and default to -ENODEV.
+
+Signed-off-by: Ye Zhang <ye.zhang at rock-chips.com>
+Reviewed-by: Sebastian Reichel <sebastian.reichel at collabora.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko at linux.intel.com>
+Link: https://lore.kernel.org/r/20241112015408.3139996-3-ye.zhang@rock-chips.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski at linaro.org>
+---
+ drivers/gpio/gpio-rockchip.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpio/gpio-rockchip.c
++++ b/drivers/gpio/gpio-rockchip.c
+@@ -667,8 +667,9 @@ static int rockchip_get_bank_data(struct
+ 	clk_prepare_enable(bank->clk);
+ 	id = readl(bank->reg_base + gpio_regs_v2.version_id);
+ 
+-	/* If not gpio v2, that is default to v1. */
+-	if (id == GPIO_TYPE_V2 || id == GPIO_TYPE_V2_1) {
++	switch (id) {
++	case GPIO_TYPE_V2:
++	case GPIO_TYPE_V2_1:
+ 		bank->gpio_regs = &gpio_regs_v2;
+ 		bank->gpio_type = GPIO_TYPE_V2;
+ 		bank->db_clk = of_clk_get(bank->of_node, 1);
+@@ -677,9 +678,14 @@ static int rockchip_get_bank_data(struct
+ 			clk_disable_unprepare(bank->clk);
+ 			return -EINVAL;
+ 		}
+-	} else {
++		break;
++	case GPIO_TYPE_V1:
+ 		bank->gpio_regs = &gpio_regs_v1;
+ 		bank->gpio_type = GPIO_TYPE_V1;
++		break;
++	default:
++		dev_err(bank->dev, "unsupported version ID: 0x%08x\n", id);
++		return -ENODEV;
+ 	}
+ 
+ 	return 0;
diff --git a/target/linux/rockchip/patches-6.12/035-03-v6.13-gpio-rockchip-support-new-version-GPIO.patch b/target/linux/rockchip/patches-6.12/035-03-v6.13-gpio-rockchip-support-new-version-GPIO.patch
new file mode 100644
index 0000000000..4c10f80e68
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/035-03-v6.13-gpio-rockchip-support-new-version-GPIO.patch
@@ -0,0 +1,34 @@
+From 8bcbd0379c05c66ce2e842c7e8901aa317cdf04e Mon Sep 17 00:00:00 2001
+From: Ye Zhang <ye.zhang at rock-chips.com>
+Date: Tue, 12 Nov 2024 09:54:07 +0800
+Subject: [PATCH] gpio: rockchip: support new version GPIO
+
+Support the next version GPIO controller on SoCs like rk3576.
+
+Signed-off-by: Ye Zhang <ye.zhang at rock-chips.com>
+Reviewed-by: Andy Shevchenko <andriy.shevchenko at linux.intel.com>
+Reviewed-by: Sebastian Reichel <sebastian.reichel at collabora.com>
+Link: https://lore.kernel.org/r/20241112015408.3139996-4-ye.zhang@rock-chips.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski at linaro.org>
+---
+ drivers/gpio/gpio-rockchip.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/gpio/gpio-rockchip.c
++++ b/drivers/gpio/gpio-rockchip.c
+@@ -35,6 +35,7 @@
+ #define GPIO_TYPE_V1		(0)           /* GPIO Version ID reserved */
+ #define GPIO_TYPE_V2		(0x01000C2B)
+ #define GPIO_TYPE_V2_1		(0x0101157C)
++#define GPIO_TYPE_V2_2		(0x010219C8)
+ 
+ static const struct rockchip_gpio_regs gpio_regs_v1 = {
+ 	.port_dr = 0x00,
+@@ -670,6 +671,7 @@ static int rockchip_get_bank_data(struct
+ 	switch (id) {
+ 	case GPIO_TYPE_V2:
+ 	case GPIO_TYPE_V2_1:
++	case GPIO_TYPE_V2_2:
+ 		bank->gpio_regs = &gpio_regs_v2;
+ 		bank->gpio_type = GPIO_TYPE_V2;
+ 		bank->db_clk = of_clk_get(bank->of_node, 1);
diff --git a/target/linux/rockchip/patches-6.12/036-01-v6.13-phy-phy-rockchip-inno-usb2-Handle-failed-extcon-allocatio.patch b/target/linux/rockchip/patches-6.12/036-01-v6.13-phy-phy-rockchip-inno-usb2-Handle-failed-extcon-allocatio.patch
new file mode 100644
index 0000000000..c058600752
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/036-01-v6.13-phy-phy-rockchip-inno-usb2-Handle-failed-extcon-allocatio.patch
@@ -0,0 +1,31 @@
+From 595ad7a336bf21f9d111a033820cd95d70343bd1 Mon Sep 17 00:00:00 2001
+From: Dragan Simic <dsimic at manjaro.org>
+Date: Thu, 5 Sep 2024 10:28:23 +0200
+Subject: [PATCH] phy: phy-rockchip-inno-usb2: Handle failed extcon allocation
+ better
+
+Return the actual error code upon failure to allocate extcon device, instead
+of hardcoding -ENOMEM.  Use dev_err_probe() to also log appropriate messages,
+which is fine because the containing function is used in the probe path.
+
+Helped-by: Heiko Stubner <heiko at sntech.de>
+Reviewed-by: Heiko Stuebner <heiko at sntech.de>
+Signed-off-by: Dragan Simic <dsimic at manjaro.org>
+Link: https://lore.kernel.org/r/cc4995aa3e569be6bc23ca126b41fba82d50eeee.1725524802.git.dsimic@manjaro.org
+Signed-off-by: Vinod Koul <vkoul at kernel.org>
+---
+ drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+@@ -435,7 +435,8 @@ static int rockchip_usb2phy_extcon_regis
+ 						rockchip_usb2phy_extcon_cable);
+ 
+ 		if (IS_ERR(edev))
+-			return -ENOMEM;
++			return dev_err_probe(rphy->dev, PTR_ERR(edev),
++					     "failed to allocate extcon device\n");
+ 
+ 		ret = devm_extcon_dev_register(rphy->dev, edev);
+ 		if (ret) {
diff --git a/target/linux/rockchip/patches-6.12/036-02-v6.13-phy-rockchip-inno-usb2-convert-clock-management-to-bulk.patch b/target/linux/rockchip/patches-6.12/036-02-v6.13-phy-rockchip-inno-usb2-convert-clock-management-to-bulk.patch
new file mode 100644
index 0000000000..7e4fa9b0bf
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/036-02-v6.13-phy-rockchip-inno-usb2-convert-clock-management-to-bulk.patch
@@ -0,0 +1,119 @@
+From 86e2ed4e9a9680013ec9ab7c0428c9b8c5108efe Mon Sep 17 00:00:00 2001
+From: Frank Wang <frank.wang at rock-chips.com>
+Date: Wed, 16 Oct 2024 15:37:10 +0800
+Subject: [PATCH] phy: rockchip: inno-usb2: convert clock management to bulk
+
+Since some Rockchip SoCs (e.g RK3576) have more than one clock,
+this converts the clock management from single to bulk method to
+make the driver more flexible.
+
+Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
+Reviewed-by: Heiko Stuebner <heiko at sntech.de>
+Link: https://lore.kernel.org/r/20241016073713.14133-1-frawang.cn@gmail.com
+Signed-off-by: Vinod Koul <vkoul at kernel.org>
+---
+ drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 45 +++++++++++++++----
+ 1 file changed, 37 insertions(+), 8 deletions(-)
+
+--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+@@ -229,9 +229,10 @@ struct rockchip_usb2phy_port {
+  * @dev: pointer to device.
+  * @grf: General Register Files regmap.
+  * @usbgrf: USB General Register Files regmap.
+- * @clk: clock struct of phy input clk.
++ * @clks: array of phy input clocks.
+  * @clk480m: clock struct of phy output clk.
+  * @clk480m_hw: clock struct of phy output clk management.
++ * @num_clks: number of phy input clocks.
+  * @phy_reset: phy reset control.
+  * @chg_state: states involved in USB charger detection.
+  * @chg_type: USB charger types.
+@@ -246,9 +247,10 @@ struct rockchip_usb2phy {
+ 	struct device	*dev;
+ 	struct regmap	*grf;
+ 	struct regmap	*usbgrf;
+-	struct clk	*clk;
++	struct clk_bulk_data	*clks;
+ 	struct clk	*clk480m;
+ 	struct clk_hw	clk480m_hw;
++	int			num_clks;
+ 	struct reset_control	*phy_reset;
+ 	enum usb_chg_state	chg_state;
+ 	enum power_supply_type	chg_type;
+@@ -310,6 +312,13 @@ static int rockchip_usb2phy_reset(struct
+ 	return 0;
+ }
+ 
++static void rockchip_usb2phy_clk_bulk_disable(void *data)
++{
++	struct rockchip_usb2phy *rphy = data;
++
++	clk_bulk_disable_unprepare(rphy->num_clks, rphy->clks);
++}
++
+ static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw)
+ {
+ 	struct rockchip_usb2phy *rphy =
+@@ -376,7 +385,9 @@ rockchip_usb2phy_clk480m_register(struct
+ {
+ 	struct device_node *node = rphy->dev->of_node;
+ 	struct clk_init_data init;
++	struct clk *refclk = NULL;
+ 	const char *clk_name;
++	int i;
+ 	int ret = 0;
+ 
+ 	init.flags = 0;
+@@ -386,8 +397,15 @@ rockchip_usb2phy_clk480m_register(struct
+ 	/* optional override of the clockname */
+ 	of_property_read_string(node, "clock-output-names", &init.name);
+ 
+-	if (rphy->clk) {
+-		clk_name = __clk_get_name(rphy->clk);
++	for (i = 0; i < rphy->num_clks; i++) {
++		if (!strncmp(rphy->clks[i].id, "phyclk", 6)) {
++			refclk = rphy->clks[i].clk;
++			break;
++		}
++	}
++
++	if (!IS_ERR(refclk)) {
++		clk_name = __clk_get_name(refclk);
+ 		init.parent_names = &clk_name;
+ 		init.num_parents = 1;
+ 	} else {
+@@ -1407,11 +1425,13 @@ static int rockchip_usb2phy_probe(struct
+ 	if (IS_ERR(rphy->phy_reset))
+ 		return PTR_ERR(rphy->phy_reset);
+ 
+-	rphy->clk = devm_clk_get_optional_enabled(dev, "phyclk");
+-	if (IS_ERR(rphy->clk)) {
+-		return dev_err_probe(&pdev->dev, PTR_ERR(rphy->clk),
+-				     "failed to get phyclk\n");
+-	}
++	ret = devm_clk_bulk_get_all(dev, &rphy->clks);
++	if (ret == -EPROBE_DEFER)
++		return dev_err_probe(&pdev->dev, -EPROBE_DEFER,
++				     "failed to get phy clock\n");
++
++	/* Clocks are optional */
++	rphy->num_clks = ret < 0 ? 0 : ret;
+ 
+ 	ret = rockchip_usb2phy_clk480m_register(rphy);
+ 	if (ret) {
+@@ -1419,6 +1439,14 @@ static int rockchip_usb2phy_probe(struct
+ 		return ret;
+ 	}
+ 
++	ret = clk_bulk_prepare_enable(rphy->num_clks, rphy->clks);
++	if (ret)
++		return dev_err_probe(dev, ret, "failed to enable phy clock\n");
++
++	ret = devm_add_action_or_reset(dev, rockchip_usb2phy_clk_bulk_disable, rphy);
++	if (ret)
++		return ret;
++
+ 	if (rphy->phy_cfg->phy_tuning) {
+ 		ret = rphy->phy_cfg->phy_tuning(rphy);
+ 		if (ret)
diff --git a/target/linux/rockchip/patches-6.12/036-03-v6.13-phy-rockchip-inno-usb2-Add-usb2-phys-support-for-rk3576.patch b/target/linux/rockchip/patches-6.12/036-03-v6.13-phy-rockchip-inno-usb2-Add-usb2-phys-support-for-rk3576.patch
new file mode 100644
index 0000000000..d44ab4abbe
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/036-03-v6.13-phy-rockchip-inno-usb2-Add-usb2-phys-support-for-rk3576.patch
@@ -0,0 +1,143 @@
+From 3d7de6e870ece5a32153382df9df6fb87613335e Mon Sep 17 00:00:00 2001
+From: William Wu <william.wu at rock-chips.com>
+Date: Wed, 16 Oct 2024 15:37:13 +0800
+Subject: [PATCH] phy: rockchip: inno-usb2: Add usb2 phys support for rk3576
+
+The RK3576 SoC has two independent USB2.0 PHYs, and each PHY has
+one port. This adds device specific data for it.
+
+Signed-off-by: William Wu <william.wu at rock-chips.com>
+Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
+Reviewed-by: Heiko Stuebner <heiko at sntech.de>
+Link: https://lore.kernel.org/r/20241016073713.14133-4-frawang.cn@gmail.com
+Signed-off-by: Vinod Koul <vkoul at kernel.org>
+---
+ drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 103 ++++++++++++++++++
+ 1 file changed, 103 insertions(+)
+
+--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
++++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+@@ -1524,6 +1524,30 @@ static int rk3128_usb2phy_tuning(struct
+ 				BIT(2) << BIT_WRITEABLE_SHIFT | 0);
+ }
+ 
++static int rk3576_usb2phy_tuning(struct rockchip_usb2phy *rphy)
++{
++	int ret;
++	u32 reg = rphy->phy_cfg->reg;
++
++	/* Deassert SIDDQ to power on analog block */
++	ret = regmap_write(rphy->grf, reg + 0x0010, GENMASK(29, 29) | 0x0000);
++	if (ret)
++		return ret;
++
++	/* Do reset after exit IDDQ mode */
++	ret = rockchip_usb2phy_reset(rphy);
++	if (ret)
++		return ret;
++
++	/* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */
++	ret |= regmap_write(rphy->grf, reg + 0x000c, GENMASK(27, 24) | 0x0900);
++
++	/* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */
++	ret |= regmap_write(rphy->grf, reg + 0x0010, GENMASK(20, 19) | 0x0010);
++
++	return ret;
++}
++
+ static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy)
+ {
+ 	int ret;
+@@ -1952,6 +1976,84 @@ static const struct rockchip_usb2phy_cfg
+ 	{ /* sentinel */ }
+ };
+ 
++static const struct rockchip_usb2phy_cfg rk3576_phy_cfgs[] = {
++	{
++		.reg = 0x0,
++		.num_ports	= 1,
++		.phy_tuning	= rk3576_usb2phy_tuning,
++		.clkout_ctl	= { 0x0008, 0, 0, 1, 0 },
++		.port_cfgs	= {
++			[USB2PHY_PORT_OTG] = {
++				.phy_sus	= { 0x0000, 8, 0, 0, 0x1d1 },
++				.bvalid_det_en	= { 0x00c0, 1, 1, 0, 1 },
++				.bvalid_det_st	= { 0x00c4, 1, 1, 0, 1 },
++				.bvalid_det_clr = { 0x00c8, 1, 1, 0, 1 },
++				.ls_det_en	= { 0x00c0, 0, 0, 0, 1 },
++				.ls_det_st	= { 0x00c4, 0, 0, 0, 1 },
++				.ls_det_clr	= { 0x00c8, 0, 0, 0, 1 },
++				.disfall_en	= { 0x00c0, 6, 6, 0, 1 },
++				.disfall_st	= { 0x00c4, 6, 6, 0, 1 },
++				.disfall_clr	= { 0x00c8, 6, 6, 0, 1 },
++				.disrise_en	= { 0x00c0, 5, 5, 0, 1 },
++				.disrise_st	= { 0x00c4, 5, 5, 0, 1 },
++				.disrise_clr	= { 0x00c8, 5, 5, 0, 1 },
++				.utmi_avalid	= { 0x0080, 1, 1, 0, 1 },
++				.utmi_bvalid	= { 0x0080, 0, 0, 0, 1 },
++				.utmi_ls	= { 0x0080, 5, 4, 0, 1 },
++			}
++		},
++		.chg_det = {
++			.cp_det		= { 0x0080, 8, 8, 0, 1 },
++			.dcp_det	= { 0x0080, 8, 8, 0, 1 },
++			.dp_det		= { 0x0080, 9, 9, 1, 0 },
++			.idm_sink_en	= { 0x0010, 5, 5, 1, 0 },
++			.idp_sink_en	= { 0x0010, 5, 5, 0, 1 },
++			.idp_src_en	= { 0x0010, 14, 14, 0, 1 },
++			.rdm_pdwn_en	= { 0x0010, 14, 14, 0, 1 },
++			.vdm_src_en	= { 0x0010, 7, 6, 0, 3 },
++			.vdp_src_en	= { 0x0010, 7, 6, 0, 3 },
++		},
++	},
++	{
++		.reg = 0x2000,
++		.num_ports	= 1,
++		.phy_tuning	= rk3576_usb2phy_tuning,
++		.clkout_ctl	= { 0x2008, 0, 0, 1, 0 },
++		.port_cfgs	= {
++			[USB2PHY_PORT_OTG] = {
++				.phy_sus	= { 0x2000, 8, 0, 0, 0x1d1 },
++				.bvalid_det_en	= { 0x20c0, 1, 1, 0, 1 },
++				.bvalid_det_st	= { 0x20c4, 1, 1, 0, 1 },
++				.bvalid_det_clr = { 0x20c8, 1, 1, 0, 1 },
++				.ls_det_en	= { 0x20c0, 0, 0, 0, 1 },
++				.ls_det_st	= { 0x20c4, 0, 0, 0, 1 },
++				.ls_det_clr	= { 0x20c8, 0, 0, 0, 1 },
++				.disfall_en	= { 0x20c0, 6, 6, 0, 1 },
++				.disfall_st	= { 0x20c4, 6, 6, 0, 1 },
++				.disfall_clr	= { 0x20c8, 6, 6, 0, 1 },
++				.disrise_en	= { 0x20c0, 5, 5, 0, 1 },
++				.disrise_st	= { 0x20c4, 5, 5, 0, 1 },
++				.disrise_clr	= { 0x20c8, 5, 5, 0, 1 },
++				.utmi_avalid	= { 0x2080, 1, 1, 0, 1 },
++				.utmi_bvalid	= { 0x2080, 0, 0, 0, 1 },
++				.utmi_ls	= { 0x2080, 5, 4, 0, 1 },
++			}
++		},
++		.chg_det = {
++			.cp_det		= { 0x2080, 8, 8, 0, 1 },
++			.dcp_det	= { 0x2080, 8, 8, 0, 1 },
++			.dp_det		= { 0x2080, 9, 9, 1, 0 },
++			.idm_sink_en	= { 0x2010, 5, 5, 1, 0 },
++			.idp_sink_en	= { 0x2010, 5, 5, 0, 1 },
++			.idp_src_en	= { 0x2010, 14, 14, 0, 1 },
++			.rdm_pdwn_en	= { 0x2010, 14, 14, 0, 1 },
++			.vdm_src_en	= { 0x2010, 7, 6, 0, 3 },
++			.vdp_src_en	= { 0x2010, 7, 6, 0, 3 },
++		},
++	},
++	{ /* sentinel */ }
++};
++
+ static const struct rockchip_usb2phy_cfg rk3588_phy_cfgs[] = {
+ 	{
+ 		.reg = 0x0000,
+@@ -2123,6 +2225,7 @@ static const struct of_device_id rockchi
+ 	{ .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs },
+ 	{ .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
+ 	{ .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs },
++	{ .compatible = "rockchip,rk3576-usb2phy", .data = &rk3576_phy_cfgs },
+ 	{ .compatible = "rockchip,rk3588-usb2phy", .data = &rk3588_phy_cfgs },
+ 	{ .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs },
+ 	{}
diff --git a/target/linux/rockchip/patches-6.12/036-04-v6.13-phy-rockchip-usbdp-add-rk3576-device-match-data.patch b/target/linux/rockchip/patches-6.12/036-04-v6.13-phy-rockchip-usbdp-add-rk3576-device-match-data.patch
new file mode 100644
index 0000000000..de0a11fd88
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/036-04-v6.13-phy-rockchip-usbdp-add-rk3576-device-match-data.patch
@@ -0,0 +1,73 @@
+From a76de028c619dd18f89786805bcc7bb4d379ea9f Mon Sep 17 00:00:00 2001
+From: Frank Wang <frank.wang at rock-chips.com>
+Date: Mon, 14 Oct 2024 10:03:42 +0800
+Subject: [PATCH] phy: rockchip: usbdp: add rk3576 device match data
+
+This adds RK3576 device match data support.
+
+Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
+Acked-by: Dragan Simic <dsimic at manjaro.org>
+Reviewed-by: Heiko Stuebner <heiko at sntech.de>
+Link: https://lore.kernel.org/r/20241014020342.15974-2-frawang.cn@gmail.com
+Signed-off-by: Vinod Koul <vkoul at kernel.org>
+---
+ drivers/phy/rockchip/phy-rockchip-usbdp.c | 41 +++++++++++++++++++++++
+ 1 file changed, 41 insertions(+)
+
+--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
++++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
+@@ -1556,6 +1556,43 @@ static const char * const rk_udphy_rst_l
+ 	"init", "cmn", "lane", "pcs_apb", "pma_apb"
+ };
+ 
++static const struct rk_udphy_cfg rk3576_udphy_cfgs = {
++	.num_phys = 1,
++	.phy_ids = { 0x2b010000 },
++	.num_rsts = ARRAY_SIZE(rk_udphy_rst_list),
++	.rst_list = rk_udphy_rst_list,
++	.grfcfg	= {
++		/* u2phy-grf */
++		.bvalid_phy_con		= RK_UDPHY_GEN_GRF_REG(0x0010, 1, 0, 0x2, 0x3),
++		.bvalid_grf_con		= RK_UDPHY_GEN_GRF_REG(0x0000, 15, 14, 0x1, 0x3),
++
++		/* usb-grf */
++		.usb3otg0_cfg		= RK_UDPHY_GEN_GRF_REG(0x0030, 15, 0, 0x1100, 0x0188),
++
++		/* usbdpphy-grf */
++		.low_pwrn		= RK_UDPHY_GEN_GRF_REG(0x0004, 13, 13, 0, 1),
++		.rx_lfps		= RK_UDPHY_GEN_GRF_REG(0x0004, 14, 14, 0, 1),
++	},
++	.vogrfcfg = {
++		{
++			.hpd_trigger	= RK_UDPHY_GEN_GRF_REG(0x0000, 11, 10, 1, 3),
++			.dp_lane_reg    = 0x0000,
++		},
++	},
++	.dp_tx_ctrl_cfg = {
++		rk3588_dp_tx_drv_ctrl_rbr_hbr_typec,
++		rk3588_dp_tx_drv_ctrl_rbr_hbr_typec,
++		rk3588_dp_tx_drv_ctrl_hbr2,
++		rk3588_dp_tx_drv_ctrl_hbr3,
++	},
++	.dp_tx_ctrl_cfg_typec = {
++		rk3588_dp_tx_drv_ctrl_rbr_hbr_typec,
++		rk3588_dp_tx_drv_ctrl_rbr_hbr_typec,
++		rk3588_dp_tx_drv_ctrl_hbr2,
++		rk3588_dp_tx_drv_ctrl_hbr3,
++	},
++};
++
+ static const struct rk_udphy_cfg rk3588_udphy_cfgs = {
+ 	.num_phys = 2,
+ 	.phy_ids = {
+@@ -1603,6 +1640,10 @@ static const struct rk_udphy_cfg rk3588_
+ 
+ static const struct of_device_id rk_udphy_dt_match[] = {
+ 	{
++		.compatible = "rockchip,rk3576-usbdp-phy",
++		.data = &rk3576_udphy_cfgs
++	},
++	{
+ 		.compatible = "rockchip,rk3588-usbdp-phy",
+ 		.data = &rk3588_udphy_cfgs
+ 	},
diff --git a/target/linux/rockchip/patches-6.12/036-05-v6.14-phy-rockchip-naneng-combo-add-rk3576-support.patch b/target/linux/rockchip/patches-6.12/036-05-v6.14-phy-rockchip-naneng-combo-add-rk3576-support.patch
new file mode 100644
index 0000000000..4f466c42d5
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/036-05-v6.14-phy-rockchip-naneng-combo-add-rk3576-support.patch
@@ -0,0 +1,355 @@
+From ba8ad7eece66ac5c579dd8de39efc72770e7cf64 Mon Sep 17 00:00:00 2001
+From: Kever Yang <kever.yang at rock-chips.com>
+Date: Wed, 6 Nov 2024 10:13:57 +0800
+Subject: [PATCH] phy: rockchip-naneng-combo: add rk3576 support
+
+Rockchip RK3576 integrates two naneng-combo PHY, PHY0 is used for
+PCIE and SATA, PHY1 is used for PCIE, SATA and USB3.
+
+This adds device specific data support.
+
+Signed-off-by: Kever Yang <kever.yang at rock-chips.com>
+Signed-off-by: William Wu <william.wu at rock-chips.com>
+Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
+Reviewed-by: Heiko Stuebner <heiko at sntech.de>
+Test-by: Kever Yang <kever.yang at rock-chips.com>
+Link: https://lore.kernel.org/r/20241106021357.19782-2-frawang.cn@gmail.com
+Signed-off-by: Vinod Koul <vkoul at kernel.org>
+---
+ .../rockchip/phy-rockchip-naneng-combphy.c    | 279 ++++++++++++++++++
+ 1 file changed, 279 insertions(+)
+
+--- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
++++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
+@@ -37,6 +37,10 @@
+ #define PHYREG8				0x1C
+ #define PHYREG8_SSC_EN			BIT(4)
+ 
++#define PHYREG10			0x24
++#define PHYREG10_SSC_PCM_MASK		GENMASK(3, 0)
++#define PHYREG10_SSC_PCM_3500PPM	7
++
+ #define PHYREG11			0x28
+ #define PHYREG11_SU_TRIM_0_7		0xF0
+ 
+@@ -61,17 +65,26 @@
+ #define PHYREG16			0x3C
+ #define PHYREG16_SSC_CNT_VALUE		0x5f
+ 
++#define PHYREG17			0x40
++
+ #define PHYREG18			0x44
+ #define PHYREG18_PLL_LOOP		0x32
+ 
++#define PHYREG21			0x50
++#define PHYREG21_RX_SQUELCH_VAL		0x0D
++
+ #define PHYREG27			0x6C
+ #define PHYREG27_RX_TRIM_RK3588		0x4C
+ 
++#define PHYREG30			0x74
++
+ #define PHYREG32			0x7C
+ #define PHYREG32_SSC_MASK		GENMASK(7, 4)
++#define PHYREG32_SSC_DIR_MASK		GENMASK(5, 4)
+ #define PHYREG32_SSC_DIR_SHIFT		4
+ #define PHYREG32_SSC_UPWARD		0
+ #define PHYREG32_SSC_DOWNWARD		1
++#define PHYREG32_SSC_OFFSET_MASK	GENMASK(7, 6)
+ #define PHYREG32_SSC_OFFSET_SHIFT	6
+ #define PHYREG32_SSC_OFFSET_500PPM	1
+ 
+@@ -79,6 +92,7 @@
+ #define PHYREG33_PLL_KVCO_MASK		GENMASK(4, 2)
+ #define PHYREG33_PLL_KVCO_SHIFT		2
+ #define PHYREG33_PLL_KVCO_VALUE		2
++#define PHYREG33_PLL_KVCO_VALUE_RK3576	4
+ 
+ struct rockchip_combphy_priv;
+ 
+@@ -98,6 +112,7 @@ struct rockchip_combphy_grfcfg {
+ 	struct combphy_reg pipe_rxterm_set;
+ 	struct combphy_reg pipe_txelec_set;
+ 	struct combphy_reg pipe_txcomp_set;
++	struct combphy_reg pipe_clk_24m;
+ 	struct combphy_reg pipe_clk_25m;
+ 	struct combphy_reg pipe_clk_100m;
+ 	struct combphy_reg pipe_phymode_sel;
+@@ -587,6 +602,266 @@ static const struct rockchip_combphy_cfg
+ 	.combphy_cfg	= rk3568_combphy_cfg,
+ };
+ 
++static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv)
++{
++	const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
++	unsigned long rate;
++	u32 val;
++
++	switch (priv->type) {
++	case PHY_TYPE_PCIE:
++		/* Set SSC downward spread spectrum */
++		val = FIELD_PREP(PHYREG32_SSC_MASK, PHYREG32_SSC_DOWNWARD);
++		rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32);
++
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true);
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true);
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_pcie, true);
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_pcie, true);
++		break;
++
++	case PHY_TYPE_USB3:
++		/* Set SSC downward spread spectrum */
++		val = FIELD_PREP(PHYREG32_SSC_MASK, PHYREG32_SSC_DOWNWARD);
++		rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32);
++
++		/* Enable adaptive CTLE for USB3.0 Rx */
++		val = readl(priv->mmio + PHYREG15);
++		val |= PHYREG15_CTLE_EN;
++		writel(val, priv->mmio + PHYREG15);
++
++		/* Set PLL KVCO fine tuning signals */
++		rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, BIT(3), PHYREG33);
++
++		/* Set PLL LPF R1 to su_trim[10:7]=1001 */
++		writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12);
++
++		/* Set PLL input clock divider 1/2 */
++		val = FIELD_PREP(PHYREG6_PLL_DIV_MASK, PHYREG6_PLL_DIV_2);
++		rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, val, PHYREG6);
++
++		/* Set PLL loop divider */
++		writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18);
++
++		/* Set PLL KVCO to min and set PLL charge pump current to max */
++		writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11);
++
++		/* Set Rx squelch input filler bandwidth */
++		writel(PHYREG21_RX_SQUELCH_VAL, priv->mmio + PHYREG21);
++
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false);
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false);
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->usb_mode_set, true);
++		break;
++
++	case PHY_TYPE_SATA:
++		/* Enable adaptive CTLE for SATA Rx */
++		val = readl(priv->mmio + PHYREG15);
++		val |= PHYREG15_CTLE_EN;
++		writel(val, priv->mmio + PHYREG15);
++
++		/* Set tx_rterm = 50 ohm and rx_rterm = 43.5 ohm */
++		val = PHYREG7_TX_RTERM_50OHM << PHYREG7_TX_RTERM_SHIFT;
++		val |= PHYREG7_RX_RTERM_44OHM << PHYREG7_RX_RTERM_SHIFT;
++		writel(val, priv->mmio + PHYREG7);
++
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_sata, true);
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_sata, true);
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_sata, true);
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_sata, true);
++		rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_con0_for_sata, true);
++		rockchip_combphy_param_write(priv->pipe_grf, &cfg->pipe_con1_for_sata, true);
++		break;
++
++	default:
++		dev_err(priv->dev, "incompatible PHY type\n");
++		return -EINVAL;
++	}
++
++	rate = clk_get_rate(priv->refclk);
++
++	switch (rate) {
++	case REF_CLOCK_24MHz:
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_24m, true);
++		if (priv->type == PHY_TYPE_USB3 || priv->type == PHY_TYPE_SATA) {
++			/* Set ssc_cnt[9:0]=0101111101 & 31.5KHz */
++			val = FIELD_PREP(PHYREG15_SSC_CNT_MASK, PHYREG15_SSC_CNT_VALUE);
++			rockchip_combphy_updatel(priv, PHYREG15_SSC_CNT_MASK,
++						 val, PHYREG15);
++
++			writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16);
++		} else if (priv->type == PHY_TYPE_PCIE) {
++			/* PLL KVCO tuning fine */
++			val = FIELD_PREP(PHYREG33_PLL_KVCO_MASK, PHYREG33_PLL_KVCO_VALUE_RK3576);
++			rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK,
++						 val, PHYREG33);
++
++			/* Set up rx_pck invert and rx msb to disable */
++			writel(0x00, priv->mmio + PHYREG27);
++
++			/*
++			 * Set up SU adjust signal:
++			 * su_trim[7:0],   PLL KVCO adjust bits[2:0] to min
++			 * su_trim[15:8],  PLL LPF R1 adujst bits[9:7]=3'b011
++			 * su_trim[31:24], CKDRV adjust
++			 */
++			writel(0x90, priv->mmio + PHYREG11);
++			writel(0x02, priv->mmio + PHYREG12);
++			writel(0x57, priv->mmio + PHYREG14);
++
++			writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16);
++		}
++		break;
++
++	case REF_CLOCK_25MHz:
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_25m, true);
++		break;
++
++	case REF_CLOCK_100MHz:
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true);
++		if (priv->type == PHY_TYPE_PCIE) {
++			/* gate_tx_pck_sel length select work for L1SS */
++			writel(0xc0, priv->mmio + PHYREG30);
++
++			/* PLL KVCO tuning fine */
++			val = FIELD_PREP(PHYREG33_PLL_KVCO_MASK, PHYREG33_PLL_KVCO_VALUE_RK3576);
++			rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK,
++						 val, PHYREG33);
++
++			/* Set up rx_trim: PLL LPF C1 85pf R1 1.25kohm */
++			writel(0x4c, priv->mmio + PHYREG27);
++
++			/*
++			 * Set up SU adjust signal:
++			 * su_trim[7:0],   PLL KVCO adjust bits[2:0] to min
++			 * su_trim[15:8],  bypass PLL loop divider code, and
++			 *                 PLL LPF R1 adujst bits[9:7]=3'b101
++			 * su_trim[23:16], CKRCV adjust
++			 * su_trim[31:24], CKDRV adjust
++			 */
++			writel(0x90, priv->mmio + PHYREG11);
++			writel(0x43, priv->mmio + PHYREG12);
++			writel(0x88, priv->mmio + PHYREG13);
++			writel(0x56, priv->mmio + PHYREG14);
++		} else if (priv->type == PHY_TYPE_SATA) {
++			/* downward spread spectrum +500ppm */
++			val = FIELD_PREP(PHYREG32_SSC_DIR_MASK, PHYREG32_SSC_DOWNWARD);
++			val |= FIELD_PREP(PHYREG32_SSC_OFFSET_MASK, PHYREG32_SSC_OFFSET_500PPM);
++			rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32);
++
++			/* ssc ppm adjust to 3500ppm */
++			rockchip_combphy_updatel(priv, PHYREG10_SSC_PCM_MASK,
++						 PHYREG10_SSC_PCM_3500PPM,
++						 PHYREG10);
++		}
++		break;
++
++	default:
++		dev_err(priv->dev, "Unsupported rate: %lu\n", rate);
++		return -EINVAL;
++	}
++
++	if (priv->ext_refclk) {
++		rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true);
++		if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) {
++			val = FIELD_PREP(PHYREG33_PLL_KVCO_MASK, PHYREG33_PLL_KVCO_VALUE_RK3576);
++			rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK,
++						 val, PHYREG33);
++
++			/* Set up rx_trim: PLL LPF C1 85pf R1 2.5kohm */
++			writel(0x0c, priv->mmio + PHYREG27);
++
++			/*
++			 * Set up SU adjust signal:
++			 * su_trim[7:0],   PLL KVCO adjust bits[2:0] to min
++			 * su_trim[15:8],  bypass PLL loop divider code, and
++			 *                 PLL LPF R1 adujst bits[9:7]=3'b101.
++			 * su_trim[23:16], CKRCV adjust
++			 * su_trim[31:24], CKDRV adjust
++			 */
++			writel(0x90, priv->mmio + PHYREG11);
++			writel(0x43, priv->mmio + PHYREG12);
++			writel(0x88, priv->mmio + PHYREG13);
++			writel(0x56, priv->mmio + PHYREG14);
++		}
++	}
++
++	if (priv->enable_ssc) {
++		val = readl(priv->mmio + PHYREG8);
++		val |= PHYREG8_SSC_EN;
++		writel(val, priv->mmio + PHYREG8);
++
++		if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_24MHz) {
++			/* Set PLL loop divider */
++			writel(0x00, priv->mmio + PHYREG17);
++			writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18);
++
++			/* Set up rx_pck invert and rx msb to disable */
++			writel(0x00, priv->mmio + PHYREG27);
++
++			/*
++			 * Set up SU adjust signal:
++			 * su_trim[7:0],   PLL KVCO adjust bits[2:0] to min
++			 * su_trim[15:8],  PLL LPF R1 adujst bits[9:7]=3'b101
++			 * su_trim[23:16], CKRCV adjust
++			 * su_trim[31:24], CKDRV adjust
++			 */
++			writel(0x90, priv->mmio + PHYREG11);
++			writel(0x02, priv->mmio + PHYREG12);
++			writel(0x08, priv->mmio + PHYREG13);
++			writel(0x57, priv->mmio + PHYREG14);
++			writel(0x40, priv->mmio + PHYREG15);
++
++			writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16);
++
++			val = FIELD_PREP(PHYREG33_PLL_KVCO_MASK, PHYREG33_PLL_KVCO_VALUE_RK3576);
++			writel(val, priv->mmio + PHYREG33);
++		}
++	}
++
++	return 0;
++}
++
++static const struct rockchip_combphy_grfcfg rk3576_combphy_grfcfgs = {
++	/* pipe-phy-grf */
++	.pcie_mode_set		= { 0x0000, 5, 0, 0x00, 0x11 },
++	.usb_mode_set		= { 0x0000, 5, 0, 0x00, 0x04 },
++	.pipe_rxterm_set	= { 0x0000, 12, 12, 0x00, 0x01 },
++	.pipe_txelec_set	= { 0x0004, 1, 1, 0x00, 0x01 },
++	.pipe_txcomp_set	= { 0x0004, 4, 4, 0x00, 0x01 },
++	.pipe_clk_24m		= { 0x0004, 14, 13, 0x00, 0x00 },
++	.pipe_clk_25m		= { 0x0004, 14, 13, 0x00, 0x01 },
++	.pipe_clk_100m		= { 0x0004, 14, 13, 0x00, 0x02 },
++	.pipe_phymode_sel	= { 0x0008, 1, 1, 0x00, 0x01 },
++	.pipe_rate_sel		= { 0x0008, 2, 2, 0x00, 0x01 },
++	.pipe_rxterm_sel	= { 0x0008, 8, 8, 0x00, 0x01 },
++	.pipe_txelec_sel	= { 0x0008, 12, 12, 0x00, 0x01 },
++	.pipe_txcomp_sel	= { 0x0008, 15, 15, 0x00, 0x01 },
++	.pipe_clk_ext		= { 0x000c, 9, 8, 0x02, 0x01 },
++	.pipe_phy_status	= { 0x0034, 6, 6, 0x01, 0x00 },
++	.con0_for_pcie		= { 0x0000, 15, 0, 0x00, 0x1000 },
++	.con1_for_pcie		= { 0x0004, 15, 0, 0x00, 0x0000 },
++	.con2_for_pcie		= { 0x0008, 15, 0, 0x00, 0x0101 },
++	.con3_for_pcie		= { 0x000c, 15, 0, 0x00, 0x0200 },
++	.con0_for_sata		= { 0x0000, 15, 0, 0x00, 0x0129 },
++	.con1_for_sata		= { 0x0004, 15, 0, 0x00, 0x0000 },
++	.con2_for_sata		= { 0x0008, 15, 0, 0x00, 0x80c1 },
++	.con3_for_sata		= { 0x000c, 15, 0, 0x00, 0x0407 },
++	/* php-grf */
++	.pipe_con0_for_sata	= { 0x001C, 2, 0, 0x00, 0x2 },
++	.pipe_con1_for_sata	= { 0x0020, 2, 0, 0x00, 0x2 },
++};
++
++static const struct rockchip_combphy_cfg rk3576_combphy_cfgs = {
++	.num_phys = 2,
++	.phy_ids = {
++		0x2b050000,
++		0x2b060000
++	},
++	.grfcfg		= &rk3576_combphy_grfcfgs,
++	.combphy_cfg	= rk3576_combphy_cfg,
++};
++
+ static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv)
+ {
+ 	const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg;
+@@ -779,6 +1054,10 @@ static const struct of_device_id rockchi
+ 		.data = &rk3568_combphy_cfgs,
+ 	},
+ 	{
++		.compatible = "rockchip,rk3576-naneng-combphy",
++		.data = &rk3576_combphy_cfgs,
++	},
++	{
+ 		.compatible = "rockchip,rk3588-naneng-combphy",
+ 		.data = &rk3588_combphy_cfgs,
+ 	},
diff --git a/target/linux/rockchip/patches-6.12/037-01-v6.15-scsi-ufs-core-Export-ufshcd_dme_reset-and.patch b/target/linux/rockchip/patches-6.12/037-01-v6.15-scsi-ufs-core-Export-ufshcd_dme_reset-and.patch
new file mode 100644
index 0000000000..f7419f61be
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/037-01-v6.15-scsi-ufs-core-Export-ufshcd_dme_reset-and.patch
@@ -0,0 +1,66 @@
+From 6b070711b702638622f4b7072e36328a47356576 Mon Sep 17 00:00:00 2001
+From: Shawn Lin <shawn.lin at rock-chips.com>
+Date: Wed, 5 Feb 2025 14:15:54 +0800
+Subject: [PATCH] scsi: ufs: core: Export ufshcd_dme_reset() and
+ ufshcd_dme_enable()
+
+These two APIs will be used by glue driver if they need a different HCE
+process.
+
+Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam at linaro.org>
+Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
+Link: https://lore.kernel.org/r/1738736156-119203-6-git-send-email-shawn.lin@rock-chips.com
+Reviewed-by: Bart Van Assche <bvanassche at acm.org>
+Signed-off-by: Martin K. Petersen <martin.petersen at oracle.com>
+---
+ drivers/ufs/core/ufshcd.c | 6 ++++--
+ include/ufs/ufshcd.h      | 2 ++
+ 2 files changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/ufs/core/ufshcd.c
++++ b/drivers/ufs/core/ufshcd.c
+@@ -4043,7 +4043,7 @@ static int ufshcd_dme_link_startup(struc
+  *
+  * Return: 0 on success, non-zero value on failure.
+  */
+-static int ufshcd_dme_reset(struct ufs_hba *hba)
++int ufshcd_dme_reset(struct ufs_hba *hba)
+ {
+ 	struct uic_command uic_cmd = {
+ 		.command = UIC_CMD_DME_RESET,
+@@ -4057,6 +4057,7 @@ static int ufshcd_dme_reset(struct ufs_h
+ 
+ 	return ret;
+ }
++EXPORT_SYMBOL_GPL(ufshcd_dme_reset);
+ 
+ int ufshcd_dme_configure_adapt(struct ufs_hba *hba,
+ 			       int agreed_gear,
+@@ -4082,7 +4083,7 @@ EXPORT_SYMBOL_GPL(ufshcd_dme_configure_a
+  *
+  * Return: 0 on success, non-zero value on failure.
+  */
+-static int ufshcd_dme_enable(struct ufs_hba *hba)
++int ufshcd_dme_enable(struct ufs_hba *hba)
+ {
+ 	struct uic_command uic_cmd = {
+ 		.command = UIC_CMD_DME_ENABLE,
+@@ -4096,6 +4097,7 @@ static int ufshcd_dme_enable(struct ufs_
+ 
+ 	return ret;
+ }
++EXPORT_SYMBOL_GPL(ufshcd_dme_enable);
+ 
+ static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba)
+ {
+--- a/include/ufs/ufshcd.h
++++ b/include/ufs/ufshcd.h
+@@ -1361,6 +1361,8 @@ extern int ufshcd_system_thaw(struct dev
+ extern int ufshcd_system_restore(struct device *dev);
+ #endif
+ 
++extern int ufshcd_dme_reset(struct ufs_hba *hba);
++extern int ufshcd_dme_enable(struct ufs_hba *hba);
+ extern int ufshcd_dme_configure_adapt(struct ufs_hba *hba,
+ 				      int agreed_gear,
+ 				      int adapt_val);
diff --git a/target/linux/rockchip/patches-6.12/037-02-v6.15-scsi-ufs-rockchip-Initial-support-for-UFS.patch b/target/linux/rockchip/patches-6.12/037-02-v6.15-scsi-ufs-rockchip-Initial-support-for-UFS.patch
new file mode 100644
index 0000000000..c206d9b0a4
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/037-02-v6.15-scsi-ufs-rockchip-Initial-support-for-UFS.patch
@@ -0,0 +1,511 @@
+From d3cbe455d6eb600dee27bf5294f6fe8c2bb06b5f Mon Sep 17 00:00:00 2001
+From: Shawn Lin <shawn.lin at rock-chips.com>
+Date: Wed, 5 Feb 2025 14:15:55 +0800
+Subject: [PATCH] scsi: ufs: rockchip: Initial support for UFS
+
+RK3576 SoC contains a UFS controller, add initial support for it.
+The features are:
+
+  1. support UFS 2.0 features
+  2. High speed up to HS-G3
+  3. 2RX-2TX lanes
+  4. auto H8 entry and exit
+
+Software limitation:
+
+  1. HCE procedure: enable controller->enable intr->dme_reset->dme_enable
+  2. disable unipro timeout values before power mode change
+
+[mkp: fix build errors]
+
+Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
+Link: https://lore.kernel.org/r/1738736156-119203-7-git-send-email-shawn.lin@rock-chips.com
+Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam at linaro.org>
+Reviewed-by: Ulf Hansson <ulf.hansson at linaro.org>
+Signed-off-by: Martin K. Petersen <martin.petersen at oracle.com>
+---
+ drivers/ufs/host/Kconfig        |  12 ++
+ drivers/ufs/host/Makefile       |   1 +
+ drivers/ufs/host/ufs-rockchip.c | 354 ++++++++++++++++++++++++++++++++
+ drivers/ufs/host/ufs-rockchip.h |  90 ++++++++
+ 4 files changed, 457 insertions(+)
+ create mode 100644 drivers/ufs/host/ufs-rockchip.c
+ create mode 100644 drivers/ufs/host/ufs-rockchip.h
+
+--- a/drivers/ufs/host/Kconfig
++++ b/drivers/ufs/host/Kconfig
+@@ -142,3 +142,15 @@ config SCSI_UFS_SPRD
+ 
+ 	  Select this if you have UFS controller on Unisoc chipset.
+ 	  If unsure, say N.
++
++config SCSI_UFS_ROCKCHIP
++	tristate "Rockchip UFS host controller driver"
++	depends on SCSI_UFSHCD_PLATFORM && (ARCH_ROCKCHIP || COMPILE_TEST)
++	help
++	  This selects the Rockchip specific additions to UFSHCD platform driver.
++	  UFS host on Rockchip needs some vendor specific configuration before
++	  accessing the hardware which includes PHY configuration and vendor
++	  specific registers.
++
++	  Select this if you have UFS controller on Rockchip chipset.
++	  If unsure, say N.
+--- a/drivers/ufs/host/Makefile
++++ b/drivers/ufs/host/Makefile
+@@ -10,5 +10,6 @@ obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += uf
+ obj-$(CONFIG_SCSI_UFS_HISI) += ufs-hisi.o
+ obj-$(CONFIG_SCSI_UFS_MEDIATEK) += ufs-mediatek.o
+ obj-$(CONFIG_SCSI_UFS_RENESAS) += ufs-renesas.o
++obj-$(CONFIG_SCSI_UFS_ROCKCHIP) += ufs-rockchip.o
+ obj-$(CONFIG_SCSI_UFS_SPRD) += ufs-sprd.o
+ obj-$(CONFIG_SCSI_UFS_TI_J721E) += ti-j721e-ufs.o
+--- /dev/null
++++ b/drivers/ufs/host/ufs-rockchip.c
+@@ -0,0 +1,354 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Rockchip UFS Host Controller driver
++ *
++ * Copyright (C) 2025 Rockchip Electronics Co., Ltd.
++ */
++
++#include <linux/clk.h>
++#include <linux/gpio.h>
++#include <linux/mfd/syscon.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pm_domain.h>
++#include <linux/pm_wakeup.h>
++#include <linux/regmap.h>
++#include <linux/reset.h>
++
++#include <ufs/ufshcd.h>
++#include <ufs/unipro.h>
++#include "ufshcd-pltfrm.h"
++#include "ufs-rockchip.h"
++
++static int ufs_rockchip_hce_enable_notify(struct ufs_hba *hba,
++					 enum ufs_notify_change_status status)
++{
++	int err = 0;
++
++	if (status == POST_CHANGE) {
++		err = ufshcd_dme_reset(hba);
++		if (err)
++			return err;
++
++		err = ufshcd_dme_enable(hba);
++		if (err)
++			return err;
++
++		return ufshcd_vops_phy_initialization(hba);
++	}
++
++	return 0;
++}
++
++static void ufs_rockchip_set_pm_lvl(struct ufs_hba *hba)
++{
++	hba->rpm_lvl = UFS_PM_LVL_5;
++	hba->spm_lvl = UFS_PM_LVL_5;
++}
++
++static int ufs_rockchip_rk3576_phy_init(struct ufs_hba *hba)
++{
++	struct ufs_rockchip_host *host = ufshcd_get_variant(hba);
++
++	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(PA_LOCAL_TX_LCC_ENABLE, 0x0), 0x0);
++	/* enable the mphy DME_SET cfg */
++	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(MPHY_CFG, 0x0), MPHY_CFG_ENABLE);
++	for (int i = 0; i < 2; i++) {
++		/* Configuration M - TX */
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD, SEL_TX_LANE0 + i), 0x06);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD_EN, SEL_TX_LANE0 + i), 0x02);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_VALUE, SEL_TX_LANE0 + i), 0x44);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE1, SEL_TX_LANE0 + i), 0xe6);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE2, SEL_TX_LANE0 + i), 0x07);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_TASE_VALUE, SEL_TX_LANE0 + i), 0x93);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_BASE_NVALUE, SEL_TX_LANE0 + i), 0xc9);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_POWER_SAVING_CTRL, SEL_TX_LANE0 + i), 0x00);
++		/* Configuration M - RX */
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD, SEL_RX_LANE0 + i), 0x06);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD_EN, SEL_RX_LANE0 + i), 0x00);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE, SEL_RX_LANE0 + i), 0x58);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_PVALUE1, SEL_RX_LANE0 + i), 0x8c);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_PVALUE2, SEL_RX_LANE0 + i), 0x02);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_OPTION, SEL_RX_LANE0 + i), 0xf6);
++		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_POWER_SAVING_CTRL, SEL_RX_LANE0 + i), 0x69);
++	}
++
++	/* disable the mphy DME_SET cfg */
++	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(MPHY_CFG, 0x0), MPHY_CFG_DISABLE);
++
++	ufs_sys_writel(host->mphy_base, 0x80, CMN_REG23);
++	ufs_sys_writel(host->mphy_base, 0xB5, TRSV0_REG14);
++	ufs_sys_writel(host->mphy_base, 0xB5, TRSV1_REG14);
++
++	ufs_sys_writel(host->mphy_base, 0x03, TRSV0_REG15);
++	ufs_sys_writel(host->mphy_base, 0x03, TRSV1_REG15);
++
++	ufs_sys_writel(host->mphy_base, 0x38, TRSV0_REG08);
++	ufs_sys_writel(host->mphy_base, 0x38, TRSV1_REG08);
++
++	ufs_sys_writel(host->mphy_base, 0x50, TRSV0_REG29);
++	ufs_sys_writel(host->mphy_base, 0x50, TRSV1_REG29);
++
++	ufs_sys_writel(host->mphy_base, 0x80, TRSV0_REG2E);
++	ufs_sys_writel(host->mphy_base, 0x80, TRSV1_REG2E);
++
++	ufs_sys_writel(host->mphy_base, 0x18, TRSV0_REG3C);
++	ufs_sys_writel(host->mphy_base, 0x18, TRSV1_REG3C);
++
++	ufs_sys_writel(host->mphy_base, 0x03, TRSV0_REG16);
++	ufs_sys_writel(host->mphy_base, 0x03, TRSV1_REG16);
++
++	ufs_sys_writel(host->mphy_base, 0x20, TRSV0_REG17);
++	ufs_sys_writel(host->mphy_base, 0x20, TRSV1_REG17);
++
++	ufs_sys_writel(host->mphy_base, 0xC0, TRSV0_REG18);
++	ufs_sys_writel(host->mphy_base, 0xC0, TRSV1_REG18);
++
++	ufs_sys_writel(host->mphy_base, 0x03, CMN_REG25);
++
++	ufs_sys_writel(host->mphy_base, 0x03, TRSV0_REG3D);
++	ufs_sys_writel(host->mphy_base, 0x03, TRSV1_REG3D);
++
++	ufs_sys_writel(host->mphy_base, 0xC0, CMN_REG23);
++	udelay(1);
++	ufs_sys_writel(host->mphy_base, 0x00, CMN_REG23);
++
++	usleep_range(200, 250);
++	/* start link up */
++	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(MIB_T_DBG_CPORT_TX_ENDIAN, 0), 0x0);
++	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(MIB_T_DBG_CPORT_RX_ENDIAN, 0), 0x0);
++	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(N_DEVICEID, 0), 0x0);
++	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(N_DEVICEID_VALID, 0), 0x1);
++	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(T_PEERDEVICEID, 0), 0x1);
++	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, 0), 0x1);
++
++	return 0;
++}
++
++static int ufs_rockchip_common_init(struct ufs_hba *hba)
++{
++	struct device *dev = hba->dev;
++	struct platform_device *pdev = to_platform_device(dev);
++	struct ufs_rockchip_host *host;
++	int err;
++
++	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
++	if (!host)
++		return -ENOMEM;
++
++	host->ufs_sys_ctrl = devm_platform_ioremap_resource_byname(pdev, "hci_grf");
++	if (IS_ERR(host->ufs_sys_ctrl))
++		return dev_err_probe(dev, PTR_ERR(host->ufs_sys_ctrl),
++				"Failed to map HCI system control registers\n");
++
++	host->ufs_phy_ctrl = devm_platform_ioremap_resource_byname(pdev, "mphy_grf");
++	if (IS_ERR(host->ufs_phy_ctrl))
++		return dev_err_probe(dev, PTR_ERR(host->ufs_phy_ctrl),
++				"Failed to map mphy system control registers\n");
++
++	host->mphy_base = devm_platform_ioremap_resource_byname(pdev, "mphy");
++	if (IS_ERR(host->mphy_base))
++		return dev_err_probe(dev, PTR_ERR(host->mphy_base),
++				"Failed to map mphy base registers\n");
++
++	host->rst = devm_reset_control_array_get_exclusive(dev);
++	if (IS_ERR(host->rst))
++		return dev_err_probe(dev, PTR_ERR(host->rst),
++				"failed to get reset control\n");
++
++	reset_control_assert(host->rst);
++	udelay(1);
++	reset_control_deassert(host->rst);
++
++	host->ref_out_clk = devm_clk_get_enabled(dev, "ref_out");
++	if (IS_ERR(host->ref_out_clk))
++		return dev_err_probe(dev, PTR_ERR(host->ref_out_clk),
++				"ref_out clock unavailable\n");
++
++	host->rst_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
++	if (IS_ERR(host->rst_gpio))
++		return dev_err_probe(dev, PTR_ERR(host->rst_gpio),
++				"failed to get reset gpio\n");
++
++	err = devm_clk_bulk_get_all_enabled(dev, &host->clks);
++	if (err)
++		return dev_err_probe(dev, err, "failed to enable clocks\n");
++
++	host->hba = hba;
++
++	ufshcd_set_variant(hba, host);
++
++	return 0;
++}
++
++static int ufs_rockchip_rk3576_init(struct ufs_hba *hba)
++{
++	struct device *dev = hba->dev;
++	int ret;
++
++	hba->quirks = UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING;
++
++	/* Enable BKOPS when suspend */
++	hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
++	/* Enable putting device into deep sleep */
++	hba->caps |= UFSHCD_CAP_DEEPSLEEP;
++	/* Enable devfreq of UFS */
++	hba->caps |= UFSHCD_CAP_CLK_SCALING;
++	/* Enable WriteBooster */
++	hba->caps |= UFSHCD_CAP_WB_EN;
++
++	/* Set the default desired pm level in case no users set via sysfs */
++	ufs_rockchip_set_pm_lvl(hba);
++
++	ret = ufs_rockchip_common_init(hba);
++	if (ret)
++		return dev_err_probe(dev, ret, "ufs common init fail\n");
++
++	return 0;
++}
++
++static int ufs_rockchip_device_reset(struct ufs_hba *hba)
++{
++	struct ufs_rockchip_host *host = ufshcd_get_variant(hba);
++
++	gpiod_set_value_cansleep(host->rst_gpio, 1);
++	usleep_range(20, 25);
++
++	gpiod_set_value_cansleep(host->rst_gpio, 0);
++	usleep_range(20, 25);
++
++	return 0;
++}
++
++static const struct ufs_hba_variant_ops ufs_hba_rk3576_vops = {
++	.name = "rk3576",
++	.init = ufs_rockchip_rk3576_init,
++	.device_reset = ufs_rockchip_device_reset,
++	.hce_enable_notify = ufs_rockchip_hce_enable_notify,
++	.phy_initialization = ufs_rockchip_rk3576_phy_init,
++};
++
++static const struct of_device_id ufs_rockchip_of_match[] = {
++	{ .compatible = "rockchip,rk3576-ufshc", .data = &ufs_hba_rk3576_vops },
++	{ },
++};
++MODULE_DEVICE_TABLE(of, ufs_rockchip_of_match);
++
++static int ufs_rockchip_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	const struct ufs_hba_variant_ops *vops;
++	int err;
++
++	vops = device_get_match_data(dev);
++	if (!vops)
++		return dev_err_probe(dev, -ENODATA, "ufs_hba_variant_ops not defined.\n");
++
++	err = ufshcd_pltfrm_init(pdev, vops);
++	if (err)
++		return dev_err_probe(dev, err, "ufshcd_pltfrm_init failed\n");
++
++	return 0;
++}
++
++static void ufs_rockchip_remove(struct platform_device *pdev)
++{
++	ufshcd_pltfrm_remove(pdev);
++}
++
++#ifdef CONFIG_PM
++static int ufs_rockchip_runtime_suspend(struct device *dev)
++{
++	struct ufs_hba *hba = dev_get_drvdata(dev);
++	struct ufs_rockchip_host *host = ufshcd_get_variant(hba);
++
++	clk_disable_unprepare(host->ref_out_clk);
++
++	/* Do not power down the genpd if rpm_lvl is less than level 5 */
++	dev_pm_genpd_rpm_always_on(dev, hba->rpm_lvl < UFS_PM_LVL_5 ? true : false);
++
++	return ufshcd_runtime_suspend(dev);
++}
++
++static int ufs_rockchip_runtime_resume(struct device *dev)
++{
++	struct ufs_hba *hba = dev_get_drvdata(dev);
++	struct ufs_rockchip_host *host = ufshcd_get_variant(hba);
++	int err;
++
++	err = clk_prepare_enable(host->ref_out_clk);
++	if (err) {
++		dev_err(hba->dev, "failed to enable ref_out clock %d\n", err);
++		return err;
++	}
++
++	reset_control_assert(host->rst);
++	udelay(1);
++	reset_control_deassert(host->rst);
++
++	return ufshcd_runtime_resume(dev);
++}
++#endif
++
++#ifdef CONFIG_PM_SLEEP
++static int ufs_rockchip_system_suspend(struct device *dev)
++{
++	struct ufs_hba *hba = dev_get_drvdata(dev);
++	struct ufs_rockchip_host *host = ufshcd_get_variant(hba);
++	int err;
++
++	/*
++	 * If spm_lvl is less than level 5, it means we need to keep the host
++	 * controller in powered-on state. So device_set_awake_path() is
++	 * calling pm core to notify the genpd provider to meet this requirement
++	 */
++	if (hba->spm_lvl < UFS_PM_LVL_5)
++		device_set_awake_path(dev);
++
++	err = ufshcd_system_suspend(dev);
++	if (err) {
++		dev_err(hba->dev, "UFSHCD system susped failed %d\n", err);
++		return err;
++	}
++
++	clk_disable_unprepare(host->ref_out_clk);
++
++	return 0;
++}
++
++static int ufs_rockchip_system_resume(struct device *dev)
++{
++	struct ufs_hba *hba = dev_get_drvdata(dev);
++	struct ufs_rockchip_host *host = ufshcd_get_variant(hba);
++	int err;
++
++	err = clk_prepare_enable(host->ref_out_clk);
++	if (err) {
++		dev_err(hba->dev, "failed to enable ref_out clock %d\n", err);
++		return err;
++	}
++
++	return ufshcd_system_resume(dev);
++}
++#endif
++
++static const struct dev_pm_ops ufs_rockchip_pm_ops = {
++	SET_SYSTEM_SLEEP_PM_OPS(ufs_rockchip_system_suspend, ufs_rockchip_system_resume)
++	SET_RUNTIME_PM_OPS(ufs_rockchip_runtime_suspend, ufs_rockchip_runtime_resume, NULL)
++	.prepare	 = ufshcd_suspend_prepare,
++	.complete	 = ufshcd_resume_complete,
++};
++
++static struct platform_driver ufs_rockchip_pltform = {
++	.probe = ufs_rockchip_probe,
++	.remove = ufs_rockchip_remove,
++	.driver = {
++		.name = "ufshcd-rockchip",
++		.pm = &ufs_rockchip_pm_ops,
++		.of_match_table = ufs_rockchip_of_match,
++	},
++};
++module_platform_driver(ufs_rockchip_pltform);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Rockchip UFS Host Driver");
+--- /dev/null
++++ b/drivers/ufs/host/ufs-rockchip.h
+@@ -0,0 +1,90 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Rockchip UFS Host Controller driver
++ *
++ * Copyright (C) 2025 Rockchip Electronics Co., Ltd.
++ */
++
++#ifndef _UFS_ROCKCHIP_H_
++#define _UFS_ROCKCHIP_H_
++
++#define SEL_TX_LANE0 0x0
++#define SEL_TX_LANE1 0x1
++#define SEL_TX_LANE2 0x2
++#define SEL_TX_LANE3 0x3
++#define SEL_RX_LANE0 0x4
++#define SEL_RX_LANE1 0x5
++#define SEL_RX_LANE2 0x6
++#define SEL_RX_LANE3 0x7
++
++#define VND_TX_CLK_PRD                  0xAA
++#define VND_TX_CLK_PRD_EN               0xA9
++#define VND_TX_LINERESET_PVALUE2        0xAB
++#define VND_TX_LINERESET_PVALUE1        0xAC
++#define VND_TX_LINERESET_VALUE          0xAD
++#define VND_TX_BASE_NVALUE              0x93
++#define VND_TX_TASE_VALUE               0x94
++#define VND_TX_POWER_SAVING_CTRL        0x7F
++#define VND_RX_CLK_PRD                  0x12
++#define VND_RX_CLK_PRD_EN               0x11
++#define VND_RX_LINERESET_PVALUE2        0x1B
++#define VND_RX_LINERESET_PVALUE1        0x1C
++#define VND_RX_LINERESET_VALUE          0x1D
++#define VND_RX_LINERESET_OPTION         0x25
++#define VND_RX_POWER_SAVING_CTRL        0x2F
++#define VND_RX_SAVE_DET_CTRL            0x1E
++
++#define CMN_REG23                       0x8C
++#define CMN_REG25                       0x94
++#define TRSV0_REG08                     0xE0
++#define TRSV1_REG08                     0x220
++#define TRSV0_REG14                     0x110
++#define TRSV1_REG14                     0x250
++#define TRSV0_REG15                     0x134
++#define TRSV1_REG15                     0x274
++#define TRSV0_REG16                     0x128
++#define TRSV1_REG16                     0x268
++#define TRSV0_REG17                     0x12C
++#define TRSV1_REG17                     0x26c
++#define TRSV0_REG18                     0x120
++#define TRSV1_REG18                     0x260
++#define TRSV0_REG29                     0x164
++#define TRSV1_REG29                     0x2A4
++#define TRSV0_REG2E                     0x178
++#define TRSV1_REG2E                     0x2B8
++#define TRSV0_REG3C                     0x1B0
++#define TRSV1_REG3C                     0x2F0
++#define TRSV0_REG3D                     0x1B4
++#define TRSV1_REG3D                     0x2F4
++
++#define MPHY_CFG                        0x200
++#define MPHY_CFG_ENABLE                 0x40
++#define MPHY_CFG_DISABLE                0x0
++
++#define MIB_T_DBG_CPORT_TX_ENDIAN       0xc022
++#define MIB_T_DBG_CPORT_RX_ENDIAN       0xc023
++
++struct ufs_rockchip_host {
++	struct ufs_hba *hba;
++	void __iomem *ufs_phy_ctrl;
++	void __iomem *ufs_sys_ctrl;
++	void __iomem *mphy_base;
++	struct gpio_desc *rst_gpio;
++	struct reset_control *rst;
++	struct clk *ref_out_clk;
++	struct clk_bulk_data *clks;
++	uint64_t caps;
++};
++
++#define ufs_sys_writel(base, val, reg)                                    \
++	writel((val), (base) + (reg))
++#define ufs_sys_readl(base, reg) readl((base) + (reg))
++#define ufs_sys_set_bits(base, mask, reg)                                 \
++	ufs_sys_writel(                                                   \
++		(base), ((mask) | (ufs_sys_readl((base), (reg)))), (reg))
++#define ufs_sys_ctrl_clr_bits(base, mask, reg)                                 \
++	ufs_sys_writel((base),                                            \
++			    ((~(mask)) & (ufs_sys_readl((base), (reg)))), \
++			    (reg))
++
++#endif /* _UFS_ROCKCHIP_H_ */
diff --git a/target/linux/rockchip/patches-6.12/037-03-v6.15-scsi-ufs-rockchip-Fix-devm_clk_bulk_get_all_enabled.patch b/target/linux/rockchip/patches-6.12/037-03-v6.15-scsi-ufs-rockchip-Fix-devm_clk_bulk_get_all_enabled.patch
new file mode 100644
index 0000000000..afeec6f2ec
--- /dev/null
+++ b/target/linux/rockchip/patches-6.12/037-03-v6.15-scsi-ufs-rockchip-Fix-devm_clk_bulk_get_all_enabled.patch
@@ -0,0 +1,26 @@
+From 4fffffd3b13439980d778c58b1f63439287b9fdc Mon Sep 17 00:00:00 2001
+From: Shawn Lin <shawn.lin at rock-chips.com>
+Date: Wed, 26 Feb 2025 14:52:13 +0800
+Subject: [PATCH] scsi: ufs: rockchip: Fix devm_clk_bulk_get_all_enabled()
+ return value
+
+A positive value is for the number of clocks obtained if assigned.
+
+Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
+Link: https://lore.kernel.org/r/1740552733-182527-1-git-send-email-shawn.lin@rock-chips.com
+Signed-off-by: Martin K. Petersen <martin.petersen at oracle.com>
+---
+ drivers/ufs/host/ufs-rockchip.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/ufs/host/ufs-rockchip.c
++++ b/drivers/ufs/host/ufs-rockchip.c
+@@ -171,7 +171,7 @@ static int ufs_rockchip_common_init(stru
+ 				"failed to get reset gpio\n");
+ 
+ 	err = devm_clk_bulk_get_all_enabled(dev, &host->clks);
+-	if (err)
++	if (err < 0)
+ 		return dev_err_probe(dev, err, "failed to enable clocks\n");
+ 
+ 	host->hba = hba;




More information about the lede-commits mailing list