From: Tianling Shen Date: Sat, 13 Sep 2025 11:49:35 +0000 (+0800) Subject: rockchip: backport driver updates for rk3576 X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=0a6ed6db2615f4f531663b34f688bdaf5b7178b3;p=openwrt%2Fstaging%2Fnbd.git rockchip: backport driver updates for rk3576 Backport clk/phy/rng/ufs/usb driver updates for rk3576. Signed-off-by: Tianling Shen Link: https://github.com/openwrt/openwrt/pull/20041 Signed-off-by: Hauke Mehrtens --- 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 +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@0 { +(1) endpoint {...}; + }; + port@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@0 { +(1) endpoint@0 {...}; +(2) endpoint@1 {...}; + }; +(B) port@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 +Link: https://lore.kernel.org/r/87ldyeb5t9.wl-kuninori.morimoto.gx@renesas.com +Signed-off-by: Rob Herring (Arm) +--- + 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@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 + #include + #include + +@@ -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 +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 +Reviewed-by: Manivannan Sadhasivam +Signed-off-by: Cristian Ciocaltea +Link: https://lore.kernel.org/r/20241019-clk_bulk_ena_fix-v4-1-57f108f64e70@collabora.com +Signed-off-by: Stephen Boyd +--- + 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 &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 { 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 +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 +Signed-off-by: Herbert Xu +--- + 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 +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 +Acked-by: Heiko Stuebner +Link: https://lore.kernel.org/r/1738736156-119203-3-git-send-email-shawn.lin@rock-chips.com +Signed-off-by: Ulf Hansson +--- + 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 +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 +Signed-off-by: Heiko Stuebner +Link: https://lore.kernel.org/r/20250210224510.1194963-2-heiko@sntech.de +Signed-off-by: Heiko Stuebner +--- + 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 +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 +Acked-by: "Rob Herring (Arm)" +Link: https://lore.kernel.org/r/20250310-rk3576-scmi-clocks-v1-1-e165deb034e8@collabora.com +Signed-off-by: Heiko Stuebner +--- + 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 +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 +Signed-off-by: Nicolas Frattaroli +Link: https://lore.kernel.org/r/20250502-rk3576-sai-v3-1-376cef19dd7c@collabora.com +Signed-off-by: Heiko Stuebner +--- + 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 +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 +Link: https://lore.kernel.org/r/20250502-rk3576-sai-v3-2-376cef19dd7c@collabora.com +Signed-off-by: Heiko Stuebner +--- + 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 + #include ++#include + + 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 +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 +Link: https://lore.kernel.org/r/20250502-rk3576-sai-v3-3-376cef19dd7c@collabora.com +Signed-off-by: Heiko Stuebner +--- + 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 ++ * ++ * 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 ++#include ++#include ++#include ++#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 +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 +Link: https://lore.kernel.org/r/20250502-rk3576-sai-v3-4-376cef19dd7c@collabora.com +Signed-off-by: Heiko Stuebner +--- + 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 +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 +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 +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 + #include + #include ++#include + #include + #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 +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 +Signed-off-by: Shawn Lin +Reviewed-by: Ulf Hansson +Acked-by: Heiko Stuebner +Link: https://lore.kernel.org/r/1738736156-119203-5-git-send-email-shawn.lin@rock-chips.com +Signed-off-by: Ulf Hansson +--- + 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 + #include + #include + #include +@@ -20,6 +21,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -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 +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 +Suggested-by: Heiko Stuebner +Fixes: 58ebba35ddab ("pmdomain: rockchip: Add smc call to inform firmware") +Signed-off-by: Shawn Lin +Reviewed-by: Heiko Stuebner +Tested-by: Steven Price +Link: https://lore.kernel.org/r/1739926689-151827-1-git-send-email-shawn.lin@rock-chips.com +Signed-off-by: Ulf Hansson +--- + 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 +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 +Fixes: 61eeb9678789 ("pmdomain: rockchip: Check if SMC could be handled by TA") +Signed-off-by: Ulf Hansson +--- + 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 +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 +Reviewed-by: Heiko Stuebner +Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-1-b6e9efbf1015@collabora.com +Signed-off-by: Daniel Lezcano +--- + 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 +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 +[ported to mainline, reworded commit message] +Signed-off-by: Nicolas Frattaroli +Reviewed-by: Heiko Stuebner +Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-3-b6e9efbf1015@collabora.com +Signed-off-by: Daniel Lezcano +--- + 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 +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 +Link: https://lore.kernel.org/r/20250610-rk3576-tsadc-upstream-v6-5-b6e9efbf1015@collabora.com +Signed-off-by: Daniel Lezcano +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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 +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 +Reviewed-by: Andy Shevchenko +Reviewed-by: Sebastian Reichel +Link: https://lore.kernel.org/r/20241112015408.3139996-2-ye.zhang@rock-chips.com +Signed-off-by: Bartosz Golaszewski +--- + 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 +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 +Reviewed-by: Sebastian Reichel +Reviewed-by: Andy Shevchenko +Link: https://lore.kernel.org/r/20241112015408.3139996-3-ye.zhang@rock-chips.com +Signed-off-by: Bartosz Golaszewski +--- + 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 +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 +Reviewed-by: Andy Shevchenko +Reviewed-by: Sebastian Reichel +Link: https://lore.kernel.org/r/20241112015408.3139996-4-ye.zhang@rock-chips.com +Signed-off-by: Bartosz Golaszewski +--- + 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 +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 +Reviewed-by: Heiko Stuebner +Signed-off-by: Dragan Simic +Link: https://lore.kernel.org/r/cc4995aa3e569be6bc23ca126b41fba82d50eeee.1725524802.git.dsimic@manjaro.org +Signed-off-by: Vinod Koul +--- + 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 +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 +Reviewed-by: Heiko Stuebner +Link: https://lore.kernel.org/r/20241016073713.14133-1-frawang.cn@gmail.com +Signed-off-by: Vinod Koul +--- + 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 +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 +Signed-off-by: Frank Wang +Reviewed-by: Heiko Stuebner +Link: https://lore.kernel.org/r/20241016073713.14133-4-frawang.cn@gmail.com +Signed-off-by: Vinod Koul +--- + 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 +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 +Acked-by: Dragan Simic +Reviewed-by: Heiko Stuebner +Link: https://lore.kernel.org/r/20241014020342.15974-2-frawang.cn@gmail.com +Signed-off-by: Vinod Koul +--- + 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 +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 +Signed-off-by: William Wu +Signed-off-by: Frank Wang +Reviewed-by: Heiko Stuebner +Test-by: Kever Yang +Link: https://lore.kernel.org/r/20241106021357.19782-2-frawang.cn@gmail.com +Signed-off-by: Vinod Koul +--- + .../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 +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 +Signed-off-by: Shawn Lin +Link: https://lore.kernel.org/r/1738736156-119203-6-git-send-email-shawn.lin@rock-chips.com +Reviewed-by: Bart Van Assche +Signed-off-by: Martin K. Petersen +--- + 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 +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 +Link: https://lore.kernel.org/r/1738736156-119203-7-git-send-email-shawn.lin@rock-chips.com +Reviewed-by: Manivannan Sadhasivam +Reviewed-by: Ulf Hansson +Signed-off-by: Martin K. Petersen +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#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 +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 +Link: https://lore.kernel.org/r/1740552733-182527-1-git-send-email-shawn.lin@rock-chips.com +Signed-off-by: Martin K. Petersen +--- + 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;